/* HutScanner public pages - one stylesheet, no framework, no JS dependency.
 *
 * Tokens are lifted from branding/BRAND.md so these pages match the app instead
 * of inventing their own look. The previous version used a system font stack and
 * a green accent that appears nowhere in the brand spec, which also put us in the
 * same colour lane as the competitor everyone compares us to. Cold brand blue +
 * cyan is the spec, and it reads as an instrument rather than a hiking blog.
 *
 * Light is the default. Dark is supported both ways: prefers-color-scheme for the
 * OS signal, and [data-theme] if a toggle is ever added.
 */

/* Archivo, per BRAND.md section 4. Self-hosted subset woff2 (~45 KiB for three
 * weights) rather than a Google Fonts link: these pages otherwise make zero
 * third-party requests, and the privacy policy is the reason to keep it that way.
 * Rebuild with branding/build_web_fonts.py. */
@font-face {
  font-family: 'Archivo';
  src: url('/static/fonts/archivo-400.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Archivo';
  src: url('/static/fonts/archivo-600.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Archivo';
  src: url('/static/fonts/archivo-700.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}

:root {
  /* light (BRAND.md 3.1 neutrals + 3.2 brand accent) */
  --bg: #F4F7F9;          /* graphite.50  - page */
  --surface: #FBFCFD;     /* paper        - cards, header */
  --surface-2: #E9EEF1;   /* graphite.100 - insets, stat band */
  --fg: #0C1116;          /* ink          - body text */
  --heading: #121A21;     /* graphite.900 */
  --muted: #4A5A64;       /* graphite.600 - secondary text */
  --line: #D6DEE3;        /* graphite.200 - hairlines */
  --line-strong: #B4C0C7; /* graphite.300 */
  --brand: #114E6B;       /* brand.700    - primary actions */
  --brand-hover: #0B3247; /* brand.900 */
  --link: #1C6E92;        /* brand.500 */
  --accent: #1C6E92;      /* highlight text; cyan is too light on white */
  --cyan: #17B0C4;        /* brand cyan - rules, marks, active bits */
  --tint: #E6EFF4;        /* brand.tint  - tinted fills */
  --accent-fg: #FFFFFF;
  --shadow: 0 1px 2px rgba(12, 17, 22, .05), 0 8px 24px -12px rgba(12, 17, 22, .18);
  --shadow-lift: 0 2px 4px rgba(12, 17, 22, .06), 0 18px 44px -18px rgba(12, 17, 22, .28);
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #121A21;        /* graphite.900 */
    --surface: #1E2A33;   /* graphite.800 */
    --surface-2: #172128;
    --fg: #E9EEF1;        /* graphite.100 */
    --heading: #FBFCFD;
    --muted: #8A99A2;     /* graphite.400 */
    --line: #324049;      /* graphite.700 */
    --line-strong: #4A5A64;
    --brand: #1C6E92;     /* brand.500 - brand.700 is too dark on a dark page */
    --brand-hover: #17B0C4;
    --link: #5FD0DC;      /* cyan.soft */
    --accent: #17B0C4;    /* cyan reads correctly on dark */
    --cyan: #17B0C4;
    --tint: #0B3247;
    --accent-fg: #FFFFFF;
    --shadow: 0 1px 2px rgba(0, 0, 0, .3), 0 8px 24px -12px rgba(0, 0, 0, .5);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, .35), 0 18px 44px -18px rgba(0, 0, 0, .6);
  }
}
:root[data-theme="light"] {
  --bg: #F4F7F9; --surface: #FBFCFD; --surface-2: #E9EEF1;
  --fg: #0C1116; --heading: #121A21; --muted: #4A5A64;
  --line: #D6DEE3; --line-strong: #B4C0C7;
  --brand: #114E6B; --brand-hover: #0B3247; --link: #1C6E92;
  --accent: #1C6E92; --cyan: #17B0C4; --tint: #E6EFF4; --accent-fg: #FFFFFF;
}
:root[data-theme="dark"] {
  --bg: #121A21; --surface: #1E2A33; --surface-2: #172128;
  --fg: #E9EEF1; --heading: #FBFCFD; --muted: #8A99A2;
  --line: #324049; --line-strong: #4A5A64;
  --brand: #1C6E92; --brand-hover: #17B0C4; --link: #5FD0DC;
  --accent: #17B0C4; --cyan: #17B0C4; --tint: #0B3247; --accent-fg: #FFFFFF;
}

* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--fg);
  font: 400 16px/1.65 'Archivo', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--link); }
a:hover { color: var(--brand-hover); }
h1, h2, h3, .stat-num, .wm { color: var(--heading); letter-spacing: -.01em; }
img { max-width: 100%; }

.wrap { max-width: 720px; margin: 0 auto; padding: 24px 20px 8px; }
h1 { font-size: 1.75rem; font-weight: 700; line-height: 1.25; margin: 0 0 8px; }
h2 { font-size: 1.2rem; font-weight: 600; margin: 28px 0 8px; }
.breadcrumb { font-size: .85rem; color: var(--muted); margin-bottom: 14px; }
.breadcrumb a { color: var(--muted); }
.facts { color: var(--muted); margin: 0 0 6px; }
.blurb { margin: 8px 0 4px; }

section { border-top: 1px solid var(--line); padding-top: 4px; }
.availability .checked { font-weight: 600; }
.disclaimer, .note { color: var(--muted); font-size: .9rem; }

.legs { list-style: none; padding: 0; margin: 8px 0; }
.legs li { padding: 6px 0; border-bottom: 1px solid var(--line); }

/* --- Buttons -------------------------------------------------------------- */
.btn, .btn-out {
  display: inline-block; padding: 11px 18px; border-radius: 10px;
  text-decoration: none; font-weight: 600;
  transition: background-color .15s ease, border-color .15s ease, transform .15s ease;
}
.btn { background: var(--brand); color: var(--accent-fg); }
.btn:hover { background: var(--brand-hover); color: var(--accent-fg); }
.btn-out { border: 1px solid var(--line-strong); color: var(--link); background: var(--surface); }
.btn-out:hover { border-color: var(--brand); }
.btn-lg {
  padding: 15px 30px; font-size: 1.08rem; border-radius: 12px;
  box-shadow: var(--shadow);
}
.btn-lg:hover { transform: translateY(-1px); box-shadow: var(--shadow-lift); }
.cta { margin-top: 20px; }
.cta p { margin: 10px 0; }
.topcta { margin: 0 0 16px; }

.scroll { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; font-size: .95rem; }
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); }
th { color: var(--muted); font-weight: 600; }

.filter {
  width: 100%; max-width: 100%; box-sizing: border-box;
  padding: 11px 14px; margin: 6px 0 12px; font-size: 1rem;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface); color: var(--fg);
  font-family: inherit;
}
.filter:focus { outline: 2px solid var(--cyan); outline-offset: 1px; border-color: var(--cyan); }
.filter-empty { color: var(--muted); }

.cols { list-style: none; padding: 0; margin: 8px 0;
  columns: 2; column-gap: 28px; }
.cols li { break-inside: avoid; padding: 3px 0; }
.muted { color: var(--muted); font-size: .85rem; }
@media (max-width: 520px) { .cols { columns: 1; } }

/* --- Site header (all pages) ---------------------------------------------- *
 * Not just decoration: a real header with named sections is part of what earns
 * a sitelinks box, which is the whole reason the R-series work exists. */
.site-header {
  border-bottom: 1px solid var(--line); background: var(--surface);
}
.site-header .inner {
  max-width: 1040px; margin: 0 auto; padding: 12px 20px;
  display: flex; align-items: center; gap: 20px;
}
.brand {
  display: inline-flex; align-items: center; gap: 9px;
  text-decoration: none; margin-right: auto;
}
.brand svg { display: block; width: 30px; height: 30px; border-radius: 8px; flex: none; }
/* The wordmark is set as text, not the branding/*.svg lockup: that file draws
 * <text> and @imports Archivo from Google Fonts (its own note says to outline it
 * before production). Text in the self-hosted face is crisper and theme-aware. */
.wm { font-size: 1.22rem; font-weight: 700; letter-spacing: -.025em; white-space: nowrap; }
.wm .light { font-weight: 500; color: var(--muted); }
.site-nav { display: flex; align-items: center; gap: 20px; font-size: .95rem; }
.site-nav a { color: var(--fg); text-decoration: none; font-weight: 500; }
.site-nav a:hover { color: var(--link); }
.site-nav .btn { padding: 8px 15px; font-size: .93rem; color: var(--accent-fg); }

/* --- Narrow-screen menu ---------------------------------------------------- *
 * The nav links used to be display:none'd on a phone, which left the whole site
 * reachable only through "Open the planner". They fold into this menu instead.
 * Wide screens never see it; narrow screens see it INSTEAD of the inline links,
 * so the two are never both present. */
.nav-more { display: none; position: relative; }
.nav-more > summary {
  list-style: none; cursor: pointer; display: inline-flex;
  align-items: center; justify-content: center;
  width: 38px; height: 38px; margin-left: 2px;
  border: 1px solid var(--line); border-radius: 8px;
}
.nav-more > summary::-webkit-details-marker { display: none; }
.nav-more > summary:focus-visible { outline: 2px solid var(--link); outline-offset: 2px; }
/* Three bars drawn from one element: the box is the middle bar, the two
 * pseudo-elements are the outer ones. No icon font, no extra request. */
.nav-more .bars, .nav-more .bars::before, .nav-more .bars::after {
  display: block; width: 18px; height: 2px; border-radius: 2px;
  background: var(--fg); content: "";
}
.nav-more .bars { position: relative; }
.nav-more .bars::before { position: absolute; top: -6px; }
.nav-more .bars::after { position: absolute; top: 6px; }
.nav-more[open] > summary { border-color: var(--link); }
.nav-drop {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 20;
  min-width: 190px; padding: 6px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 10px 28px rgba(0,0,0,.18);
  display: flex; flex-direction: column;
}
.nav-drop a {
  padding: 10px 12px; border-radius: 7px; white-space: nowrap;
}
.nav-drop a:hover { background: var(--bg); }
/* Screen-reader-only label on the burger: the icon is decorative, so without
 * this the control announces as an unnamed toggle. */
.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

@media (max-width: 640px) {
  .site-header .inner { padding: 10px 16px; gap: 12px; }
  .site-nav { gap: 10px; font-size: .9rem; }
  /* Swap the inline links for the menu - fold, do not hide. */
  .site-nav .nav-link { display: none; }
  .nav-more { display: block; }
  /* Without nowrap the CTA breaks over two lines and doubles the header height. */
  .site-nav .btn { padding: 8px 12px; font-size: .88rem; white-space: nowrap; }
  .wm { font-size: 1.1rem; }
}

/* Below ~430px the three header items (wordmark, CTA, menu) no longer fit and the
 * CTA was pushing the menu button clean off the right edge - which would have
 * reintroduced the very bug this menu exists to fix. The wordmark TEXT gives way
 * first: the logo beside it is still the brand and still links home, so nothing
 * is lost but the repetition. */
@media (max-width: 430px) {
  .wm { display: none; }
  .brand svg { width: 32px; height: 32px; }
}

/* --- Web vs phone, side by side ------------------------------------------- *
 * Two columns of what each platform is better at. They stack below 780px and the
 * WEB column comes first in the DOM, so a phone reading this gets the thing it
 * can actually use right now at the top rather than a pitch to install an app. */
.platforms {
  display: grid; grid-template-columns: 1fr 1fr; gap: 26px;
  max-width: 900px; margin: 26px auto 0; text-align: left;
}
.platform {
  border: 1px solid var(--line); border-radius: 12px; padding: 20px 22px;
  background: var(--surface);
}
.platform-head {
  margin: 0 0 4px; font-size: .82rem; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted);
}
.platform-list { list-style: none; padding: 0; margin: 0; }
.platform-list li { margin: 16px 0 0; }
/* The first item in each column is that platform's headline reason for existing,
 * so it gets the accent rule and the others do not. */
.platform-list li:first-child {
  margin-top: 12px; padding-left: 12px; border-left: 3px solid var(--link);
}
.platform-list h4 { margin: 0 0 4px; font-size: 1.02rem; line-height: 1.35; }
.platform-list p { margin: 0; color: var(--muted); line-height: 1.55; }

@media (max-width: 780px) {
  .platforms { grid-template-columns: 1fr; gap: 18px; }
}

/* --- Video ---------------------------------------------------------------- *
 * Two forms. The landing page uses `.video-facade`: a still with a play button
 * that LINKS to YouTube, so the page loads no third-party script and sets no
 * cookies unless someone asks for the video. A guide that carries a `video:` id
 * uses `.embed`, a real nocookie iframe, because the reader came for it. */
.video-wrap {
  position: relative; max-width: 760px; margin: 22px auto 0;
  aspect-ratio: 16 / 9; border-radius: 12px; overflow: hidden;
  border: 1px solid var(--line); background: #000;
}
.video-wrap.embed { margin: 0 0 26px; }
.video-wrap iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video-facade {
  display: block; width: 100%; height: 100%; position: relative;
  padding: 0; border: 0; background: none; cursor: pointer;
}
.video-facade img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Play button: a rounded rectangle with a CSS triangle, so no icon asset. */
.video-facade .play {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 74px; height: 52px; border-radius: 13px;
  background: rgba(18, 18, 18, .78); transition: background .15s ease;
}
.video-facade .play::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  transform: translate(-42%, -50%);
  border-style: solid; border-width: 11px 0 11px 19px;
  border-color: transparent transparent transparent #fff;
}
.video-facade:hover .play, .video-facade:focus-visible .play { background: #c00; }
.video-note { margin: 12px 0 0; font-size: .9rem; }

/* --- Help / contact ------------------------------------------------------- *
 * Written after shipping the page with five class names that had no rules at
 * all, so the mail buttons sat against each other with no gap and the prose ran
 * full-bleed. */
.page-head { max-width: 720px; margin: 0 auto 6px; }
.page-head h1 { margin: 0 0 6px; }
.mail { font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace); }
.prose { max-width: 720px; margin: 0 auto; }
.prose h2 { margin: 30px 0 8px; font-size: 1.18rem; }
.prose p { line-height: 1.62; }
.prose ul { padding-left: 20px; }
.prose li { margin: .45rem 0; line-height: 1.55; }

/* The mail buttons. flex-wrap + gap is the whole fix: they were inline elements
 * with nothing between them, so on a narrow screen they ran together. */
.help-actions {
  display: flex; flex-wrap: wrap; gap: 12px;
  max-width: 720px; margin: 30px auto 8px;
}
.help-actions .btn { white-space: nowrap; }
/* Below ~520px three side-by-side buttons cannot read; stack them full width so
 * each is a proper tap target rather than three slivers. */
@media (max-width: 520px) {
  .help-actions { flex-direction: column; align-items: stretch; }
  .help-actions .btn { text-align: center; }
}
.page-help .more-links { max-width: 720px; margin-left: auto; margin-right: auto; }

/* --- Landing page --------------------------------------------------------- */
.page-home .wrap { max-width: 1040px; padding-bottom: 0; }

.hero { text-align: center; padding: 44px 0 8px; }
.hero h1 {
  font-size: clamp(2rem, 5.2vw, 3.35rem); line-height: 1.1;
  letter-spacing: -.025em; max-width: 20em; margin: 0 auto 18px;
}
.hero h1 .accent { color: var(--accent); }
.hero .lede {
  font-size: clamp(1.02rem, 1.6vw, 1.22rem); color: var(--muted);
  max-width: 34em; margin: 0 auto 28px; line-height: 1.55;
}
.hero .cta-note { color: var(--muted); font-size: .9rem; margin: 14px 0 0; }

/* Product screenshot in a browser frame. The single highest-value element on the
 * page: it demos the chain before a word is read. Renders only if the image
 * exists, so a missing capture degrades to a text hero rather than a broken box. */
.shot {
  /* Full content measure: the per-hut "Bed free <date>" labels in the capture are
     the point of the image, and every pixel of width makes them more readable. */
  margin: 36px auto 0; max-width: 1000px;
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
  background: var(--surface); box-shadow: var(--shadow-lift);
  transition: border-color .15s ease, box-shadow .15s ease;
}
/* The whole frame is a link to the planner. Transition and lift live here rather
   than on .shot so the hover target and the animated box are the same element. */
.shot-link { display: block; text-decoration: none; color: inherit; }
.shot:has(.shot-link:hover) { border-color: var(--cyan); box-shadow: var(--shadow-lift); }
.shot:has(.shot-link:focus-visible) { outline: 3px solid var(--cyan); outline-offset: 2px; }

.shot-bar {
  display: flex; align-items: center; gap: 7px;
  padding: 9px 13px; border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
.shot-bar i {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--line-strong); flex: none;
}
.shot-bar span {
  margin: 0 auto; font-size: .78rem; color: var(--muted);
  /* nudge the faux URL back to true centre past the three dots */
  padding-right: 51px;
}
.shot img { display: block; width: 100%; height: auto; }
.shot-cap { color: var(--muted); font-size: .85rem; text-align: center; margin: 12px 0 0; }

/* Generated figures. These are the most persuasive things on the page and were
 * previously buried inside a paragraph. */
.stats {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  margin: 44px 0 0; background: var(--line);
  border: 1px solid var(--line); border-radius: 14px; overflow: hidden;
}
.stat { background: var(--surface); padding: 22px 18px; text-align: center; }
.stat-num {
  display: block; font-size: clamp(1.7rem, 3.4vw, 2.4rem);
  font-weight: 700; line-height: 1.1; letter-spacing: -.03em;
}
.stat-label { display: block; color: var(--muted); font-size: .88rem; margin-top: 5px; }
@media (max-width: 620px) {
  .stats { grid-template-columns: 1fr; }
  .stat { padding: 16px; display: flex; align-items: baseline; gap: 10px; text-align: left; }
  .stat-num { font-size: 1.5rem; }
  .stat-label { margin-top: 0; }
}

/* The landing page's sections are bands, not the rule-separated blocks the hut and
   region pages use, so drop the global `section` hairline for them. */
.page-home section.band { border-top: none; }
.band { padding: 40px 0 4px; }
.band > h2 {
  font-size: clamp(1.35rem, 2.6vw, 1.85rem); font-weight: 700;
  letter-spacing: -.02em; margin: 0 0 6px; text-align: center;
}
.band > .band-intro {
  color: var(--muted); text-align: center; max-width: 42em;
  margin: 0 auto 4px;
}
.band > .band-intro p { margin: 8px 0; }
/* The problem statement. Kept at body colour, not muted: it is the argument for
   the product, not a caption, and muted grey made it read as fine print. */
.lead-band { text-align: center; max-width: 40em; margin: 36px auto 0; }
.lead-band p { margin: 0; font-size: 1.1rem; line-height: 1.6; color: var(--fg); }

.cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(228px, 1fr));
  gap: 16px; margin: 24px 0 0; padding: 0; list-style: none;
}
.card {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; padding: 20px 20px 22px;
  box-shadow: var(--shadow);
}
.card h3 { font-size: 1.03rem; font-weight: 600; margin: 0 0 6px; }
/* Fixed-width accent rule. Bordering the h3 itself made the rule as wide as the
   title, so a two-line card title drew a rule twice the length of its neighbours. */
.card h3::before {
  content: ""; display: block; width: 30px; height: 3px;
  background: var(--cyan); border-radius: 2px; margin-bottom: 12px;
}
.card p { margin: 0; color: var(--muted); font-size: .95rem; line-height: 1.55; }

/* Trails: cards rather than list items, because each one is a real entry point
 * (they seed a live itinerary) and should look clickable. */
.trail-cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px; margin: 24px 0 0; padding: 0; list-style: none;
}
.trail-card {
  display: block; background: var(--surface); border: 1px solid var(--line);
  border-radius: 14px; padding: 20px; text-decoration: none; color: var(--fg);
  box-shadow: var(--shadow); transition: border-color .15s ease, transform .15s ease;
}
.trail-card:hover {
  border-color: var(--cyan); transform: translateY(-2px);
  box-shadow: var(--shadow-lift); color: var(--fg);
}
.trail-card .name {
  display: block; font-size: 1.1rem; font-weight: 700; color: var(--heading);
  margin-bottom: 3px;
}
.trail-card .meta {
  display: block; font-size: .84rem; color: var(--accent); font-weight: 600;
  margin-bottom: 8px;
}
.trail-card .summary { margin: 0; color: var(--muted); font-size: .93rem; line-height: 1.5; }
.trail-card .go { display: block; margin-top: 10px; font-size: .88rem; color: var(--link); font-weight: 600; }

/* 63 regions is a lot of link. Ruled rows made it the tallest thing on the page
   by a wide margin, so it is set as a dense index instead: every region page still
   gets its internal link, but it reads as a directory rather than as content. */
.region-grid {
  columns: 4; column-gap: 26px; margin: 20px 0 0; padding: 0; list-style: none;
  font-size: .93rem;
}
.region-grid li { break-inside: avoid; }
.region-grid a {
  display: block; padding: 4px 0; text-decoration: none; color: var(--fg);
}
.region-grid a:hover { color: var(--link); }
.region-grid .n { color: var(--muted); font-size: .78rem; margin-left: 5px; white-space: nowrap; }
@media (max-width: 900px) { .region-grid { columns: 3; } }
@media (max-width: 660px) { .region-grid { columns: 2; } }
@media (max-width: 420px) { .region-grid { columns: 1; } }
.more-links { margin: 22px 0 0; text-align: center; color: var(--muted); }

.faq { margin: 24px 0 0; }
.faq dt {
  font-weight: 600; font-size: 1.02rem; margin: 0;
  padding: 18px 0 0; border-top: 1px solid var(--line);
}
.faq dd { margin: 5px 0 18px; color: var(--muted); max-width: 46em; }
/* Markdown-authored answers arrive wrapped in <p>. */
.faq dd p { margin: 0; }
.faq dd p + p { margin-top: 8px; }
.page-home .faq { max-width: 760px; margin-inline: auto; }

/* --- 404 (backlog M2) ----------------------------------------------------- */
.notfound { text-align: center; padding: 56px 0 24px; max-width: 34em; margin: 0 auto; }
.notfound .code {
  font-size: 3.4rem; font-weight: 700; line-height: 1; letter-spacing: -.04em;
  color: var(--cyan); margin: 0 0 6px;
}
.notfound h1 { font-size: clamp(1.5rem, 3.4vw, 2.1rem); margin: 0 0 14px; }
.notfound .lede { color: var(--muted); margin-bottom: 26px; }
.notfound .lede p { margin: 0; }
.notfound .more-links { margin-top: 22px; }

.closing { text-align: center; padding: 46px 0 8px; }
.closing h2 { font-size: clamp(1.3rem, 2.4vw, 1.7rem); margin: 0 0 16px; }

/* --- Guides (Markdown-authored editorial pages) --------------------------- */
.guide-body { border-top: none; padding-top: 0; }
.guide-body h2 { font-size: 1.3rem; font-weight: 600; margin: 30px 0 8px; }
.guide-body h3 { font-size: 1.08rem; font-weight: 600; margin: 22px 0 6px; }
.guide-body p { margin: 12px 0; }
.guide-body > p:first-child { font-size: 1.12rem; color: var(--muted); }
.guide-body ul { padding-left: 20px; margin: 12px 0; }
.guide-body li { padding: 3px 0; }
.guide-body hr { border: none; border-top: 1px solid var(--line); margin: 24px 0; }
.guide-body blockquote {
  margin: 16px 0; padding: 4px 16px; border-left: 3px solid var(--cyan);
  color: var(--muted);
}
figure { margin: 18px 0; }
figure img, .guide-body img {
  display: block; width: 100%; height: auto; border-radius: 12px;
}
figure.hero { margin: 4px 0 20px; }
figcaption { color: var(--muted); font-size: .85rem; margin-top: 6px; }

/* --- Footer --------------------------------------------------------------- */
.site-footer {
  max-width: 1040px; margin: 40px auto 0; padding: 20px 20px 44px;
  border-top: 1px solid var(--line); color: var(--muted); font-size: .85rem;
}
.site-footer a { color: var(--muted); }
.site-footer a:hover { color: var(--link); }
.caveat { margin: 0 0 10px; }

/* Social profiles in the shared footer. `.social a` is underlined rather than
 * relying on colour alone: the footer's muted grey against its own background
 * does not carry enough contrast on its own to mark a link. */
.social { margin: 10px 0 0; }
.social a { text-decoration: underline; }

/* Store badges. Apple's and Google's own artwork, so it must not be recoloured,
 * cropped or given a background - the only thing set here is placement and a
 * consistent height, since the two vendors ship different aspect ratios (Apple
 * 119x40, Google 646x250) and matching on WIDTH would leave them visually
 * mismatched. Height wins; width goes auto. */
.store-badges {
  display: flex; flex-wrap: wrap; align-items: center;
  /* Centred to match the band's heading and intro, which are centred. */
  justify-content: center;
  gap: 14px; margin: 22px 0 10px;
}
.store-badges a { display: inline-flex; }
.store-badges img { height: 48px; width: auto; display: block; }
/* The Google badge ships with its own transparent padding; trimming the optical
 * difference keeps the two sitting on the same baseline. */
.store-badges a:last-child img { height: 68px; margin: -10px; }
.store-note { font-size: .78rem; color: var(--muted); margin: 6px 0 0; text-align: center; }
.caveat p { margin: 0 0 10px; }

/* Content pages keep the narrow measure; only the landing page and the chrome
 * use the wide one. */
body:not(.page-home) .site-footer { max-width: 720px; }

.facts-list,
.link-list { list-style: none; padding: 0; margin: 1rem 0; }
.facts-list li { margin: .55rem 0; line-height: 1.5; }
.link-list li { margin: .35rem 0; line-height: 1.45; }
.link-list.cols { column-width: 15rem; column-gap: 2rem; }
.link-list.cols li { break-inside: avoid; }

/* ── Newsletter signup ──────────────────────────────────────────────────────
 * Three placements share this: the landing-page band (#newsletter), the
 * /newsletter/ page, and the desktop bottom bar. One set of rules so they
 * cannot drift, with .signup-compact carrying the only real difference.
 */
.signup { margin: 20px auto 0; max-width: 560px; }
.signup-band { text-align: center; }
.signup-label {
  display: block; font-size: .82rem; font-weight: 600; color: var(--muted);
  text-align: left; margin: 0 0 6px;
}
.signup-row { display: flex; gap: 10px; align-items: stretch; }
.signup-input {
  flex: 1 1 auto; min-width: 0;
  font: inherit; font-size: 1rem;
  padding: 12px 14px;
  color: var(--fg); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: 8px;
}
.signup-input:focus-visible {
  outline: 2px solid var(--cyan); outline-offset: 1px; border-color: var(--cyan);
}
.signup-btn { flex: 0 0 auto; white-space: nowrap; }
.signup-btn[disabled] { opacity: .6; cursor: default; }

/* The row is hidden on success, and `[hidden]` from the user agent LOSES to
   .signup-row's display:flex above - so without this rule the row stays on
   screen and the button sits there reading "Sending" forever, which reads as a
   hang even though the request succeeded and the confirmation mail is sent.
   .signup-row[hidden] outranks .signup-row on specificity, so no !important is
   needed here (unlike .nl-bar[hidden] below, which fights an inline style). */
.signup-row[hidden] { display: none; }

/* Busy spinner. The request waits on Resend accepting the mail, so it is
   routinely a second or two - long enough that a static label looks stuck. Text
   stays readable and the spinner sits after it. */
.signup-btn.is-busy { position: relative; padding-right: 2.4em; }
.signup-btn.is-busy::after {
  content: "";
  position: absolute;
  top: 50%;
  right: .85em;
  width: 14px;
  height: 14px;
  margin-top: -7px;
  border: 2px solid rgba(255, 255, 255, .4);
  border-top-color: #fff;
  border-radius: 50%;
  animation: hs-spin .7s linear infinite;
}
@keyframes hs-spin { to { transform: rotate(360deg); } }
/* Reduced motion slows it rather than stopping it: "is it still working" is the
   exact question this answers, so a frozen ring would be worse than none. */
@media (prefers-reduced-motion: reduce) {
  .signup-btn.is-busy::after { animation-duration: 2.4s; }
}
.signup-note {
  font-size: .8rem; color: var(--muted); line-height: 1.5;
  margin: 10px 0 0; text-align: left;
}
.signup-msg { margin: 10px 0 0; font-size: .9rem; line-height: 1.5; text-align: left; }
.signup-msg:empty { display: none; }
.signup-msg.is-ok { color: var(--link); font-weight: 600; }
.signup-msg.is-err { color: #B3261E; }
@media (prefers-color-scheme: dark) { .signup-msg.is-err { color: #F2B8B5; } }

/* The honeypot. Visually hidden rather than display:none, which some bots skip
 * on purpose. aria-hidden + tabindex=-1 keep it away from real users. */
.hp {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.signup-perks {
  list-style: none; padding: 0;
  margin: 18px auto 4px; max-width: 780px;
  display: grid; gap: 18px; grid-template-columns: repeat(3, 1fr);
  text-align: left;
}
.signup-perks h3 { margin: 0 0 4px; font-size: 1rem; line-height: 1.35; }
.signup-perks p { margin: 0; color: var(--muted); line-height: 1.55; font-size: .94rem; }
@media (max-width: 720px) { .signup-perks { grid-template-columns: 1fr; gap: 14px; } }

/* Confirmation states on /newsletter/. Hidden until the inline script unhides
 * one, so a reader with JS off sees the page and the form rather than four
 * contradictory banners. */
.state-banner {
  max-width: 720px; margin: 18px auto; padding: 18px 20px;
  background: var(--tint); border: 1px solid var(--line-strong);
  border-left: 4px solid var(--cyan); border-radius: 10px;
}
.state-banner h2 { margin: 0 0 6px; font-size: 1.12rem; }
.state-banner p { margin: 0 0 8px; line-height: 1.55; }
.state-banner p:last-child { margin-bottom: 0; }
.state-error { border-left-color: #B3261E; }

/* ── The bottom bar ─────────────────────────────────────────────────────────
 * Docked, never overlapping the text, and gone entirely below 900px: on a phone
 * a covering dialog is Google's intrusive-interstitial pattern, and these pages
 * exist to rank. The [hidden] rule has to be !important because the element
 * carries `hidden` while position:fixed would otherwise still lay it out.
 */
.nl-bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  background: var(--surface); border-top: 2px solid var(--cyan);
  box-shadow: var(--shadow-lift);
  transform: translateY(100%);
  transition: transform .28s ease;
}
.nl-bar[hidden] { display: none !important; }
.nl-bar.is-in { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .nl-bar { transition: none; }
}
.nl-bar-inner {
  max-width: 1080px; margin: 0 auto; padding: 12px 20px;
  display: flex; align-items: center; gap: 20px;
}
.nl-bar-copy { margin: 0; font-size: .95rem; line-height: 1.45; flex: 1 1 auto; }
.nl-bar-copy strong { color: var(--heading); }
.nl-bar .signup { margin: 0; flex: 0 0 auto; width: 380px; max-width: 40vw; }
/* The bar has no room for the label or the small print - the /newsletter/ page
 * it links to carries both, and the confirmation email restates them. */
.nl-bar .signup-label, .nl-bar .signup-note { display: none; }
.nl-bar .signup-msg { position: absolute; bottom: 100%; right: 20px; margin: 0 0 6px; }
.nl-bar-close {
  flex: 0 0 auto; background: none; border: 0; cursor: pointer;
  font-size: 1.5rem; line-height: 1; color: var(--muted); padding: 4px 8px;
  border-radius: 6px;
}
.nl-bar-close:hover { color: var(--fg); background: var(--surface-2); }
@media (max-width: 899px) { .nl-bar { display: none !important; } }
