/* ═══════════════════════════════════════════════════════════════
   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 ────────────────────────────────────────────────────── */
#site-nav {
  background: var(--card);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  padding: 0.75rem 1.5rem;
  gap: 1.25rem;
  position: sticky;
  top: 0;
  z-index: 100;
}

.nav-brand {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.03em;
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
}
.nav-brand:hover { text-decoration: none; opacity: 0.8; }

.nav-divider {
  display: none;
  width: 1px;
  align-self: stretch;
  background: var(--border);
  flex-shrink: 0;
}

/* Centre tagline restored 19 Jul 2026 (refined single line + blue haze).
   .nav-desc (long description) stays hidden — only the short tagline shows.
   True page-centring (20 Jul 2026): on desktop #site-nav becomes a
   1fr / auto / 1fr grid so the tagline sits dead-centre on the PAGE, not
   just in the gap between the (narrow) wordmark and (wider) links — a plain
   flex centre lands ~85px left because those two sides aren't equal width.
   Tagline hidden ≤900px (below that there isn't room to centre it cleanly). */
.nav-center {
  text-align: center;
}

@media (min-width: 901px) {
  #site-nav { display: grid; grid-template-columns: 1fr auto 1fr; }
  .nav-brand  { justify-self: start; }
  .nav-center { justify-self: center; }
  .nav-links  { justify-self: end; margin-left: 0; }
}

.nav-tagline {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.03em;
  padding: 0.3rem 1.4rem;
  border-radius: 20px;
  background: radial-gradient(ellipse at center, rgba(47,107,255,0.16) 0%, rgba(47,107,255,0.05) 45%, transparent 72%);
  text-shadow: 0 0 16px rgba(47,107,255,0.35);
}
.nav-tagline em {
  color: var(--signal);
  font-style: normal;
}

.nav-desc {
  display: none;
}

.nav-links {
  display: flex;
  gap: 0;
  list-style: none;
  flex-shrink: 0;
  margin-left: auto;
}

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

@media (max-width: 900px) {
  .nav-center { display: none; }
}

.nav-search-btn {
  font-size: 0.95rem;
  line-height: 1;
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  padding: 0.4rem 0.5rem;
  border-radius: 6px;
  transition: color 0.15s, background 0.15s;
}
.nav-search-btn:hover { color: var(--signal); background: var(--signal-dim); }
.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-disclosure { margin-top: 0.5rem; font-size: 0.68rem; opacity: 0.8; }

/* ── 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; }

/* ── AMAZON PRODUCT 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). */
.product-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;
}
.product-card-info { flex: 1; }
.product-card-info h4 {
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 600;
  margin-bottom: 0.25rem;
}
.product-card-info p {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--muted);
  margin-bottom: 0.75rem;
}
.product-card-info .affiliate-note {
  font-size: 0.65rem;
  color: var(--muted);
  font-family: var(--font-mono);
}
.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 { max-width: 100%; }
}

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

.nav-drop-btn {
  background: none; border: none; font-family: var(--font-display);
  font-size: 0.9rem; font-weight: 500; color: var(--muted);
  letter-spacing: -0.01em; cursor: pointer;
  display: flex; align-items: center; gap: 0.3rem;
  padding: 0.4rem 0.75rem; 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(--signal); background: var(--signal-dim); }

.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-divider { display: none; }
  .nav-center { 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);
}
