/* =============================================================================
   Shared page styles for gulfcoasthomemaintenance.com.

   Lifted out of an 839 line <style> block inside index.html when the products
   moved to their own page, because two pages sharing a look cannot each keep
   their own copy of it. That is the same reasoning theme.css was split out
   under, and the same reasoning analytics.js exists for: a value pasted into
   two files is a value that drifts.

   Palette lives in theme.css and is loaded before this.
   ============================================================================= */

  /* Palette lives in theme.css, shared with the guides page. */
  * { box-sizing: border-box; }
  /* Deliberately no scroll-behavior: smooth on the root. On the root it also
     animates the browser's own jump to a #fragment on load, and that animation
     did not survive these pages: /shop/#edition, /shop/#binder, /#who and
     /calendars/#calendars all finished loading at scrollY 0 with the hash still
     in the address bar. That silently broke every "View details" button, every
     footer shop link, the "Who made this" link, and the published Pinterest
     pins that the redirect at the top of index.html exists specifically to
     rescue. Measured on the live site and fixed in 1.33.1. If smooth scrolling
     is ever wanted back, scope it to a container, never to html.

     scroll-padding-top keeps whatever a fragment does land on clear of the
     sticky bar, which measures 3.5rem. */
  html { scroll-padding-top: 4.5rem; }
  body {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font: 17px/1.65 var(--font-serif);
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
  }
  @media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }}

  /* A floor under every link, so none can fall through to the browser default.
     This file had no bare a rule at all, and the gap kept being found one link
     at a time: the comments above .shop-free a and .deck a each record a
     separate occasion when a new link came out #0000ee. Measured in 1.33.2,
     three more were still doing it, and in the dark scheme that blue lands at
     1.76 on --paper, which is not readable. This is the least specific rule in
     the file, so every component that sets its own colour still wins. The
     hand-written and generated pages already carry the same line locally.  */
  a { color: var(--accent); }

  /* Widened in 1.27.0 to the approved full-width layout. Grids scale with
     it; anything that reads as a column of text carries its own cap below,
     because a 71rem line of body copy is unreadable. */
  .wrap { max-width: 71rem; margin: 0 auto; padding: 0 1.35rem; }

  /* --- type ------------------------------------------------------------- */
  .label {
    font: 600 11px/1.4 var(--font-sans);
    letter-spacing: .18em; text-transform: uppercase;
  }
  h1 {
    font-size: clamp(2.1rem, 6.4vw, 3.6rem); line-height: 1.05;
    margin: 0 0 1.15rem; letter-spacing: -.022em; font-weight: 700;
  }
  h2 {
    font: 700 clamp(1.5rem, 4vw, 2.15rem)/1.15 var(--font-serif);
    margin: 0 0 .7rem; letter-spacing: -.018em;
  }
  h3 {
    font: 700 1.08rem/1.35 var(--font-sans);
    margin: 0 0 .35rem; letter-spacing: -.005em;
  }
  p { margin: 0 0 1.05rem; }
  .muted { color: var(--muted); }
  .deck { font-size: 1.06rem; color: var(--muted); max-width: 36rem; }
  .deck strong { color: var(--ink); font-weight: 600; }
  /* --ink is an absolute near-black, so a bolded phrase in a deck disappeared
     the moment a section carrying one moved to the deep ground. Third time an
     absolute token has been caught on a ground it was not written for, after
     the deck links and the wordmark, so this reads the surface token instead
     and follows the section wherever it goes. */
  .band .deck strong { color: var(--on-surface); }
  /* A link inside a deck had never occurred until the shop page put one there,
     so it came out browser-default blue on the deep ground. Third time this
     class of bug has shown up, so it is set for both grounds here rather than
     patched per section: sand reads on the deep one, accent on paper. */
  .deck a { color: var(--accent); }
  .band .deck a, .hero .deck a { color: var(--sand-lift); }
  /* Fourth time a link has come out unreadable because it was written on the
     paper ground and then sat on the deep one: first a deck link, then the
     wordmark, then .shop-free, then the privacy link on /about/, which landed
     at 2.25:1. Patching them one at a time is what produced the first three,
     so this is the general rule instead. Prose links only, and never a button:
     .btn on a band is dark text on gold and repainting it would undo that. */
  .band p a:not(.btn), .band li a:not(.btn) { color: var(--sand-lift); }
  /* And back again inside anything that paints itself paper while sitting on a
     band. This is the trap the palette notes warn about: pick the gold by the
     ground the element is actually on, not by the section it is nested in. The
     agent-edition line inside .buy on /shop/ is the one that caught it, at
     2.66:1. Same list of self-painting cards as the shared card rule above. */
  .band :is(.pcard, .tier, .buy, .picker, .storm-step, .grants-note) p a:not(.btn),
  .band :is(.pcard, .tier, .buy, .picker, .storm-step, .grants-note) li a:not(.btn) {
    color: var(--accent);
  }

  /* section numbering, the field-guide device */
  .sec-no {
    font: 600 11px/1 var(--font-sans);
    letter-spacing: .16em; color: var(--accent);
    display: block; margin-bottom: .7rem;
  }

  /* Sections alternate between the paper ground and the deep one. Anything that
     paints itself reads these four tokens rather than naming a color, so a
     component works on either ground without a variant. */
  section {
    padding: 4.25rem 0;
    --surface: var(--bg);
    --on-surface: var(--ink);
    --on-surface-mute: var(--muted);
    --hairline: var(--rule-soft);
    --marker: var(--must);
    background: var(--surface);
    color: var(--on-surface);
  }
  section.band {
    --surface: var(--band-bg);
    --on-surface: var(--on-deep);
    --on-surface-mute: var(--on-deep-mute);
    --hairline: color-mix(in srgb, var(--on-deep) 17%, transparent);
    --marker: var(--sand-lift);
  }
  @supports not (background: color-mix(in srgb, red 50%, transparent)) {
    section.band { --hairline: #33454a; }}
  .band h2, .band h3 { color: var(--on-surface); }
  .band p, .band .muted { color: var(--on-surface-mute); }
  .band .sec-no { color: var(--sand-lift); }
  /* A rule only earns its place between two sections of the same ground; where
     the ground changes, the change is the divider. */
  section:not(.band) + section:not(.band) { border-top: 1px solid var(--rule-soft); }

  /* --- sticky bar ------------------------------------------------------- */
  /* Moved to nav.css in 1.32.2, so the generated calculator page can carry
     the same bar without a second copy of these rules. Every page that has
     a top bar loads nav.css; nothing about it lives here any more. The
     .cta--alert and .cta--kit rules went with the move rather than into it:
     no markup had used them since the 1.25.0 nav redesign. */

  /* --- hero ------------------------------------------------------------- */
  .hero {
    position: relative; overflow: hidden;
    background: var(--deep); color: var(--on-deep);
    padding: 4.5rem 0 4rem;
  }
  /* The photograph sits well back behind a scrim. Its job is to say "you live
     here", which live oak and Spanish moss do instantly. It is not there to be
     looked at, so the gradient is heaviest on the left where the headline sits. */
  .hero-media { position: absolute; inset: 0; overflow: hidden; }
  .hero-media img {
    /* The vertical value is the crop: the photo is taller than the hero, and
       this slides the window down it. Tuned so the porch and dormers carry
       the frame instead of the canopy. */
    width: 100%; height: 100%; object-fit: cover; object-position: 62% 72%;
    display: block;
  }
  .hero-media::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background:
      linear-gradient(100deg,
        color-mix(in srgb, var(--deep) 94%, transparent) 0%,
        color-mix(in srgb, var(--deep) 82%, transparent) 34%,
        color-mix(in srgb, var(--deep) 34%, transparent) 72%,
        color-mix(in srgb, var(--deep) 18%, transparent) 100%),
      linear-gradient(to bottom,
        color-mix(in srgb, var(--deep) 45%, transparent) 0%,
        color-mix(in srgb, var(--deep) 10%, transparent) 38%,
        color-mix(in srgb, var(--deep) 78%, transparent) 88%,
        var(--deep) 100%);
  }
  /* Older engines without color-mix would show an unreadable hero, so fall back
     to a flat scrim rather than a transparent one. */
  @supports not (background: color-mix(in srgb, red 50%, transparent)) {
    .hero-media::after { background: rgba(14,36,41,.86); }}
  .hero .wrap { position: relative; z-index: 2; }
  .hero .label { color: var(--sand-lift); }
  /* Hero copy sits over a photograph, so it runs brighter than the muted tone
     used on flat dark sections, and carries a soft shadow as insurance where
     the canopy behind it lightens. */
  .hero h1 { color: #fff; text-shadow: 0 2px 24px rgba(8,24,28,.55); }
  .hero .deck {
    color: #d8d3c7; font-size: clamp(1.02rem, 2.3vw, 1.18rem); max-width: 33rem;
    text-shadow: 0 1px 14px rgba(8,24,28,.7);
  }
  .hero .deck strong { color: #fff; font-weight: 600; }
  .rule-sm {
    width: 3rem; height: 2px; background: var(--sand-lift);
    margin: 0 0 1.4rem; border: 0;
  }

  /* Hurricane season countdown. Days, not a ticking clock: seconds counting
     down to a six month season is theatre, and the number a reader can act on
     is the one measured in days, which is also the unit the whole product is
     organized around. It states a fact and links to the thing that answers it
     rather than manufacturing alarm, which is the same reason there is no
     invented rating in the structured data. */
  .season {
    display: inline-flex; align-items: center; gap: .55rem; flex-wrap: wrap;
    margin: 1.6rem 0 0; padding: .55rem .85rem;
    background: color-mix(in srgb, var(--deep) 62%, transparent);
    border: 1px solid color-mix(in srgb, var(--on-deep) 24%, transparent);
    border-radius: 3px;
    font: 500 13.5px/1.45 var(--font-sans);
    color: #ded8cb; text-shadow: 0 1px 10px rgba(8,24,28,.7);
  }
  .season-num { color: #fff; font-weight: 700; }
  .season-dot {
    width: 8px; height: 8px; border-radius: 50%; flex: none;
    background: var(--must-lift);
    animation: season-pulse 2.4s ease-out infinite;
  }
  .season-link {
    color: var(--sand-lift); text-decoration: none; white-space: nowrap;
    border-bottom: 1px solid color-mix(in srgb, var(--sand-lift) 45%, transparent);
  }
  .season-link:hover { border-bottom-color: var(--sand-lift); }
  @keyframes season-pulse {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--must-lift) 65%, transparent); }
    70%  { box-shadow: 0 0 0 7px color-mix(in srgb, var(--must-lift) 0%, transparent); }
    100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--must-lift) 0%, transparent); }}
  @supports not (background: color-mix(in srgb, red 50%, transparent)) {
    .season { background: rgba(14,36,41,.62); border-color: #3d4f52; }
    .season-link { border-bottom-color: #7a5c22; }
    @keyframes season-pulse {
      0%   { box-shadow: 0 0 0 0 rgba(224,138,114,.65); }
      70%  { box-shadow: 0 0 0 7px rgba(224,138,114,0); }
      100% { box-shadow: 0 0 0 0 rgba(224,138,114,0); }}}
  .stat { background: var(--deep); padding: 1.05rem 1.1rem; }
  .stat .n {
    font: 700 1.6rem/1 var(--font-serif); color: #fff;
    display: block; margin-bottom: .3rem; letter-spacing: -.02em;
  }
  .stat .t { font-size: .82rem; color: var(--on-deep-mute); line-height: 1.4; display: block; }

  /* --- buttons ---------------------------------------------------------- */
  .actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }
  .btn {
    font: 600 14px/1 var(--font-sans);
    text-decoration: none; padding: .8rem 1.1rem; border-radius: 2px;
    border: 1px solid var(--rule); color: var(--ink); background: transparent;
    cursor: pointer; -webkit-appearance: none; appearance: none;
    transition: border-color .15s ease, filter .15s ease, background .15s ease, color .15s ease;
  }
  .btn:hover { border-color: var(--tier-color, var(--accent)); }
  .btn:focus-visible { outline: 2px solid var(--tier-color, var(--accent)); outline-offset: 2px; }
  .btn--primary {
    background: var(--tier-color, var(--accent));
    border-color: var(--tier-color, var(--accent));
    color: var(--on-tier);
  }
  .btn--primary:hover { filter: brightness(1.09); }

  /* Every button that ends at a checkout is gold, so "this one costs money" is
     a color the reader learns once and then recognizes anywhere on the page.
     The same pair the hero's primary button already uses: --sand needs dark
     text on it, because white lands near 3:1 and fails. The hover keeps the
     border gold too, or the shared .btn:hover rule repaints it teal. */
  .btn--buy {
    background: var(--sand-lift); border-color: var(--sand-lift); color: var(--deep);
  }
  .btn--buy:hover { filter: brightness(1.08); border-color: var(--sand-lift); }
  .btn--lg { padding: .95rem 1.5rem; font-size: 15px; }
  .btn--copied { border-color: var(--tier-color, var(--accent)); color: var(--tier-color, var(--accent)); }
  .hero .btn { border-color: color-mix(in srgb, var(--on-deep) 32%, transparent); color: var(--on-deep); }
  .hero .btn:hover { border-color: var(--on-deep); }
  /* The hero's free CTA, and deliberately not gold. Gold became the checkout
     color in 1.15.1, across all six Etsy buttons, which left the loudest button
     on the page wearing the "this costs money" color to offer something free.

     Teal would be the obvious alternative, since that is what the FREE badges
     use further down. It is not usable here: --accent against --deep measures
     about 1.4:1, so a teal fill would sink into the hero's own ground. A light
     fill is the strongest thing available on a dark hero, reads instantly as
     the primary action, and cannot be confused with the buy color. */
  .hero .btn--primary {
    background: var(--on-deep); border-color: var(--on-deep); color: var(--deep);
  }
  .hero .btn--primary:hover { filter: brightness(1.08); }
  /* The calculator sits under the buttons as a line rather than beside them as
     a third one. It is the only thing on the site that answers a question
     before asking for anything, so it wants to be in the hero; but a third
     button turns a clear choice into a menu, and this is the offer most likely
     to be taken by somebody who is not yet sure they want a calendar. */
  .hero .try {
    margin: 1rem 0 0; font: 400 .95rem/1.55 var(--font-sans);
    color: var(--on-deep-mute); max-width: 30rem;
  }
  .hero .try a { color: var(--sand-lift); font-weight: 600; }

  /* --- year diagram ----------------------------------------------------- */
  .figure { margin: 2rem 0 0; }
  .figure .scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .figure svg { display: block; min-width: 34rem; width: 100%; height: auto; }
  .figcaption {
    font-size: .87rem; color: var(--on-surface-mute); margin: .9rem 0 0;
    padding-top: .9rem; border-top: 1px solid var(--hairline);
  }
  .diag-month { font: 600 11px var(--font-sans); letter-spacing: .1em; fill: var(--on-surface-mute); }
  .diag-band-label { font: 600 11px var(--font-sans); letter-spacing: .12em; fill: var(--on-surface); }
  .diag-note { font: 600 10px var(--font-sans); letter-spacing: .1em; fill: var(--marker); }
  .diag-grid { stroke: var(--hairline); stroke-width: 1; }
  .diag-marker { stroke: var(--marker); stroke-width: 1.5; stroke-dasharray: 3 3; }

  /* --- factor cards ----------------------------------------------------- */
  /* Four cells, so the track count has to divide into four exactly, auto-fit
     lands on three at some widths and leaves an empty cell showing the grid's
     own background as a block. */
  .factors {
    display: grid; grid-template-columns: 1fr;
    gap: 1px; margin-top: 2rem;
    background: var(--hairline); border: 1px solid var(--hairline);
  }
  @media (min-width: 34rem) { .factors { grid-template-columns: repeat(2, 1fr); }}
  @media (min-width: 60rem) { .factors { grid-template-columns: repeat(4, 1fr); }}
  .factor { background: var(--surface); padding: 1.5rem 1.4rem; }
  .factor svg { color: var(--accent); margin-bottom: .85rem; display: block; }
  .band .factor svg { color: var(--sand-lift); }
  .factor h3 { font-size: 1rem; color: var(--on-surface); }
  .factor p { font-size: .93rem; color: var(--on-surface-mute); margin: 0; }

  /* --- agent points ----------------------------------------------------- */
  /* Three cells, so the track count divides exactly at both widths. Same
     hairline grid as .factors rather than a new card style: this section sits
     on the paper ground and does not need to shout over the kit above it. */
  .points {
    display: grid; grid-template-columns: 1fr;
    gap: 1px; margin-top: 2rem;
    background: var(--hairline); border: 1px solid var(--hairline);
  }
  @media (min-width: 46rem) { .points { grid-template-columns: repeat(3, 1fr); }}
  .point { background: var(--surface); padding: 1.5rem 1.4rem; }
  .point .pn {
    font: 700 1.45rem/1 var(--font-serif);
    color: var(--accent); letter-spacing: -.02em; margin: 0 0 .55rem;
  }
  /* --accent is tuned for the paper ground: on the light scheme it is a dark
     teal, which lands near 2:1 on a band and is unreadable. Sand is the token
     already designated for deep grounds, and is what .sec-no uses there. The
     points grid had never sat on a band before the agent section moved. */
  .band .point .pn { color: var(--sand-lift); }
  .point h3 { font-size: 1rem; color: var(--on-surface); margin-bottom: .4rem; }
  .point p { font-size: .93rem; color: var(--on-surface-mute); margin: 0; }

  /* --- tiers ------------------------------------------------------------ */
  /* Cards keep their own paper ground wherever they sit, so they have to reset
     the section's surface tokens. Custom properties inherit, so this makes
     rules like `.band h3 { color: var(--on-surface) }` resolve correctly for
     content inside a light card on a dark section. */
  /* Two links had never been styled and were rendering browser-default blue on
     a cream card: the agent cross-sell inside the kit's buy card, and the
     privacy link under the signup. Both sit on paper, so --accent is the one
     the rest of the page already uses there. */
  .buy a:not(.btn), .signup-note a { color: var(--accent); }

  /* --- feed picker ------------------------------------------------------ */
  /* Four feeds means somebody who wants three of them ends up with three
     entries in their sidebar. A subscription is a URL fetched by Google from
     its own servers, so there is no per-person state on a static site: every
     selection is a file built in advance, and this only hands out the address
     that matches the boxes. */
  /* The loudest card on the page, deliberately. It was the quietest: paper-2
     on the paper ground measured 1.06 against it, which is not a card so much
     as a faint rectangle. This is the one thing the free section exists to get
     clicked, so it takes the treatment the tier cards use, a lifted paper card
     with a shadow, and the accent bar that means "free" everywhere else on the
     site. On the deep ground it now reads as the object in the room. */
  .picker {
    margin-top: 1.75rem; background: var(--paper);
    border: 1px solid var(--rule); border-left: 3px solid var(--accent);
    border-radius: 3px; box-shadow: var(--shadow-lg); padding: 1.6rem 1.7rem;
  }
  .picker h3 { margin-bottom: .3rem; }
  .picker > p { color: var(--muted); font-size: .95rem; margin: 0 0 1.1rem; }
  .picker-boxes { display: flex; flex-wrap: wrap; gap: .5rem .9rem; margin-bottom: 1.1rem; }
  .picker-boxes label {
    display: inline-flex; align-items: center; gap: .45rem; cursor: pointer;
    font: 600 .93rem/1.2 var(--font-sans);
    color: var(--ink); border: 1px solid var(--rule); background: var(--paper);
    padding: .55rem .75rem; border-radius: 2px;
  }
  .picker-boxes input { accent-color: var(--accent); width: 15px; height: 15px; margin: 0; }
  .picker-boxes label:focus-within { outline: 2px solid var(--accent); outline-offset: 1px; }
  .picker-summary {
    font-size: .9rem; color: var(--muted); margin: 0 0 1rem;
    padding-top: .9rem; border-top: 1px solid var(--rule);
  }
  .picker-summary strong { color: var(--ink); }
  .picker-more { font-size: .88rem; color: var(--muted); margin: 1rem 0 0; }
  .picker-more a { color: var(--accent); }

  /* --- page previews ---------------------------------------------------- */
  /* Tops of real pages, inside each product section. Cropped rather than shrunk:
     see build_site_images.py for why the crop is the protection and where each
     boundary sits. Works on either ground because everything here reads the
     surface tokens rather than naming a color. */
  .previews { margin-top: 2.4rem; }
  .previews > .label { color: var(--on-surface-mute); display: block; margin-bottom: .9rem; }
  /* One row at every width. Stacked, three page previews cost a whole screen
     each on a phone and pushed the buy button three screens down the section.
     They are crops of a page top rather than something to read, so a strip you
     swipe shows the same thing in a third of the height. The container scrolls,
     never the page. */
  .prow {
    display: grid; grid-auto-flow: column; grid-auto-columns: 72%;
    gap: 1rem; overflow-x: auto; -webkit-overflow-scrolling: touch;
    padding-bottom: .4rem;
  }
  @media (min-width: 40rem) {
    .prow { grid-auto-flow: row; grid-template-columns: repeat(3, 1fr); overflow-x: visible; }}
  /* The three crops are not the same shape, because the pages are not. Rather
     than force a common aspect ratio, which would crop sideways into the page
     margins, the figures stretch to the row and the captions drop to the
     bottom so they line up across the strip. */
  .prow figure { margin: 0; display: flex; flex-direction: column; height: 100%; }
  .prow img {
    display: block; width: 100%; height: auto; background: #fff;
    border: 1px solid var(--hairline); border-radius: 2px;
  }
  .prow figcaption {
    font: 600 10.5px/1.4 var(--font-sans);
    letter-spacing: .1em; text-transform: uppercase;
    color: var(--on-surface-mute); margin-top: auto; padding-top: .5rem;
  }
  .previews-note { color: var(--on-surface-mute); font-size: .85rem; margin: 1rem 0 0; }

  /* --- shop grid -------------------------------------------------------- */
  /* Sits directly under the hero on the same deep ground, so it reads as part
     of the header rather than as the page's first argument. Until this existed
     the only way to learn there were three products was to scroll through three
     long sections; the grid says what is for sale in one screen, and each card
     links down to the section that makes the case for it.

     A grid, not a carousel. A carousel shows one product and hides the other two
     behind an interaction most people never make, and an auto-rotating one is a
     WCAG 2.2.2 problem. The shops this is modelled on, Etsy included, are grids.

     Full page thumbnails rather than a crop. The three covers put their titles
     at different heights, so no single crop frames all three, and a whole sheet
     also says "this is a printable page" in a way a cropped one does not. */
  .shop { padding-top: 3.25rem; }
  /* h2 since 1.38.0 so the outline does not jump h1 to h3, but it is still a
     section label rather than a heading to look at, so it keeps .label sizing. */
  .shop .label { color: var(--sand-ink); display: block; margin-bottom: 1.2rem;
    font: 600 11px/1.4 var(--font-sans); letter-spacing: .18em; text-transform: uppercase; }
  .pgrid { display: grid; grid-template-columns: 1fr; gap: 1rem; }
  @media (min-width: 46rem) { .pgrid { grid-template-columns: repeat(3, 1fr); }}
  .pcard {
    display: flex; flex-direction: column; overflow: hidden;
    background: var(--paper); border: 1px solid var(--rule);
    border-radius: 3px; box-shadow: var(--shadow);
  }
  .pcard img {
    display: block; width: 100%; height: auto; aspect-ratio: 17 / 22;
    object-fit: cover; background: #fff;
    border-bottom: 1px solid var(--rule);
  }
  .pcard-body {
    padding: 1.1rem 1.15rem 1.25rem; display: flex; flex-direction: column; flex: 1;
  }
  .pcard h3 { margin-bottom: .3rem; }
  /* Scoped through .pcard, not left as bare .pcard-line / .pcard-price. This
     card sits inside a .band, and `.band p` is a class plus an element, so it
     outranks a lone class and was repainting the price in the band's muted
     tone. The price has to be the brand red here for the same reason it is on
     the buy cards: it reads as a price rather than as more type. */
  .pcard .pcard-line { color: var(--muted); font-size: .91rem; margin: 0 0 1rem; flex: 1; }
  .pcard .pcard-price {
    font: 700 1.3rem/1 var(--font-serif); color: var(--must);
    margin: 0 0 .8rem; letter-spacing: -.01em;
  }
  .pcard .btn { text-align: center; display: block; }
  /* On a phone three upright cards were 2.9 screens of contents list sitting
     directly on top of the sections it lists. Turned on its side the same card
     is a shelf row: the cover still identifies the product, the price and the
     button still work, and the grid costs about a screen instead of three. */
  @media (max-width: 46rem) {
    .pcard { flex-direction: row; align-items: stretch; }
    .pcard img {
      width: 34%; max-width: 132px; flex: none; align-self: flex-start;
      border-bottom: 0; border-right: 1px solid var(--rule);
    }
    .pcard-body { padding: .95rem 1.05rem 1.05rem; }}

  .shop-free { margin: 1.4rem 0 0; font-size: .93rem; }
  /* Nothing had styled a bare link here before, so this one came out as the
     browser's default blue. Note the ground: section.shop carries no .band, so
     this sits on paper and wants the ink gold, not the lifted one. The comment
     here used to say "the deep ground", which measurement disproved. */
  .shop-free a { color: var(--sand-ink); }

  .pcard, .tier, .month-card, .buy, .preview-warning, .android-note,
  .picker, .followup {
    --surface: var(--paper);
    --on-surface: var(--ink);
    --on-surface-mute: var(--muted);
    --hairline: var(--rule-soft);
  }

  /* The four feed cards as a group. Was an inline style on a div that was
     never closed, so the </div> written for .wrap closed this instead and
     .wrap was left to the parser. */
  .tier-list { margin-top: 2rem; }
  .tier {
    background: var(--paper); border: 1px solid var(--rule);
    border-left: 3px solid var(--tier-color); border-radius: 3px;
    box-shadow: var(--shadow); padding: 1.5rem 1.6rem;
    margin-bottom: 1rem; position: relative;
  }
  .tier--must   { --tier-color: var(--must); }
  .tier--should { --tier-color: var(--should); }
  .tier--above  { --tier-color: var(--above); }
  /* The monthly feed is not a fourth level of effort, so it takes the sand
     accent rather than extending the red/teal/olive ladder the three tiers
     use. Different kind of thing, different color family. */
  .tier--monthly { --tier-color: var(--sand-ink); }
  .tier h3 {
    color: var(--tier-color); font-size: 1.16rem;
    display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap;
  }
  .tier .badge {
    font: 600 10px/1 var(--font-sans);
    letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
    border: 1px solid var(--rule); padding: .32rem .45rem; border-radius: 2px;
  }
  /* The free feeds sit immediately below the $12.99 kit, which is exactly where
     a scanner assumes the price carries down. "Free" is repeated at the heading
     and again on every card so no one has to read a sentence to learn it. Both
     take their color from --accent against --paper, so the pair inverts with
     the palette and stays legible in dark mode. */
  .free-tag {
    font: 700 11px/1 var(--font-sans);
    letter-spacing: .14em; text-transform: uppercase;
    background: var(--accent); color: var(--paper);
    padding: .42rem .58rem; border-radius: 2px;
    display: inline-block; vertical-align: .28em; white-space: nowrap;
  }
  .tier .badge--free { color: var(--accent); border-color: var(--accent); }
  .tier p { margin: .45rem 0 0; color: var(--muted); font-size: .97rem; }
  .tier .actions { margin-top: 1.15rem; }
  .row { display: flex; gap: 1rem; padding: 1.15rem 0; border-bottom: 1px solid var(--rule-soft); }
  .row:last-child { border-bottom: 0; padding-bottom: .35rem; }
  @media (max-width: 30rem) {
    .row { flex-direction: column; gap: .35rem; }}

  /* --- lifespan chart --------------------------------------------------- */
  .lifespans { margin-top: 2rem; }
  .ls {
    display: grid; grid-template-columns: 11.5rem 1fr 4.6rem;
    align-items: center; gap: .9rem; padding: .5rem 0;
  }
  .ls + .ls { border-top: 1px solid var(--hairline); }
  .ls .name { font-size: .93rem; color: var(--on-surface); }
  .ls .track { position: relative; height: 9px; background: var(--hairline); border-radius: 9px; }
  .ls .fill {
    position: absolute; top: 0; bottom: 0; border-radius: 9px;
    background: linear-gradient(90deg,
      color-mix(in srgb, var(--accent) 55%, transparent), var(--accent));
  }
  .ls .yrs {
    font: 600 12px var(--font-sans);
    color: var(--on-surface-mute); text-align: right; white-space: nowrap;
  }
  .band .ls .fill { background: linear-gradient(90deg,
      color-mix(in srgb, var(--accent) 45%, transparent), var(--accent)); }
  /* Keep the short-lifespan items reading as a warning on the deep ground too. That contrast is the whole point of the chart. */
  .band .ls--short .fill { background: linear-gradient(90deg,
      color-mix(in srgb, var(--must-lift) 45%, transparent), var(--must-lift)); }
  .band .ls--short .yrs { color: var(--must-lift); }
  .ls--short .fill { background: linear-gradient(90deg,
      color-mix(in srgb, var(--must) 55%, transparent), var(--must)); }
  .ls--short .yrs { color: var(--must); }
  .scale {
    display: grid; grid-template-columns: 11.5rem 1fr 4.6rem; gap: .9rem;
    margin-top: .6rem;
  }
  .scale .ticks {
    grid-column: 2; display: flex; justify-content: space-between;
    font: 600 10px var(--font-sans);
    letter-spacing: .1em; color: var(--on-surface-mute);
  }
  @media (max-width: 34rem) {
    .ls { grid-template-columns: 1fr 3.9rem; row-gap: .3rem; }
    .ls .name { grid-column: 1 / -1; }
    .scale { grid-template-columns: 1fr 3.9rem; }
    .scale .ticks { grid-column: 1; }}

  /* --- buy -------------------------------------------------------------- */
  .buy {
    background: var(--paper); border: 1px solid var(--rule); border-radius: 3px;
    box-shadow: var(--shadow-lg); padding: 2rem 1.8rem; margin-top: 2.25rem; text-align: center;
  }
  .buy h3 { font-size: 1.35rem; margin-bottom: .55rem; }

  /* The price belongs next to the button. Set as a small uppercase label above
     the heading it read as a section eyebrow, because that is exactly how the
     eyebrows are styled, and it sat five lines from the thing you click. */
  .buy-action {
    margin-top: 1.6rem; padding-top: 1.5rem;
    border-top: 1px solid var(--rule);
  }
  /* The auto side margins matter: .buy p caps these at 30rem, so a shorthand
     margin that zeroes left and right drops them against the left edge of a
     wider card instead of centering them. */
  .buy-price { margin: 0 auto 1.35rem !important; }
  .buy-price .amt {
    display: block;
    font: 700 2.7rem/1 var(--font-serif);
    color: var(--must); letter-spacing: -.02em;
  }
  .buy-price .per {
    display: block; margin-top: .35rem;
    font: 400 .85rem/1.4 var(--font-sans);
    color: var(--muted);
  }
  .buy-note {
    margin: .9rem auto 0 !important;
    font: 400 .82rem/1.5 var(--font-sans);
    color: var(--muted) !important;
  }
  /* A buy button should look heavier than a nav link, and wide enough to hold
     its own against a 2.6rem price directly above it. */
  #buy-slot .btn--lg, #binder-slot .btn--lg, #agent-slot .btn--lg {
    /* inline-block, or min-width is ignored: it does not apply to inline
       elements, and an anchor is inline unless told otherwise. */
    display: inline-block;
    padding: 1.1rem 2.4rem; font-size: 16.5px; border-radius: 3px;
    min-width: 17rem; letter-spacing: .01em;
    box-shadow: 0 2px 0 color-mix(in srgb, var(--sand-lift) 62%, black);
  }
  #buy-slot .btn--lg:hover, #binder-slot .btn--lg:hover, #agent-slot .btn--lg:hover { filter: brightness(1.06); }
  @supports not (background: color-mix(in srgb, red 50%, transparent)) {
    #buy-slot .btn--lg, #binder-slot .btn--lg, #agent-slot .btn--lg { box-shadow: 0 2px 0 #7a5719; }}
  .buy p { color: var(--muted); max-width: 30rem; margin-left: auto; margin-right: auto; font-size: .97rem; }
  .buy p:last-child { margin-bottom: 0; }
  /* --- signup ----------------------------------------------------------- */
  .sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
  }
  /* Off screen until focused, rather than hidden: a skip link that display:none
     hides is a skip link no keyboard reaches. z-index clears the sticky bar,
     which is 50. */
  .skip {
    position: absolute; left: .6rem; top: -4rem; z-index: 60;
    background: var(--accent); color: var(--paper);
    font: 600 13px/1 var(--font-sans);
    padding: .75rem 1rem; border-radius: 2px; text-decoration: none;
    transition: top .12s ease;
  }
  .skip:focus { top: .6rem; }
  .signup { margin: 0; }
  .signup-row {
    display: flex; gap: .5rem; flex-wrap: wrap; justify-content: center;
    max-width: 27rem; margin: 0 auto;
  }
  /* The border is --muted, not --rule. A --rule hairline against --paper is
     1.5:1 and the field's own fill is 1.1:1 against it, so the input was a
     rectangle you could only find by clicking where you guessed it was. WCAG
     1.4.11 asks 3:1 for the boundary of a control; --muted is 5.6:1 on paper
     and 6.6:1 in dark, and it is the token already used for text at this
     weight, so nothing new enters the palette. --rule stays correct for rules
     between things, which is what it is named for. */
  .signup-row input {
    flex: 1 1 12rem; min-width: 0;
    font: 400 15px/1 var(--font-sans);
    padding: .8rem .9rem; border-radius: 2px;
    border: 1px solid var(--muted); background: var(--bg); color: var(--ink);
  }
  .signup-row input:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
  }
  .signup-row .btn { flex: 0 0 auto; }
  .signup-note {
    font: 400 12.5px/1.5 var(--font-sans);
    color: var(--muted) !important; margin: .75rem 0 0 !important;
  }
  .signup-done {
    font: 400 .95rem/1.6 var(--font-sans);
    color: var(--muted) !important; margin: 0 !important;
  }
  .signup-done strong { display: block; color: var(--accent); margin-bottom: .2rem; }
  @media (max-width: 26rem) {
    .signup-row input, .signup-row .btn { flex: 1 1 100%; }}

  /* --- misc ------------------------------------------------------------- */
  .preview-warning {
    background: var(--paper); border: 1px solid var(--must); border-left: 3px solid var(--must);
    border-radius: 3px; padding: 1rem 1.2rem; margin-bottom: 1.6rem;
    font: 15px/1.55 var(--font-sans);
    color: var(--muted);
  }
  .preview-warning strong { color: var(--must); display: block; margin-bottom: .25rem; }

  /* Shown only to Android visitors. The Google Calendar app can claim links to
     calendar.google.com and then cannot act on them, so these users may tap the
     main button and land nowhere. This gives them the working route without
     making everyone else read about it. */
  /* A details element, so it costs one line until someone needs it. The
     summary keeps a comfortable tap target and its own focus ring, since on
     this section it is the only thing between a visitor and the buttons. */
  .android-note {
    background: var(--paper); border: 1px solid var(--rule);
    border-left: 3px solid var(--accent); border-radius: 3px;
    padding: .35rem .4rem; margin-bottom: 1.6rem;
    font: 15px/1.6 var(--font-sans);
    color: var(--muted);
  }
  .android-note summary {
    cursor: pointer; padding: .65rem .85rem; border-radius: 2px;
    font-weight: 600; color: var(--ink); list-style-position: outside;
  }
  .android-note summary::marker { color: var(--accent); }
  .android-note summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
  .android-note[open] summary { margin-bottom: .1rem; }
  .android-note p, .android-note ol { margin: 0 .85rem .6rem; }
  .android-note strong { color: var(--ink); }
  .android-note ol { padding-left: 1.15rem; margin-top: .5rem; }
  .android-note li { margin-bottom: .3rem; }
  .android-note a { color: var(--accent); }

  .howto { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 1.75rem 2.25rem; margin-top: 2rem; }
  .howto h3 { font-size: .98rem; color: var(--on-surface); }
  .howto p, .howto li { color: var(--on-surface-mute); font-size: .93rem; margin: 0 0 .55rem; }
  .howto strong, .howto em { color: var(--on-surface); }
  .howto a { color: var(--sand-lift); }
  .howto ol { padding-left: 1.1rem; margin: .3rem 0 .55rem; }
  .howto li { margin-bottom: .2rem; }

  /* Per-app setup steps, folded away. This is post-purchase support sitting in
     a pre-purchase slot: three quarters of the section's text was instructions
     for an app the reader has not chosen yet, and the Android failure note
     apologized for a problem before anyone had clicked anything. Folded, it
     reaches the person who has that problem and nobody else.

     Native <details>, not a scripted accordion, so it still opens with
     JavaScript blocked and browser find-in-page can still reach inside it. */
  .setup { margin-top: 2.25rem; border-top: 1px solid var(--hairline); }
  .setup details { border-bottom: 1px solid var(--hairline); }
  .setup summary {
    cursor: pointer; list-style: none; padding: .95rem 0;
    font: 600 .98rem/1.35 var(--font-sans);
    color: var(--on-surface); display: flex; align-items: center; gap: .6rem;
  }
  .setup summary::-webkit-details-marker { display: none; }
  .setup summary::before {
    content: "+"; flex: none; width: 1rem;
    font: 400 1.1rem/1 var(--font-sans); color: var(--sand-lift);
  }
  /* U+2212 minus, not a hyphen and not an en dash. It is the character that
     pairs with "+", and the house rule keeps dashes off this site. */
  .setup details[open] > summary::before { content: "\2212"; }
  .setup summary:focus-visible { outline: 2px solid var(--sand-lift); outline-offset: 2px; }
  .setup .body { padding: 0 0 1.1rem 1.6rem; }
  .setup .body p, .setup .body li {
    color: var(--on-surface-mute); font-size: .93rem; margin: 0 0 .55rem;
  }
  .setup .body strong, .setup .body em { color: var(--on-surface); }
  .setup .body ol { padding-left: 1.1rem; margin: .3rem 0 .55rem; }
  .setup .body li { margin-bottom: .2rem; }

  /* --- who made this ---------------------------------------------------- */
  /* Narrower than the deck and set at reading size. This is the one place on
     the page written in the first person, so it is allowed to look like prose
     rather than like a product section. */
  .byline { max-width: 34rem; margin-top: 1.6rem; }
  .byline p { color: var(--muted); }
  .byline .sig {
    font: 600 11px/1 var(--font-sans);
    letter-spacing: .14em; text-transform: uppercase;
    color: var(--accent); margin-top: 1.5rem;
  }

  /* The footer moved to nav.css in 1.34.0, so the nine pages that do not load
     this file can have it too. .foot-links went with the move rather than into
     it: it was the one-line footer that /guides/ and /calculator/ used to
     carry, and nothing uses it now that every page carries the grid. */
  /* code paints itself a paper ground, so it cannot inherit its colour from
     whatever section it lands in. Inside the band on /calendars/ it was
     inheriting --on-deep-mute and rendering at 1.85:1, which is not readable
     at all. The addbyurl address is the one string on that page a reader has
     to be able to copy by eye. */
  code {
    font: 13px/1.4 ui-monospace, SFMono-Regular, Consolas, monospace;
    color: var(--ink);
    background: var(--paper-2); border: 1px solid var(--rule);
    padding: .1rem .35rem; border-radius: 2px; word-break: break-all;
  }

  /* =========================================================================
     1.25.0 redesign components. Everything below follows the design review:
     the site reads as a Gulf Coast field guide, not a home-services company.
     ========================================================================= */

  /* --- hero, two columns ------------------------------------------------- */
  /* The headline column and a floating "this month" card over the photograph.
     The card is the product demonstrating itself, filled by month-tasks.js,
     and it stays hidden until the script gives it real content: an empty card
     is worse than no card. On narrow screens it drops below the copy. */
  .hero-grid { display: grid; gap: 2.5rem; align-items: center; }
  @media (min-width: 56rem) { .hero-grid { grid-template-columns: 7fr 5fr; }}
  .hero-month {
    background: color-mix(in srgb, var(--paper) 94%, transparent);
    color: var(--ink);
    border: 1px solid var(--rule); border-radius: 4px;
    box-shadow: var(--shadow-lg); padding: 1.35rem 1.45rem;
    max-width: 22rem; justify-self: end; width: 100%;
  }
  @supports not (background: color-mix(in srgb, red 50%, transparent)) {
    .hero-month { background: var(--paper); }}
  .hero-month .label { color: var(--sand-ink); display: block; margin-bottom: .2rem; }
  .hero-month .hm-month {
    font: 600 1.55rem/1 var(--font-serif); letter-spacing: -.015em;
    margin: 0 0 .85rem; color: var(--ink);
  }
  .hero-month ul { list-style: none; margin: 0 0 1rem; padding: 0; }
  .hero-month li {
    font: 400 .92rem/1.45 var(--font-sans); color: var(--muted);
    padding: .42rem 0 .42rem 1.45rem; position: relative;
    border-top: 1px solid var(--rule-soft);
  }
  .hero-month li:first-child { border-top: 0; }
  .hero-month li::before {
    content: "\2713"; position: absolute; left: .15rem; top: .42rem;
    color: var(--accent); font-weight: 700;
  }
  .hero-month .hm-more {
    font: 600 .88rem/1.3 var(--font-sans); color: var(--accent);
    text-decoration: none;
  }
  .hero-month .hm-more:hover { text-decoration: underline; }
  .free-line {
    margin: .9rem 0 0; font: 600 11.5px/1.6 var(--font-sans);
    letter-spacing: .08em; text-transform: uppercase;
    color: var(--on-deep-mute);
  }
  .hero-alt {
    margin: 1rem 0 0; font: 400 .95rem/1.55 var(--font-sans);
    color: var(--on-deep-mute);
  }
  .hero-alt a { color: var(--sand-lift); font-weight: 600; }

  /* --- the oversized stat band ------------------------------------------- */
  /* Four numbers big enough to sell the concept before anyone reads a word.
     Dark green ground, enormous warm-white numerals, tiny uppercase captions. */
  .bigstats { padding: 3.25rem 0; }
  .bigstats .grid {
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 2.2rem 1.5rem;
  }
  @media (min-width: 46rem) { .bigstats .grid { grid-template-columns: repeat(4, 1fr); }}
  .bigstats .n {
    display: block; font: 600 clamp(3rem, 7vw, 4.6rem)/1 var(--font-serif);
    letter-spacing: -.03em; color: var(--on-deep);
  }
  .bigstats .n .unit { font-size: .38em; letter-spacing: 0; color: var(--sand-lift); }
  .bigstats .t {
    display: block; margin-top: .55rem;
    font: 600 10.5px/1.5 var(--font-sans);
    letter-spacing: .14em; text-transform: uppercase;
    color: var(--on-deep-mute);
  }

  /* --- the three doors ---------------------------------------------------- */
  /* The decision system: keep up, get ready, see what's aging. Each door names
     the reader's situation first and the thing behind it second. */
  .doors { display: grid; gap: 1rem; margin-top: 2rem; }
  @media (min-width: 46rem) { .doors { grid-template-columns: repeat(2, 1fr); }}
  @media (min-width: 72rem) { .doors { grid-template-columns: repeat(4, 1fr); }}
  .door {
    display: flex; flex-direction: column; text-decoration: none;
    background: var(--paper); border: 1px solid var(--rule);
    border-top: 3px solid var(--door-color, var(--accent));
    border-radius: 4px; box-shadow: var(--shadow); padding: 1.5rem 1.5rem 1.35rem;
    transition: box-shadow .15s ease, transform .15s ease;
  }
  .door:hover { box-shadow: var(--shadow-lg); transform: translateY(-2px); }
  .door--calendar { --door-color: var(--accent); }
  .door--storm    { --door-color: var(--must); }
  .door--aging    { --door-color: var(--sand-ink); }
  .door--grants   { --door-color: var(--above); }
  .door .label { color: var(--door-color); display: block; margin-bottom: .55rem; }
  .door h3 { font: 600 1.3rem/1.2 var(--font-serif); color: var(--ink); margin-bottom: .45rem; }
  .door .door-icon { color: var(--ink); opacity: .75; margin: .35rem 0 .7rem; }
  .door p { font: 400 .93rem/1.55 var(--font-sans); color: var(--muted); margin: 0 0 1.1rem; flex: 1; }
  .door .go { font: 600 .9rem/1.3 var(--font-sans); color: var(--door-color); }

  /* --- the desk ----------------------------------------------------------- */
  /* Real page tops overlapping like sheets emptied onto a kitchen table. The
     images are the same protective crops build_site_images.py already makes;
     the drama is in the arrangement, not in showing more of any page. */
  .desk {
    position: relative; margin-top: 2.5rem;
    display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.1rem;
  }
  @media (min-width: 46rem) { .desk { grid-template-columns: repeat(3, 1fr); }}
  .desk figure { margin: 0; position: relative; }
  .desk img {
    display: block; width: 100%; height: auto; background: #fff;
    border: 1px solid var(--hairline); border-radius: 2px;
    box-shadow: var(--shadow-lg);
  }
  .desk figure:nth-child(odd)  { transform: rotate(-1.4deg); }
  .desk figure:nth-child(even) { transform: rotate(1.1deg) translateY(.5rem); }
  .desk figcaption {
    font: 600 10.5px/1.4 var(--font-sans); letter-spacing: .1em;
    text-transform: uppercase; color: var(--on-surface-mute);
    margin-top: .6rem;
  }
  .desk-note { color: var(--on-surface-mute); font-size: .88rem; margin: 1.6rem 0 0; }
  .desk-note a { color: var(--sand-lift); }
  section:not(.band) .desk-note a { color: var(--accent); }

  /* --- width caps for the 71rem wrap --------------------------------------- */
  /* The grids scale with the wide layout; these are columns of text and
     controls, which do not. Each keeps roughly the width it was designed at
     and centers in the wider page. */
  .picker { max-width: 52rem; margin-left: auto; margin-right: auto; }
  .buy { max-width: 48rem; margin-left: auto; margin-right: auto; }
  .res-list { max-width: 54rem; }
  .tier { max-width: 54rem; }
  .preview-warning, .android-note { max-width: 54rem; }

  /* WCAG 2.1.1: a region that scrolls has to be reachable from a keyboard, and
     these are the only two on the site. Both carry role="group" and an
     aria-label in the markup; this is the ring that makes the tab stop visible
     when it lands there. */
  .scroll:focus-visible, .prow:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 3px;
  }

  /* --- /about/ ------------------------------------------------------------
     Prose in the sans face at reading width. The page is the answer to "who is
     telling me this", so it is written to be read rather than scanned, which
     is the opposite of most of the site. */
  .about-prose { max-width: 40rem; }
  .about-prose p {
    font: 400 1rem/1.75 var(--font-sans); color: var(--on-surface-mute);
    margin: 0 0 1.15rem;
  }
  .about-prose strong { color: var(--on-surface); }
  .about-prose .sig {
    font: 600 .92rem/1.4 var(--font-sans); color: var(--on-surface);
    margin-top: 1.6rem;
  }

  /* --- the home page questions -------------------------------------------
     Disclosures rather than seven open answers: the list of questions is the
     useful part at a glance, and the answers are long enough that all seven
     open would bury the section below. Native <details>, so it is keyboard
     operable and screen-reader announced without a script, and it works with
     scripting off. */
  .faq { max-width: 46rem; margin: 2.25rem auto 0; }
  .faq details {
    border-top: 1px solid var(--hairline);
  }
  .faq details:last-child { border-bottom: 1px solid var(--hairline); }
  .faq summary {
    list-style: none; cursor: pointer;
    display: flex; align-items: flex-start; gap: .75rem;
    padding: 1.05rem 0; min-height: 44px; box-sizing: border-box;
    font: 600 1rem/1.45 var(--font-sans); color: var(--on-surface);
  }
  .faq summary::-webkit-details-marker { display: none; }
  .faq summary::marker { content: ""; }
  .faq summary::before {
    content: "+"; flex: none; width: 1rem;
    font: 400 1.25rem/1.25 var(--font-sans); color: var(--marker);
  }
  .faq details[open] summary::before { content: "\2212"; }
  .faq summary:hover { color: var(--marker); }
  .faq summary:focus-visible { outline: 2px solid var(--marker); outline-offset: 2px; }
  .faq-body { padding: 0 0 1.15rem 1.75rem; }
  .faq-body p {
    font: 400 .95rem/1.7 var(--font-sans); color: var(--on-surface-mute);
    margin: 0; max-width: 38rem;
  }

  /* The line under the grant cards. The audit's H5: this page asserted four
     dollar figures and a county list with no date on any of them, while every
     detail page dated the same facts correctly and the trust panel below
     promised in so many words that it would. */
  .grants-checked {
    margin: 2rem auto 0; max-width: 42rem;
    font: 400 .9rem/1.65 var(--font-sans); color: var(--on-surface-mute);
  }
  .grants-checked #grants-checked { display: block; margin-top: .35rem; opacity: .8; }

  /* --- /storm/ ------------------------------------------------------------
     The page used to be four one-sentence cards and a buy button, which the
     audit called the strongest argument on the site that this is a storefront.
     It carries the content its own title promises now, in the same pointer
     form the resource pages use: organise the question, name the institution
     that owns the answer, and date the check. */
  .checked-line {
    font: 400 .88rem/1.6 var(--font-sans);
    color: var(--on-surface-mute); max-width: 40rem;
    margin: 1.4rem 0 0; padding-top: 1rem;
    border-top: 1px solid var(--hairline);
  }
  .storm-steps { display: grid; gap: 1rem; margin-top: 2rem; }
  @media (min-width: 62rem) { .storm-steps { grid-template-columns: repeat(3, 1fr); }}
  .storm-step {
    background: var(--paper); border: 1px solid var(--rule);
    border-top: 3px solid var(--accent); border-radius: 4px;
    padding: 1.4rem 1.4rem 1.5rem;
  }
  .storm-step h3 { font-size: 1.02rem; margin-bottom: .5rem; }
  .storm-step p {
    font: 400 .93rem/1.6 var(--font-sans); color: var(--muted); margin: 0 0 .8rem;
  }
  .storm-step p:last-child { margin-bottom: 0; }
  .storm-step strong { color: var(--ink); }
  .storm-src {
    font-size: .84rem !important; font-style: italic;
    padding-top: .7rem; border-top: 1px solid var(--rule-soft);
  }
  .storm-links { list-style: none; margin: .2rem 0 0; padding: 0; }
  .storm-links li { margin: 0 0 .45rem; font: 400 .93rem/1.4 var(--font-sans); }
  .storm-plain {
    margin: 1.8rem 0 0; padding: 0; list-style: none; max-width: 44rem;
  }
  .storm-plain li {
    font: 400 .97rem/1.65 var(--font-sans); color: var(--on-surface-mute);
    margin: 0 0 1rem; padding-left: 1.1rem; position: relative;
  }
  .storm-plain li::before {
    content: ""; position: absolute; left: 0; top: .62em;
    width: 5px; height: 5px; border-radius: 50%; background: var(--marker);
  }
  .storm-plain strong { color: var(--on-surface); }
  .storm-note {
    margin: 2rem 0 0; max-width: 44rem;
    font: 400 .95rem/1.65 var(--font-sans); color: var(--on-surface-mute);
    border-left: 3px solid var(--marker); padding-left: 1rem;
  }
  .storm-note strong { color: var(--on-surface); }
  .btn--hero {
    background: var(--deep); border-color: var(--deep); color: var(--on-deep);
    text-transform: uppercase; letter-spacing: .09em; font-size: 12px;
    padding: 1rem 1.4rem;
  }
  .btn--hero:hover { filter: brightness(1.18); }

  /* --- centered section heads --------------------------------------------- */
  .center { text-align: center; }
  .center .deck { margin-left: auto; margin-right: auto; }
  .center .sub {
    font: 400 .95rem/1.6 var(--font-sans); color: var(--on-surface-mute);
    max-width: 30rem; margin: 0 auto 1rem;
  }

  /* --- stat band, centered with dividers ---------------------------------- */
  .bigstats .grid > div { text-align: center; padding: 0 .5rem; }
  @media (min-width: 46rem) {
    .bigstats .grid { gap: 0; }
    .bigstats .grid > div + div { border-left: 1px solid color-mix(in srgb, var(--on-deep) 18%, transparent); }}

  /* --- factors, borderless icon row ---------------------------------------- */
  .factors--plain { background: none; border: 0; gap: 1.5rem 0; margin-top: 2.5rem; }
  .factors--plain .factor { background: none; padding: 0 1.4rem; }
  @media (min-width: 60rem) {
    .factors--plain .factor + .factor { border-left: 1px solid var(--hairline); }}
  .factors--plain .factor h3 {
    font: 700 11px/1.4 var(--font-sans); letter-spacing: .12em;
    text-transform: uppercase; margin-bottom: .3rem;
  }
  .factors--plain .factor p { font-size: .87rem; }
  .factors--plain .factor svg { margin-bottom: .6rem; }

  /* --- the three levels ---------------------------------------------------- */
  .levels { display: grid; gap: 1rem; margin-top: 2.25rem; text-align: left; }
  @media (min-width: 46rem) { .levels { grid-template-columns: repeat(3, 1fr); }}
  .level {
    background: var(--paper); border: 1px solid var(--rule); border-radius: 4px;
    box-shadow: var(--shadow); padding: 1.5rem 1.5rem 1.35rem;
    --surface: var(--paper); --on-surface: var(--ink); --on-surface-mute: var(--muted);
  }
  .level .chip {
    width: 30px; height: 30px; border-radius: 3px; margin-bottom: .8rem;
    display: flex; align-items: center; justify-content: center;
    background: var(--level-color); color: var(--on-tier);
  }
  .level h3 {
    font: 700 11.5px/1.3 var(--font-sans); letter-spacing: .13em;
    text-transform: uppercase; color: var(--level-color); margin-bottom: .55rem;
  }
  .level p { font: 400 .93rem/1.55 var(--font-sans); color: var(--muted); margin: 0 0 .35rem; }
  .level .go {
    display: inline-block; margin-top: .6rem;
    font: 600 .88rem/1.3 var(--font-sans); color: var(--ink); text-decoration: none;
  }
  .level .go:hover { color: var(--level-color); }
  .check-list { list-style: none; margin: 0 0 .4rem; padding: 0; }
  .check-list li {
    font: 400 .9rem/1.45 var(--font-sans); color: var(--muted);
    padding: .4rem 0 .4rem 1.4rem; position: relative;
    border-top: 1px solid var(--rule-soft);
  }
  .check-list li:first-child { border-top: 0; }
  .check-list li::before {
    content: "\2713"; position: absolute; left: .1rem; top: .4rem;
    color: var(--level-color, var(--accent)); font-weight: 700;
  }

  /* --- the fanned spread and the benefits ---------------------------------- */
  .fan {
    display: flex; justify-content: center; align-items: flex-start;
    margin: 2.5rem 0 1rem; padding: 0 1rem;
  }
  .fan figure { margin: 0; width: 27%; min-width: 9.5rem; }
  .fan figure + figure { margin-left: -2.5%; }
  .fan img {
    display: block; width: 100%; height: auto; background: #fff;
    border: 1px solid var(--rule); border-radius: 2px; box-shadow: var(--shadow-lg);
  }
  .fan figure:nth-child(1) { transform: rotate(-3deg) translateY(.6rem); }
  .fan figure:nth-child(2) { transform: rotate(-.8deg); }
  .fan figure:nth-child(3) { transform: rotate(1deg) translateY(.3rem); }
  .fan figure:nth-child(4) { transform: rotate(3.2deg) translateY(.9rem); }
  @media (max-width: 40rem) {
    .fan { flex-wrap: wrap; gap: 1rem; }
    .fan figure { width: 46%; }
    .fan figure + figure { margin-left: 0; }}
  /* The full-width fan: bigger sheets, per Chad. On the band the white pages
     carry their own light, so the shadows can be deeper. */
  .fan--wide { margin-top: 2.5rem; }
  .fan--wide figure { width: 24%; min-width: 12rem; }
  .fan--wide img { box-shadow: 0 3px 6px rgba(0,0,0,.25), 0 24px 60px -18px rgba(0,0,0,.5); }
  .btn--dark {
    background: var(--deep); border-color: var(--deep); color: var(--on-deep);
    text-transform: uppercase; letter-spacing: .09em; font-size: 12px;
  }
  .btn--dark:hover { filter: brightness(1.18); border-color: var(--deep); }
  .band .btn--dark { background: var(--sand-lift); border-color: var(--sand-lift); color: var(--deep); }

  /* --- product cards, mockup form ------------------------------------------ */
  .pcard .pcard-pages {
    font: 400 .82rem/1.4 var(--font-sans); color: var(--muted); margin: 0 0 .5rem;
  }
  .btn--outline {
    text-transform: uppercase; letter-spacing: .1em; font-size: 11.5px;
    border-color: var(--ink); color: var(--ink); text-align: center;
  }
  .btn--outline:hover { background: var(--ink); color: var(--paper); border-color: var(--ink); }

  /* --- the resources index ------------------------------------------------- */
  /* The categories are h2 since 1.38.0 and the rows h3, so the outline says
     which is which. The size stays where the design put it: this is a shelf
     label, not a page heading. */
  .res-cat {
    font: 600 1.05rem/1.3 var(--font-sans); letter-spacing: .02em;
    color: var(--ink); margin: 3rem 0 .4rem; max-width: 54rem;
  }
  .res-cat:first-of-type { margin-top: 2.5rem; }
  .res-cat-sub {
    font: 400 .95rem/1.55 var(--font-sans); color: var(--muted);
    margin: 0; max-width: 54rem;
  }
  .res-list { margin-top: 1.25rem; }
  .res-row {
    display: block; text-decoration: none; color: var(--ink);
    padding: 1.5rem 0; border-top: 1px solid var(--rule-soft);
  }
  .res-row:last-child { border-bottom: 1px solid var(--rule-soft); }
  .res-row h3 { font: 600 1.3rem/1.3 var(--font-serif); color: var(--ink); margin-bottom: .35rem; }
  .res-row:hover h3 { color: var(--accent); }
  .res-row p { font: 400 .95rem/1.55 var(--font-sans); color: var(--muted); margin: 0 0 .55rem; }
  .res-row .meta {
    font: 600 11px/1.4 var(--font-sans); letter-spacing: .08em;
    text-transform: uppercase; color: var(--muted);
  }
  .res-row .meta .read { color: var(--accent); }

  /* =========================================================================
     1.29.0: the final mockup convergence. Two-panel bands, the inline
     borrowed-time panel, the footer signup, and the chips the widget shares
     with the calculator page.
     ========================================================================= */

  .duo { display: grid; gap: 1rem; margin-top: 2rem; align-items: stretch; }
  @media (min-width: 60rem) { .duo { grid-template-columns: 3fr 2fr; }}
  .panel {
    background: var(--paper); border: 1px solid var(--rule); border-radius: 4px;
    box-shadow: var(--shadow); padding: 1.6rem 1.6rem 1.5rem;
    --surface: var(--paper); --on-surface: var(--ink); --on-surface-mute: var(--muted);
    --hairline: var(--rule-soft);
  }
  .panel > .label { color: var(--sand-ink); display: block; margin-bottom: .35rem; }
  .panel h3 { font: 600 1.35rem/1.25 var(--font-serif); color: var(--ink); margin-bottom: .35rem; }
  .panel .panel-sub { font: 400 .92rem/1.5 var(--font-sans); color: var(--muted); margin: 0 0 1.2rem; }

  .now-tiers { display: grid; gap: 1.25rem; }
  @media (min-width: 34rem) { .now-tiers { grid-template-columns: repeat(3, 1fr); }}
  .now-tier h4 {
    font: 700 10.5px/1.3 var(--font-sans); letter-spacing: .13em;
    text-transform: uppercase; color: var(--tier-color, var(--accent));
    margin: 0 0 .3rem; display: flex; align-items: center; gap: .4rem;
  }
  .now-tier { --level-color: var(--tier-color); }
  .now-tier--must   { --tier-color: var(--must); }
  .now-tier--should { --tier-color: var(--sand-ink); }
  .now-tier--above  { --tier-color: var(--should); }
  .now-tier .go {
    display: inline-block; margin-top: .4rem;
    font: 600 .84rem/1.3 var(--font-sans); color: var(--ink); text-decoration: none;
  }
  .now-tier .go:hover { color: var(--tier-color); }

  /* The inline borrowed-time panel, rendered by calc-widget.js. */
  .mini-year-row { display: flex; gap: .5rem; align-items: stretch; margin: 0 0 1rem; }
  .mini-year-row input {
    width: 7.5rem; font: 600 1.35rem/1.2 var(--font-serif); text-align: center;
    padding: .55rem .5rem; border-radius: 3px;
    border: 1px solid var(--muted); background: var(--bg); color: var(--ink);
  }
  .mini-year-row input:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
  }
  .mini-sys {
    display: flex; gap: .8rem; align-items: center;
    border: 1px solid var(--rule); border-radius: 4px; background: var(--bg);
    padding: .7rem .85rem; margin: 0 0 .6rem;
  }
  .mini-main { flex: 1; min-width: 0; }
  .mini-name {
    display: block; font: 700 10px/1.4 var(--font-sans);
    letter-spacing: .11em; text-transform: uppercase; color: var(--muted);
  }
  .mini-age { display: block; font: 600 1.05rem/1.3 var(--font-serif); color: var(--ink); }
  .mini-life { display: block; font: 400 11.5px/1.45 var(--font-sans); color: var(--muted); }
  .chip {
    flex: none; font: 700 9px/1.35 var(--font-sans);
    letter-spacing: .08em; text-transform: uppercase;
    padding: .45rem .55rem; border-radius: 2px; text-align: center; max-width: 7.5rem;
  }
  .chip--overdue { background: var(--must); color: var(--on-tier); }
  .chip--soon    { background: var(--must); color: var(--on-tier); }
  .chip--watch   { background: color-mix(in srgb, var(--sand) 78%, black); color: #fdfbf4; }
  .chip--ok      { background: var(--should); color: var(--on-tier); }

  /* Story left, the trust list right. */
  .trust-list { display: flex; flex-direction: column; gap: 1.1rem; }
  .trust-item { display: flex; gap: .85rem; align-items: flex-start; }
  .trust-item svg { flex: none; color: var(--sand-ink); margin-top: .1rem; }
  .trust-item h3 {
    font: 700 .95rem/1.35 var(--font-sans); color: var(--on-surface); margin: 0 0 .15rem;
  }
  .trust-item p { font: 400 .87rem/1.5 var(--font-sans); color: var(--on-surface-mute); margin: 0; }

  /* The footer signup row, above the link columns. */
  .foot-signup {
    display: grid; gap: 1.25rem; margin: 0 0 2.25rem; padding: 0 0 2.25rem;
    border-bottom: 1px solid color-mix(in srgb, var(--on-deep) 15%, transparent);
  }
  @media (min-width: 52rem) { .foot-signup { grid-template-columns: 1fr auto; align-items: center; }}
  .foot-signup .fs-title {
    font: 600 11px/1.5 var(--font-sans); letter-spacing: .14em;
    text-transform: uppercase; color: var(--sand-lift); margin: 0 0 .3rem;
  }
  .foot-signup .fs-sub { font: 400 .9rem/1.5 var(--font-sans); color: var(--on-deep-mute); margin: 0; max-width: 26rem; }
  .foot-signup .signup-row { justify-content: flex-start; max-width: none; }
  footer .signup-note { color: var(--on-deep-mute) !important; }
  footer .signup-done { color: var(--on-deep-mute) !important; }
  footer .signup-done strong { color: var(--sand-lift); }

  /* Removed in 1.37.0, all of it unreferenced by any page or script:
     .hero--split and its .hero-copy/.hero-photo half of an alternate hero,
     .res-card superseded by .res-row, .month-card, .mini-calc, .benefits,
     .get-grid, .pgrid--sm, .todo-cols, .followup, .buy-blurb, .stats,
     .pcard-more, .now-head, the .k-- and .level-- colour sets, .points--four,
     and .cta--alert/.cta--kit, which a comment above claimed had already gone
     with the 1.32.2 nav move and which had not.

     .android-note, .preview-warning, .btn--copied, the .mini-* set and the
     .chip-- set look unused by the same test and are not: calendar-buttons.js
     and calc-widget.js build that markup at runtime. Do not sweep them.
     ========================================================================= */
