/* ═══════════════════════════════════════════════════════════════
   johnny1ski.com — shared site stylesheet
   ═══════════════════════════════════════════════════════════════ */

/* Skin (colour + type tokens) lives in the shared theme.css — edit that
   one file to reskin the site AND the CMS admin at once. Must be the first
   statement so its :root tokens land before any rule below uses them. */
@import url('theme.css');

/* ── STRUCTURAL TOKENS ──────────────────────────────────────── */
/* Layout-only tokens stay here; everything visual (colours, fonts,
   the --blue-1..4 ramp) now comes from theme.css. */
:root {
  --radius:    10px;
  --max-w:     860px;
  --nav-h:     56px;
}

/* ── RESET ──────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }

body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  line-height: 1.6;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

img { max-width: 100%; display: block; }
a { color: var(--signal); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ── NAV — two-band header (27 Jul 2026, Theme A) ────────────
   .nav-row-top (brand + tagline) and .nav-row-links (link list +
   hamburger) are two stacked bands, both direct children of #site-nav.
   .nav-mobile-menu MUST be nested inside .nav-row-links, as the last
   element before that div's closing tag — the checkbox-hack toggle below
   (`.nav-toggle-input:checked ~ .nav-mobile-menu`) is a sibling combinator,
   so it only works if .nav-mobile-menu shares a parent with the checkbox.
   (Bug found 28 Jul 2026: .nav-mobile-menu had been left as a sibling of
   .nav-row-links instead, one level too shallow, which silently broke the
   toggle — the hamburger looked like it didn't respond at all. .nav-row-links
   itself has no `position` set, so nesting one level deeper doesn't change
   what `top: 100%` resolves against — #site-nav's sticky positioning is
   still the containing block either way, so it still drops below BOTH
   bands correctly.) */
#site-nav {
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  z-index: 100;
}

.nav-row-top {
  background: var(--nav-bg);
  padding: 1.1rem 1.5rem 0.9rem;
  text-align: center;
}

.nav-brand {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 2.1rem;
  font-weight: 600;
  color: var(--nav-text);
  letter-spacing: 0.01em;
  text-decoration: none;
}
.nav-brand:hover { text-decoration: none; opacity: 0.85; }

.nav-tagline {
  margin-top: 0.4rem;
  font-family: var(--font-body);
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--nav-tagline);
}

.nav-row-links {
  background: var(--nav-bg-2);
  display: flex;
  align-items: center;
  padding: 0.55rem 1.5rem;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  list-style: none;
  width: 100%;
}

/* Search sits on its own at the far right; everything else (Home, Posts,
   Links) stays grouped on the left. Marked with a dedicated class rather
   than :last-child so it stays robust if items are ever reordered. */
.nav-search-item {
  margin-left: auto;
}

.nav-links > li {
  display: flex;
  align-items: center;
}
.nav-links > li > a {
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--nav-link);
  letter-spacing: 0.01em;
  text-decoration: none;
  transition: color 0.15s;
  padding: 0.4rem 0.9rem;
  border-radius: 6px;
  line-height: 1;
}
.nav-links > li > a:hover,
.nav-links > li > a.active { color: var(--nav-link-active); }

.nav-search-btn {
  font-size: 0.95rem;
  line-height: 1;
  background: none;
  border: none;
  color: var(--nav-link);
  cursor: pointer;
  padding: 0.4rem 0.5rem;
  border-radius: 6px;
  transition: color 0.15s, background 0.15s;
}
.nav-search-btn:hover { color: var(--nav-link-active); background: rgba(255,255,255,0.14); }
.nav-search-btn--mobile {
  display: none;
}
.nav-mobile-menu .nav-search-btn--mobile {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  text-align: left;
  font-family: var(--font-body);
  font-size: 0.9rem;
  padding: 0.6rem 0.5rem;
  border-radius: 6px;
  color: var(--text);
  transition: background 0.12s, color 0.12s;
}
.nav-mobile-menu .nav-search-btn--mobile:hover { background: var(--signal-dim); color: var(--signal); }

/* ── FOOTER ─────────────────────────────────────────────────── */
#site-footer {
  margin-top: auto;
  border-top: 1px solid var(--border);
  padding: 2rem 1.5rem;
  text-align: center;
  font-size: 0.75rem;
  color: var(--muted);
  font-family: var(--font-mono);
}
#site-footer a { color: var(--muted); }
#site-footer a:hover { color: var(--signal); }
.footer-sites { margin-top: 0.5rem; font-size: 0.68rem; opacity: 0.8; }
.footer-sites a { color: var(--muted); }
.footer-sites a:hover { color: var(--signal); }

/* ── PAGE WRAPPER ───────────────────────────────────────────── */
.page-wrap {
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 3rem 1.5rem;
  flex: 1;
}

/* ── PAGE HEADER (hero area for inner pages) ────────────────── */
.page-header {
  margin-bottom: 2.5rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--border);
}
.page-header .eyebrow {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.6rem;
}
.page-header h1 {
  font-family: var(--font-display);
  font-size: clamp(1.7rem, 4vw, 2.4rem);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: var(--text);
  margin-bottom: 0.5rem;
}
.page-header .standfirst {
  font-size: 1.05rem;
  color: var(--muted);
  font-weight: 300;
  max-width: 600px;
}

/* ── POST CARDS (index listing) ─────────────────────────────── */
.post-grid {
  display: grid;
  gap: 1.25rem;
}

.post-card {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 1.5rem;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 0.5rem 1rem;
  align-items: start;
  transition: border-color 0.2s, transform 0.2s;
  text-decoration: none;
  color: inherit;
}
.post-card:hover {
  border-color: var(--signal);
  transform: translateY(-2px);
  text-decoration: none;
}
/* Title turns blue when hovering anywhere on the card (matches the hero). */
.post-card:hover h2 { color: var(--signal); }

.post-card-meta {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 0.4rem;
  grid-column: 1 / -1;
}
.post-card h2 {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--text);
  /* Match the photo card: 2-line title cap for equal heights. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.post-card p {
  font-size: 0.85rem;
  color: var(--muted);
  grid-column: 1 / -1;
  margin-top: 0.25rem;
  /* Match the photo card: 3-line meta cap, ellipsis is the "more" cue. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}
.post-card-arrow {
  color: var(--signal);
  font-size: 1.2rem;
  align-self: center;
}

/* Single source of truth for .post-category (used on both blog cards and
   article headers). History: (1) 8 July 2026 the per-category colours
   (.cat-review / .cat-3dprint / .cat-rant / .cat-tool / .cat-gear) were
   removed — an unrequested embellishment on the no-lead-photo fallback
   card no live post has triggered; a leftover cat-* class in old HTML is
   now a harmless no-op. (2) 11 July 2026 this was found defined twice (a
   muted copy here and a green copy in the article-header section) with the
   green one winning by source order. Consolidated to this single rule;
   Richard chose to KEEP the green (var(--signal)). One badge style, green,
   everywhere. */
.post-category {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 0.2rem 0.75rem;
  border-radius: 20px;
  border: 1px solid currentColor;
  margin-left: 0.75rem;
  margin-bottom: 0.85rem;
  color: var(--signal);
}

/* ── ARTICLE BODY ───────────────────────────────────────────── */
.article-body {
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.75;
  color: var(--text);
}
/* Found 25 July 2026 via the test post: on fixed-left/fixed-right posts,
   the text column and photo column both start at the same grid-row top,
   but h2/h3's own top margin (2.5rem/2rem) pushed the *visible* heading
   text down below that line while the photo (no margin) sat flush —
   e.g. a post opening with an H2 misaligned by 40px against the photo.
   Zeroing whichever element actually leads the body fixes it for any
   post regardless of what that first element is (heading, paragraph,
   Amazon card, etc), without changing spacing for the same element type
   when it isn't first. */
.article-body > *:first-child { margin-top: 0; }
.article-body h2 {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  margin: 2.5rem 0 0.75rem;
  color: var(--text);
}
.article-body h3 {
  font-family: var(--font-display);
  font-size: 0.98rem;
  font-weight: 500;
  margin: 2rem 0 0.5rem;
  color: var(--text);
}
.article-body p { margin-bottom: 1.25rem; }
.article-body ul,
.article-body ol {
  margin: 0 0 1.25rem 1.5rem;
}
.article-body li { margin-bottom: 0.4rem; }
.article-body blockquote {
  border-left: 3px solid var(--signal);
  margin: 1.5rem 0;
  padding: 0.5rem 0 0.5rem 1.25rem;
  color: var(--muted);
  font-style: italic;
}
.article-body img {
  border-radius: var(--radius);
  margin: 1.5rem 0;
  border: 1px solid var(--border);
}
.article-body a { color: var(--signal); }
/* Pre-existing bug, surfaced 25 July 2026 by the Amazon card's button:
   `.article-body a` (class+element) beats plain `.btn` (one class) on
   specificity, so any <a class="btn"> inside a post body silently lost
   its white text to this rule — invisible white-on-blue became
   blue-on-blue. Affects the existing 🔗 Button shortcut too, not just
   the new Amazon one. Fixed by out-specifying it here. */
.article-body a.btn { color: #fff; }
/* Found 28 July 2026: a standalone CTA button (the 🔗 Button toolbar
   shortcut, its own line) lands in a plain <p> via autoParagraphs(), and
   .article-body p only carries margin-bottom — no margin-top anywhere —
   so it sat exactly as close to the text above it as any two ordinary
   paragraphs, with none of the "this is its own callout" breathing room
   the Amazon/Download/Map boxes get (all margin: 2rem 0). Richard was
   adding blank spacer paragraphs by hand to work around it. Scoped to a
   button on its own line only, via :only-child — an inline link
   button/CTA sitting mid-sentence (case 'link' in format-toolbar.js) is
   untouched, since it's still meant to sit inside running text. */
.article-body p:has(> a.btn:only-child) { margin: 2rem 0; }

/* ── ALIGNMENT (28 July 2026, toolbar Left/Center/Right/Justify) ─────
   admin/format-toolbar.js's applyAlign() adds align-center/align-right/
   align-justify directly onto whichever tag it decides is the block being
   aligned — a heading, blockquote or paragraph gets the class straight on
   itself (plain text-align); a standalone Button gets it on the <a> even
   though visually it's the <p> autoParagraphs() wraps it in that needs
   centering, so that's targeted via :has() below instead; Amazon/Download
   cards get it on their own outer <div> but need margin, not text-align,
   since they're a fixed-max-width box sitting in a full-width column, not
   full-width text. Left is the default and never gets a class — see
   withAlignClass() in format-toolbar.js. Map embeds, lists, dividers and
   inline photos are deliberately not part of this system (see the comment
   above applyAlign() for why); Justify is restricted client-side to plain
   paragraphs only. */
.article-body h2.align-center,
.article-body h3.align-center,
.article-body blockquote.align-center,
.article-body p.align-center { text-align: center; }
.article-body h2.align-right,
.article-body h3.align-right,
.article-body blockquote.align-right,
.article-body p.align-right { text-align: right; }
.article-body p.align-justify { text-align: justify; }

.article-body .product-card.align-center,
.article-body .download-card.align-center { margin-left: auto; margin-right: auto; }
.article-body .product-card.align-right,
.article-body .download-card.align-right { margin-left: auto; margin-right: 0; }

.article-body p:has(> a.btn.align-center) { text-align: center; }
.article-body p:has(> a.btn.align-right) { text-align: right; }

/* ── AMAZON PRODUCT CARD / DOWNLOAD CARD ───────────────────────
   Amazon card live since 25 July 2026 (🛒 Amazon toolbar button,
   admin/format-toolbar.js). No image (dropped same day — the post's own
   photos already cover that). Background matched to the page (var(--bg),
   not var(--surface)) and width capped so the card reads as a compact,
   deliberate callout rather than a stretched, half-empty box — agreed with
   Richard via a few rounds of visual mockups (see PROJECT-NOTES.md).

   Download card added 26 July 2026 (📥 Download toolbar button) — a PDF
   instruction manual or GPX route file, same title/button/description
   shape as the Amazon card, so it shares every rule below via a combined
   selector rather than a second, easily-drifting copy of the same CSS. */
.product-card,
.download-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.1rem 1.3rem;
  margin: 2rem 0;
  max-width: 420px;
  display: flex;
  gap: 1.25rem;
  align-items: center;
}
/* Even spacing between title/blurb/button/disclosure (26 July 2026) — the
   old approach used margin-bottom on h4/p, which only adds space AFTER an
   element, not before. That left the gap above the button (h4's
   margin-bottom, or the blurb p's) looking normal, but nothing added any
   space BEFORE the affiliate-note paragraph that follows the button, so it
   sat squashed underneath it — reported by Richard as uneven/unbalanced.
   Switched to a flex column with one shared `gap`, so every child (title,
   blurb if present, button, disclosure) sits evenly spaced regardless of
   which ones exist, instead of each element carrying its own one-sided
   margin. `align-items: flex-start` keeps the button hugging its own text
   width rather than stretching to the card's full width, which is what a
   flex column would otherwise do to a block-level child by default. */
.product-card-info,
.download-card-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.75rem;
}
.product-card-info h4,
.download-card-info h4 {
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 600;
}
.product-card-info p,
.download-card-info p {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--muted);
}
.product-card-info .affiliate-note {
  font-size: 0.65rem;
  color: var(--muted);
  font-family: var(--font-mono);
}

/* Map embed (26 July 2026, 🗺️ Map toolbar button) — promoted here from
   journey-to-looe.html's own one-off <style> tag, since the Dynamic Page
   Builder needed the exact same box for its own map pages and repeating it
   as per-page Custom CSS every time wasn't worth it. Responsive 16:9 box
   (3:4 on narrow screens) so a Google My Maps iframe never overflows or
   looks tiny — the iframe itself just fills whatever the container gives
   it. See PROJECT-NOTES.md, "Map embed toolbar button".
   Widened 28 July 2026: Google My Maps reserves its own space for a
   legend/layers panel inside the iframe, so the visible map was cramped
   even at "full width" — pulled out flush to .page-wrap's own edge
   (cancels its 1.5rem side padding, never wider than the page itself, so
   this can't cause a horizontal scrollbar) and given more height (4:3,
   1:1 on mobile, instead of 16:9/3:4) so more of the real map shows
   without needing the embed's own "expand" control. A true edge-to-edge
   viewport bleed was considered and dropped — the standard 100vw trick
   can sit a few px past the true viewport edge on some browsers, not
   worth risking on this design. Toolbar map template also gained an
   explicit "Open in Google Maps" link below the box, see
   admin/format-toolbar.js and PROJECT-NOTES.md. */
.map-container {
  width: calc(100% + 3rem);
  margin: 2rem -1.5rem;
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid var(--border);
  aspect-ratio: 4 / 3;
}
.map-container iframe {
  width: 100%;
  height: 100%;
  border: none;
  display: block;
}
/* When the toolbar's "Open in Google Maps" link follows directly (wrapped
   in its own <p> by autoParagraphs()), .article-body p:has(> a.btn:only-child)
   already gives it margin: 2rem 0 — that collapses with this box's own
   2rem bottom margin rather than stacking, so the gap stays a clean 2rem
   either way, button present or not. No extra rule needed here. */
@media (max-width: 600px) {
  .map-container { width: 100%; margin-left: 0; margin-right: 0; aspect-ratio: 1 / 1; }
}

.btn {
  display: inline-block;
  background: var(--signal);
  color: #fff;
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  padding: 0.55rem 1.2rem;
  border-radius: 8px;
  text-decoration: none;
  transition: opacity 0.15s;
}
.btn:hover { opacity: 0.85; text-decoration: none; }

/* ── SECTION DIVIDER ────────────────────────────────────────── */
.section-label {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 1.25rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--border);
}

/* ── PHOTO POST CARD (blog index & homepage) ────────────────── */
.post-card--photo {
  background: var(--card);
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  padding: 1.25rem;
  display: flex;
  gap: 1.25rem;
  align-items: flex-start;
  transition: border-color 0.2s, transform 0.2s;
  text-decoration: none;
  color: inherit;
  position: relative;
}
.post-card--photo:hover {
  border-color: var(--signal);
  transform: translateY(-2px);
  text-decoration: none;
}

.post-card__thumb-link { flex-shrink: 0; text-decoration: none; }

.post-card__thumb {
  width: 160px;
  height: 120px;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid var(--border);
}

.post-card__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.25s ease;
}

.post-card--photo:hover .post-card__thumb img {
  transform: scale(1.04);
}

.post-card__body {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  min-width: 0;
}

.post-card__category {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--signal);
}

.post-card__title {
  font-family: var(--font-display);
  font-size: 0.98rem;
  font-weight: 500;
  line-height: 1.2;
  letter-spacing: -0.015em;
  margin: 0;
  /* Cap to 2 lines so every card is the same height (see .post-card__excerpt). */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.post-card__title a { color: var(--text); text-decoration: none; }
.post-card__title a:hover { color: var(--signal); }
/* Title turns blue when hovering anywhere on the photo card (matches the hero). */
.post-card--photo:hover .post-card__title a { color: var(--signal); }
/* Stretched link: the title link's overlay covers the whole card, so clicking
   (or hovering) anywhere in the box follows it — the photo card is an <article>
   of separate links, so without this the padding/excerpt/blank areas were dead.
   Matches the hero, which is a single <a>. */
.post-card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.post-card__excerpt {
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.5;
  margin: 0;
  /* Fixed card size: clamp the meta text to 3 lines. The truncation adds its
     own "…" — that ellipsis is the "there's more" cue, so no "Read more" label. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

.post-card__meta {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  color: var(--muted);
  margin: 0;
  letter-spacing: 0.03em;
}
.post-card__meta span { margin: 0 0.3rem; }

/* ── HOMEPAGE WIDGET SHAPES ─────────────────────────────────────
   Front-end styles for the homepage block generator (built 20 Jul 2026 —
   admin/inc/homepage-builder.php). Each block is a .hp-block; the card
   shapes (grid2x2/row3/river/carousel) reuse the existing .post-card /
   .post-card--photo markup inside a shape wrapper, so a card looks the same
   everywhere. Alignment-first: equal columns, one shared gap, equal-height
   cards. All grids collapse to a single column on mobile (see media query). */
.homepage {
  display: flex;
  flex-direction: column;
  gap: 2.75rem;
}

.hp-block { margin: 0; width: 100%; }

/* Block heading — a small mono eyebrow, consistent across every shape. */
.hp-block-title {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 1rem;
}

/* Block header row: title left, "View all →" right. */
.hp-block-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 1rem;
}
.hp-block-head .hp-block-title { margin: 0; }
.hp-view-all {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--signal);
  text-decoration: none;
  white-space: nowrap;
}
.hp-view-all:hover { text-decoration: underline; }

/* Shared grid: equal columns, one gap, stretch so cards are equal height. */
.hp-grid {
  display: grid;
  gap: 1.25rem;
  align-items: stretch;
}
.hp-grid--2x2  { grid-template-columns: repeat(2, 1fr); }
.hp-grid--row3 { grid-template-columns: repeat(3, 1fr); }
.hp-grid--river { grid-template-columns: 1fr; }

/* Cards fill their cell and match heights within a row. */
.hp-grid > .post-card,
.hp-grid > .post-card--photo,
.hp-carousel-track > .post-card,
.hp-carousel-track > .post-card--photo { width: 100%; height: 100%; margin: 0; }

/* ── Hero ─────────────────────────────────────────────────────── */
.hp-hero-card {
  display: block;
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  background: var(--card);
  transition: border-color 0.2s, transform 0.2s;
}
.hp-hero-card:hover { border-color: var(--signal); transform: translateY(-2px); text-decoration: none; }
.hp-hero-media { width: 100%; aspect-ratio: 16 / 9; overflow: hidden; }
.hp-hero-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hp-hero-text { padding: 1.75rem 2rem 2rem; }

/* Desktop: lay the hero out as a full-width side-by-side feature (image left,
   text right, vertically centred) so the card FILLS the whole content width
   and reads as a deliberate hero aligned with the grids below — rather than a
   half-empty box when the top image scrolls past. Stacks (image on top) below
   720px, and a no-photo hero always spans full width as text only. */
@media (min-width: 721px) {
  .hp-hero-card:not(.hp-hero-card--nophoto) {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    align-items: stretch;
  }
  .hp-hero-card:not(.hp-hero-card--nophoto) .hp-hero-media {
    aspect-ratio: auto;
    height: 100%;
    min-height: 320px;
  }
  .hp-hero-card:not(.hp-hero-card--nophoto) .hp-hero-text { align-self: center; }
}
.hp-hero-card .post-card__category { display: inline-block; margin-bottom: 0.6rem; }
.hp-hero-title {
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 3.2vw, 2.05rem);
  font-weight: 600;
  line-height: 1.12;
  letter-spacing: -0.025em;
  color: var(--text);
  margin: 0 0 0.6rem;
}
.hp-hero-card:hover .hp-hero-title { color: var(--signal); }
.hp-hero-excerpt { font-size: 1rem; color: var(--muted); margin: 0 0 0.75rem; max-width: 60ch; }
.hp-hero-meta { font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.06em; color: var(--muted); margin: 0; }

/* ── Carousel — vanilla horizontal scroll, prev/next in js/site.js ─ */
.hp-carousel { position: relative; }
.hp-carousel-track {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 0.5rem;
  scrollbar-width: thin;
}
.hp-carousel-track > .post-card,
.hp-carousel-track > .post-card--photo {
  flex: 0 0 300px;
  max-width: 300px;
  scroll-snap-align: start;
}
/* Photo cards stack vertically in the NARROW shapes (carousel ~300px, row3
   ~270px) — image on top, text below. The side-by-side thumbnail layout only
   has room in the wider shapes (grid2x2, river); in a narrow column it squeezes
   the text to one word per line. */
.hp-carousel-track > .post-card--photo,
.hp-grid--row3 > .post-card--photo {
  flex-direction: column;
  gap: 0.9rem;
  align-items: stretch;
}
.hp-carousel-track > .post-card--photo .post-card__thumb,
.hp-grid--row3 > .post-card--photo .post-card__thumb {
  width: 100%;
  height: 170px;
}
.hp-carousel-nav {
  position: absolute;
  top: calc(50% + 0.75rem);
  transform: translateY(-50%);
  z-index: 2;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  border: 1px solid var(--card-border);
  background: var(--card);
  color: var(--signal);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 10px rgba(22, 24, 29, 0.10);
  transition: border-color 0.2s, opacity 0.2s;
}
.hp-carousel-nav:hover { border-color: var(--signal); }
.hp-carousel-nav[disabled] { opacity: 0.35; cursor: default; }
.hp-carousel-prev { left: -0.5rem; }
.hp-carousel-next { right: -0.5rem; }

/* ── Block quote (authored pull-quote, full container width) ──── */
.hp-quote {
  text-align: center;
  padding: 1rem 0;
}
.hp-quote-text {
  margin: 0 auto;
  max-width: 46rem;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(0.75rem, 1.7vw, 1.125rem);
  line-height: 1.28;
  letter-spacing: -0.02em;
  color: var(--text);
}
/* Big curly quotes hugging the text (accent colour): open before, close after.
   line-height:0 keeps the oversized glyphs from stretching the line box;
   vertical-align drops them to sit roughly centred on the text. */
/* Just the two quote characters from the typeface, larger and in the accent
   colour. No transforms or vertical-align tricks — they sit where the font
   draws them and stay level because they share the text's baseline. */
.hp-quote-text::before,
.hp-quote-text::after {
  color: var(--signal);
  font-size: 1.6em;
  line-height: 0;
}
.hp-quote-text::before { content: "\201C"; }
.hp-quote-text::after  { content: "\201D"; }
.hp-quote-text em { font-style: italic; }
.hp-quote-text strong { font-weight: 700; }
.hp-quote-cite {
  display: block;
  margin: 1.1rem auto 0;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  font-style: normal;
}
.hp-quote-cite::before { content: "— "; }

/* ── Text block (authored free-form content, 23 July 2026) ──────
   Reuses .article-body's typography wholesale (h2/h3/p/ul/ol/blockquote/
   img/a) rather than a second set of rules — same content, same styling,
   whether it's in a post or a text block. Only override: .article-body's
   first heading carries a 2.5rem top margin meant to separate it from
   preceding body text; inside a block that's already sitting in the
   .homepage flex column's own 2.75rem block gap, so it would double up. */
.hp-text > *:first-child { margin-top: 0; }

/* ── Newsletter band ──────────────────────────────────────────── */
.hp-newsletter {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 2.25rem 2rem;
  text-align: center;
}
.hp-newsletter-title {
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  margin: 0 0 0.5rem;
}
.hp-newsletter-text { font-size: 0.95rem; color: var(--muted); margin: 0; }
.hp-newsletter-form {
  display: flex;
  gap: 0.6rem;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 1.25rem;
}
.hp-newsletter-form input[type="email"] {
  font-family: var(--font-body);
  font-size: 0.95rem;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--card-border);
  border-radius: 8px;
  background: var(--card);
  color: var(--text);
  min-width: 260px;
}
.hp-newsletter-form input[type="email"]:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px var(--signal-dim);
}
.hp-newsletter-form button {
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 600;
  padding: 0.6rem 1.4rem;
  border: none;
  border-radius: 8px;
  background: var(--signal);
  color: #fff;
  cursor: pointer;
  transition: opacity 0.2s;
}
.hp-newsletter-form button:hover { opacity: 0.88; }
/* Honeypot — kept in the DOM for bots, invisible + unfocusable for humans. */
.hp-newsletter-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}
.hp-newsletter-note { margin-top: 0.9rem; font-size: 0.9rem; color: var(--signal); }
.hp-newsletter-note.is-error { color: var(--warn); }

/* ── BLOG CATEGORY FILTER ────────────────────────────────────── */
.blog-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 2rem;
}
.blog-filter__btn {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--card-border);
  border-radius: 20px;
  background: var(--card);
  color: var(--muted);
  cursor: pointer;
  transition: border-color 0.2s, color 0.2s, background 0.2s;
}
.blog-filter__btn:hover { border-color: var(--signal); color: var(--text); }
.blog-filter__btn.is-active {
  background: var(--signal);
  border-color: var(--signal);
  color: #fff;
}
.post-card.is-hidden,
.post-card--photo.is-hidden { display: none; }

/* ── FIXED-LEFT / FIXED-RIGHT LEAD-PHOTO LAYOUT ─────────────── */
/* .breadcrumb / .post-header / .post-title / .post-meta (the old Photo
   Post article header) retired 24 July 2026 — every post uses the same
   .page-header now (see PROJECT-NOTES.md, "Merge Standard/Photo Post into
   one template"). .post-divider / .post-body / .post-lead-photo below are
   still live — they're the 'fixed-left'/'fixed-right' lead-photo
   placements, two of five options any post can now pick, not a second
   template. 'fixed-right' (added 24 July 2026, later the same day) is a
   plain mirror of 'fixed-left' — same grid, columns reversed, and
   templates.php swaps which element comes first in the markup so the
   narrow photo column lands on the right without any JS/order trickery. */

.post-divider {
  border: none;
  border-top: 1px solid var(--border);
  margin: 0 0 2rem;
}

.post-body {
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: 2.25rem;
  align-items: start;
}

.post-body--right { grid-template-columns: 1fr 300px; }

.post-lead-photo { position: sticky; top: 1.5rem; }

.post-lead-photo img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: 10px;
  border: 1px solid var(--border);
}

.photo-caption {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--muted);
  margin-top: 0.5rem;
  line-height: 1.4;
}

/* .post-text's own typography rules retired 24 July 2026 — every post uses
   .article-body now (see PROJECT-NOTES.md, "Merge Standard/Photo Post into
   one template"); the fixed-left lead-photo layout's text column carries
   both classes (class="post-text article-body") so .post-text still hooks
   the grid layout below, it just no longer duplicates typography rules
   .article-body already has (and had more of, since .post-text never
   picked up heading/list/quote/image styling — a gap this merge fixes). */

.post-extra-photos {
  margin-top: 2.5rem;
  padding-top: 1.75rem;
  border-top: 1px solid var(--border);
}

.extra-photos-label {
  font-family: var(--font-mono);
  font-size: 0.65rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 1rem;
}

.post-back {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}
.post-back a {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--signal);
  text-decoration: none;
}
.post-back a:hover { text-decoration: underline; }

/* Prev/next post nav (24 July 2026, simplified same day — see
   PROJECT-NOTES.md) — filled in client-side by initPostNav() in
   js/site.js, which reads search-index.json (the same newest-first order
   the CMS already generates on every rebuild) to work out each post's
   neighbours. Just "Previous"/"Next" labels, no post title — Richard felt
   the title made the row too busy for what it's for. Prev/next links
   start [hidden] in the markup and are only revealed once JS resolves a
   real neighbour, so a post at either end of the list (or a page load
   before JS runs) just shows "All posts" alone rather than a dead arrow. */
.post-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
  font-family: var(--font-mono);
}
.post-nav__link {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.78rem;
  color: var(--signal);
  text-decoration: none;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.post-nav__link[hidden] { display: none; }
.post-nav__link:hover { text-decoration: underline; }
.post-nav__all {
  flex: 0 0 auto;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.72rem;
}
.post-nav__all:hover { text-decoration: underline; }
.post-nav__arrow { font-size: 1rem; line-height: 1; }

/* Any post generated before this simplification still has an empty
   <span class="post-nav__title"> in its markup (JS no longer writes to
   it) — hidden outright so nothing shows even for those older files
   without needing to regenerate them again. */
.post-nav__title { display: none; }

@media (max-width: 560px) {
  .post-nav { flex-wrap: wrap; }
  .post-nav__all { order: -1; flex: 1 1 100%; text-align: center; margin-bottom: 0.4rem; }
}

@media (max-width: 680px) {
  .post-body,
  .post-body--right { grid-template-columns: 1fr; gap: 1.5rem; }
  .post-lead-photo { position: static; }
  /* fixed-right's markup has the text div first (so the photo lands in
     the right-hand column on desktop) — on a single stacked column that
     would put the photo below the text, so pull it back above with
     `order` to match fixed-left's stacked look. */
  .post-body--right .post-lead-photo { order: -1; }
}

/* Narrow phones: the photo card stacks (image above text).
 *
 * `align-items: stretch` and the explicit width on the thumb LINK are both
 * load-bearing (21 July 2026). Switching to `flex-direction: column` alone
 * left the flex items shrink-wrapping their content, so
 * `.post-card__thumb-link` had no width of its own and the thumb's
 * `width: 100%` resolved against an already-collapsed box. The result was
 * thumbnails that kept the 180px height but took whatever width their
 * photo's natural shape implied — measured at 412px wide: portrait photos
 * 134px, landscape ones 257–266px, none of them full width. Cards looked
 * ragged and inconsistent on mobile. Stretching the link makes the 100%
 * mean the card width, as intended. */
@media (max-width: 500px) {
  .post-card--photo { flex-direction: column; align-items: stretch; }
  .post-card__thumb-link { width: 100%; }
  .post-card__thumb { width: 100%; height: 180px; }
}

/* Homepage shapes collapse gracefully on narrow screens. */
@media (max-width: 720px) {
  .hp-grid--row3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 500px) {
  .hp-grid--2x2,
  .hp-grid--row3 { grid-template-columns: 1fr; }
  .hp-hero-text { padding: 1.25rem 1.25rem 1.5rem; }
  .hp-carousel-nav { display: none; }
}

/* ── PHOTO GALLERY + LIGHTBOX ────────────────────────────────────
   Shared component used by the Photo Post template (multi-photo
   extra-photos section) and by the standalone Photo Tool's
   multi-photo snippet on freeform pages.

   Visible state is a fixed 2-column thumbnail grid (crops to a
   consistent frame — normal for a thumbnail). Clicking a thumbnail
   opens a single shared full-screen lightbox (one instance per page,
   reused across every .photo-gallery on it) which shows each photo
   at its natural aspect ratio, un-cropped, so portrait and landscape
   photos both display correctly — only the small grid preview crops.

   Markup (filenames baked in at generation time — no runtime
   folder discovery):
     <div class="photo-gallery"
          data-path="../images/articles/my-post/"
          data-images="a.webp|b.webp|c.webp"
          data-captions="Caption one||Caption three">
       <!-- thumbnails injected by initGalleries() in js/site.js -->
     </div>
   ───────────────────────────────────────────────────────────── */
.photo-gallery {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.75rem;
  margin: 1.5rem 0;
}
.photo-gallery__thumb {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--surface);
  cursor: pointer;
}
.photo-gallery__thumb img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
  transition: transform 0.25s ease;
}
.photo-gallery__thumb:hover img { transform: scale(1.04); }
.photo-gallery__caption {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.65rem;
  color: var(--muted);
  padding: 0.4rem 0.6rem;
  background: var(--card);
  text-align: left;
}

@media (max-width: 500px) {
  .photo-gallery { grid-template-columns: 1fr; }
}

/* ── LIGHTBOX — one shared overlay per page ─────────────────── */
.photo-lightbox {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(16, 18, 26, 0.9);
  z-index: 1000;
  align-items: center;
  justify-content: center;
  padding: 3rem 1rem;
}
.photo-lightbox.open { display: flex; }
body.lightbox-open { overflow: hidden; }

.photo-lightbox__figure {
  max-width: min(90vw, 1000px);
  max-height: 85vh;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.photo-lightbox__img {
  max-width: 100%;
  max-height: 75vh;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
  border-radius: 6px;
  background: var(--card);
}
.photo-lightbox__caption {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--subtle);
  margin-top: 0.75rem;
  text-align: center;
}
.photo-lightbox__counter {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--subtle);
  margin-top: 0.4rem;
  letter-spacing: 0.08em;
}
.photo-lightbox__close,
.photo-lightbox__arrow {
  position: fixed;
  background: rgba(255,255,255,0.1);
  border: 1px solid rgba(255,255,255,0.3);
  color: #fff;
  border-radius: 50%;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s;
}
.photo-lightbox__close:hover,
.photo-lightbox__arrow:hover { background: rgba(255,255,255,0.25); }
.photo-lightbox__close {
  top: 1.25rem;
  right: 1.25rem;
  width: 40px;
  height: 40px;
  font-size: 1.3rem;
}
.photo-lightbox__arrow {
  top: 50%;
  transform: translateY(-50%);
  width: 46px;
  height: 46px;
  font-size: 1.4rem;
}
.photo-lightbox__arrow--prev { left: 1rem; }
.photo-lightbox__arrow--next { right: 1rem; }

@media (max-width: 600px) {
  .photo-lightbox__close { width: 34px; height: 34px; font-size: 1.1rem; top: 0.75rem; right: 0.75rem; }
  .photo-lightbox__arrow { width: 38px; height: 38px; font-size: 1.2rem; }
  .photo-lightbox__arrow--prev { left: 0.5rem; }
  .photo-lightbox__arrow--next { right: 0.5rem; }
}

/* ── SEARCH OVERLAY — fuzzy site search, one shared instance per page ── */
.search-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(16, 18, 26, 0.85);
  z-index: 1000;
  align-items: flex-start;
  justify-content: center;
  padding: 4rem 1rem 2rem;
}
.search-overlay.open { display: flex; }
body.search-open { overflow: hidden; }

.search-overlay__panel {
  width: 100%;
  max-width: 640px;
  max-height: 80vh;
  background: var(--card);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.search-overlay__head {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.9rem 1rem;
  border-bottom: 1px solid var(--border);
}
.search-overlay__icon {
  font-size: 0.95rem;
  color: var(--muted);
  flex-shrink: 0;
}
.search-overlay__input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: none;
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--text);
}
.search-overlay__input::placeholder { color: var(--muted); }
.search-overlay__close {
  flex-shrink: 0;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1.1rem;
  cursor: pointer;
  padding: 0.2rem 0.4rem;
  border-radius: 6px;
}
.search-overlay__close:hover { color: var(--signal); background: var(--signal-dim); }

.search-overlay__results {
  overflow-y: auto;
  padding: 0.5rem;
}
.search-overlay__empty {
  padding: 1.5rem 1rem;
  text-align: center;
  font-size: 0.85rem;
  color: var(--muted);
}

.search-overlay__result {
  display: block;
  padding: 0.75rem 0.85rem;
  border-radius: 8px;
  text-decoration: none;
  color: var(--text);
  transition: background 0.12s;
}
.search-overlay__result:hover,
.search-overlay__result.active {
  background: var(--signal-dim);
  text-decoration: none;
}
.search-overlay__result-meta {
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--signal);
  margin-bottom: 0.2rem;
}
.search-overlay__result-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 0.92rem;
  color: var(--text);
  margin-bottom: 0.2rem;
}
.search-overlay__result-excerpt {
  font-size: 0.8rem;
  color: var(--muted);
  line-height: 1.5;
}

@media (max-width: 600px) {
  .search-overlay { padding: 2.5rem 0.5rem 1rem; }
  .search-overlay__panel { max-height: 85vh; }
}

/* Single "floating" image — freeform pages, text wraps around it */
.floating-photo {
  float: right;
  max-width: 340px;
  margin: 0 0 1.5rem 1.5rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--card);
  overflow: hidden;
}
.floating-photo--left { float: left; margin: 0 1.5rem 1.5rem 0; }
.floating-photo img { width: 100%; height: auto; display: block; }
.floating-photo figcaption {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--muted);
  padding: 0.5rem 0.75rem;
}
@media (max-width: 600px) {
  .floating-photo, .floating-photo--left { float: none; max-width: 100%; margin: 0 0 1.5rem; }
}

/* Single in-flow image — same look as .floating-photo (border, radius,
   figcaption style) but no float, so it just sits in the text and scrolls
   with it rather than being wrapped. Added 24 July 2026 for the admin
   insert-photo toolbar's "inline" placement option, alongside float
   left/right. Centred by default since an in-flow image with no wrap
   reads oddly hugging one edge. */
.inline-photo {
  max-width: 100%;
  margin: 0 auto 1.5rem;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--card);
  overflow: hidden;
}
.inline-photo img { width: 100%; height: auto; display: block; }
.inline-photo figcaption {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--muted);
  padding: 0.5rem 0.75rem;
}

/* ── UTILITIES ──────────────────────────────────────────────── */
.mt3 { margin-top: 3rem; }

@media (max-width: 600px) {
  .product-card,
  .download-card { max-width: 100%; }
}

/* ── NAV DROPDOWNS ──────────────────────────────────────────── */
.nav-has-drop { position: relative; }

.nav-drop-btn {
  /* Sits in the blue .nav-row-links band, so it takes the same --nav-link
     colours as its sibling <a> tags rather than --muted/--signal (which
     are tuned for text on --card, not on the blue nav bands). */
  background: none; border: none; font-family: var(--font-body);
  font-size: 0.85rem; font-weight: 500; color: var(--nav-link);
  letter-spacing: 0.01em; cursor: pointer;
  display: flex; align-items: center; gap: 0.3rem;
  padding: 0.4rem 0.9rem; border-radius: 6px; line-height: 1;
  transition: color 0.15s, background 0.15s; white-space: nowrap;
}
.nav-drop-btn:hover,
.nav-drop-btn[aria-expanded="true"] { color: var(--nav-link-active); background: rgba(255,255,255,0.14); }

.nav-chevron { font-size: 0.65rem; opacity: 0.6; transition: transform 0.2s; display: inline-block; }
.nav-drop-btn[aria-expanded="true"] .nav-chevron { transform: rotate(180deg); }

.nav-dropdown {
  display: none; position: absolute; top: calc(100% + 8px); right: 0;
  background: var(--card); border: 1px solid var(--border); border-radius: 10px;
  min-width: 190px; padding: 0.4rem; list-style: none; z-index: 200;
  box-shadow: 0 4px 16px rgba(0,0,0,0.08);
}
.nav-dropdown.open { display: block; }
.nav-dropdown li a {
  display: block; padding: 0.5rem 0.85rem; font-size: 0.82rem; color: var(--text);
  border-radius: 7px; text-decoration: none; font-weight: 400;
  letter-spacing: normal; text-transform: none; transition: background 0.12s, color 0.12s;
}
.nav-dropdown li a:hover { background: var(--signal-dim); color: var(--signal); }
.nav-dropdown li.coming-soon a { opacity: 0.4; pointer-events: none; cursor: default; }
.nav-drop-label { font-family: var(--font-mono); font-size: 0.6rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); padding: 0.5rem 0.85rem 0.2rem; }
.nav-drop-divider { height: 1px; background: var(--border); margin: 0.3rem 0.5rem; }

/* ── HAMBURGER — CSS only, no JS needed ─────────────────────── */
.nav-toggle-input { display: none; }

.nav-hamburger {
  display: none; flex-direction: column; gap: 5px;
  cursor: pointer; padding: 1rem; margin-left: auto; flex-shrink: 0;
  -webkit-tap-highlight-color: transparent;
}
.nav-hamburger span {
  display: block; width: 22px; height: 2px;
  background: var(--text); border-radius: 2px; transition: all 0.25s;
  pointer-events: none;
}

/* Mobile menu — shown via CSS when checkbox checked */
.nav-mobile-menu {
  display: none; position: absolute; top: 100%; left: 0; right: 0;
  background: var(--card); border-bottom: 1px solid var(--border);
  padding: 0.75rem 1.25rem 1.25rem; z-index: 999; flex-direction: column;
}
.nav-toggle-input:checked ~ .nav-mobile-menu { display: flex; }

.nav-mobile-link {
  font-size: 0.9rem; color: var(--text); padding: 0.6rem 0.5rem;
  border-radius: 6px; text-decoration: none; transition: background 0.12s, color 0.12s;
}
.nav-mobile-link:hover { background: var(--signal-dim); color: var(--signal); text-decoration: none; }
.nav-mobile-sub { padding-left: 1.1rem; font-size: 0.85rem; color: var(--muted); }
.nav-mobile-sub:hover { color: var(--signal); }

.nav-mobile-section {
  font-family: var(--font-mono); font-size: 0.62rem; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted);
  padding: 0.75rem 0.5rem 0.2rem; border-top: 1px solid var(--border); margin-top: 0.4rem;
}
.nav-mobile-sublabel {
  font-family: var(--font-mono); font-size: 0.58rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--muted);
  padding: 0.4rem 0.5rem 0.1rem 1.1rem; opacity: 0.7;
}

/* ── MAKE NAV OVERFLOW VISIBLE for dropdowns ────────────────── */
#site-nav { overflow: visible; }

/* ── MOBILE BREAKPOINT ──────────────────────────────────────── */
@media (max-width: 600px) {
  .nav-links { display: none; }
  .nav-hamburger { display: flex; }
}

/* ── CONTACT FORM ───────────────────────────────────────────── */
.contact-form {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  max-width: 560px;
}
.form-group {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.form-group label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.form-group input,
.form-group textarea {
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--text);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.65rem 0.85rem;
  transition: border-color 0.15s;
}
.form-group input:focus,
.form-group textarea:focus {
  outline: none;
  border-color: var(--signal);
}
.form-group textarea { resize: vertical; min-height: 140px; }

.contact-form .btn {
  border: none;
  cursor: pointer;
  align-self: flex-start;
}

/* Honeypot field — hidden from sighted users, tabbed-past by keyboard nav,
   but left in the markup/DOM for bots that fill in every field indiscriminately */
.contact-form__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.contact-message {
  border-radius: var(--radius);
  padding: 1rem 1.25rem;
  margin-bottom: 1.5rem;
  font-size: 0.95rem;
}
.contact-message--success {
  background: var(--signal-dim);
  border: 1px solid var(--signal);
  color: var(--signal);
}
.contact-message--error {
  background: rgba(181,86,44,0.08);
  border: 1px solid var(--warn);
  color: var(--warn);
}
