/* ══════════════════════════════════════════════════════════════════════════════
   WaterWiseKids — MOBILE APP EXPERIENCE v3
   Airbnb-inspired: minimal header, search pill, category icons, card feed,
   offer rows, stat chips, section dividers, polished bottom nav.
   Only active at ≤768px — desktop is completely untouched.
   2026-04-07
═══════════════════════════════════════════════════════════════════════════════ */

/* Hidden on desktop */
.mobile-bottom-nav,
.mobile-app-search-header,
.mobile-category-bar,
.mobile-divider { display: none; }

@media (max-width: 768px) {

  /* ═══════════════════════════════════
     GLOBAL
  ═══════════════════════════════════ */
  * { -webkit-tap-highlight-color: transparent; }
  html { scroll-behavior: smooth; -webkit-overflow-scrolling: touch; }

  body {
    padding-bottom: calc(68px + env(safe-area-inset-bottom, 0px));
    background: #fff;
    animation: appFadeIn 0.3s ease-out;
  }

  @keyframes appFadeIn {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  /* ═══════════════════════════════════
     HEADER — Minimal sticky app bar
  ═══════════════════════════════════ */
  /* PROTECTED: hamburger MUST remain display:flex on mobile. Do NOT hide it. */
  .hamburger { display: flex !important; padding: 8px; min-height: 44px; min-width: 44px; align-items: center; justify-content: center; background: transparent; border: none; }
  .nav-links { display: none !important; }
  nav.mobile-open .nav-links {
    display: flex !important;
    position: absolute;
    top: 48px;
    left: 0;
    right: 0;
    flex-direction: column;
    background: white;
    border-bottom: 1px solid #e5e7eb;
    padding: 8px 16px;
    gap: 0;
    z-index: 199;
    box-shadow: 0 4px 12px rgba(0,0,0,0.1);
  }
  /* 2026-08-22 CSS-regression fix, re-pinned 2026-08-30. 158 pages ship
     `<header><nav>` with no `.container` wrapper, so nav spans the full viewport
     and the absolutely positioned open drawer above rendered edge-to-edge
     (x=0, w=390/320) while the `.container`-wrapped pages inset it by the
     container's own side padding. Scoped to bare navs only, so contained pages
     are untouched.
     The inset must equal the shared chrome gutter, which is literal 20px at
     <=768px (see rem-side-gutter fix d38dcbe). It was hardcoded 16px here when
     the container gutter was still 16px; measured 2026-08-30 at 375px the
     contained cohort sat at x=20/right=355 (matching the footer rail) while the
     bare cohort sat at x=16/right=359 — 4px proud of its own footer on both
     sides. Keep this value in step with `header .container` padding. */
  /* `width:auto` is load-bearing: printable-checklist.css / printable-poster.css
     set `width:100%` on the open drawer, which over-constrains left+right and
     would push the panel off the right edge. */
  header > nav.mobile-open .nav-links { left: 20px; right: 20px; width: auto; }

  /* 2026-08-25 mobile-consistency fix. main.css `.nav-links` sets
     `align-items: center` for the HORIZONTAL desktop nav. The open drawer flips
     to `flex-direction: column` but never re-asserted align-items, so every <li>
     shrink-wrapped to its label width (42-129px inside a 288px panel) and the
     rest of each row was dead to taps; "About" measured 42px wide, under the
     44px minimum. Re-assert the cross-axis property instead of relying on the
     flex default. */
  nav.mobile-open .nav-links { align-items: stretch !important; }
  nav.mobile-open .nav-links > li { width: 100%; }

  nav.mobile-open .nav-links > li > a {
    padding: 12px 0;
    border-bottom: 1px solid #f3f4f6;
    display: block;
    width: 100%;
    box-sizing: border-box;
    min-height: 44px;
    color: #374151;
    font-size: 15px;
  }

  header {
    padding: 0 !important;
    background: rgba(255,255,255,0.97) !important;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: none !important;
    box-shadow: none !important;
    position: sticky !important;
    top: 0;
    z-index: 200;
  }

  /* css-regression-checker 2026-08-30: the header gutter was 16px while the footer
     and the body text column both sit at 20px on mobile (m-app.css's general
     `.container` override is 20px !important, and `footer > .container` resets to
     0 on top of `footer{padding:20px}`). Measured 68/68 reps at 390px: logo 16px,
     footer title 20px, body copy 20px — the header was the sole outlier. Align to 20px. */
  header .container {
    padding: 0 20px !important;
  }

  header nav {
    min-height: 48px;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .nav-logo {
    font-size: 0.95rem !important;
    font-weight: 800 !important;
    color: #0369a1 !important;
    display: flex !important;
    align-items: center !important;
    gap: 8px !important;
  }

  .nav-logo img {
    width: 24px !important;
    height: 24px !important;
  }


  /* ═══════════════════════════════════
     HERO — Compact, content-first
  ═══════════════════════════════════ */
  .hero {
    padding: 16px 20px 8px !important;
    background: #fff !important;
    color: #171717 !important;
    text-align: left !important;
  }

  .hero-content {
    max-width: 100% !important;
    margin: 0 !important;
  }

  /* 2026-08-31 visual QA: 1.5rem resolved to 21px at the 14px mobile root —
     exactly the size of the bare section h2s on 20 templates, so the page
     title tied (and on 6 templates lost to) its own subheads. Raised to
     1.6rem so the mobile type scale reads h1 22.4 > h2 21 > h3 18.2. */
  .hero h1 {
    font-size: 1.6rem !important;
    font-weight: 800 !important;
    color: #171717 !important;
    margin-bottom: 6px !important;
    line-height: 1.2 !important;
    letter-spacing: -0.3px !important;
  }

  .hero p {
    font-size: 0.85rem !important;
    color: #737373 !important;
    margin-bottom: 0 !important;
    line-height: 1.4 !important;
  }

  .hero-ctas { display: none !important; }

  /* Page Hero (education hub, etc.)
     2026-08-19 visual QA: `.tools-hero` was the ONLY hero on the site that
     kept its dark desktop gradient on mobile — every other template flattens
     to white here, so /tools/ read as a different site. Added to all three
     rules together: flattening the background WITHOUT repainting the
     descendants leaves white-on-white text (see mobile-hero-cascade-orphans). */
  .page-hero,
  .page-hero--edu,
  .tools-hero {
    padding: 12px 20px 8px !important;
    min-height: auto !important;
    background: #fff !important;
    text-align: left !important;
  }

  /* 2026-08-31 visual QA: this ran 1.35rem = 18.9px while the same pages'
     section h2s rendered 21-28px — the page title was the SMALLEST heading on
     screen. gear.css, advertise.css and for-swim-schools all declare their own
     mobile `h1 { font-size: 2rem }`; this !important had been silently killing
     every one of them. Matched to `.hero h1` above so all mobile heroes agree,
     and the 2px sub-gap raised to the 6px `.hero h1` already uses. */
  .page-hero h1,
  .page-hero--edu h1,
  .tools-hero h1 {
    font-size: 1.6rem !important;
    color: #171717 !important;
    margin-bottom: 6px !important;
    font-weight: 800 !important;
  }

  .page-hero .hero-sub,
  .page-hero--edu .hero-sub,
  .tools-hero p {
    font-size: 0.8rem !important;
    color: #737373 !important;
    margin-bottom: 0 !important;
  }

  /* Breadcrumb — minimal */
  /* css-regression-checker 2026-09-01: this rule is the mobile app-shell's
     breadcrumb treatment (white bar, 0.7rem, on the 20px site rail). It reached
     only the 68 pages that use `<nav class="page-breadcrumb">`. The other 410
     pages ship the SAME bar as an unclassed `<div>` with the desktop styling
     hardcoded inline (`background:#f8fafc; border-bottom:1px solid #e5e7eb;
     padding:10px 24px; font-size:0.85rem`), which no class selector can reach —
     so on every phone those pages showed a grey 11.9px band whose text sat at
     24px while the logo above it and the body copy below it sat at 20px.
     Measured at 390px: class variant 20px/#fff/11px, inline variant 24px/
     #f8fafc/11.9px. The 2026-08-31 pass already registered this variant as the
     site's fourth breadcrumb markup — but only on the min-width rule near the
     end of this file, so the container itself was never restyled.
     `!important` in an author stylesheet outranks a non-important inline
     declaration, so the attribute selector fixes all three properties without
     editing 410 HTML files. Verified: the substring matches exactly 410 divs
     sitewide and zero non-breadcrumb elements. Deliberately NOT requiring
     `[style*="0.85rem"]` the way the 08-31 rule does — 5 of the 410 carry no
     font-size and that qualifier silently drops them.
     See [[unclassed_inline_hero_invisible_to_design_system]],
     [[hardcoded_inset_decouples_from_the_gutter_it_copied]]. */
  .page-breadcrumb,
  div[style*="padding: 10px 24px"] {
    padding: 6px 20px 0 !important;
    background: #fff !important;
    font-size: 0.7rem !important;
  }

  /* css-regression-checker 2026-09-01: `.hub-section` is defined only in the
     per-page <style> block of the 5 hub pages (beginner-swim-lessons,
     toddler-swim-lessons, kids-swim-lessons, statistics, statistics/
     state-of-drowning-prevention) as `padding: 40px 24px`, with no mobile
     override anywhere. Every other full-bleed band on those pages drops to the
     20px mobile rail via `.container`; these sections stayed at 24px, so the
     hub body copy sat 4px inboard of its own header and footer. Measured 24px
     vs a 20px rail at 390px on all 5. */
  .hub-section {
    padding-left: 20px !important;
    padding-right: 20px !important;
  }

  /* ═══════════════════════════════════
     SEARCH PILL — Airbnb style
  ═══════════════════════════════════ */
  .mobile-app-search-header {
    display: block;
    padding: 4px 16px 12px;
    background: #fff;
  }

  .mobile-app-search-pill {
    display: flex;
    align-items: center;
    gap: 12px;
    background: #fff;
    border: 1px solid #e5e5e5;
    border-radius: 32px;
    padding: 12px 18px;
    box-shadow: 0 2px 12px rgba(0,0,0,0.08);
    text-decoration: none;
    color: inherit;
    transition: box-shadow 0.2s;
  }

  .mobile-app-search-pill:active {
    box-shadow: 0 1px 4px rgba(0,0,0,0.06);
  }

  .mobile-app-search-pill svg {
    width: 18px;
    height: 18px;
    color: #0369a1;
    flex-shrink: 0;
  }

  .search-pill-text {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
  }

  .search-pill-title {
    font-size: 0.81rem;
    font-weight: 600;
    color: #171717;
  }

  .search-pill-sub {
    font-size: 0.68rem;
    color: #6b7280;
  }

  /* ═══════════════════════════════════
     CATEGORY BAR — Horizontal scroll icons
  ═══════════════════════════════════ */
  .mobile-category-bar {
    display: flex;
    gap: 0;
    padding: 0 16px;
    overflow-x: auto;
    background: #fff;
    border-bottom: 1px solid #e5e5e5;
    position: sticky;
    top: 48px;
    z-index: 190;
  }

  .mobile-category-bar::-webkit-scrollbar { display: none; }
  .mobile-category-bar { scrollbar-width: none; }

  .mobile-cat-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 10px 16px 8px;
    font-size: 0.6rem;
    font-weight: 600;
    color: #6b7280;
    white-space: nowrap;
    flex-shrink: 0;
    border-bottom: 2px solid transparent;
    transition: all 0.15s;
    text-decoration: none;
  }

  .mobile-cat-item.active {
    color: #171717;
    border-bottom-color: #171717;
  }

  .mobile-cat-item:active { opacity: 0.7; }

  .mobile-cat-item svg {
    width: 24px;
    height: 24px;
    stroke-width: 1.5;
  }

  /* ═══════════════════════════════════
     STATS BAR — Horizontal chip pills
  ═══════════════════════════════════ */
  .stats-bar {
    background: #fff !important;
    padding: 12px 0 12px 20px !important;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  .stats-bar::-webkit-scrollbar { display: none; }
  .stats-bar { scrollbar-width: none; }

  .stats-bar-inner {
    display: flex !important;
    gap: 10px !important;
    flex-wrap: nowrap !important;
    justify-content: flex-start !important;
    padding-right: 20px;
  }

  .stat-item {
    background: #e0f2fe;
    padding: 12px 20px;
    border-radius: 14px;
    min-width: 110px;
    flex-shrink: 0;
    text-align: center;
  }

  .stat-item .stat-number,
  .stat-card .stat-number {
    color: #075985 !important;
    font-size: 1.25rem !important;
    font-weight: 800 !important;
  }

  .stat-item .stat-label,
  .stat-card .stat-label {
    color: #0369a1 !important;
    font-size: 0.62rem !important;
    font-weight: 600 !important;
    margin-top: 2px;
  }

  /* ---- 2026-08-22 visual QA: the chip-pill treatment above only matches the
     homepage markup (.stats-bar > .stats-bar-inner > .stat-item). /teens/ uses
     a second variant — .stats-bar > .stat > .stat-value + .stat-label — with
     NO .stats-bar-inner and NO .stat-item, so it received only the bare
     `background:#fff` from .stats-bar and kept its desktop grid. Rendered at
     390px the teens hero stats block was 326px tall (vs 98px on the homepage):
     four full-width rows of 25.2px values with no chip, no grouping and a
     rgba(255,255,255,0.1) background that is invisible once .hero is flattened
     to white. Same defect family as .stat-item vs .stat elsewhere — the class
     the stylesheet targets is not the class the page ships. ---- */
  .stats-bar > .stat {
    background: #e0f2fe;
    padding: 12px 14px;
    border-radius: 14px;
    text-align: center;
    justify-content: center;
  }

  .stats-bar > .stat .stat-value {
    color: #075985 !important;
    font-size: 1.15rem !important;
    font-weight: 800 !important;
    margin-bottom: 2px !important;
    line-height: 1.2 !important;
  }

  .stats-bar > .stat .stat-label {
    color: #0369a1 !important;
    font-weight: 600 !important;
    opacity: 1 !important;
    /* Deliberately NO font-size here: the shared "11px floor" block below
       (.stat-label { font-size: 11px !important }) must win. An earlier draft
       of this rule mirrored .stat-item's 0.62rem, which is 8.68px against the
       14px mobile root and, being the more specific selector, silently beat
       the floor. */
  }

  /* Two-up grid + symmetric padding, scoped so the homepage scroll-strip
     variant (which needs the left-only padding) is untouched. */
  .stats-bar:has(> .stat) {
    display: grid !important;
    grid-template-columns: repeat(2, 1fr) !important;
    gap: 10px !important;
    padding: 12px 20px !important;
    margin-top: 16px !important;
    overflow-x: visible;
  }

  /* ═══════════════════════════════════
     SECTION DIVIDERS
  ═══════════════════════════════════ */
  .mobile-divider {
    display: block;
    height: 8px;
    background: #fafafa;
    border: none;
    margin: 0;
  }

  /* ═══════════════════════════════════
     SECTION HEADERS
  ═══════════════════════════════════ */
  .section-header {
    text-align: left !important;
    margin-bottom: 16px !important;
    padding: 0 4px;
  }

  .section-header h2 {
    font-size: 1.25rem !important;
    font-weight: 800 !important;
    letter-spacing: -0.3px;
  }

  .section-header p {
    font-size: 0.81rem !important;
    margin: 0 !important;
  }

  .section-title {
    font-size: 1.25rem !important;
    font-weight: 800 !important;
    letter-spacing: -0.3px;
    margin-bottom: 16px !important;
  }

  /* ═══════════════════════════════════
     CONTENT SECTIONS — Clean spacing
  ═══════════════════════════════════ */
  .what-we-offer,
  .featured-articles,
  .about-snippet {
    padding: 20px !important;
    background: #fff;
  }

  /* ═══════════════════════════════════
     OFFER CARDS — Row layout with chevron
     (iOS Settings style)
  ═══════════════════════════════════ */
  .offer-cards {
    grid-template-columns: 1fr !important;
    gap: 12px !important;
  }

  .offer-card {
    display: block !important;
    background: #fff !important;
    border: 1px solid #f5f5f5 !important;
    border-radius: 16px !important;
    padding: 20px !important;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04) !important;
    transition: transform 0.1s !important;
  }

  .offer-card:active { transform: scale(0.98); }

  .offer-card:hover {
    transform: none !important;
    box-shadow: 0 1px 2px rgba(0,0,0,0.04) !important;
    border-color: #f5f5f5 !important;
  }

  .offer-icon {
    width: 48px !important;
    height: 48px !important;
    border-radius: 14px;
    background: #e0f2fe;
    display: flex !important;
    align-items: center;
    justify-content: center;
    font-size: 1.4rem !important;
    margin-bottom: 12px !important;
    flex-shrink: 0;
  }

  .offer-card h3 {
    font-size: 1.05rem !important;
    margin-bottom: 6px !important;
  }

  .offer-card p {
    font-size: 0.9rem !important;
    color: #525252 !important;
    margin-bottom: 14px !important;
    line-height: 1.5 !important;
  }

  /* Hide the original CTA button in offer cards */
  .offer-card .btn,
  .offer-card .offer-cta {
    display: none !important;
  }

  .offer-card a {
    display: inline-block !important;
    color: #0369a1 !important;
    font-weight: 600 !important;
    font-size: 0.9rem !important;
  }

  /* ---- Trailing chevron lands at the LEFT rail — 2026-09-06 ----------------
     The section header above says "Row layout with chevron (iOS Settings
     style)", and m-app.js appends the chevron with
     `margin-left:auto; flex-shrink:0; display:flex`, which is the auto-margin
     idiom for pushing a trailing item to the right of a flex ROW.

     `.offer-card` is `display: block !important` (four rules up), so the
     appended span is a full-width block-level flex container: `margin-left`
     resolves against zero free space and is inert, and the 16px SVG renders at
     the card's LEFT rail on its own line directly under the "Explore Guides ->"
     link — it reads as a stray glyph, not an affordance. Measured on
     / at 390px: span 268 x 16 at x=61 (the card's own content-box left edge),
     198 x 16 at x=61 at 320px; the card content box ends at 329 / 259.

     Giving the span an explicit 16px width lets the auto margin do the job it
     was written to do. `!important` is required and is not defensive padding:
     m-app.js sets `margin-left` and `display` as INLINE styles, which outrank
     any un-flagged declaration in this sheet. Height, line position and the
     card's own padding are untouched — only the chevron's x moves. */
  .mobile-offer-arrow {
    display: flex !important;
    width: 16px !important;
    margin-left: auto !important;
    margin-right: 0 !important;
  }

  /* ═══════════════════════════════════
     ARTICLE CARDS — Large image, clean
  ═══════════════════════════════════ */
  .articles-grid {
    grid-template-columns: 1fr !important;
    gap: 20px !important;
  }

  .article-card {
    background: #fff !important;
    border: none !important;
    border-radius: 16px !important;
    overflow: hidden;
    box-shadow: none !important;
    transition: transform 0.15s !important;
  }

  .article-card:active { transform: scale(0.98); }

  .article-card:hover {
    transform: none !important;
    box-shadow: none !important;
    border-color: transparent !important;
  }

  .article-card-image {
    height: 200px !important;
    border-radius: 16px !important;
    overflow: hidden;
  }

  .article-card-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .article-card-body {
    padding: 14px 4px 4px !important;
  }

  .article-card-category {
    font-size: 0.68rem !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
    letter-spacing: 0.04em !important;
    color: #0369a1 !important;
    background: none !important;
    padding: 0 !important;
    border: none !important;
    border-radius: 0 !important;
    margin-bottom: 4px !important;
    display: inline-block;
  }

  .article-card-title {
    font-size: 0.94rem !important;
    font-weight: 700 !important;
    line-height: 1.3 !important;
    margin-bottom: 4px !important;
    color: #171717 !important;
  }

  .article-card-excerpt {
    font-size: 0.81rem !important;
    color: #737373 !important;
    line-height: 1.4 !important;
    margin-bottom: 0 !important;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* Hide "Read Guide →" — whole card is tappable */
  .article-card-link { display: none !important; }

  /* ═══════════════════════════════════
     SEARCH BAR (education hub)
  ═══════════════════════════════════ */
  .search-filter-bar {
    padding: 12px 0 !important;
    border: none !important;
    margin-top: 0 !important;
  }

  .search-box input {
    border-radius: 24px !important;
    padding: 12px 40px 12px 42px !important;
    font-size: 0.88rem !important;
    background: #f5f5f5 !important;
    border: 1px solid #e5e5e5 !important;
    box-shadow: none !important;
  }

  .search-box input:focus {
    border-color: #0284c7 !important;
    background: #fff !important;
    box-shadow: 0 0 0 3px rgba(2,132,199,0.1) !important;
  }

  .category-filters {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    padding-bottom: 4px;
    scrollbar-width: none;
  }

  .category-filters::-webkit-scrollbar { display: none; }

  .cat-btn {
    border-radius: 20px !important;
    padding: 7px 16px !important;
    font-size: 0.75rem !important;
    font-weight: 600 !important;
    white-space: nowrap;
    border: 1px solid #e5e5e5 !important;
    background: #fff !important;
    color: #525252 !important;
    transition: all 0.15s !important;
  }

  .cat-btn.active {
    background: #171717 !important;
    color: #fff !important;
    border-color: #171717 !important;
  }

  /* Sticky search */
  .search-filter-bar.sticky {
    position: fixed;
    top: 48px;
    left: 0;
    right: 0;
    z-index: 199;
    background: rgba(255,255,255,0.98) !important;
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    padding: 10px 16px 6px !important;
    box-shadow: 0 1px 6px rgba(0,0,0,0.06);
  }

  .search-sticky-spacer { display: none; }
  .search-sticky-spacer.active { display: block; }

  /* ═══════════════════════════════════
     NEWSLETTER — Rounded card
  ═══════════════════════════════════ */
  .newsletter-section,
  .newsletter,
  section[class*="newsletter"] {
    margin: 16px 20px !important;
    border-radius: 20px !important;
    padding: 24px 22px !important;
    background: #e0f2fe !important;
    border: none !important;
  }

  /* 2026-09-05 visual QA: the `margin: 16px 20px` above assumes a full-bleed
     parent — true on 81 of the 82 pages carrying a newsletter block, where the
     card renders at x=20 w=350 exactly like every other card. On the education
     hub the block sits inside `.articles-section`, which already supplies the
     20px rail, so the margin stacked and the card rendered at x=40 w=310 while
     the three `.article-card` siblings directly above it sat at x=20 w=350 — a
     20px step between identical cards on one screen. Same stacked-gutter shape
     as `footer > .container` and `.page-hero > .container`. */
  .articles-section > .newsletter-section {
    margin-left: 0 !important;
    margin-right: 0 !important;
  }

  .newsletter-section h2,
  .newsletter h2,
  section[class*="newsletter"] h2 {
    font-size: 1rem !important;
    font-weight: 800 !important;
    text-align: left !important;
    color: #171717 !important;
  }

  .newsletter-section p,
  .newsletter p {
    font-size: 0.75rem !important;
    text-align: left !important;
    color: #525252 !important;
  }

  .newsletter-section input[type="email"],
  .newsletter input[type="email"],
  section[class*="newsletter"] input[type="email"] {
    border-radius: 24px !important;
    padding: 11px 16px !important;
    font-size: 0.81rem !important;
    border: 1px solid #e5e5e5 !important;
    background: #fff !important;
  }

  .newsletter-section button[type="submit"],
  .newsletter button[type="submit"],
  section[class*="newsletter"] button[type="submit"] {
    border-radius: 24px !important;
    padding: 11px 18px !important;
    font-size: 0.81rem !important;
    font-weight: 700 !important;
  }

  /* ═══════════════════════════════════
     B2B CALLOUT
  ═══════════════════════════════════ */
  .b2b-callout,
  section[class*="b2b"] {
    margin: 0 20px 16px !important;
    border-radius: 20px !important;
    padding: 24px 22px !important;
  }

  /* ═══════════════════════════════════
     PROGRAMS GRID
  ═══════════════════════════════════ */
  .state-grid,
  .programs-grid {
    display: grid !important;
    grid-template-columns: repeat(2, 1fr) !important;
    gap: 12px !important;
  }

  .state-card,
  .program-card {
    border-radius: 16px !important;
    padding: 16px !important;
    border: none !important;
    box-shadow: 0 1px 3px rgba(0,0,0,0.04) !important;
  }

  /* ═══════════════════════════════════
     BOTTOM NAVIGATION BAR
  ═══════════════════════════════════ */
  .mobile-bottom-nav {
    display: flex;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 300;

    /* Reset the desktop <nav> box that main.css applies to every bare `nav`
       element (main.css:530 `nav { height: 72px; gap: var(--spacing-lg) }` and
       main.css:1574 `@media (max-width:768px) nav { flex-wrap: wrap }`).
       This bar is a <nav>, so it inherited all three:
         - height: 72px forced the bar 12px taller than its 60px content, so the
           `body { padding-bottom: 68px }` gutter below under-reserved by 4px and
           the last 4px of the footer sat underneath the bar (measured on 140 of
           154 sampled pages; the printable templates, which never load main.css,
           already rendered the correct 60px).
         - flex-wrap: wrap + a 16px row-gap made a fixed-height bar able to wrap
           to a second row that the 72px height would then clip.
         - gap: var(--spacing-lg) added 16px of column gap, eating horizontal
           room on the 5-item strip at 320px.
       Class beats element, so no !important is needed. Found by rendering. */
    height: auto;
    flex-wrap: nowrap;
    gap: 0;
    background: rgba(255,255,255,0.98);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-top: 1px solid rgba(0,0,0,0.06);
    box-shadow: 0 -1px 8px rgba(0,0,0,0.04);
    padding: 6px 0 calc(6px + env(safe-area-inset-bottom, 0px));
    /* 2026-08-26 visual QA: the bar used `space-around` with intrinsically-sized
       items, so the five icons landed at uneven centres — measured 77.5/84/79.5/73px
       apart at 390px and 154.5/160/154.5/149px at 768px (11px of ragged rhythm in
       a fixed bar that appears on every page). The equal-column form was already
       proven correct in the @media (max-width:360px) block below, where it was
       introduced to cure a 320px overflow; it was simply never promoted to the
       whole mobile range. `space-between` + `flex:1 1 0` gives five identical
       tracks, so the icons sit on an exact 1/5 rhythm at any width. */
    justify-content: space-between;
    align-items: center;
  }

  .mobile-bottom-nav a {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    text-decoration: none;
    color: #6b7280;
    font-size: 0.62rem;
    font-weight: 500;
    padding: 4px 12px 2px;
    min-height: 44px;
    /* Equal tracks — see the justify-content note above. min-width:0 lets a long
       label ("Lessons") shrink instead of stealing width from its neighbours. */
    flex: 1 1 0;
    min-width: 0;
    position: relative;
    transition: color 0.2s;
  }

  .mobile-bottom-nav a.active {
    color: #0369a1;
    font-weight: 700;
  }

  .mobile-bottom-nav a.active::after {
    content: '';
    position: absolute;
    bottom: -1px;
    left: 50%;
    transform: translateX(-50%);
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: #0284c7;
  }

  .mobile-bottom-nav svg {
    width: 24px;
    height: 24px;
    stroke-width: 1.6;
    transition: transform 0.15s;
  }

  .mobile-bottom-nav a.active svg {
    stroke-width: 2.2;
    transform: scale(1.05);
  }

  .mobile-bottom-nav span { display: block; }

  /* ═══════════════════════════════════
     FOOTER — Minimal
  ═══════════════════════════════════ */
  footer {
    background: #fff !important;
    padding: 20px !important;
    text-align: center;
  }

  footer .footer-links { display: none !important; }

  footer .footer-bottom {
    font-size: 0.68rem;
  }

  /* The white mobile footer above inherits the text colours that main.css /
     printable-*.css set for the DARK navy desktop footer (#e0f2fe, #93c5fd,
     #bae6fd, #7dd3fc). On white those land at 1.1-1.8:1 contrast, i.e.
     effectively invisible. Repaint the whole footer for a light background.
     Hex fallbacks are required: printables load no main.css, so the custom
     properties are undefined there. */
  footer,
  footer .footer-title {
    color: var(--gray-800, #1f2937) !important;
  }

  footer .footer-tagline,
  footer .footer-contact,
  footer .footer-bottom,
  footer .footer-bottom p {
    color: var(--gray-500, #6b7280) !important;
  }

  footer .footer-contact a,
  footer .footer-bottom a,
  footer .footer-links a {
    color: var(--blue-700, #0369a1) !important;
  }

  footer .footer-bottom {
    border-top-color: var(--gray-200, #e5e7eb) !important;
  }

  /* ═══════════════════════════════════
     GENERAL OVERRIDES
  ═══════════════════════════════════ */
  .container {
    padding-left: 20px !important;
    padding-right: 20px !important;
  }

  /* 2026-08-25 css-regression sweep: `footer` above already sets
     `padding:20px !important`, so the general `.container` gutter stacked on top
     of it and inset the footer column to 40px per side on the 644 `.container`-
     wrapped pages, while the 84 printables (whose footer wrapper carries no
     `.container` class) sat at 20px. Cancel the second gutter so every template
     renders the same 20px footer inset. */
  footer > .container {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }

  /* 2026-09-05 visual QA: the same stacked-gutter defect as `footer > .container`
     immediately above. `.page-hero` / `.tools-hero` set
     `padding: 12px 20px 8px !important`, and the general `.container` rule adds
     another 20px, so on the four pages whose hero wraps its content in a
     `.container` — /education/, /education.html, /swimmers-hub/, /tools/ — the H1
     and its sub rendered at x=40 while every h2 and paragraph below them sat at
     x=20, a visible 20px step in the page's left edge. The other 188
     `.page-hero` pages put their content directly in the hero and already render
     at 20px, so this brings the four outliers into line rather than moving the
     rail. `.page-hero--edu` / `.page-hero--hub` are additional classes on
     elements that also carry `.page-hero`, so they are covered here.

     2026-09-18 mobile-consistency: bare `.hero` was MISSED by the 09-05 pass.
     The hero ships under several class names and that selector list enumerated
     only the two in front of it. `.hero` above sets
     `padding: 16px 20px 8px !important`, so the one page of eight whose hero
     wraps a `.container` - /special-needs-swimming.html - rendered its H1 and
     sub at x=40 against a 20px body and footer rail. The other seven put their
     content in `.hero-content` and already read 20. The remaining hero class
     names were censused at 390 at the same time: `.fwsp-hero` and `.pbsc-hero`
     do wrap a `.container` but set no horizontal padding of their own, so they
     measure 20 and are deliberately NOT listed here. */
  .page-hero > .container,
  .tools-hero > .container,
  .hero > .container {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }

  /* 2026-09-20 mobile-consistency: THREE more nestings of the same stacked
     gutter, and between them they are the largest instance of this defect class
     yet found - 122 + 3 + 1 pages. The rule immediately above cancels a
     `.container` nested INSIDE a hero, with a `>` combinator. These are the
     shapes that combinator cannot reach:

     (a) THE NESTING INVERTED - 122 local landing / root article pages put the
         hero INSIDE the page's own `.container`, not the other way round. The
         `.container` supplies the 20px rail and `.page-hero.compact` adds its
         own 20 on top, so the kicker, H1 and lead rendered at x=40 while the
         header logo, the sibling `.tldr-box`, the article body below and the
         footer all sat at 20. Verified by eye at 390: a clear 20px step.

     (b) THREE-DEEP - the 3 photo-hero pages nest
         `.page-hero > .page-hero-content > .container` (article.css:524 adds
         the overlay content layer). Hero 20 + content layer 1rem + container 20
         stacked to x=54, the largest single inset measured on this site.

     (c) THE HOMEPAGE - `.what-we-offer` and `.featured-articles` (and
         `.featured-programs` at 16) each wrap their body in a `.container`, so
         their section headers rendered at 44/44/40 while the sibling
         `.about-snippet`, the H1 and the lead all sat at 20. `.about-snippet`
         is the one homepage section with no inner `.container` and it was
         already correct - that sibling disagreement is what identified it.

     In every case the OUTERMOST box already sits on the 20px rail, so the fix
     is to cancel each nested gutter rather than move the rail. Verified inert
     above the 768 cap: 834 / 1280 / 1440 all measure identically before and
     after. Residue after this rule: the two homepage section headers land at 24
     rather than 20 because `.section-header` carries its own 4px - below the
     visible-step threshold, left alone deliberately. */
  .container > .page-hero,
  .page-hero > .page-hero-content,
  .page-hero-content > .container,
  .what-we-offer > .container,
  .featured-articles > .container,
  .featured-programs > .container {
    padding-left: 0 !important;
    padding-right: 0 !important;
  }

  .btn {
    border-radius: 24px !important;
    font-weight: 700 !important;
  }

  .grid-2, .grid-3, .grid-4 {
    grid-template-columns: 1fr !important;
    gap: 16px !important;
  }

  /* Back-to-top above bottom nav */
  .back-to-top,
  [class*="back-to-top"],
  a[href="#top"],
  .scroll-top-btn,
  #scrollTopBtn {
    bottom: calc(76px + env(safe-area-inset-bottom, 0px)) !important;
    border-radius: 50% !important;
  }

} /* end @media (max-width: 768px) */

/* ═══════════════════════════════════════════════════════════════════════════════
   MOBILE FORM CONTROLS + TAP TARGETS
   (mobile-consistency-checker, 2026-08-15)

   m-app.css is injected at runtime by main.js at <=768px, so it loads AFTER
   main.css and its !important rules beat the sitewide iOS anti-zoom pin added
   on 2026-08-14. Two controls still resolved below 16px and made iOS Safari
   auto-zoom the viewport on focus:
       .search-box input                     0.88rem -> 12.3px  (state directory,
                                                                 education hub,
                                                                 swimmers hub)
       .newsletter* input[type="email"]      0.81rem -> 11.3px  (every newsletter
                                                                 signup sitewide)
   Because this file has no static <link> in any HTML page, plain-grep audits
   never saw it. Pin the controls here, after those rules, so the cascade lands
   on 16px. If a future rule sets a mobile input below 16px, mirror it here too.

   Also lifts sub-44px tap targets to the 44px minimum.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  .search-box input,
  .search-controls input,
  .newsletter-section input[type="email"],
  .newsletter input[type="email"],
  section[class*="newsletter"] input[type="email"],
  input[type='text'],
  input[type='email'],
  input[type='password'],
  input[type='number'],
  input[type='tel'],
  input[type='date'],
  input[type='url'],
  input[type='search'],
  textarea,
  select {
    font-size: 16px !important;
  }

  /* Newsletter submit sat at 39px */
  .newsletter-section button[type="submit"],
  .newsletter button[type="submit"],
  section[class*="newsletter"] button[type="submit"] {
    min-height: 44px !important;
  }

  /* State-directory result cards: 13 school rows x 2 buttons per page,
     rendering at 34px and 28px tall. */
  .visit-site-btn,
  .review-btn {
    min-height: 44px !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
  }

  /* Education-hub category filter pills: 95 controls per page rendering
     28px tall with 10.5px labels (0.75rem at the 14px mobile root).
     They live in a nowrap horizontal scroller, so a taller, larger-text
     pill only grows the strip - it cannot cause horizontal overflow. */
  .cat-btn {
    min-height: 44px !important;
    font-size: 13px !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
  }

  .scroll-top-btn,
  #scrollTopBtn {
    min-height: 44px !important;
    min-width: 44px !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   MOBILE TAP TARGETS + MINIMUM READABLE TEXT — ROUND 2
   (mobile-consistency-checker, 2026-08-16)

   Measured in a real 390x844 iOS-UA render, not by grepping CSS. The 2026-08-15
   pass fixed the newsletter/search/directory controls it knew about; a full
   render sweep across 18 template types surfaced a second tier that the static
   audits could not see because the heights come from padding + line-height
   resolved against the 14px mobile root, never from an explicit height rule.

   Sub-44px controls found (Apple HIG minimum is 44x44):
     .print-btn                33px tall  - the ONLY CTA on 74 printable pages
     .search-button            33px       - aquatic jobs board search
     .btn.btn-primary (search) 36px       - 56 state directory pages
     button[type=submit]       39px       - /swim-schools.html search
     .button                   39px       - 128 pages
     .button-secondary         43px       - 127 pages
     .pill                     30px       - jobs board filters
     input / select            38px       - jobs board form
     .back-link                20px       - 177 pages
     .breadcrumb a             11-15px    - 82 pages (worst target on the site)
     .nav-logo                 24px       - 710 pages (header home link)

   Text resolving under 11px (unreadable without pinch-zoom):
     .stat-label 8.7px, .article-card-category 9.5px, .search-pill-sub 9.5px,
     breadcrumb 9.8px, .tag 10.1px, .footer-bottom 10.2px, .school-chain /
     .kicker / newsletter copy 10.5px, .meta-chip 10.9px.

   Bottom-nav and category-strip labels are held at 10px rather than 11px: they
   sit in fixed 5-across / nowrap-scroller strips where 11px reflows the row.
   Every rule below only ever GROWS a box, and all are inside horizontal
   scrollers or full-width stacks, so none can introduce horizontal overflow -
   re-verified at 390px and 320px after the change.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  /* ---- Tap targets: primary + secondary actions ---- */
  .print-btn,
  .search-button,
  .search-group .btn,
  .search-group button,
  .swim-schools-page button[type="submit"],
  .btn,
  .button,
  .button-secondary,
  .package-button,
  .post-button,
  .teen-button,
  .pill,
  button[type="submit"],
  input[type="submit"] {
    min-height: 44px !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
  }

  /* ---- Tap targets: form controls ---- */
  input[type='text'],
  input[type='email'],
  input[type='password'],
  input[type='number'],
  input[type='tel'],
  input[type='date'],
  input[type='url'],
  input[type='search'],
  select,
  .search-box input,
  .search-controls input {
    min-height: 44px !important;
  }

  textarea {
    min-height: 88px !important;
  }

  /* ---- Tap targets: navigation-style links ----
     inline-flex + min-height grows the hit box without changing the visual
     baseline, because these are already standalone block-level links. ---- */
  .back-link,
  a.back-link {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 44px !important;
    /* 2026-08-24: min-height alone left "← Back" at 42.3px wide (text-width
       bound), 1.7px short of the 44px target. min-width completes the pass;
       inline-flex + flex-start keeps the label visually unmoved. */
    min-width: 44px !important;
    justify-content: flex-start !important;
  }

  .breadcrumb a,
  .breadcrumbs a,
  .page-breadcrumb a,
  nav[aria-label="Breadcrumb"] a,
  nav[aria-label="breadcrumb"] a {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 36px !important;
    padding: 4px 2px !important;
  }

  .nav-logo {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 44px !important;
  }

  /* ---- Minimum readable text: 11px floor ---- */
  .stat-item .stat-label,
  .stat-card .stat-label,
  .stat-label,
  .article-card-category,
  .search-pill-sub,
  .school-chain,
  .kicker,
  .meta-chip,
  .newsletter-privacy,
  .newsletter-section p,
  .newsletter p,
  .page-breadcrumb,
  /* css-regression-checker 2026-09-01: the unclassed inline breadcrumb bar (410
     pages) must be listed here as well as on the container rule above. That rule
     sets `font-size:0.7rem`, which resolves to 9.8px against this sheet's 14px
     mobile root — under the 11px floor. The class variant only clears the floor
     because it is named in THIS rule; adding a variant to the styling rule and
     not to the floor rule ships the variant below the floor.
     See [[text_floor_hides_in_shared_stylesheets]]. */
  div[style*="padding: 10px 24px"],
  div[style*="padding: 10px 24px"] a,
  div[style*="padding: 10px 24px"] span,
  .breadcrumb,
  .breadcrumb a,
  .breadcrumb .current,
  .breadcrumbs,
  .breadcrumbs a,
  footer .footer-bottom,
  footer .footer-bottom p,
  .tag {
    font-size: 11px !important;
  }

  /* ---- Compact strips: 10px floor (11px reflows the row) ---- */
  .mobile-bottom-nav a,
  .mobile-cat-item {
    font-size: 10px !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   MOBILE TAP TARGETS — ROUND 3 (residual, same 2026-08-16 sweep)

   Second render pass after the block above closed the bulk of the gap. What
   remained were the elements no class-based selector reached:

     "Explore Guides ->" / "Suggest a topic ->" / "Complete Guide ->" /
     "View on Amazon ->" / "Read the Guide ->"   20-27px tall arrow CTA links.
     These are the conversion links on the homepage offer block, education hub,
     teens hub and the gear page (the gear ones are affiliate links), so they
     matter more than their markup suggests.

     <button aria-label="Search for swim schools">  39px on /swim-schools.html,
     styled entirely with an inline style attribute and carrying no type or
     class, so both the .btn and button[type=submit] selectors missed it.
     Bare `button` is added here instead - min-height only ever grows a box.

     The article-template breadcrumb bar is an anonymous
     <div style="background:#f8fafc;border-bottom:..."> emitted straight after
     </header> on 394 pages, with no class to hook. `header + div[style*=
     "border-bottom"] a` reaches it and cannot reach the three hero divs that
     also sit in that position, because those set background-image with no
     border-bottom.

     .print-toolbar a ("<- Back to full guide") is 16px tall on 74 printables.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  button,
  a[data-cta],
  .cta-link,
  .feature-link,
  .gear-link,
  .gear-page .gear-link,
  .popular-links a,
  .print-toolbar a,
  .article-card > a,
  .guide-body > a,
  header + div[style*="border-bottom"] a {
    min-height: 44px !important;
    display: inline-flex !important;
    align-items: center !important;
  }

  /* Bare buttons centre their own label; the arrow links stay left-aligned so
     they keep sitting flush with the copy above them. */
  button {
    justify-content: center !important;
  }

  /* The hamburger is already a 44px flex box - do not let the rule above
     restyle its bar stacking. */
  .nav-toggle {
    flex-direction: column !important;
    justify-content: center !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   NARROW-VIEWPORT HORIZONTAL OVERFLOW (<=360px)
   (mobile-consistency-checker, 2026-08-16)

   Pre-existing, not a regression from the tap-target work above - reproduced
   identically against the unmodified live tree. Invisible at 390px, which is
   why every prior render check missed it. Two causes:

   1. `grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))` cannot
      shrink its track below the 280px minimum, so on a 320px screen (iPhone
      SE 1st gen, and any phone with display zoom on) the track outgrew its
      ~278px container. Fixed at source by rewriting every minmax min >=200px
      across the CSS and the inline <style> blocks to
      `minmax(min(280px, 100%), 1fr)` - the standard safe form, a no-op at any
      width where the container is already wide enough.

   2. .mobile-bottom-nav laid out 5 items at their intrinsic width with
      `padding: 4px 12px` and no flex-shrink, totalling 350px and pushing the
      document to a 350px scrollWidth. Equal 1fr-style columns with min-width:0
      make the strip fit any viewport.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 360px) {

  .mobile-bottom-nav {
    max-width: 100vw !important;
    justify-content: space-between !important;
  }

  .mobile-bottom-nav a {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    padding-left: 2px !important;
    padding-right: 2px !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   MINIMUM READABLE TEXT — RESIDUAL (mobile-consistency-checker, 2026-08-16)

   Final render sweep across 24 more templates. What was left had no class to
   hook: sub-rem sizes written straight into a `style` attribute, which then
   resolve against the 14px mobile root and land under 11px:

     style="font-size: 0.75rem"   -> 10.5px  (61 pages - the newsletter /
                                              lead-magnet "No spam. Unsubscribe
                                              anytime" line and its inline link)
     style="font-size: 0.72rem"   -> 10.1px  (7 pages)
     style="font-size: 0.7rem"    ->  9.8px  (2 pages)
     style="font-size: 0.68rem"   ->  9.5px  (2 pages)

   Attribute-substring selectors reach these without touching the HTML, which
   keeps the 61-page diff out of the deploy. If the generator is ever changed to
   emit a class here, prefer the class and drop these.

   Plus three one-off badge classes measured at 9.8-10.5px.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  [style*="font-size: 0.75rem"],
  [style*="font-size: 0.72rem"],
  [style*="font-size: 0.7rem"],
  [style*="font-size: 0.68rem"],
  [style*="font-size: 0.65rem"],
  [style*="font-size: 0.6rem"],
  [style*="font-size: .75rem"],
  [style*="font-size: .72rem"],
  [style*="font-size: .7rem"],
  [style*="font-size: .68rem"],
  [style*="font-size: .65rem"],
  .pillar-card-stroke,
  .ww-badge-brand,
  .org-badge {
    font-size: 11px !important;
  }

  /* Inline links inherit the parent's inline font-size, so pin them too. */
  [style*="font-size: 0.75rem"] a,
  [style*="font-size: 0.72rem"] a,
  [style*="font-size: 0.7rem"] a,
  [style*="font-size: 0.68rem"] a,
  [style*="font-size: .75rem"] a,
  [style*="font-size: .72rem"] a,
  [style*="font-size: .7rem"] a,
  [style*="font-size: .68rem"] a {
    font-size: 11px !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   VISUAL QA — 2026-08-17 (render-based audit, 16 templates x 4 viewports)

   P0. INVISIBLE HERO TEXT ON MOBILE — 126 pages.
   The block at the top of this file flattens .page-hero to `background:#fff` for
   the app-style mobile layout, then recolors only `h1` and `.hero-sub`. Three
   other hero children are styled white-on-gradient in main.css and were never
   recolored, so on phones they rendered WHITE ON WHITE — completely invisible:

     .page-hero .lead      rgba(255,255,255,.9)  — 126 pages (local landing +
                                                   guide intros; the first
                                                   paragraph a mobile visitor
                                                   reads on every /beginner-,
                                                   /kids-, /toddler- city page)
     .page-hero .kicker    rgba(255,255,255,.8)  — 126 pages
     .page-hero .meta-chip #fff on rgba(255,255,255,.15) + white border — 378x

   Desktop is unaffected (the gradient is still there); this is mobile-only.
   Grep audits cannot see it: both stylesheets are individually correct, the
   defect only exists in the cascade at <=768px. Found by rendering.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  .page-hero .lead,
  .page-hero--edu .lead,
  .page-hero.compact .lead {
    color: #737373 !important;
    font-size: 0.8rem !important;
    line-height: 1.5 !important;
    margin-bottom: 0 !important;
  }

  .page-hero .kicker,
  .page-hero--edu .kicker,
  .page-hero.compact .kicker {
    color: #0369a1 !important;
  }

  .page-hero .meta-chip,
  .page-hero--edu .meta-chip {
    background: #f1f5f9 !important;
    color: #475569 !important;
    border-color: #cbd5e1 !important;
  }

  /* ---- VISUAL QA 2026-08-20: HERO CASCADE ORPHANS, ROUND 2 ----
     The 2026-08-17 block above repainted .lead, .kicker and .meta-chip, but
     the flatten rule sets `background:#fff` on the hero WITHOUT resetting the
     hero's own inherited `color`, which main.css leaves at #fff for the dark
     desktop gradient. Any hero child that is NOT one of those three classes
     therefore still inherits WHITE ON WHITE.

     Live example found by rendering: the post-consolidation notice
     "Now merged: our full <town> swim lesson guide covers costs..." is a bare
     <p> (no class) inside .page-hero on the four merged twin pages
     (ambler / elkins-park / flourtown / glenside). It rendered completely
     invisible at <=768px — measured contrast 1.00:1 — while still occupying
     its full vertical space, so the hero showed an unexplained blank gap.
     That paragraph is the only link pointing mobile readers at the canonical
     /swim-lessons/<town> target, so the consolidation lost its handoff.

     Fix at the ROOT rather than per-class: reset `color` on the hero itself so
     inheritance is correct for every present and future descendant, then give
     links an AA-passing brand blue. Keeping the three class rules above means
     no visual change for anything already correct. ---- */
  .page-hero,
  .page-hero--edu,
  .page-hero.compact,
  .tools-hero {
    color: #404040 !important;
  }

  .page-hero p,
  .page-hero--edu p,
  .page-hero.compact p,
  .page-hero strong,
  .page-hero--edu strong,
  .page-hero.compact strong,
  .tools-hero strong {
    color: #404040 !important;
  }

  .page-hero a,
  .page-hero--edu a,
  .page-hero.compact a,
  .tools-hero a {
    color: #0369a1 !important;
    text-decoration: underline !important;
  }

  /* ---- Contrast: neutral grays below WCAG AA 4.5:1 on white ----
     #a3a3a3 measured 2.52:1 and #9ca3af 2.54:1. These are neutrals, not brand
     colors, so darkening is safe. #6b7280 measures 4.83:1.
     2026-09-21: this rule only reached the LABEL spans. The parent
     .mobile-bottom-nav a / .mobile-cat-item kept #a3a3a3, and that is the colour
     the inline SVG icons inherit via stroke="currentColor" - so the icons stayed
     at 2.52:1 while their labels passed. The parent declarations are now #6b7280
     too; a contrast fix scoped to the text-bearing descendant does not fix the
     icon beside it. ---- */
  .mobile-bottom-nav a span,
  .mobile-cat-item span,
  .mobile-app-search-pill .search-pill-text span,
  .poster-footer .footer-tagline {
    color: #6b7280 !important;
  }

  /* ---- Sub-11px text floor: badges the 2026-08-16 sweep did not reach ---- */
  .tool-badge,
  .package-badge {
    font-size: 11px !important;
  }

  /* ---- Tap targets below the 44px house standard ----
     All are >=24px so WCAG 2.5.8 AA passes; these bring them to the 44px bar
     used by the 2026-08-16 sweep. min-height + inline-flex avoids reflowing the
     rows the way extra padding would. ---- */
  .state-directory-module .state-grid a,
  .hub-local-links a,
  .page-breadcrumb .breadcrumb-inner a {
    min-height: 44px !important;
    display: inline-flex !important;
    align-items: center !important;
  }

  .state-directory-module .state-grid a {
    display: flex !important;
  }

  .search-filter-bar .search-box .search-clear {
    min-width: 44px !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   VISUAL QA — 2026-08-18 (render-based mobile audit)

   P0. INVISIBLE HERO TEXT ON MOBILE — the bare `.hero` variant.
   The 2026-08-17 sweep (block above) fixed `.page-hero`, but this file ALSO
   flattens the older bare `.hero` to `background:#fff` at the top (~line 103)
   and only recolors `h1` and `p`. Eleven pages still use bare `.hero` with a
   gradient + white children declared in their own inline <style>, so those
   children rendered WHITE ON WHITE at <=768px:

     .hero .kicker            rgba(255,255,255,.7) on #fff — contrast 1.00
     .hero .button-secondary  #fff text + rgba(255,255,255,.5) border on #fff
                              — the entire button was invisible

   Confirmed by render on /find-swim-lessons.html (SECTION.hero computed
   bgc=rgb(255,255,255)). Desktop keeps its gradient and is unaffected. Scoped
   to `.hero` only so the gradient heroes that this file does NOT repaint
   (e.g. .fwsp-hero on /tools/family-water-safety-plan.html) keep white text.
   Colors match the .page-hero fix above for consistency.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  .hero .kicker {
    color: #0369a1 !important;
  }

  .hero .button-secondary {
    color: #0369a1 !important;
    border-color: #0369a1 !important;
  }

  /* ---- Hard-coded pixel headings in inline styles (2026-08-31 visual QA) ----
     `/swim-schools.html` and `/adult-swimming-lessons.html` build their heroes
     from an UNCLASSED <section> with everything inline, so not one selector in
     this file can reach them. Both shipped a 42px h1 to a 390px phone — the
     only two pages on the site that never compacted — plus 24/28px section h2s.
     Author `!important` beats a normal inline declaration, so attribute
     selectors fix it without touching the markup or the desktop rendering.
     Same shape as the 0.72rem inline-font-size block further down. ---- */
  h1[style*="font-size: 42px"] { font-size: 1.6rem !important; }
  h2[style*="font-size: 28px"],
  h2[style*="font-size: 24px"] { font-size: 1.5rem !important; }

  /* ---- Sub-11px text floor: .age-badge (6 guide pages) rendered 10.5px.
     Not covered by the 2026-08-16 sweep or the .tool-badge/.package-badge
     rule above. The 10px .mobile-bottom-nav / .mobile-cat-item labels are a
     DELIBERATE exception (11px reflows the strip) — do not raise them. ---- */
  .age-badge {
    font-size: 11px !important;
  }

  /* ---- WCAG 2.5.8 AA target size (24x24 minimum) ----
     Bare checkboxes rendered 13x13 on /swim-schools/add.html and 17x17 on
     /tools/family-water-safety-plan.html — both below the 24px AA floor.
     Sizing the control (not padding the row) avoids reflowing the lists. ---- */
  input[type="checkbox"],
  input[type="radio"] {
    min-width: 24px !important;
    min-height: 24px !important;
  }

  /* Pair each control with a 44px row so the whole line is tappable. */
  .checkbox-item,
  .checkbox-item label {
    min-height: 44px !important;
    display: flex !important;
    align-items: center !important;
    gap: 8px !important;
  }

  /* ---- Residual sub-44px tap targets found this pass ----
     The browse-by-state chips are inline-styled with min-height:44px but no
     min-width, so 2-letter states (HI/IL/RI/FL/WI) rendered 38-43px wide.
     A stylesheet !important beats the element's non-important inline style.
     min-width is inert on non-replaced inline elements, so the same-prefix
     links that appear inside prose are unaffected. ---- */
  a[href^="/swim-lessons/directory/"] {
    min-width: 44px !important;
    justify-content: center !important;
  }

  .state-grid a,
  .state-list a {
    min-width: 44px !important;
    min-height: 44px !important;
  }

  .source-list a,
  .sources a,
  .hero-actions .button,
  .hero-actions .button-secondary {
    min-height: 44px !important;
    display: inline-flex !important;
    align-items: center !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   MOBILE TAP TARGETS — ROUND 3
   (css-regression-checker, 2026-08-18)

   Measured in a real 390x844 and 320x700 render across 22 template types.

   `footer .footer-links { display: none }` above hides the main footer nav on
   mobile, so the ONLY tappable links left in the footer are the contact email
   and the two legal links in .footer-bottom. All three are plain inline <a>
   inside a <p>, so their box height is just the line box — measured at 14px on
   every one of the 22 templates (12px on special-needs-swimming.html). Apple
   HIG minimum is 44px.

   Rounds 1 and 2 fixed the .footer-bottom FONT SIZE (11px floor, above) but not
   the tap height, and no static audit can see this: there is no height rule to
   grep for — the failure is the absence of one.

   inline-block + min-height only grows the line box inside a centred paragraph,
   so this cannot introduce horizontal overflow. Re-verified at 390px and 320px.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  footer .footer-bottom p a,
  footer .footer-contact a {
    display: inline-block !important;
    min-height: 44px !important;
    line-height: 44px !important;
    padding: 0 6px !important;
    vertical-align: middle !important;
  }

  /* The separator pipes and the copyright text sit on the same line as the now
     44px-tall links; keep the paragraph from collapsing around them. */
  footer .footer-bottom p {
    line-height: 44px !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   2026-08-18 — MOBILE CONSISTENCY SWEEP
   Grid-track blowout: .main-layout is a grid, and grid items default to
   min-width:auto, so ANY child whose min-content width exceeds the column
   (a wide table, a bare URL, a "·"-joined string) widens the whole track and
   the layout viewport with it. Three article pages rendered 369-414px wide
   inside a 320px viewport. Because the layout viewport stretches to match,
   documentElement.scrollWidth == window.innerWidth and the page LOOKS clean to
   a naive overflow check — it is only visible when scrollWidth is compared
   against the NOMINAL device width. min-width:0 lets the track shrink; the
   wrapping/scrolling rules below then contain the wide content itself.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  .main-layout > * {
    min-width: 0 !important;
  }

  /* Bare URLs in citation blocks measured 374px min-content. break-word only
     splits a word that cannot otherwise fit, so normal prose is unaffected. */
  .article-body,
  .article-body p,
  .article-body li,
  .article-body td,
  .article-content p,
  .article-content li {
    overflow-wrap: break-word !important;
    word-break: break-word !important;
  }

  /* "Elbow·Elbow·Tummy·Knee" is one unbreakable 368px token to the line
     breaker — U+00B7 is not a break opportunity. */
  .stat-box span,
  .stat-box .stat-label,
  .stat-box .stat-number {
    overflow-wrap: anywhere !important;
  }

  /* Wide data tables stay readable by scrolling inside their own box instead
     of widening the page. */
  .article-body table,
  .article-content table,
  table.pricing-table {
    display: block !important;
    width: 100% !important;
    max-width: 100% !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
  }

  /* ---- Tap targets below the 44px house standard ----
     These are standalone navigation/CTA links, not inline links inside a
     sentence, so the WCAG 2.5.8 inline exception does not apply.
       .toc-item      285x28  (table-of-contents rows, 6 pages)
       .visit-btn     240x40  (scholarship CTA buttons, 11 pages)
       .wwk-city-link  95x35  (nearby-city chips, 10 pages) ---- */
  .toc-item,
  .visit-btn,
  .wwk-city-link {
    min-height: 44px !important;
    display: inline-flex !important;
    align-items: center !important;
  }

  .toc-item {
    display: flex !important;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════════
   MOBILE TAP TARGETS — ROUND 4 (2026-08-19 css-regression render sweep)

   Two breadcrumb MARKUP VARIANTS were never reached by the 2026-08-16 rules,
   because those rules were written against the classes on the pages that were
   sampled, not against every variant in the tree:

     .wwk-breadcrumbs a   13 /swim-lessons/ town pages (local-pages.css). Bare
                          inline <a>, measured 15px tall at 390px AND 320px —
                          the worst tap target left on the site.
     .breadcrumb a        rendered OUTSIDE a .breadcrumb-inner wrapper (e.g.
                          /special-needs-swimming.html) only ever matched the
                          36px rule above, 8px under the 44px house standard.

   Both live in a full-width, wrap-capable row and this rule only ever GROWS
   height, so neither can introduce horizontal overflow.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
  .wwk-breadcrumbs a,
  .breadcrumb a,
  .breadcrumbs a,
  .page-breadcrumb a,
  nav[aria-label="Breadcrumb"] a,
  nav[aria-label="breadcrumb"] a {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 44px !important;
  }

  /* Keep the ">" text separators vertically centred against the taller links. */
  .wwk-breadcrumbs {
    display: flex !important;
    flex-wrap: wrap !important;
    align-items: center !important;
    column-gap: 4px !important;
    row-gap: 0 !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════════════
   MOBILE TAP TARGETS — ROUND 4 (2026-08-19 mobile-consistency sweep)

   Found by rendering at 320 / 390 / 768px, not by grepping. Each of these is a
   NAVIGATION or CTA target, never inline prose, so the WCAG 2.2 inline-target
   exemption does not cover them. Sizes below are the measured heights at 390px.

   Site tiers, per the 2026-08-16 precedent: dense nav lists -> 36px (clears the
   WCAG 2.5.8 AA 24x24 minimum without visually thickening the block); standalone
   CTAs -> 44px.

   Every rule here only ever GROWS height, and each target sits in a full-width
   or wrap-capable block, so none can introduce horizontal overflow. Verified by
   re-rendering at 320px after the change: zero overflow, unchanged from before.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  /* --- Directory back-links: 16px, bare <a>, no padding (51 state pages) ------
     The 2026-08-16 pass lifted `.back-link` (singular). These are bare <a>
     children of `.back-links` (plural, on the CONTAINER) and carry no class of
     their own, so that selector never reached them. */
  .back-links a {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 44px !important;
    min-width: 44px !important;
    justify-content: flex-start !important;
  }

  /* --- Sidebar table-of-contents: 28px (273 pages) ------------------------- */
  .toc-list .toc-item a,
  .toc-item > a,
  a.toc-item {
    display: block !important;
    min-height: 36px !important;
    padding-top: 9px !important;
    padding-bottom: 9px !important;
  }

  /* --- Jump-link row: 32px (scholarships) --------------------------------- */
  .jump-links a {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 44px !important;
  }

  /* --- Arrow CTAs on the swim-lessons hub: 40px --------------------------- */
  .guide-link,
  .cta-box-btn {
    display: flex !important;
    align-items: center !important;
    min-height: 44px !important;
  }

  /* --- Partnership callout CTA: 22px, under the 24x24 AA minimum ---------- */
  .partnership-callout a {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 44px !important;
  }

  /* --- Primary CTAs one pixel short of 44 (43px measured) ----------------- */
  .contact-cta a,
  .jobs-grid a[href*="/jobs/post"] {
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    min-height: 44px !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════════════
   TEXT FLOOR — ROUND 2 (2026-08-19). Residual sub-11px labels.
   `.featured-badge` reaches 13 directory pages; `.card-label` the lessons hub.
   Both measured 10.5px at 390px (0.75rem against the 14px mobile root).
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {
  .featured-badge,
  .card-label {
    font-size: 11px !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════════════
   MOBILE CONSISTENCY — 2026-08-20
   Found by rendering at 320/390/768px, not by grepping. All five defect groups
   below were invisible to static CSS audits: the failing numbers are never
   written down, they emerge at runtime from rem values resolving against the
   14px mobile root, and from links whose box height comes from line-height.

   Scope note: every rule here sits inside `@media (max-width: 768px)`. Print
   media resolves to ~816px CSS at Letter, so none of this reaches the PDF —
   the printable card layouts render identically on paper.
═══════════════════════════════════════════════════════════════════════════════ */

@media (max-width: 768px) {

  /* --- 1. Inline-styled breadcrumb bar — 398 pages ------------------------
     A fourth breadcrumb variant, and the only one with NO class to hook: the
     bar is a bare <div style="padding: 10px 24px; font-size: 0.85rem"> wrapping
     bare <a> elements. Links measured 35.1 x 14px — the 14px comes straight
     from the line box. Reached with an attribute selector so the fix stays out
     of a 398-file HTML diff.

     Bar padding is trimmed from 10px to 5px as the links grow 14px -> 24px, so
     total bar height stays at 34px. The breadcrumb looks identical; only the
     touch box changes. 24px is the WCAG 2.2 AA minimum and is the same value
     already used for the three classed breadcrumb variants — 44px visibly
     thickens the bar on every article page.

     The link rule deliberately carries NO `!important`. A handful of these
     pages are also matched by an earlier pass that lifted breadcrumb links to
     44px with `!important`; an important 24px here would have overridden that
     downward and made an already-passing target worse. This rule raises and
     never lowers. The bar's own padding does need `!important`, because that
     padding is set in an inline `style` attribute.                          */
  [style*="padding: 10px 24px"][style*="0.85rem"] {
    padding-top: 5px !important;
    padding-bottom: 5px !important;
  }
  [style*="padding: 10px 24px"][style*="0.85rem"] a {
    display: inline-block;
    min-height: 24px;
    padding-top: 5px;
    padding-bottom: 5px;
  }

  /* --- 2. Sidebar table-of-contents links — 301 pages ---------------------
     `.toc-list a` measured 22.4px tall. Block display, so the WCAG 2.2 inline
     exemption does not apply. 1.6px short of the minimum.                    */
  .toc-list a {
    display: block !important;
    min-height: 24px !important;
    line-height: 1.7 !important;
  }

  /* --- 3. Lead-magnet direct link — 4 pages -------------------------------
     `.lead-magnet-direct-link` measured 21.4px. An inline-block CTA inside a
     lead-magnet box, not prose, so it is not exempt.                         */
  .lead-magnet-direct-link {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 24px !important;
  }

  /* --- 4. Sidebar / related navigational lists — 135+ pages ---------------
     `→ Open Water Safety Checklist` style links measured 15px tall. They are
     `display: inline` inside <li>, but these are navigation lists, not inline
     citations inside a sentence, so the prose exemption does not cover them.
     Deliberately NOT extended to `.article-body p a` / `.article-body li a` —
     those are genuine inline citations and WCAG 2.2 exempts inline targets.  */
  .sidebar-card ul li a,
  .sidebar-widget ul li a,
  .note-list a,
  .related-articles ul li a {
    display: inline-block !important;
    min-height: 24px !important;
    line-height: 24px !important;
  }

  /* --- 5. Text floor, round 3 — printable worksheets ----------------------
     40 distinct sub-11px instances across 22 printables, all from page-level
     inline <style> blocks using rem: `.72rem` -> 10.08px, `.78rem` -> 10.92px,
     `.65rem` -> 9.1px, all against the `html { font-size: 14px }` that
     printable-checklist.css and printable-poster.css set at their own small
     breakpoint. This is the same trap as the 0.72rem fix: the author's rem
     value clears 11px at a 16px root and fails at the sheet's own 14px root.

     Floors are applied per-selector rather than with a blanket `*` rule so the
     print layout of the cut-out wallet cards is not disturbed — and every one
     of these is screen-only, since print never matches max-width: 768px.     */

  /* CPR quick card */
  .cpr-card-sub, .cpr-brand { font-size: 11px !important; }
  .cpr-step, .cpr-911, .cpr-cut-note { font-size: 11px !important; }

  /* Spot-the-drowning-signs card */
  .sd-card-tag, .sd-brand { font-size: 11px !important; }
  .sd-sign, .sd-test, .sd-cut-note { font-size: 11px !important; }

  /* Reach-throw-don't-go card */
  .rtg-brand, .rtg-911, .rtg-cut-note { font-size: 11px !important; }

  /* Water-watcher card */
  .ww-badge-sub, .ww-badge-rules, .ww-cut-note { font-size: 11px !important; }

  /* Swim milestones checklist */
  .sm-age, .sm-date, .sm-anchor { font-size: 11px !important; }

  /* Practice log */
  .pl-tag, .pl-focus-note { font-size: 11px !important; }

  /* Goal-setting worksheet */
  .gs-hint { font-size: 11px !important; }

  /* School comparison worksheet */
  .cw-hint, .cw-school { font-size: 11px !important; }

  /* Family pledge / reward chart / level placement / competency */
  .sig-label { font-size: 11px !important; }
  .cl-levelnote, .cl-mastered { font-size: 11px !important; }

  /* Water-confidence challenge grid */
  .wcc-day p, .wcc-day p strong, .wcc-day .n { font-size: 11px !important; }

  /* Reward chart legend + star numbers */
  .rc-legend, .rc-legend span, .rc-legend strong { font-size: 11px !important; }
  .star-box .num { font-size: 11px !important; }

  /* Bold runs that inherit a sub-11px cell/row size */
  .cw-crit strong, .cpr-step strong, .sd-sign strong { font-size: 11px !important; }


  /* --- Worksheet tables -----------------------------------------------------
     Restated as `max(11px, <the table's own rem value>)` rather than a flat
     11px. A flat value would floor the 10.08px cells correctly but would also
     SHRINK the cells already sitting at 11.9px, and would erase the deliberate
     size step between a header row and its body rows. `max()` raises only what
     is failing and leaves everything else byte-identical.

     Each N below is the value declared in that table's own rule — it clears
     11px at a 16px root and fails at the 14px root the printable stylesheets
     set at their small breakpoint. That mismatch is the whole bug.          */
  .cw-table th, .cw-table td { font-size: max(11px, 0.78rem) !important; }
  .cw-section-row td        { font-size: max(11px, 0.75rem) !important; }
  .cw-hint                  { font-size: max(11px, 0.70rem) !important; }
  .pl-table                 { font-size: max(11px, 0.78rem) !important; }
  .pl-table th              { font-size: max(11px, 0.72rem) !important; }
  .roster-table th          { font-size: max(11px, 0.76rem) !important; }
  .ratio-table th, .ratio-table td { font-size: max(11px, 0.85rem) !important; }
  .gs-section-row td        { font-size: max(11px, 0.74rem) !important; }

  /* --- Inline `style="font-size:0.78rem"` — no class to hook ---------------
     The fall schedule planner sizes its two tables, and the first-month guide
     its note paragraph, with an inline style attribute. 0.78rem is the only
     inline value on any printable that fails: it lands at 10.92px against the
     14px root. 0.8rem (11.2px) and 0.82rem (11.48px) already clear the floor
     and are deliberately left alone.

     Cover both spellings — with and without the space after the colon. The
     earlier `.75rem` sweep missed the leading-dot variant and left two pages
     broken, so the spelling check is not optional.                          */
  .checklist-page [style*="font-size:0.78rem"],
  .checklist-page [style*="font-size: 0.78rem"],
  .checklist-page [style*="font-size:.78rem"],
  .checklist-page [style*="font-size: .78rem"],
  .checklist-page [style*="font-size:0.78rem"] td,
  .checklist-page [style*="font-size:0.78rem"] td strong,
  .checklist-page [style*="font-size:0.78rem"] th {
    font-size: 11px !important;
  }


  /* ═══ MOBILE TAP TARGETS — ROUND 6 (2026-08-21 mobile-consistency render sweep)

     Table-of-contents links render at 24-38px across THREE markup variants,
     none of which reaches the 44px house standard that this same stylesheet
     applies to every other standalone navigational link. Measured at 390px:

       <a class="toc-item">                 1,735 inst / 233 pages   38.2px
       <div class="toc-list"><a> (no class)   628 inst /  64 pages   24.0px
       <div class="toc-item"><a>              402 inst              inner a 36px

     Round 3 (line ~1450) correctly set `.toc-item { min-height: 44px }`. Round 4
     then added `a.toc-item { min-height: 36px }` — specificity (0,1,1) beats
     (0,1,0), so the narrower later rule silently REGRESSED the earlier 44px
     decision to 36px. Round 5's `.toc-list a { min-height: 24px }` (0,1,1), being
     later still, knocked the in-list case down again. The stylesheet ended up
     holding three contradictory decisions about one element.

     Resolve to 44px — the site's own stated house standard, and the value round 3
     already chose — and cover all three variants in one rule placed last so no
     earlier round can win on order. Padding is restated rather than inherited
     because the round-4 9px rule only reaches two of the three variants.        */
  .toc-list a,
  .toc-list .toc-item a,
  .toc-item > a,
  a.toc-item {
    display: flex !important;
    align-items: center !important;
    min-height: 44px !important;
    /* Round 6 set only the HEIGHT axis. `display:flex` makes each link a flex
       ITEM of its `.toc-item` parent (which m-app.css line ~1683 sets to
       `display:flex`), so the box shrink-wraps to the label text and the WIDTH
       stays text-bound. Measured 2026-09-05 at both 320px and 375px: the
       "FAQ" row renders 23.0 x 44 on /education/drowning-prevention-guide.html,
       /education/pool-party-safety.html and /education/water-safety-for-kids.html
       -- the only interactive controls on the site under the WCAG 2.5.8 AA
       24x24 minimum. Every other TOC row is 62-146px wide purely because its
       label is longer, so this is latent on any short entry, not those 3 pages.
       Same omission shape as .back-link (42.3x44 for 8 days, fixed 787bd4c).
       `justify-content: flex-start` keeps the label's left edge unmoved; the
       box only grows rightward inside a 285px column, so it cannot reflow. */
    min-width: 44px !important;
    justify-content: flex-start !important;
    padding-top: 6px !important;
    padding-bottom: 6px !important;
    line-height: 1.4 !important;
  }

  /* ═══ TEXT FLOOR — ROUND 4 (2026-08-22 mobile-consistency render sweep) ═══

     `.myth-label` / `.truth-label` on /education/water-safety-myths.html render
     at 10.08px. Source is article.css:882, which declares `font-size: .72rem`
     for both. That value clears the floor at a 16px root (11.52px) and fails
     against the 14px mobile root — the identical trap documented for the
     printable worksheets above, but in a SHARED stylesheet rather than a
     page-level <style> block, so the earlier printable-scoped sweeps never
     reached it. Only one page carries these classes today.

     Restated as `max(11px, 0.72rem)` using the rule's OWN declared value, not
     `1em`: `max(11px, 1em)` would resolve against the inherited parent size and
     INFLATE these pills instead of flooring them. At a 16px root max() picks
     0.72rem and the desktop rendering stays byte-identical.                   */
  .myth-label,
  .truth-label { font-size: max(11px, 0.72rem) !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════════
   MOBILE CONSISTENCY — 2026-08-27 (render sweep, 32 pages / 20 markup variants)

   ROOT CAUSE, stated once because it explains every rule in this block:

   main.css sets `html { font-size: 15px }` at max-width:768px and
   `html { font-size: 14px }` at max-width:480px. Every `rem` font-size in
   THIS stylesheet was authored against the 16px default root, so on a phone
   each one renders 6.25%-12.5% smaller than the value the author wrote. The
   trap was already documented twice — for the printable worksheets' own
   `html { font-size: 14px }` breakpoint, and for `.myth-label` in article.css
   (Text Floor Round 4 above) — but was never swept across m-app.css itself,
   because a grep for "font-size below the floor" run against a 16px root shows
   these rules as passing. Rendered measurement at 375px is the only signal.

   Nine declarations clear 12px at a 16px root and fall under it at 14px:
     .hero p                 0.85rem  13.60px @16  ->  11.90px @14
     .page-hero .hero-sub    0.80rem  12.80px @16  ->  11.20px @14
     .page-hero .lead        0.80rem  12.80px @16  ->  11.20px @14
     .section-header p       0.81rem  12.96px @16  ->  11.34px @14
     .article-card-excerpt   0.81rem  12.96px @16  ->  11.34px @14
     .search-pill-title      0.81rem  12.96px @16  ->  11.34px @14
     .newsletter … p         0.75rem  12.00px @16  ->  10.50px @14
     .cat-btn                0.75rem  12.00px @16  ->  10.50px @14
     .newsletter … submit    0.81rem  12.96px @16  ->  11.34px @14

   Two floors, not one, because these are two different kinds of text:
     - PROSE (hero lead-ins, card excerpts, section intros, newsletter body)
       gets 12px, the legible-body-text bar. `.article-card-excerpt` alone is
       353 card descriptions on /education/ — the site's largest index page.
     - CONTROLS (filter chips, submit button) get 11px, matching the floor the
       Text Floor Round 4 pass set for non-prose labels. Chips are scanned, not
       read, and 12px reflows the horizontally-scrolling filter row.

   Deliberately NOT floored — these are sub-11px BY DESIGN and are documented
   as intentional elsewhere in this file: `.mobile-bottom-nav a` (9.92px),
   `.mobile-cat-item` (9.60px), `.stat-label`, `.article-card-category`,
   `.search-pill-sub`, `.footer-bottom`, and every printable-scoped selector.

   Written as `max(Npx, <the rule's own rem value>)` rather than a bare px, so
   the declaration still resolves to the authored size wherever the root is
   16px, and floors only where the shrunken root would otherwise win. NOT
   `max(Npx, 1em)` — `1em` resolves against the inherited parent size and would
   INFLATE these rather than floor them (the trap noted in Round 4).

   All nine only ever GROW line-height, never width, so none can introduce
   horizontal overflow: the 6-width probe (320/360/375/390/414/430) measured
   scrollWidth === innerWidth on every template before and after.
═══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  /* --- Prose floor: 12px --- */
  .hero p { font-size: max(12px, 0.85rem) !important; }

  .page-hero .hero-sub,
  .page-hero--edu .hero-sub,
  .tools-hero p { font-size: max(12px, 0.8rem) !important; }

  .page-hero .lead,
  .page-hero--edu .lead,
  .page-hero.compact .lead { font-size: max(12px, 0.8rem) !important; }

  .section-header p { font-size: max(12px, 0.81rem) !important; }

  .article-card-excerpt { font-size: max(12px, 0.81rem) !important; }

  .search-pill-title { font-size: max(12px, 0.81rem) !important; }

  .newsletter-section p,
  .newsletter p { font-size: max(12px, 0.75rem) !important; }

  /* --- Control floor: 11px --- */
  .cat-btn { font-size: max(11px, 0.75rem) !important; }

  .newsletter-section button[type="submit"],
  .newsletter button[type="submit"],
  section[class*="newsletter"] button[type="submit"] {
    font-size: max(11px, 0.81rem) !important;
  }

  /* --- Breadcrumb "Home": 44px tall but only 33-37px wide ------------------
     Every breadcrumb pass so far (2026-08-16, and the four-variant pass above)
     set `min-height: 44px` and stopped there. min-height without min-width is
     a half-fix: the first crumb is always the word "Home", which measures
     33-37px depending on variant, so the target has been failing the 44px
     house standard on its short axis this entire time across all four markup
     variants — .page-breadcrumb, .wwk-breadcrumbs, nav[aria-label=Breadcrumb]
     and the bare inline-styled bar.

     Safe on every variant: the bars are already `display:flex; flex-wrap:wrap`
     (or a plain wrapping text row), the links are already inline-flex, and the
     widest crumb on any page is the current-page label — a <span>, not an <a>,
     so it is untouched. Worst case one link grows 11px on a row that has room
     to wrap, which is why the 6-width overflow probe stays clean.

     min-width is inert on non-replaced INLINE elements, so prose links that
     happen to sit inside a breadcrumb container cannot be affected.           */
  .wwk-breadcrumbs a,
  .breadcrumb a,
  .breadcrumbs a,
  .page-breadcrumb a,
  .page-breadcrumb .breadcrumb-inner a,
  nav[aria-label="Breadcrumb"] a,
  nav[aria-label="breadcrumb"] a,
  [style*="padding: 10px 24px"][style*="0.85rem"] a {
    min-width: 44px !important;
    justify-content: center !important;
    text-align: center !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   2026-08-31 — WCAG 2.5.8 AA (24x24) for STANDALONE LIST NAVIGATION LINKS

   Found by rendering at 375px, not by grepping: a bare `<li><a>…</a></li>`
   anchor is INLINE, so its box is the line box and nothing else. With the
   mobile root at 14px (see main.css `html{font-size:14px}` <=480px) these
   resolve to 15-17px tall — under the 24px AA floor — even though no rule
   anywhere sets a height. As with .footer-bottom, the failure is the ABSENCE
   of a rule, so there is nothing for a static audit to find.

   Measured at 375px before the fix:
     .sidebar-toc ul li a      15.0px   (66 pages — the in-guide TOC)
     .screen-only ul li a      17.0px   (19 pages — "Related Reading")
     .state-grid li a          16.0px   ( 5 pages — directory state index)
     .link-grid / .link-list   16.0px   (town/city cross-links)

   These are one-link-per-item NAVIGATION affordances, not prose, so the
   WCAG 2.5.8 inline-target exemption does NOT apply to them.

   DELIBERATELY NOT TOUCHED:
     - .related-articles ul li a sits at exactly 24px — already clears AA, and
       the house target is AA (breadcrumbs were set to 36px, not 44px, for the
       same reason: 44 visibly thickens every article page).
     - a.internal-link is used for INLINE citations inside sentences (Red Cross,
       BLS) as well as for nav rows, so it cannot be targeted by class.
     - .cl-footer p a and .tldr-box a are inline citations inside prose.

   inline-flex + min-height grows only the line box; the anchors are 40-238px
   wide (all far over the 24px min-width) so no min-width is needed and no
   horizontal overflow can be introduced. Re-verified at 375px and 320px.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  .sidebar-toc ul li a,
  .screen-only ul li a,
  .state-grid li a,
  .link-grid li a,
  .link-list li a,
  .note-list li a {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 24px !important;
    /* 2026-09-07: floored on the width axis too. The block comment above states no
       min-width is needed "because the anchors are 40-238px wide" -- an assumption
       about the content, not a property of the rule, and a 3-character TOC label
       falsified it (see the .wwk-navlist note). Both rules in this family carry the
       floor so a page matching either selector is covered. */
    min-width: 24px !important;
    justify-content: flex-start !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   STANDALONE LINK LISTS — the UNCLASSED half of the same defect
   ---------------------------------------------------------------------------
   The block above floors the six variants that happen to carry a class. A
   375px render sweep on 2026-09-03 found the identical 17px anchor inside
   containers with no usable hook: article.article, .article-related,
   .article-body, .article-sources, .sidebar-box, div.card, div.container and
   main.article. Listing those ancestors here would go stale the next time a
   template invents a wrapper, and — worse — .article-body also holds PROSE
   bullets whose inline links are WCAG 2.5.8-exempt and must NOT be grown.

   So the shape, not the ancestor, is the selector: m-app.js tags a list with
   .wwk-navlist only when every <li> contains a single anchor and no other
   text. That test cannot be written in CSS, which is why the class is applied
   at runtime.

   min-height only grows the line box, and these anchors run 40-238px wide (all
   clear of the 24px min-width), so no horizontal overflow is possible.
   .related-articles li a already sits at exactly 24px, so the floor is a no-op
   there — the house target is AA, not AAA (see the breadcrumbs-at-36px note).
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  .wwk-navlist > li > a {
    display: inline-flex !important;
    align-items: center !important;
    min-height: 24px !important;
    /* 2026-09-07: the "anchors run 40-238px wide" note above was measured on the
       corpus as it stood and is no longer true. A 375/320/430px render sweep found
       the 3-character "FAQ" row of the .sidebar-toc list on
       /education/daycare-school-water-safety-questions-checklist.html at 23.0 x 24
       -- the only interactive control on the site under the WCAG 2.5.8 AA 24x24
       minimum. inline-flex shrink-wraps to the label, so min-height floors ONE axis
       only; this is the same omission shape as .back-link and the .toc-list family.
       24px (not 44) is the house floor, and every other anchor here already exceeds
       it, so this cannot widen an existing control or reflow a row. */
    min-width: 24px !important;
    justify-content: flex-start !important;
  }
}
/* ════════════════════════════════════════════════════════════════════════════
   WWK mobile P1 "quick wins" — task WWK-MOBILE-IMPLEMENTATION-018 r1, from the accepted
   WWK-REQ-efec08fb r1 package (proposals/p1-quick-wins) plus the r1 F1 correction at
   the end (guide search bar sticks below the real header). Loads only at <=768px with
   the rest of m-app.css. Additive block: remove it (and restore the main.js loader
   version) to roll back. Rules cite observations O-n in that package's evidence.
   ════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  /* O-4: stop shrinking the root on phones (main.css sets 15px <=768, 14px <=480).
     Every rem-based size in main.css/m-app.css grows back to its authored value. */
  html { font-size: 16px !important; }

  /* O-4: lead-ins and card excerpts were floored at 12px; raise to 15px */
  .hero p,
  .page-hero .hero-sub, .page-hero--edu .hero-sub, .tools-hero p,
  .page-hero .lead, .page-hero--edu .lead, .page-hero.compact .lead,
  .section-header p,
  .article-card-excerpt,
  .newsletter-section p, .newsletter p { font-size: 15px !important; line-height: 1.5 !important; }

  /* O-2 / O-4: navigation and filter labels 10–11px -> 12–14px */
  .mobile-bottom-nav a span { font-size: 12px !important; }
  .mobile-cat-item span { font-size: 12px !important; }
  .cat-btn { font-size: 14px !important; min-height: 44px; }
  .page-breadcrumb, .page-breadcrumb a, .page-breadcrumb .current { font-size: 13px !important; }
  /* O-4: remaining 11px labels measured after the root fix */
  .stat-label, .article-card-category, .search-pill-sub,
  .footer-bottom, .footer-bottom p, .footer-bottom a { font-size: 12px !important; }

  /* O-3: show the two hero tasks on phones (m-app.css line ~170 hides them) */
  .hero-ctas { display: flex !important; flex-direction: column; gap: 10px; margin-top: 16px; }
  .hero-ctas .btn { width: 100%; min-height: 48px; justify-content: center; }
  /* the white phone hero turns .btn-primary into white-on-white; give it a visible outline */
  .hero-ctas .btn-primary { background: #fff !important; color: #075985 !important; border: 2px solid #075985 !important; }

  /* O-2: social buttons (menu, article footers) to 44px */
  .wwk-soc, .wwk-soc-row.wwk-soc-sm > li > a.wwk-soc { min-height: 44px !important; display: inline-flex; align-items: center; }

  /* O-7 / O-5: standalone link lists (tagged .wwk-navlist by main.js) to 44px rows.
     Needs !important: m-app.css's own `.wwk-navlist > li > a { min-height: 24px !important }`
     currently overrides /tools/'s inline `.pl-list a { min-height: 44px }`. */
  ul.wwk-navlist > li > a { display: flex !important; align-items: center !important; min-height: 44px !important; }

  /* O-10 link-in-text-block: links inside running text get an underline, not colour alone */
  main p a:not(.btn), .tldr-box a:not(.btn), .quick-answer a:not(.btn), .offer-card p a { text-decoration: underline; text-underline-offset: 2px; }

  /* O-10 target-size (/about/ .offer-card): more line spacing for in-text links */
  .offer-card p { line-height: 1.75 !important; }
}

/* O-8: respect reduced-motion (no rule existed) */
@media (prefers-reduced-motion: reduce) {
  body { animation: none !important; }
  html { scroll-behavior: auto !important; }
}

/* ── F1 (r1): /education/ guide search bar sticks BELOW the real sticky header ──────────
   The original rule earlier in this file, `.search-filter-bar.sticky { position: fixed; top: 48px }`,
   pins the bar under the 72px sticky header, hiding the top 14px of the search field (it was so
   before P1; the P1 search shortcut now lands users there). That rule and m-app.js are left
   untouched: the F1 script below strips the original `.sticky` class and pins the bar itself
   with `.wwk-p1-fixed` at the measured header height (--wwk-p1-header-h), using its own spacer.
   Mixed-file safety: the original spacer is hidden only under html.wwk-p1-search, which the F1
   script sets after detecting this block (.wwk-p1-search-spacer computes to display:none). New CSS
   with an old m-app.js therefore keeps the original behaviour, and an old m-app.css with a new
   m-app.js makes the script stand down. */
@media (max-width: 768px) {
  html.wwk-p1-search .search-sticky-spacer, html.wwk-p1-search .search-sticky-spacer.active { display: none !important; }
  .wwk-p1-search-spacer { display: none; }
  .wwk-p1-search-spacer.is-active { display: block; }
  .search-filter-bar.wwk-p1-fixed {
    position: fixed;
    top: var(--wwk-p1-header-h, 72px);
    left: 0;
    right: 0;
    z-index: 199;
    background: rgba(255,255,255,0.98) !important;
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    padding: 10px 16px 6px !important;
    box-shadow: 0 1px 6px rgba(0,0,0,0.06);
  }
  /* anchor landings (/education/#articleSearch) and keyboard focus keep the field clear of the header */
  #articleSearch { scroll-margin-top: calc(var(--wwk-p1-header-h, 72px) + 16px); }
}
