/*
  Global stylesheet — single source of truth for the site.

  Structure:
    1) Reset and base
    2) Design tokens (:root) — palette and typography are owned by the
       `brand-guidelines` skill. Spacing, radii, shadows, motion, and layout
       tokens are owned here.
    3) Layout primitives
    4) Components (site-wide reusable primitives only — page-specific
       styles live with their page builds, not here)
    5) Utilities
*/

/* ===== 1) Reset and base ===== */
*, *::before, *::after {
  box-sizing: border-box;
}

html {
  font-size: 16px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-body);
  line-height: 1.5;
  color: var(--color-text);
  background: var(--color-bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-heading);
  line-height: 1.2;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
}

:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ===== 2) Design tokens ===== */
/* Palette and typography are written by the `brand-guidelines` skill from
   `build-assets/0-the-brand/tokens.json`. Spacing, radii, shadows, and
   transitions are owned here. */
:root {
  /* Palette (brand-guidelines) */
  --color-bg: #f5f9fd;
  --color-surface: #ffffff;
  --color-surface-soft: #e9f2fc;
  --color-text: #0b1730;
  --color-text-soft: #57647c;
  --color-primary: #1868d6;
  --color-primary-dark: #0f4fa8;
  --color-primary-light: #d6e9fc;
  --color-accent: #2fb2ff;
  --color-focus: #1868d6;
  --color-navy: #0b1730; /* brand-guidelines */

  /* Typography (brand-guidelines) */
  --font-heading: "Manrope", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --font-body: "Inter", "Segoe UI", "Helvetica Neue", Arial, sans-serif;

  /* Spacing */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;

  /* Radii */
  --radius-sm: 0.5rem;
  --radius-md: 0.875rem;
  --radius-lg: 1.25rem;
  --radius-pill: 999px;

  /* Shadows */
  --shadow-sm: 0 1px 3px rgba(60, 64, 67, 0.12);
  --shadow-md: 0 4px 14px rgba(17, 24, 39, 0.08);
  --shadow-lg: 0 12px 32px rgba(17, 24, 39, 0.12);

  /* Motion */
  --transition-fast: 180ms ease;
  --transition-base: 260ms ease;

  /* Layout */
  --container-max: 72rem;

  /* Aesthetic dials — the `design-system` skill SETS these per brand to diverge
     the look (sharp vs soft, flat vs shadowed, square vs pill). Defaults below are
     a soft/rounded baseline; a brand's design stage should deliberately override
     them so two brands don't look like the same template reskinned. */
  --logo-height: 2.75rem;    /* prominent by default — the logo is a brand asset, not chrome */
  --logo-max-width: 22rem;   /* generous, so wide wordmarks stay tall (not width-starved) */
  --header-bg: var(--color-navy);   /* the logo is light-on-transparent — requires a dark band (brand.md) */
  --header-fg: #ffffff;             /* flips to white so nav/logo stay legible on the navy band */
  --footer-bg: var(--color-navy);   /* bookend the header band at the bottom of every page */
  --footer-fg: color-mix(in srgb, #ffffff 70%, transparent);
  --btn-radius: var(--radius-pill);
  --card-radius: var(--radius-md);
  --card-shadow: var(--shadow-sm);
  --card-border: 1px solid color-mix(in srgb, var(--color-text) 6%, transparent);
  --chip-radius: var(--radius-pill);
  --content-prose: 65ch;
  --content-wide: 72rem;

}

/* Logo grows on wider screens — override the dial at the breakpoint, never per-page. */
@media (min-width: 1024px) {
  :root { --logo-height: 3.5rem; }
}

/* ===== 3) Layout primitives ===== */
.container {
  width: min(100% - 2rem, var(--container-max));
  margin-inline: auto;
}

.section {
  padding: var(--space-10) 0;
}

.grid {
  display: grid;
  gap: var(--space-4);
}

.grid-2 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(1, minmax(0, 1fr)); }

@media (min-width: 600px) {
  .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 768px) {
  .grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ===== 4) Components ===== */

/* Site header */
.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--header-bg);
  color: var(--header-fg);
  border-bottom: 1px solid color-mix(in srgb, var(--color-text) 8%, transparent);
}

.site-header-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
}

.site-logo {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* The header logo is an <img src="{{logo.src}}"> whose REAL file is injected by the CMS
   at upload — its aspect ratio is unknown at build time. HEIGHT is the primary driver
   (a generous --logo-height), so every logo renders at a consistent, prominent height
   regardless of ratio; --logo-max-width is a generous, viewport-relative safety cap that
   only clamps a very wide logo on small screens (it then scales down proportionally, never
   distorts). Never pin width AND height. Tune the dials per brand — never a per-page size. */
.site-logo img {
  height: auto;
  width: auto;
  max-height: var(--logo-height);
  max-width: min(var(--logo-max-width, 22rem), 55vw);
  object-fit: contain;
}

.site-nav {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
}

.site-cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* CMS-rendered menus — `{{menu.navigation}}` and `{{menu.footer}}` expand to
   <ul class="canvas-navigation-menu"> / <ul class="canvas-footer-menu"> with
   <li><a> children. Style the generated classes directly. */

.canvas-navigation-menu {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.canvas-navigation-menu > li {
  margin: 0;
}

.canvas-navigation-menu a {
  color: var(--header-fg);
  font-weight: 600;
  font-size: 0.95rem;
  padding: var(--space-2) 0;
  border-bottom: 2px solid transparent;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.canvas-navigation-menu a:hover,
.canvas-navigation-menu a:focus-visible {
  color: var(--color-primary);
  border-bottom-color: var(--color-primary);
}

.canvas-footer-menu {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-6);
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
}

/* Footers can carry many links. With 8+ items, flow them into responsive columns
   (auto-fill grid) instead of one long, sprawling wrapping row — multi-column on wide
   screens, collapsing toward a single column on mobile. Short footer menus keep the
   inline row above. Where :has() is unsupported it harmlessly stays the flex row. */
.canvas-footer-menu:has(> li:nth-child(8)) {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 9rem), 1fr));
  align-items: start;
  gap: var(--space-2) var(--space-5);
}

.canvas-footer-menu > li {
  margin: 0;
}

.canvas-footer-menu a {
  color: var(--color-text-soft);
  font-size: 0.9rem;
  transition: color var(--transition-fast);
}

.canvas-footer-menu a:hover,
.canvas-footer-menu a:focus-visible {
  color: var(--color-primary);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Breadcrumb */
.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) 0;
  color: var(--color-text-soft);
  font-size: 0.9rem;
}

.breadcrumb a {
  color: var(--color-primary);
}

.breadcrumb a:hover {
  text-decoration: underline;
}

.breadcrumb-separator {
  color: var(--color-text-soft);
  opacity: 0.6;
}

.breadcrumb [aria-current="page"] {
  color: var(--color-text);
  font-weight: 600;
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  border: 0;
  text-decoration: none;
  white-space: nowrap;
  border-radius: var(--btn-radius);
  min-height: 2.75rem;
  padding: 0.78rem 1.4rem;
  font-size: 0.98rem;
  line-height: 1;
  letter-spacing: 0.01em;
  font-weight: 700;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), opacity var(--transition-fast);
}

.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn:focus-visible { outline-offset: 3px; }

.btn[disabled],
.btn[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.55;
  pointer-events: none;
  box-shadow: none;
}

.btn-primary {
  background: var(--color-primary);
  color: #fff;
  box-shadow: 0 4px 12px color-mix(in srgb, var(--color-primary) 28%, transparent);
}

.btn-primary:hover {
  background: var(--color-primary-dark);
  box-shadow: 0 6px 16px color-mix(in srgb, var(--color-primary) 40%, transparent);
}

.btn-secondary {
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid color-mix(in srgb, var(--color-text) 16%, transparent);
}

.btn-secondary:hover {
  background: color-mix(in srgb, var(--color-surface-soft) 75%, #fff);
}

.btn-ghost {
  background: transparent;
  color: var(--color-text);
  border: 1px solid color-mix(in srgb, var(--color-text) 20%, transparent);
}

.btn-ghost:hover {
  background: color-mix(in srgb, var(--color-surface-soft) 65%, transparent);
}

/* Header Login CTA: solid white pill with blue text so it pops against the header
   band (dark navy on the hero, or any other header background), rather than
   blending in like the transparent/outline default .btn-ghost. */
.site-header .btn-ghost {
  background: var(--color-surface);
  color: var(--color-primary);
  border-color: transparent;
}

.site-header .btn-ghost:hover {
  background: var(--color-surface);
  color: var(--color-primary-dark);
  box-shadow: 0 4px 12px color-mix(in srgb, #000 18%, transparent);
}

.btn-sm {
  min-height: 2.2rem;
  padding: 0.55rem 1rem;
  font-size: 0.87rem;
}

.btn-lg {
  min-height: 3rem;
  padding: 0.9rem 1.7rem;
  font-size: 1.04rem;
}

.btn-block { width: 100%; }

.btn-icon {
  width: 2.75rem;
  min-width: 2.75rem;
  padding: 0;
  border-radius: 50%;
}

/* Card */
.card {
  background: var(--color-surface);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
  border: var(--card-border);
  padding: var(--space-5);
}

.card-title {
  margin: 0 0 var(--space-2);
  font-size: 1.08rem;
}

.card-text {
  margin: 0;
  color: var(--color-text-soft);
}

/* Chip — pill-shaped tag */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.45rem 0.95rem;
  border-radius: var(--chip-radius);
  background: var(--color-surface);
  color: var(--color-primary);
  font-size: 0.82rem;
  font-weight: 600;
  border: 1px solid color-mix(in srgb, var(--color-primary) 25%, transparent);
}

.chip-row {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* Site footer */
.site-footer {
  padding: var(--space-10) 0;
  background: var(--footer-bg);
  color: var(--footer-fg);
  font-size: 0.9rem;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4) var(--space-6);
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
}

.footer-links a {
  color: color-mix(in srgb, #ffffff 78%, transparent);
  font-weight: 600;
  font-size: 0.92rem;
}

.footer-links a:hover,
.footer-links a:focus-visible {
  color: #ffffff;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.footer-legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: 1px solid color-mix(in srgb, #ffffff 14%, transparent);
  font-size: 0.85rem;
}

.footer-legal a {
  color: color-mix(in srgb, #ffffff 55%, transparent);
}

.footer-legal a:hover,
.footer-legal a:focus-visible {
  color: #ffffff;
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Native primary nav (this brand uses hand-coded menus, not the CMS {{menu.*}} placeholders).
   Mobile drawer is CSS-only (checkbox hack: a hidden checkbox + <label> toggles a sibling
   nav via :checked ~ selector) — deliberately no <script>, so every page stays safe for a
   bulk ZIP import (custom body JS gets a page rejected outright; JSON-LD is the only
   exception). See notes/decisions.md for why native menus were chosen for this brand. */
.nav-toggle-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}
.nav-toggle {
  display: inline-flex;
  flex-direction: column;
  gap: 4px;
  padding: var(--space-2);
  cursor: pointer;
  /* The open drawer (.site-nav, below) is position:fixed with z-index:30 and
     spans the full viewport height starting at inset top:0 — it physically
     paints over this button's spot in the header unless this sits above it
     in the same stacking context. Without this, the bars morph into an X
     correctly but the X is invisible, painted over by the drawer, and the
     button becomes unclickable while the drawer is open. */
  position: relative;
  z-index: 31;
}
.nav-toggle-bar {
  width: 24px;
  height: 2px;
  background: var(--header-fg);
  display: block;
  transition: transform var(--transition-fast), opacity var(--transition-fast);
}
.nav-toggle-input:focus-visible + .nav-toggle {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}
/* Hamburger -> X, CSS-only (checkbox hack, no <script>). Confirms to the user that
   tapping again closes the drawer, rather than leaving the icon static in both states. */
.nav-toggle-input:checked + .nav-toggle .nav-toggle-bar:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}
.nav-toggle-input:checked + .nav-toggle .nav-toggle-bar:nth-child(2) {
  opacity: 0;
}
.nav-toggle-input:checked + .nav-toggle .nav-toggle-bar:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}
.site-nav .nav-list {
  display: flex;
  align-items: center;
  gap: var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}
.site-nav .nav-list a {
  color: var(--header-fg);
  font-weight: 600;
}
.site-nav .nav-list a:hover,
.site-nav .nav-list a:focus-visible {
  color: var(--color-accent);
}

/* Compound override: .nav-list a above (class+element) otherwise beats the plain
   .site-header .btn-ghost rule and repaints Login's text white again. Match its own
   specificity so the white-pill/blue-text Login button actually holds. */
.site-nav .nav-list a.btn-ghost {
  color: var(--color-primary);
}
.site-nav .nav-list a.btn-ghost:hover,
.site-nav .nav-list a.btn-ghost:focus-visible {
  color: var(--color-primary-dark);
}

/* Breakpoint is 1024px, not the usual 768px: this header carries 5 nav links + Login +
   Join beside the logo, and that set doesn't fit a tablet-width row without crowding or
   wrapping — checked against the actual item count, not a generic default. */
@media (max-width: 1023px) {
  .site-nav {
    position: fixed;
    inset: 0 0 0 auto;
    width: min(80vw, 20rem);
    background: var(--color-navy);
    box-shadow: var(--shadow-lg);
    transform: translateX(100%);
    transition: transform var(--transition-base);
    padding: var(--space-8) var(--space-6);
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    z-index: 30;
    overflow-y: auto;
  }
  .nav-toggle-input:checked ~ .site-nav { transform: translateX(0); }
  .site-nav .nav-list { flex-direction: column; align-items: flex-start; gap: var(--space-4); }
  .site-nav .nav-list li { width: 100%; }
  .site-nav .nav-list .btn { width: 100%; }
}
@media (min-width: 1024px) {
  .nav-toggle { display: none; }
}

/* "More" nav dropdown — CSS-only checkbox hack, same technique as .nav-toggle above.
   Houses the portfolio pages that don't have their own top-level nav slot: Safety,
   Comparisons, Articles. Desktop only: opens as a small absolute panel below the
   trigger (click via the checkbox, or hover as a mouse convenience) — the compact
   bar doesn't have room to spell out every page. Mobile: the drawer has plenty of
   room, so the trigger/checkbox are hidden and the same markup renders flat instead
   (see the max-width:1023px block below) — every page reads as a normal list item,
   no submenu. No <script> anywhere — safe for bulk ZIP import. */
.nav-item-dropdown { position: relative; }
.nav-dropdown-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}
.nav-dropdown-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: var(--header-fg);
  font-weight: 600;
  cursor: pointer;
}
.nav-dropdown-toggle:hover,
.nav-dropdown-input:focus-visible + .nav-dropdown-toggle {
  color: var(--color-accent);
}
.nav-dropdown-caret {
  display: inline-block;
  width: 0.4em;
  height: 0.4em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  margin-top: -3px;
}
.nav-dropdown-menu {
  display: none;
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
}
.nav-dropdown-input:checked ~ .nav-dropdown-menu,
.nav-item-dropdown:hover .nav-dropdown-menu {
  display: block;
}
/* Chained through .site-nav .nav-list so this beats .site-nav .nav-list a's own
   color rule (same bare-class-loses-to-more-specific-base-rule pattern documented
   elsewhere in this file — .nav-dropdown-menu is a descendant of .nav-list, so its
   links are also matched, and out-specified, by that base rule). Without this, the
   desktop panel's items are invisible except while actively :hover'd, because only
   the :hover pair happens to tie-and-win on source order. */
.site-nav .nav-list .nav-dropdown-menu a {
  color: var(--header-fg);
  font-weight: 600;
}
.site-nav .nav-list .nav-dropdown-menu a:hover,
.site-nav .nav-list .nav-dropdown-menu a:focus-visible {
  color: var(--color-accent);
}
@media (max-width: 1023px) {
  /* The drawer has plenty of room for every page, so on mobile the "More" trigger
     is hidden and its items render as ordinary flat list entries, not a submenu. */
  .nav-dropdown-toggle,
  .nav-dropdown-input { display: none; }
  .nav-dropdown-menu {
    display: block;
    margin: 0;
    padding: 0;
    width: 100%;
  }
  /* No margin-top here: the parent .nav-list's flex `gap` already opens the space
     between "About" and this list's first item — an added margin here double-counted
     that same gap. */
  .nav-dropdown-menu li { width: 100%; }
  .nav-dropdown-menu li + li { margin-top: var(--space-4); }
}
@media (min-width: 1024px) {
  .nav-dropdown-menu {
    position: absolute;
    top: 100%;
    left: 0;
    margin-top: var(--space-3);
    min-width: 11rem;
    background: var(--color-surface);
    border-radius: var(--card-radius);
    box-shadow: var(--shadow-md);
    padding: var(--space-2);
    z-index: 40;
  }
  .site-nav .nav-list .nav-dropdown-menu a {
    color: var(--color-text);
    display: block;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
  }
  .site-nav .nav-list .nav-dropdown-menu a:hover,
  .site-nav .nav-list .nav-dropdown-menu a:focus-visible {
    background: var(--color-surface-soft);
    color: var(--color-primary);
  }
  .nav-dropdown-menu li + li { margin-top: 0.15rem; }
}

/* Landing/hero header — a touch more logo presence than the standard band */
.site-header--hero { --logo-height: 3.25rem; }
@media (min-width: 1024px) {
  .site-header--hero { --logo-height: 3.75rem; }
}

/* Above-the-fold: header + hero together fill exactly one screen, on desktop and mobile,
   so the next section is invisible until the first scroll — matches the reference site.
   Rather than hand-computing the header's rendered height and subtracting it (fragile —
   drifted out of sync before, when the logo's max-width cap silently won over its
   max-height dial on narrow phones and left a strip of the next section peeking through
   the bottom), the header + hero are wrapped in `.hero-viewport`, a column flexbox pinned
   to one viewport tall. The header takes its own natural height; the hero flexes to fill
   whatever's left — exact on any screen, with zero guessing, however the header renders. */
.hero-viewport {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}
.hero-viewport > .site-header {
  flex: 0 0 auto;
}
/* Hero — full-bleed photo with a readable scrim, mirrors a long-form dating-site landing */
.hero {
  position: relative;
  overflow: hidden;
  flex: 1 1 0%;
  /* Floor instead of a max-height media query — keeps the headline/CTAs from being
     crushed on short viewports (landscape phones, a small laptop window with browser
     chrome) without needing a separate breakpoint to express the same idea. */
  min-height: 30rem;
  display: flex;
  align-items: flex-end;
  color: #ffffff;
}
.hero-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 22%;
  z-index: 0;
}
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Left-to-right darkening exists only to keep the hero copy (left-aligned) readable —
     tuned lighter than the original pass (was 94/78/25) because the full-strength version
     was hiding too much of the photo on the left. Keep this in mind if the desktop photo
     changes again: a lighter/darker source may need this retuned either direction. */
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--color-navy) 55%, transparent) 0%, color-mix(in srgb, var(--color-navy) 40%, transparent) 48%, color-mix(in srgb, var(--color-navy) 12%, transparent) 100%),
    linear-gradient(0deg, color-mix(in srgb, var(--color-navy) 55%, transparent) 0%, transparent 40%);
}
@media (max-width: 767px) {
  /* Mobile text runs full-width rather than offset to one side, so the desktop scrim's
     left-to-right darkening doesn't apply here. The CURRENT mobile photo (as of
     2026-09-08, `hero 3 mobile.png`) has its own strong gradient baked in across nearly
     the whole frame, so this only needs to be a light top-up, not the main contrast
     source. This has flip-flopped across revisions (see notes/quirks.md) — open the
     actual file before changing this if the photo is swapped again. */
  .hero-bg { object-position: center 5%; }
  .hero-scrim {
    background: linear-gradient(0deg, color-mix(in srgb, var(--color-navy) 35%, transparent) 0%, transparent 40%);
  }

  /* Content sits closer to the bottom of the hero than dead-center — user asked for this
     explicitly across the whole mobile range (375x667 through 414x896 and up, right up to
     the desktop breakpoint), after trying full centering and finding it left the block
     adrift with too much empty space both above and below. `align-items:flex-end` matches
     the base (non-mobile) behavior; the gap from the very bottom edge still comes from
     `.hero-content`'s own `padding-block: var(--space-12)` below, so it won't run off
     the screen. If a future photo/type change makes this crowd the bottom again, nudge
     that padding rather than reintroducing a separate centering override here. */
  .hero { align-items: flex-end; }

  /* Bigger type and wider gaps on mobile specifically, so the block reads as filling more
     of the screen rather than just being a short block relocated. */
  .hero-eyebrow { font-size: 0.85rem; margin-bottom: var(--space-6); }
  .hero h1 { font-size: clamp(2.6rem, 2rem + 5vw, 3.4rem); margin-bottom: var(--space-8); }
  .hero-sub { font-size: 1.2rem; margin-bottom: var(--space-10); }
  .hero-ctas { gap: var(--space-5); margin-bottom: var(--space-10); }
}
.hero-content {
  position: relative;
  z-index: 2;
  max-width: 38rem;
  padding-block: var(--space-12);
}
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-size: 0.78rem;
  font-weight: 700;
  padding: 0.4rem 0.9rem;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, #ffffff 16%, transparent);
  color: #ffffff;
  margin-bottom: var(--space-5);
}
.hero h1 {
  font-size: clamp(2.4rem, 1.9rem + 2.8vw, 4rem);
  font-weight: 800;
  margin: 0 0 var(--space-6);
  letter-spacing: -0.01em;
  line-height: 1.08;
}
.hero h1 .accent { color: var(--color-accent); }
.hero-sub {
  font-size: 1.1rem;
  color: color-mix(in srgb, #ffffff 85%, transparent);
  max-width: 40ch;
  /* Was `var(--space-7)` — that token doesn't exist in the spacing scale (jumps
     6 → 8, see :root), so the margin was silently invalid and collapsed to 0,
     which is why the subhead and CTAs read as touching. Using the real next step. */
  margin: 0 0 var(--space-8);
}
.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-bottom: var(--space-8);
}
@media (max-width: 480px) {
  .hero-ctas { flex-direction: column; align-items: stretch; }
  .hero-ctas .btn { width: 100%; }
}
.btn-on-dark {
  background: #ffffff;
  color: var(--color-navy);
  box-shadow: var(--shadow-md);
}
.btn-on-dark:hover {
  background: var(--color-primary-light);
}
.hero .btn-ghost {
  color: #ffffff;
  border-color: color-mix(in srgb, #ffffff 35%, transparent);
}
.hero-trust {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-6);
  padding: 0;
  margin: 0;
  list-style: none;
  font-size: 0.9rem;
  color: color-mix(in srgb, #ffffff 82%, transparent);
}
.hero-trust li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.hero-trust svg { flex: none; }

/* Stat chip, in normal flow directly under the CTAs (not floating) — on both desktop and
   mobile, so it always sits right where the content ends instead of at a fixed offset
   from the hero's own corner (which read as disconnected from the text on wide screens). */
.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1rem;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--color-navy) 55%, transparent);
  backdrop-filter: blur(6px);
  color: #ffffff;
  font-size: 0.85rem;
  font-weight: 600;
  box-shadow: var(--shadow-md);
}
.hero-badge-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--color-accent);
}
/* Stats bar — directly under the hero */
.stats-bar {
  padding-block: var(--space-8);
  background: var(--color-surface);
  border-bottom: 1px solid color-mix(in srgb, var(--color-text) 8%, transparent);
}
@media (max-width: 767px) {
  /* Section padding-block values across this page are tuned for desktop and don't
     shrink on their own — stacked one after another on a small screen, they read as
     a lot of dead space between blocks. Tightened here (and on the other homepage
     sections below) to a smaller but still-breathing mobile scale, not edge-to-edge. */
  .stats-bar { padding-block: var(--space-6); }
  /* Modifier only present in homepage-v2.html's markup (its scroll-by-scroll mobile
     pass wants this block thinner still). homepage.html doesn't carry this class,
     so the shared rule directly above still governs it untouched. Tightened twice
     already (24px->12px->8px); user feedback said still not thin enough, so cut
     to the smallest space token still on the scale. */
  .stats-bar--v2-thin { padding-block: var(--space-1); }
  /* Scoped via the same v2-only modifier (compound selector, so it out-specifies
     the shared .trust-stat rules below and the shared 480px trim further down —
     homepage.html's .stats-bar never carries --v2-thin, so it keeps those as-is). */
  .stats-bar--v2-thin .trust-stat strong { font-size: 1.05rem; }
  .stats-bar--v2-thin .trust-stat span { font-size: 0.65rem; }
}
.stats-bar .grid {
  /* Always 3 side-by-side columns, even on narrow phones — was `auto-fit` with a 200px
     minimum, which meant a ~340px mobile container could only fit one column and the
     stats stacked into a list instead of sitting next to each other as designed. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-3);
  text-align: center;
}
@media (max-width: 480px) {
  /* Copy is trimmed to fit ~100px-wide columns at this width (see homepage.html) — this
     just tightens the type a step further so the trimmed copy doesn't wrap awkwardly. */
  .trust-stat strong { font-size: 1.3rem; }
  .trust-stat span { font-size: 0.72rem; }
}

/* Feature grid */
/* Shared by the "Why Gaydar" feature grid AND the FAQ section (same class, both
   markup blocks reuse it) — tightening this narrows both of those gaps in one place. */
.feature-grid-section { padding-block: var(--space-8); }
@media (max-width: 767px) {
  .feature-grid-section { padding-block: var(--space-6); }
}
.section-head {
  max-width: var(--content-prose);
  margin: 0 auto var(--space-10);
  text-align: center;
}
.section-eyebrow {
  display: inline-block;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--color-primary);
  margin-bottom: var(--space-3);
}
.section-head h2 {
  font-size: clamp(1.8rem, 1.5rem + 1.2vw, 2.5rem);
  margin: 0 0 var(--space-3);
}
.section-head p {
  color: var(--color-text-soft);
  font-size: 1.05rem;
  margin: 0;
}
.section-head--left {
  text-align: left;
  margin-left: 0;
  max-width: none;
}
/* Section-head photo card — an image inserted between a section's <h2> and
   its supporting paragraph. Introduced for homepage-v2.html's locations
   section only ("Real members, wherever you are"); no other page's markup
   references this class, so it has no effect on the shared .section-head
   rules above (including the live homepage.html, which doesn't carry it). */
.section-head-photo {
  margin: var(--space-5) auto var(--space-6);
  max-width: 480px;
  border-radius: var(--card-radius);
  overflow: hidden;
  box-shadow: var(--card-shadow);
}
.section-head-photo img {
  display: block;
  width: 100%;
  height: 240px;
  object-fit: cover;
  object-position: center;
}
/* Per-photo vertical crop override, same idea as .img-card-photo--top —
   this photo is a portrait-orientation source, so a plain center crop
   inside the wide 480x240 card cuts off the subject's head. Pulls the
   crop window up so the face stays fully in frame. Chained with the base
   ".section-head-photo img" element+class above (not a standalone class)
   so it actually out-specifies that rule's own object-position: center —
   as a bare class it was losing that cascade fight and never applying,
   which is why the head was still being cut off. */
.section-head-photo img.section-head-photo-img--top { object-position: center 20%; }
/* Taller size variant — used for the "Why Gaydar" section-head-photo, whose
   push needs to clear more already-visible content in the next section than
   the locations card did. Compound with the base class (not a plain height
   override elsewhere) so it wins the same-specificity tie by source order. */
.section-head-photo--tall img { height: 280px; }
/* A second crop-position variant for a landscape (not portrait) source photo
   whose two faces sit near the top of the frame — pulls the crop all the way
   to the top edge so neither face is cropped off. Chained the same way as
   .section-head-photo-img--top above, for the same specificity reason. */
.section-head-photo img.section-head-photo-img--top-strong { object-position: center 0%; }
/* When this photo card sits inside a left-aligned section-head (the safety
   section's copy column, .section-head--left), left-align the card too
   instead of the base component's centered auto margins — matches the
   left-aligned heading/paragraph around it. Only affects .section-head-photo
   nested in .section-head--left; the centered locations/why-gaydar cards
   (plain .section-head, centered) are untouched. */
.section-head--left .section-head-photo { margin-left: 0; margin-right: 0; }
.feature-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}
.feature-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--color-surface);
  border-radius: var(--card-radius);
  border: var(--card-border);
  box-shadow: var(--card-shadow);
  transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}
.feature-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-md);
}
.feature-icon {
  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-md);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--color-primary) 12%, transparent);
  color: var(--color-primary);
}
.feature-card h3 {
  margin: 0;
  font-size: 1.12rem;
}
.feature-card p {
  margin: 0;
  color: var(--color-text-soft);
}

/* Trust / proof strip */
.trust-strip {
  padding-block: var(--space-10);
  background: var(--color-surface-soft);
}
.trust-strip .grid {
  grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
  text-align: center;
}
.trust-stat strong {
  display: block;
  font-family: var(--font-heading);
  font-size: clamp(1.6rem, 1.3rem + 1vw, 2.2rem);
  color: var(--color-navy);
}
.trust-stat span {
  color: var(--color-text-soft);
  font-size: 0.92rem;
}

/* CTA band */
.cta-band {
  padding-block: var(--space-12);
  background: linear-gradient(135deg, var(--color-navy), color-mix(in srgb, var(--color-primary) 55%, var(--color-navy)));
  color: #ffffff;
  text-align: center;
}
.cta-band h2 {
  font-size: clamp(1.8rem, 1.5rem + 1.2vw, 2.4rem);
  margin: 0 0 var(--space-4);
}
.cta-band p {
  color: color-mix(in srgb, #ffffff 82%, transparent);
  max-width: 50ch;
  margin: 0 auto var(--space-8);
  font-size: 1.05rem;
}

/* Locations section */
.locations-section { padding-block: var(--space-8); }
@media (max-width: 767px) {
  .locations-section { padding-block: var(--space-6); }
}
/* Homepage — "Locations" and "Why Gaydar" (below) desktop split.
   IMPORTANT: this only changes >=900px. Below that, .locations-split/
   .why-split are a single-column CSS Grid whose grid-template-areas
   reproduce the ORIGINAL (pre-split) mobile layout exactly — same visual
   order, same centering, same narrow text column — just authored as named
   grid areas instead of a nested .section-head wrapper, so the SAME direct
   children can be rearranged into a two-column desktop layout via a second
   grid-template-areas underneath, with no DOM duplication and no JS.
   (An earlier version of this nested the photo inside .section-head for
   mobile and pulled it out into a sibling column for desktop — two
   different DOM shapes for one component — which is what broke the mobile
   centering; this version uses one DOM shape for both.) */
.locations-split,
.why-split {
  display: grid;
  text-align: center;
}
.locations-split { grid-template-areas: "eyebrow" "heading" "photo" "para" "stats" "chips" "footnote"; }
.why-split { grid-template-areas: "eyebrow" "heading" "photo" "para" "cards" "dots"; }
.locations-split > .section-eyebrow,
.why-split > .section-eyebrow { grid-area: eyebrow; }
.locations-split > h2,
.why-split > h2 {
  grid-area: heading;
  max-width: var(--content-prose);
  margin: 0 auto var(--space-3);
  font-size: clamp(1.8rem, 1.5rem + 1.2vw, 2.5rem);
}
.locations-split > .content-split-photo,
.why-split > .content-split-photo {
  grid-area: photo;
  margin: var(--space-5) auto var(--space-6);
  max-width: 480px;
  border-radius: var(--card-radius);
  overflow: hidden;
  box-shadow: var(--card-shadow);
}
.locations-split > .content-split-photo img,
.why-split > .content-split-photo img { width: 100%; object-fit: cover; display: block; }
.locations-split > .content-split-photo img { height: 240px; }
.locations-split > .content-split-photo img.section-head-photo-img--top { object-position: center 20%; }
.why-split > .content-split-photo img { height: 280px; }
.why-split > .content-split-photo img.section-head-photo-img--top-strong { object-position: center 0%; }
.locations-split > .locations-intro,
.why-split > .why-intro {
  max-width: var(--content-prose);
  margin: 0 auto var(--space-10);
  color: var(--color-text-soft);
  font-size: 1.05rem;
}
.locations-split > .locations-intro { grid-area: para; }
.why-split > .why-intro { grid-area: para; }
.locations-split > .locations-meta { grid-area: stats; }
.locations-split > .location-grid { grid-area: chips; }
.locations-split > .locations-footnote { grid-area: footnote; }
.why-split > .carousel-track { grid-area: cards; }
.why-split > .carousel-dots { grid-area: dots; }

@media (min-width: 900px) {
  .locations-split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: "eyebrow photo" "heading photo" "para photo" "stats photo" "chips photo" "footnote photo";
    gap: var(--space-12);
    align-items: stretch;
    text-align: left;
  }
  /* photo sits first in the DOM for "Why Gaydar" (left column at desktop) */
  .why-split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: "photo eyebrow" "photo heading" "photo para" "photo cards";
    gap: var(--space-12);
    align-items: stretch;
    text-align: left;
  }
  .locations-split > h2, .locations-split > .locations-intro,
  .why-split > h2, .why-split > .why-intro { text-align: left; max-width: none; margin-left: 0; margin-right: 0; }
  .locations-split > .locations-meta,
  .locations-split > .location-grid { justify-content: flex-start; }
  .locations-split > .locations-footnote { text-align: left; }
  /* .content-split-photo (a plain div) stretches to the full row height for
     free under align-items:stretch; the <img> itself still needs an
     explicit height to actually fill that stretched parent — this is what
     eliminates the empty space above/below a fixed-height photo next to a
     taller text/card column. */
  .locations-split > .content-split-photo,
  .why-split > .content-split-photo { margin: 0; max-width: none; height: 100%; }
  .locations-split > .content-split-photo img,
  .why-split > .content-split-photo img { height: 100%; min-height: 320px; }
  /* "Why Gaydar" only: the carousel becomes a static 3-across, 2-row grid
     at this width, since desktop has no need to swipe — same carousel->grid
     switch already used for body.hp-v2 .articles-carousel elsewhere in this
     file. .carousel-dots aren't needed once it's no longer a carousel. */
  .why-split > .carousel-track {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-3);
    overflow-x: visible;
  }
  /* 3 cards fit a half-width column at only ~170px each — the base
     .feature-card sizing (space-6 padding, 3rem icon, 1.12rem heading) was
     tuned for the old 280px carousel card and reads cramped at that width,
     so trim it down here rather than reuse those sizes as-is. */
  .why-split > .carousel-track > .feature-card {
    flex: none;
    padding: var(--space-4);
    gap: var(--space-2);
  }
  .why-split .feature-icon { width: 2.25rem; height: 2.25rem; }
  .why-split > .carousel-track > .feature-card h3 { font-size: 0.92rem; }
  .why-split > .carousel-track > .feature-card p { font-size: 0.82rem; }
  .why-split > .carousel-dots { display: none; }
}
.locations-meta {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: var(--space-8);
  margin: 0 0 var(--space-8);
}
.locations-meta-item { text-align: center; min-width: 0; }
.locations-meta-item strong {
  display: block;
  font-family: var(--font-heading);
  font-size: clamp(1.4rem, 1.2rem + 0.8vw, 1.9rem);
  color: var(--color-navy);
}
.locations-meta-item span {
  color: var(--color-text-soft);
  font-size: 0.88rem;
}
@media (max-width: 480px) {
  /* Was wrapping onto separate rows on narrow phones — 3 items at the wide desktop gap
     (space-8) plus the "Active members worldwide" label were too wide to fit one row.
     Force a single row, same as the stats-bar fix above, and tighten to match. */
  .locations-meta { gap: var(--space-3); }
  .locations-meta-item strong { font-size: 1.2rem; }
  .locations-meta-item span { font-size: 0.7rem; }
}
.location-grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: center;
}
.location-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.6rem 1.15rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-text) 10%, transparent);
  color: var(--color-text);
  font-weight: 600;
  font-size: 0.92rem;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}
.location-chip:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-sm);
  background: var(--color-primary);
  color: #ffffff;
  border-color: transparent;
}
.location-chip.is-more {
  background: var(--color-surface-soft);
  color: var(--color-text-soft);
}
.locations-footnote {
  text-align: center;
  color: var(--color-text-soft);
  font-size: 0.88rem;
  margin-top: var(--space-6);
}
.locations-cta { text-align: center; margin-top: var(--space-6); }

/* Location pages — site/locations.html (category landing) + site/locations/<country>.html.
   Reuses .breadcrumb, .section-head/.section-eyebrow, .feature-grid/.feature-card,
   .locations-meta/.locations-meta-item, .location-grid/.location-chip, .cta-band,
   .members-block and [data-members] — this block only adds what those don't cover:
   the plain-content intro block and a couple of spacing tweaks specific to this page type. */
.location-hero { padding-block: var(--space-10) var(--space-8); }
.location-hero h1 {
  font-size: clamp(2rem, 1.6rem + 1.6vw, 2.75rem);
  margin: 0 0 var(--space-4);
}
.location-hero .location-intro {
  color: var(--color-text-soft);
  font-size: 1.05rem;
  max-width: 65ch;
  margin: 0 0 var(--space-6);
}
.location-section { padding-block: var(--space-8); }
.location-caption {
  text-align: center;
  color: var(--color-text-soft);
  font-size: 0.9rem;
  margin-top: var(--space-6);
}
.location-crosslinks { padding-block: var(--space-8); text-align: center; }
.location-crosslinks .section-head { margin-bottom: var(--space-6); }
.location-nation-group {
  margin-bottom: var(--space-6);
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-6);
}
.location-nation-group:last-child { margin-bottom: 0; }
.location-nation-group h3 {
  font-size: 1.05rem;
  margin: 0 0 var(--space-4);
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}
.location-region-count {
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--color-text-soft);
}

/* Capped region grid + "show more" — CSS-only checkbox toggle, no JS.
   Used on the UK's England group (43 regions) and the USA's flat 51-state
   list; a group under the cap simply never gets a .location-more-input, so
   nothing extra renders. See notes/decisions.md for the cap threshold. */
.location-more-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.location-chip--extra { display: none; }
.location-more-input:checked ~ .location-grid .location-chip--extra { display: inline-flex; }
.location-more-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.6rem 1.15rem;
  border-radius: var(--radius-pill);
  background: var(--color-primary-light);
  color: var(--color-primary-dark);
  font-weight: 700;
  font-size: 0.92rem;
  cursor: pointer;
  transition: background var(--transition-fast);
}
.location-more-toggle:hover { background: color-mix(in srgb, var(--color-primary-light) 70%, var(--color-primary)); }
.location-more-toggle .show-less-text { display: none; }
.location-more-input:checked ~ .location-grid .location-more-toggle .show-more-text { display: none; }
.location-more-input:checked ~ .location-grid .location-more-toggle .show-less-text { display: inline; }
.location-more-arrow { display: inline-block; transition: transform var(--transition-fast); }
.location-more-input:checked ~ .location-grid .location-more-toggle .location-more-arrow { transform: rotate(180deg); }

/* Feature carousel — CSS-only scroll-snap track with anchor-link dots (no JS) */
.carousel-track {
  display: flex;
  gap: var(--space-6);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-bottom: var(--space-2);
  scrollbar-width: none;
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-track > .feature-card {
  flex: 0 0 min(280px, 80vw);
  scroll-snap-align: start;
}
.carousel-dots {
  display: flex;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-6);
}
.carousel-dots a {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: color-mix(in srgb, var(--color-text) 18%, transparent);
  transition: background var(--transition-fast), transform var(--transition-fast);
}
.carousel-dots a:hover,
.carousel-dots a:focus-visible {
  background: var(--color-primary);
  transform: scale(1.15);
}

/* Articles teaser — homepage preview of the Articles hub: category-filter
   pills + a horizontal card carousel. Layout/UX pattern is borrowed from a
   competitor homepage the user pointed to, but recolored to Gaydar's own
   blue palette rather than copying their pink. New to homepage-v2.html only
   — nothing else on the site references these classes.
   Category filter is CSS-only: hidden radio inputs + label pills, toggled
   via :has() on the section (same checkbox-hack technique, and the same
   visually-hidden method, as the .nav-toggle-input mobile-drawer pattern
   above — no JS, so the page stays safe for a bulk ZIP import). */
.articles-teaser { padding-block: var(--space-12); background: var(--color-surface-soft); }
@media (max-width: 767px) {
  .articles-teaser { padding-block: var(--space-8); }
}
.articles-filter {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin: 0 0 var(--space-8);
}
.articles-filter-input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}
.filter-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.6rem 1.15rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  border: 1px solid color-mix(in srgb, var(--color-text) 10%, transparent);
  color: var(--color-text);
  font-weight: 600;
  font-size: 0.88rem;
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.articles-filter-input:checked + .filter-pill {
  background: var(--color-primary);
  border-color: transparent;
  color: #ffffff;
}
.articles-filter-input:focus-visible + .filter-pill {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
}
.articles-carousel {
  display: flex;
  gap: var(--space-6);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-bottom: var(--space-2);
  scrollbar-width: none;
}
.articles-carousel::-webkit-scrollbar { display: none; }
.articles-carousel > .article-tile {
  flex: 0 0 min(300px, 82vw);
  scroll-snap-align: start;
}
.article-tile .img-card-excerpt {
  margin: 0 0 var(--space-3);
  font-size: 0.92rem;
  color: var(--color-text-soft);
}
.article-tile .img-card-meta {
  margin: 0;
  font-size: 0.8rem;
  color: color-mix(in srgb, var(--color-text) 55%, transparent);
}
.articles-teaser-cta { text-align: center; margin-top: var(--space-8); }
/* Only one radio can be checked at a time; when a category other than "All"
   is checked, hide every tile that doesn't carry the matching modifier
   class. "All Articles" (checked by default) matches none of these, so
   nothing is hidden. */
.articles-teaser:has(#artfilter-dating-tips:checked) .article-tile:not(.article-tile--dating-tips) { display: none; }
.articles-teaser:has(#artfilter-first-dates:checked) .article-tile:not(.article-tile--first-dates) { display: none; }
.articles-teaser:has(#artfilter-relationships:checked) .article-tile:not(.article-tile--relationships) { display: none; }
.articles-teaser:has(#artfilter-safety:checked) .article-tile:not(.article-tile--safety) { display: none; }

/* Steps */
.steps-section { padding-block: var(--space-12); background: var(--color-surface-soft); }
@media (max-width: 767px) {
  .steps-section { padding-block: var(--space-8); }
}
.steps-grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  counter-reset: step;
}
.step-card {
  position: relative;
  padding: var(--space-6);
  padding-top: var(--space-8);
  background: var(--color-surface);
  border-radius: var(--card-radius);
  border: var(--card-border);
  box-shadow: var(--card-shadow);
}
.step-number {
  position: absolute;
  top: -1rem;
  left: var(--space-6);
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--color-navy);
  color: #ffffff;
  font-family: var(--font-heading);
  font-weight: 700;
  box-shadow: var(--shadow-md);
}
.step-card h3 { margin: 0 0 var(--space-2); font-size: 1.1rem; }
.step-card p { margin: 0; color: var(--color-text-soft); }
.steps-cta { text-align: center; margin-top: var(--space-8); }
@media (max-width: 767px) {
  /* Drop the white card chrome and center the number — the number was absolutely
     positioned to overlap the card's top edge, which only makes sense with the card
     background/border still there, so it moves into normal flow here instead. */
  .step-card {
    background: none;
    border: none;
    box-shadow: none;
    padding: 0;
    text-align: center;
  }
  .step-number {
    position: static;
    margin: 0 auto var(--space-4);
  }
}

/* Live community section (member loop) */
.members-block { padding-block: var(--space-12); }
@media (max-width: 767px) {
  .members-block { padding-block: var(--space-8); }
}
.members-block__caption {
  display: none;
  text-align: center;
  color: var(--color-text-soft);
  font-size: 0.9rem;
  margin-top: var(--space-6);
}
/* :nth-child(3), not (4) — every member-loop section now sets
   data-members-join-tile="off" (the join CTA lives outside the grid, in
   .members-block__join below), so the grid holds real members only, with
   no trailing join tile taking up one of the slots. */
.members-block:has(.lp-grid .mem-card:nth-child(3)) .members-block__caption { display: block; }
.members-block__fallback {
  display: none;
  text-align: center;
  padding: var(--space-8);
  background: var(--color-surface-soft);
  border-radius: var(--card-radius);
  color: var(--color-text-soft);
}
.members-block:has(.lp-members__empty) .members-block__fallback,
.members-block:has([data-members]:empty) .members-block__fallback { display: block; }
.members-block__cta { text-align: center; margin-top: var(--space-8); }

/* Join CTA — sits BELOW the member grid as its own button, not as a
   trailing tile inside it. Every member-loop section now carries
   data-members-join-tile="off", so the CMS never appends a join tile to
   the grid; this authored <div>+<a class="btn btn-primary"> is what
   replaces it, reusing the site's standard button component. */
.members-block__join { text-align: center; margin: var(--space-6) 0 0; }
.members-block:has(.lp-members__empty) .members-block__join,
.members-block:has([data-members]:empty) .members-block__join { display: none; }

/* ===== Member loops — see references/placeholders/members.loop.md =====
   Authored with data-members-styles="none": the CMS emits classes and no CSS. */
[data-members] .lp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(11rem, calc(50% - 0.375rem)), 1fr));
  gap: var(--space-4);
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: calc(6 * 14rem + 5 * 0.75rem);
}
/* Member showcase as a swipe carousel on mobile — opt-in via .members-block--carousel
   on the wrapping .members-block (used on location pages, where a 12-card grid was a
   long vertical scroll). Same CSS-only scroll-snap technique as .carousel-track
   above, just applied to the member grid instead of feature cards; no JS either way. */
@media (max-width: 767px) {
  .members-block--carousel [data-members] .lp-grid {
    display: flex;
    flex-wrap: nowrap;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    max-width: none;
    padding-bottom: var(--space-2);
  }
  .members-block--carousel [data-members] .lp-grid::-webkit-scrollbar { display: none; }
  .members-block--carousel [data-members] .mem-card {
    flex: 0 0 min(11rem, 80vw);
    scroll-snap-align: start;
  }
}
[data-members] .mem-card {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  box-sizing: border-box;
  margin: 0;
  overflow: hidden;
  position: relative;
  padding: 0;
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
}
[data-members] .mem-card img,
[data-members] .mem-card__initial {
  aspect-ratio: 3 / 4;
  width: 100%;
  flex: 0 0 auto;
  border-radius: 0;
}
[data-members] .mem-card img { display: block; object-fit: cover; object-position: 50% 25%; }
[data-members] .mem-card__initial {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-surface-soft);
  color: var(--color-text);
  font-size: 1.75rem;
  font-weight: 600;
  text-transform: uppercase;
}
[data-members] .mem-card__name,
[data-members] .mem-card__where {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-left: var(--space-3);
  padding-right: var(--space-3);
}
[data-members] .mem-card__name {
  margin: var(--space-3) 0 0;
  font-family: var(--font-heading);
  font-size: 0.9rem;
  line-height: 1.3;
  color: var(--color-text);
}
[data-members] .mem-card__where {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.3;
  color: var(--color-text-soft);
}
[data-members] .mem-card__name:empty,
[data-members] .mem-card__where:empty { display: none; }
[data-members] .mem-card__line {
  margin: 0.15rem 0 0;
  padding-left: var(--space-3);
  padding-right: var(--space-3);
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--color-text-soft);
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
[data-members] .badge {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 1;
  max-width: calc(100% - 1.5rem);
  font-size: 0.75rem;
  padding: 0.1rem 0.5rem;
  border-radius: var(--chip-radius);
  color: var(--color-navy);
  background: var(--color-primary-light);
}
[data-members] .mem-card__cta {
  margin-top: auto;
  padding: 0.4rem var(--space-3) var(--space-3);
  font-size: 0.75rem;
  color: var(--color-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
}
[data-members] .mem-card .mem-card__cta::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
}
[data-members] a:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px var(--color-surface);
}
[data-members] .lp-members__empty {
  margin: 0;
  color: var(--color-text-soft);
  font-size: 0.95rem;
  text-align: center;
}

/* Trust row — static icon+divider list (replaced the old auto-scrolling
   pill marquee at user request — the motion read as noisy on the homepage).
   Mobile-first: base rules show only the 3 shortest, least-redundant trust
   points (the other two duplicate the stats-bar's own "1.8M+"/community
   messaging above), each with its icon, in one non-wrapping line — measured
   against real glyph widths to confirm it fits with room to spare on every
   phone width down to 320px. From tablet up, all 5 items reappear with
   roomier type. */
.trust-row { padding-block: var(--space-5); }
.trust-row-list {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: nowrap;
  gap: 0.5rem;
  overflow-x: auto;
  scrollbar-width: none;
}
.trust-row-list::-webkit-scrollbar { display: none; }
.trust-item {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  white-space: nowrap;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--color-text);
}
.trust-item svg { flex: none; color: var(--color-accent); }
.trust-divider { width: 1px; height: 12px; flex: none; background: color-mix(in srgb, var(--color-text) 18%, transparent); }
.trust-item--wide, .trust-divider--wide { display: none; }
@media (min-width: 768px) {
  .trust-row-list { gap: 1.25rem; overflow-x: visible; flex-wrap: wrap; }
  .trust-item { font-size: 0.88rem; gap: 0.5rem; }
  .trust-divider { height: 16px; }
  .trust-item--wide { display: inline-flex; }
  .trust-divider--wide { display: block; }
}

/* Safety section */
.safety-section { padding-block: var(--space-8); }
@media (max-width: 767px) {
  .safety-section { padding-block: var(--space-6); }
}
.safety-grid {
  display: grid;
  gap: var(--space-6);
  align-items: start;
}
@media (min-width: 900px) {
  .safety-grid { grid-template-columns: 0.9fr 1.1fr; }
}
.safety-icons {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr));
  margin: var(--space-4) 0 0;
}
@media (max-width: 767px) {
  /* Was `auto-fit` with a 160px minimum — on a ~340px mobile container that fits 2
     columns, so the 3rd card wrapped onto its own row instead of all 3 stacking
     cleanly one per row. Force a single column here. */
  .safety-icons { grid-template-columns: 1fr; }
}
.safety-icon-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-5);
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--card-radius);
}
.safety-icon-item .feature-icon { margin-bottom: 0; }
.safety-icons-cta {
  grid-column: 1 / -1;
  text-align: center;
  margin-top: var(--space-2);
}
.section-head p.safety-copy {
  font-size: 1.05rem;
  margin: 0 0 var(--space-6);
}
@media (max-width: 899px) {
  /* Below the 900px breakpoint, .safety-grid drops to a single column, so this
     paragraph's own bottom margin used to sit directly on top of the grid's own
     gap before .safety-icons — dropped the paragraph's margin above, and the
     grid's own 24px gap (var(--space-6), same value used side-by-side on
     desktop) still read as too wide once it was the only thing stacked
     between text and cards, so it's tightened further here too. */
  .section-head p.safety-copy { margin-bottom: 0; }
  .safety-grid { gap: var(--space-2); }
}
.safety-icon-item h3 { margin: 0; font-size: 1rem; }
.safety-icon-item p { margin: 0; color: var(--color-text-soft); font-size: 0.9rem; }
/* New wrapper around each card's heading+paragraph (added only in
   homepage-v2.html's markup) — keeps that pair's own spacing intact
   regardless of whether .safety-icon-item itself is laid out as a column
   (icon on top) or a row (icon on the left), since h3/p stay stacked inside
   their own flex column either way. Unused elsewhere, so zero effect on any
   page that doesn't include this wrapper div in its markup. */
.safety-icon-item-body { display: flex; flex-direction: column; gap: var(--space-2); }

/* Safety-section responsive layout swap — homepage-v2.html only. User chose
   these from a 3-option visual mockup: Option A (icon-left row cards) below
   768px, Option B (full-width centered copy + 3-across centered icon grid)
   at 768px and up. Everything here is scoped under .safety-section--v2, a
   modifier class only present on homepage-v2.html's <section> — homepage.html's
   plain .safety-section keeps its original 2-col-at-900px split and
   icon-top cards at every width, untouched. */
@media (max-width: 767px) {
  .safety-section--v2 .safety-icon-item {
    flex-direction: row;
    align-items: flex-start;
    gap: var(--space-4);
  }
  .safety-section--v2 .safety-icon-item .feature-icon { flex: none; }
}
@media (min-width: 768px) {
  .safety-section--v2 .safety-grid { grid-template-columns: 1fr; gap: var(--space-2); }
  .safety-section--v2 .section-head--left {
    text-align: center;
    max-width: var(--content-prose);
    margin-inline: auto;
  }
  .safety-section--v2 .section-head--left .section-head-photo { margin-inline: auto; }
  /* Found the real remaining culprit: .section-head p.safety-copy carries
     its own margin-bottom: var(--space-6) (24px) from its base rule — the
     sitewide fix already zeroes that, but only up to max-width: 899px. At
     >=900px (ordinary desktop widths, where this v2 layout also applies)
     that 24px margin was still live and stacking on top of .safety-grid's
     own gap above, no matter how far that gap got tightened — which is why
     "more" kept not being enough. Zeroed it here for the full v2 desktop
     range so the grid gap is genuinely the only source of spacing. */
  .safety-section--v2 .section-head p.safety-copy { margin-bottom: 0; }
  /* .safety-icons also carries its own margin-top: var(--space-4) from the
     shared base rule, which was compounding with .safety-grid's own gap
     above (32px gap + 24px margin-top = 56px total) — that's the "gap is
     far too large" the user flagged. Zeroed here so the grid gap above is
     the single source of spacing between the copy and the 3 cards. */
  .safety-section--v2 .safety-icons { grid-template-columns: repeat(3, 1fr); margin-top: 0; }
  .safety-section--v2 .safety-icon-item { flex-direction: column; align-items: center; text-align: center; }
}

/* ===== Desktop optimization pass — homepage-v2.html only (body.hp-v2) =====
   The whole page was built mobile-first, section by section; at wide desktop
   widths the content container was still just capped at the sitewide
   --container-max (1152px), so past ~1200px viewport width it stops growing
   and the page reads as a narrow single column floating in an ever-larger
   sea of empty margin — that's the "squished to the centre" feeling the
   user flagged. This widens the content column from 1024px up (still
   leaving deliberate side margins, not full-bleed — the user explicitly
   wants that empty space kept, just not this cramped) and reflows two grids
   to use the extra width as real multi-column layouts instead of a taller
   version of the mobile single column. Everything below is scoped under
   body.hp-v2, a class only present on this page's <body> — homepage.html
   and every other page's use of .container/.section-head/etc. is untouched
   regardless of viewport width.
   The hero and header are explicitly excluded and reset back to the normal
   container formula — "above the fold is perfect, nothing to touch here"
   has applied to this whole scroll-by-scroll pass from the start, mobile
   or desktop. */
@media (min-width: 1024px) {
  body.hp-v2 .container { width: min(100% - 3rem, 84rem); }
  /* More specific than the rule above (extra .site-header-row / .hero
     context), so these win regardless of source order and restore the
     hero/header to the site's normal container width exactly. */
  body.hp-v2 .site-header-row,
  body.hp-v2 .hero .container { width: min(100% - 2rem, var(--container-max)); }

  /* Section intros were capped at the sitewide --content-prose (65ch,
     ~600-650px) — fine for body text, but a heading+paragraph pinned to
     that width inside an 84rem-wide container is most of the "squished"
     effect. Widen it a step for this page only. */
  body.hp-v2 .section-head { max-width: 50rem; }
  body.hp-v2 .safety-section--v2 .section-head--left { max-width: 50rem; }

  /* Community/Safety/Locations/Features: 4 cards in one row instead of 2x2,
     now that there's room for it. */
  body.hp-v2 .img-card-grid.img-card-grid--v2-4up { grid-template-columns: repeat(4, 1fr); }

  /* Articles teaser: the horizontal swipe carousel is a mobile/tablet
     pattern; at desktop width show all 6 as a static 3x2 grid instead —
     the flex/scroll-snap properties on .article-tile are simply ignored
     once the parent is a grid, no separate override needed for them. */
  body.hp-v2 .articles-carousel {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    overflow-x: visible;
  }

  /* A little more breathing room now that these rows have real width to
     spread across, instead of the tighter mobile/tablet gap. */
  body.hp-v2 .locations-meta { gap: var(--space-10); }
  body.hp-v2 .trust-row-list { gap: var(--space-8); }

  /* Stats-bar was only thinned for mobile (.stats-bar--v2-thin's other rule
     lives in the max-width:767px block above) — at desktop it was still
     falling through to the sitewide default padding-block (--space-8,
     32px), which read as a thick white block. Halved to --space-4 (16px)
     here, with the 3 stat numbers/labels sized down to match so they still
     fit comfortably in the shorter block. */
  body.hp-v2 .stats-bar--v2-thin { padding-block: var(--space-4); }
  body.hp-v2 .stats-bar--v2-thin .trust-stat strong { font-size: 1.4rem; }
  body.hp-v2 .stats-bar--v2-thin .trust-stat span { font-size: 0.8rem; }
}

/* FAQ accordion — native <details>/<summary>, zero JavaScript */
.faq-list { display: flex; flex-direction: column; gap: var(--space-3); }
.faq-list--narrow { max-width: 44rem; margin-inline: auto; }
.faq-list--narrow + .faq-list--narrow { margin-top: var(--space-8); }
.faq-item {
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--card-radius);
  padding: var(--space-2) var(--space-5);
}
.faq-item summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  font-weight: 600;
  font-family: var(--font-heading);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+";
  flex: none;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--color-primary);
  transition: transform var(--transition-fast);
}
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-item p {
  margin: 0 0 var(--space-4);
  color: var(--color-text-soft);
}

/* Member profile */
.profile-head {
  display: flex;
  gap: var(--space-5);
  align-items: center;
  margin-bottom: var(--space-6);
}
.profile-avatar {
  width: 7.5rem;
  height: 7.5rem;
  border-radius: var(--radius-pill);
  object-fit: cover;
}
.profile-id {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.profile-headline {
  font-size: 1.15rem;
  font-weight: 600;
}
.profile-bio {
  color: var(--color-text-soft);
  max-width: 60ch;
}
.profile-interests {
  margin-top: var(--space-3);
}

/* ===== 5) Utilities ===== */
.text-center { text-align: center; }
.muted { color: var(--color-text-soft); }

.mt-4 { margin-top: var(--space-4); }
.mt-6 { margin-top: var(--space-6); }
.mb-0 { margin-bottom: 0; }
.mb-4 { margin-bottom: var(--space-4); }
.mb-6 { margin-bottom: var(--space-6); }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}

/* ===== 6) Pricing page =====
   Free-tier strip matches matelocal.com/pricing's actual treatment: a
   bold "starts free" label row (headline + a lighter "no card
   required" note, not one combined sentence) followed by a feature
   list. Confirmed via computed-style + real icon markup pulled directly
   from matelocal.com/pricing at both breakpoints: it's the SAME single
   icon per feature at every width — matelocal doesn't swap to a
   different glyph or add a checkmark on desktop. Only the CSS box
   around that one icon changes: on mobile the <svg> itself gets the
   circular badge treatment (background + padding + 50% radius,
   confirmed via getComputedStyle: 38x38px box, 10px padding); on
   desktop (>=768px) the <li> itself becomes the pill (background +
   padding + pill radius, confirmed: 8px/16px padding, 99px radius) and
   the icon returns to a flat 14px glyph with no box of its own. */
.free-tier-strip {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  background: var(--color-surface-soft);
  border-radius: var(--radius-lg);
}
.free-tier-strip__label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
}
.free-tier-strip__label strong {
  font-family: var(--font-heading);
  font-size: 1.1rem;
  color: var(--color-text);
}
.free-tier-strip__label span {
  color: var(--color-text-soft);
  font-size: 0.88rem;
}
.free-tier-strip__list {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6) var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
  text-align: center;
}
.free-tier-strip__list li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-text-soft);
  font-size: 0.9rem;
  font-weight: 600;
}
.free-tier-strip__list li svg {
  width: 1.1rem;
  height: 1.1rem;
  padding: 0.6rem;
  border-radius: 50%;
  background: var(--color-primary-light);
  color: var(--color-primary);
  box-sizing: content-box;
}
@media (min-width: 768px) {
  .free-tier-strip__list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    text-align: left;
  }
  .free-tier-strip__list li {
    flex-direction: row;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5em 1em;
    border-radius: var(--radius-pill);
    background: var(--color-primary-light);
    color: var(--color-primary-dark);
    font-size: 0.88rem;
  }
  .free-tier-strip__list li svg {
    width: 0.85rem;
    height: 0.85rem;
    padding: 0;
    border-radius: 0;
    background: none;
    color: inherit;
  }
}

.tier-section { padding-block: var(--space-8) var(--space-4); }

.tier-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
  margin-top: var(--space-8);
}
@media (min-width: 768px) {
  .tier-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
}

.tier-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--space-8);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}
.tier-card--light {
  background: var(--color-surface);
  color: var(--color-text);
  border: var(--card-border);
}
.tier-card--dark {
  background: var(--color-navy);
  color: var(--color-surface);
}
/* "Best value" corner ribbon on the highlighted tier — matches
   matelocal.com/pricing's VIP+ card ribbon. */
.tier-ribbon {
  position: absolute;
  top: var(--space-5);
  right: var(--space-5);
  padding: 0.35em 0.9em;
  border-radius: var(--radius-pill);
  background: var(--color-primary-light);
  color: var(--color-primary-dark);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

.tier-badge { display: block; margin-bottom: var(--space-5); }
.tier-card--light .tier-badge { color: var(--color-text); }
.tier-card--dark .tier-badge { color: var(--color-surface); }

.tier-tagline {
  font-family: var(--font-heading);
  font-size: 1.15rem;
  font-weight: 700;
  margin: 0 0 var(--space-3);
}
.tier-description {
  margin: 0 0 var(--space-5);
  color: inherit;
  opacity: 0.85;
}

.tier-features {
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.tier-features li {
  padding-left: var(--space-6);
  position: relative;
}
.tier-features li::before {
  content: "✓";
  position: absolute;
  left: 0;
  font-weight: 700;
  color: var(--color-accent);
}

.price-block { margin-bottom: var(--space-6); }
.price-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding-block: var(--space-3);
}
.price-row--prime { border-bottom: 1px solid color-mix(in srgb, currentColor 12%, transparent); }
.price-row--secondary { padding-block-end: var(--space-2); }
.price-cell { display: flex; align-items: baseline; gap: var(--space-2); flex: 1 1 auto; min-width: 9rem; }
.price-row--prime .price-cell { font-size: 1.35rem; }
.price-row--secondary .price-cell { font-size: 1.05rem; opacity: 0.85; }
.price-currency {
  font-family: var(--font-heading);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  opacity: 0.6;
  align-self: center;
}
.price-amount { font-family: var(--font-heading); font-weight: 800; }
.price-amount--secondary { font-weight: 600; opacity: 0.75; font-size: 0.85em; }
.price-unit { font-size: 0.65em; font-weight: 500; opacity: 0.7; margin-left: 0.15em; }

.price-others { margin-top: var(--space-3); }
.price-others summary {
  cursor: pointer;
  font-size: 0.85rem;
  font-weight: 600;
  color: inherit;
  opacity: 0.75;
}
.price-others-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2) var(--space-4);
  margin-top: var(--space-3);
  font-size: 0.82rem;
  opacity: 0.8;
}
@media (min-width: 480px) {
  .price-others-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.tier-cta { margin-top: auto; text-align: center; }

.other-lengths-note {
  text-align: center;
  color: var(--color-text-soft);
  font-size: 0.9rem;
  max-width: 36rem;
  margin: var(--space-8) auto 0;
}

/* Renamed from an earlier .trust-strip: that name collides with the
   unrelated (and currently unused) homepage stats-strip rule above
   (§ Trust / proof strip, line ~865) — same class, two incompatible
   components. .pricing-trust also now matches matelocal's own class
   name and sits in its own section between the pricing cards and the
   FAQ, same as matelocal.com/pricing, rather than nested inside the
   cards section. */
.pricing-trust {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-4) var(--space-6);
  color: var(--color-text-soft);
  font-size: 0.9rem;
}
.pricing-trust span { display: inline-flex; align-items: center; gap: 0.4rem; }
.pricing-trust svg { flex: none; color: var(--color-primary); }

.cta-inline-link { color: #ffffff; text-decoration: underline; text-underline-offset: 0.15em; }
.cta-inline-link:hover { color: color-mix(in srgb, #ffffff 80%, transparent); }

/* ===== 7) Features page ===== */
.platform-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  margin-top: var(--space-8);
}
@media (min-width: 640px) { .platform-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .platform-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.platform-card {
  padding: var(--space-6);
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--radius-md);
  text-align: center;
}
.platform-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: var(--radius-pill);
  background: var(--color-primary-light);
  color: var(--color-primary);
  margin-bottom: var(--space-4);
}
.platform-icon--brand { background: var(--color-surface-soft); color: var(--color-text); }
.platform-card h3 { margin: 0 0 var(--space-2); font-size: 1.05rem; }
.platform-card p { margin: 0 0 var(--space-3); color: var(--color-text-soft); font-size: 0.9rem; }
.platform-availability {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--color-primary);
  background: var(--color-primary-light);
  padding: 0.2em 0.7em;
  border-radius: var(--radius-pill);
}

.tiers-mini-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
  margin-top: var(--space-8);
  align-items: stretch;
}
@media (min-width: 768px) { .tiers-mini-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.tier-mini-card {
  display: flex;
  flex-direction: column;
  padding: var(--space-6);
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.tier-mini-name { font-family: var(--font-heading); font-size: 1.3rem; margin: 0 0 var(--space-2); }
.tier-mini-tag { font-weight: 700; color: var(--color-text-soft); margin: 0 0 var(--space-4); font-size: 0.9rem; }
.tier-mini-card--vip { border-top: 3px solid var(--color-primary); }
.tier-mini-card--vip .tier-badge { color: var(--color-primary-dark); }
.tier-mini-card--vip-plus { border-top: 3px solid var(--color-accent); background: var(--color-navy); color: var(--color-surface); }
.tier-mini-card--vip-plus .tier-mini-tag { color: color-mix(in srgb, var(--color-surface) 75%, transparent); }
.tier-mini-card--vip-plus .tier-badge { color: var(--color-surface); }

.comparison-table-wrap { overflow-x: auto; margin-top: var(--space-8); border-radius: var(--radius-md); border: var(--card-border); }
.comparison-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; background: var(--color-surface); }
.comparison-table th, .comparison-table td { padding: var(--space-3) var(--space-4); text-align: center; border-bottom: 1px solid color-mix(in srgb, var(--color-text) 8%, transparent); white-space: nowrap; }
.comparison-table thead th { background: var(--color-surface-soft); font-family: var(--font-heading); }
.comparison-table th[scope="row"] { text-align: left; font-weight: 600; white-space: normal; }
.comparison-table tbody tr:last-child th, .comparison-table tbody tr:last-child td { border-bottom: none; }

.algo-grid { display: grid; grid-template-columns: 1fr; gap: var(--space-8); margin-top: var(--space-8); }
@media (min-width: 768px) { .algo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.algo-block h3 { margin: 0 0 var(--space-3); font-size: 1.1rem; }
.algo-block p { color: var(--color-text-soft); }

.supermatch-card {
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--radius-lg);
  padding: var(--space-8);
  box-shadow: var(--shadow-sm);
}
.supermatch-card p { color: var(--color-text-soft); }

/* Hero with one large, full-width banner photo below the intro copy,
   matching the actual hero layout on both matelocal.com/site-features
   (centered copy) and matelocal.com/safety (left-aligned copy) — rather
   than the side-by-side split used for the hero elsewhere on the site.
   Base is left-aligned with a readable max-width; add
   .hero-banner-copy--centered to center it (features page). */
.hero-banner-copy { max-width: var(--content-prose); }
.hero-banner-copy--centered { text-align: center; margin: 0 auto; }
.hero-banner-copy--centered .breadcrumb { justify-content: center; }
.hero-banner-photo { margin-top: var(--space-8); }
.hero-banner-photo img {
  width: 100%;
  height: 220px;
  object-fit: cover;
  object-position: center 30%;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}
@media (min-width: 640px) { .hero-banner-photo img { height: 300px; } }
@media (min-width: 1024px) { .hero-banner-photo img { height: 380px; } }
/* Per-photo vertical crop overrides — the shared "center 30%" position
   above is tuned for most hero photos, but the hero box is much wider
   than it is tall, so a photo whose subject doesn't sit in that exact
   band gets cropped wrong. --low pulls the crop window DOWN (for a
   subject lower in frame, e.g. someone looking down at a phone in
   their hands, where 30% left too much empty headroom and cut off the
   phone). --high pulls the crop window UP (for a subject higher in
   frame, e.g. two heads near the top of an overhead shot, where 30%
   cropped into a head). Add whichever modifier a specific photo needs
   alongside .hero-banner-photo on its wrapping div. */
.hero-banner-photo--low img { object-position: center 62%; }
.hero-banner-photo--high img { object-position: center 48%; }

/* Community page hero: matelocal's actual order is copy → photo → intro
   paragraph (the paragraph comes AFTER the banner photo here, unlike
   features/safety where it comes before) — this modifier covers just
   that trailing paragraph; .hero-banner-copy/.hero-banner-photo above
   are reused unchanged for the heading block and photo. */
.hero-banner-intro { max-width: var(--content-prose); margin-top: var(--space-6); color: var(--color-text-soft); }

/* Soft-tint section band — alternates with the plain page background to
   break the page into readable chapters, matching the section rhythm on
   matelocal.com/site-features (hero / tint / plain / tint / plain / tint / plain). */
.location-section--soft { background: var(--color-surface-soft); }

/* Platform availability — 2-up from the smallest breakpoint (matelocal
   shows 2 columns even on phones, not a single stacked column), 4-up from
   desktop; rounded-square icon badge instead of a pill, matching their
   app-icon style. */
.platform-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 1024px) { .platform-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.platform-icon { width: 3.25rem; height: 3.25rem; border-radius: var(--radius-md); }

/* Comparison table — fixed, narrow tick columns so the feature-name column
   gets the remaining space and wraps onto two lines, instead of the whole
   table needing a horizontal scroll on small screens (matelocal's table
   reads edge-to-edge on mobile with no scroll needed). */
.comparison-table { table-layout: fixed; }
.comparison-table th:not([scope="row"]), .comparison-table td:not(:first-child) { width: 3.4rem; }
@media (max-width: 640px) {
  .comparison-table th, .comparison-table td { padding: var(--space-2) var(--space-3); font-size: 0.82rem; }
  .comparison-table th:not([scope="row"]), .comparison-table td:not(:first-child) { width: 2.5rem; }
}

/* Matching algorithm — "what it considers" as short wrapped tags rather
   than a checkmarked sentence list (scans faster, matches matelocal's
   tag-style treatment of the same 8 factors); the visibility sub-heading
   reads as a small accent label rather than a plain h3. */
.algo-tags { display: flex; flex-wrap: wrap; gap: var(--space-3); list-style: none; margin: 0; padding: 0; }
.algo-tags li {
  padding: 0.5em 1.1em;
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--radius-pill);
  font-size: 0.9rem;
  font-weight: 600;
}
.algo-block h3.h3--accent {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-primary);
  margin: 0 0 var(--space-3);
}

/* Closing CTA as an inset, rounded card with a photo rather than a
   full-bleed band — matches matelocal's contained CTA card treatment on
   the features page specifically. */
.cta-band--card { background: transparent; padding-block: var(--space-6) var(--space-12); }
@media (max-width: 767px) {
  .cta-band--card { padding-block: var(--space-5) var(--space-8); }
}
.cta-band--card .container {
  background: linear-gradient(135deg, var(--color-navy), color-mix(in srgb, var(--color-primary) 55%, var(--color-navy)));
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  padding: var(--space-8);
}
@media (min-width: 768px) {
  .cta-band--card .container { padding: var(--space-10); }
}

/* ===== 8) Safety page ===== */
.safety-page-section { padding-block: var(--space-4) var(--space-8); }
.safety-category { margin-bottom: var(--space-10); }
.safety-category:last-child { margin-bottom: 0; }
.safety-category-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}
.safety-category-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-pill);
  background: var(--color-primary-light);
  color: var(--color-primary);
}
.safety-category-icon svg { width: 1.4rem; height: 1.4rem; }
.safety-category-head h2 { margin: 0 0 var(--space-1); font-size: 1.35rem; }
.safety-category-head p { margin: 0; color: var(--color-text-soft); }
/* Feature card grid — 2 cards per row on desktop (reuses the .grid.grid-2
   utility, 1-up below 600px), chosen by the user over a full-width row
   list and a collapsed accordion after a visual side-by-side comparison
   (build-assets/0-the-brand/notes/decisions.md has the full writeup). */
.safety-feature-card {
  padding: var(--space-6);
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}
.safety-feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: var(--radius-pill);
  background: var(--color-primary-light);
  color: var(--color-primary);
  margin-bottom: var(--space-4);
}
.safety-feature-icon svg { width: 1.35rem; height: 1.35rem; }
.safety-feature-card h3 { margin: 0 0 var(--space-2); font-size: 1.02rem; }
.safety-feature-card > p { margin: 0 0 var(--space-3); color: var(--color-text-soft); font-size: 0.92rem; }
.safety-protects {
  margin: 0;
  padding-left: var(--space-3);
  border-left: 3px solid var(--color-accent);
  font-size: 0.92rem;
  color: var(--color-text);
}
.safety-protects-label { font-weight: 700; color: var(--color-primary); }

/* ===== 9) Community page ===== */
.review-ticker {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding-block: var(--space-4) var(--space-8);
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.review-ticker-row {
  display: flex;
  gap: var(--space-4);
  width: max-content;
}
.review-ticker-row--1 { animation: reviewScrollRTL 90s linear infinite; }
.review-ticker-row--2 { animation: reviewScrollLTR 100s linear infinite; }
.review-ticker-row--3 { animation: reviewScrollRTL 80s linear infinite; }
@keyframes reviewScrollRTL {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
@keyframes reviewScrollLTR {
  from { transform: translateX(-50%); }
  to   { transform: translateX(0); }
}
.review-ticker:hover .review-ticker-row,
.review-ticker:focus-within .review-ticker-row {
  animation-play-state: paused;
}
@media (prefers-reduced-motion: reduce) {
  .review-ticker-row { animation: none; flex-wrap: wrap; width: 100%; justify-content: center; }
  .review-ticker { overflow: visible; mask-image: none; -webkit-mask-image: none; }
}
.review-card {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  width: 20rem;
  flex: none;
  padding: var(--space-5);
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  color: inherit;
  text-decoration: none;
}
a.review-card:hover { box-shadow: var(--shadow-md); }
.review-card-avatar-wrap { flex: none; }
.review-card-avatar {
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  object-fit: cover;
  object-position: center top;
}
.avatar-silhouette {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: var(--color-surface-soft);
  color: var(--color-text-soft);
}
.avatar-silhouette svg { width: 2rem; height: 2rem; }
.review-card-body { min-width: 0; }
.review-card-name {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--color-text);
}
.review-card-loc {
  display: block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 0.8rem;
  color: var(--color-text-soft);
}
.review-card-stars { margin: var(--space-1) 0; color: var(--color-accent); display: flex; gap: 0.1rem; }
.review-card-stars svg { flex: none; }
.review-card-quote {
  margin: 0;
  font-size: 0.9rem;
  color: var(--color-text-soft);
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

/* Member profile pages — matches matelocal's actual profile layout:
   no boxed card wrapping everything, instead a two-column grid (fixed
   ~260px photo beside the flowing profile content, stacking to one
   column on mobile with the photo centered on top); a plain bio
   paragraph (not a blockquote); a distinct "What NAME says" review
   card; and a vertical row-list of related members with avatar + meta
   (matelocal's related-members are full-width rows, not link pills).
   A gender badge (matelocal shows e.g. "WOMAN") is deliberately not
   replicated here — Gaydar's members are uniformly gay/bi men, so a
   badge repeated on every single profile would carry no information;
   logged as a deliberate deviation in notes/decisions.md. */
.profile-hero { padding-block: var(--space-10) var(--space-8); }
.profile-layout { display: grid; gap: var(--space-8); align-items: start; }
@media (min-width: 700px) {
  .profile-layout { grid-template-columns: 260px minmax(0, 1fr); }
}
.profile-avatar-wrap {
  width: 100%;
  max-width: 260px;
  aspect-ratio: 1 / 1;
  margin-inline: auto;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-surface-soft);
}
@media (min-width: 700px) { .profile-avatar-wrap { margin-inline: 0; } }
.profile-avatar-wrap .profile-avatar {
  width: 100%;
  height: 100%;
  border-radius: 0;
  object-fit: cover;
  object-position: center top;
  box-shadow: none;
}
.profile-avatar-wrap .avatar-silhouette {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-soft);
}
.profile-avatar-wrap .avatar-silhouette svg { width: 45%; height: 45%; }
.profile-name-row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.profile-name-row h1 { margin: 0; }
.verified-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.2em 0.7em;
  border-radius: var(--radius-pill);
  background: var(--color-primary-light);
  color: var(--color-primary);
  font-size: 0.78rem;
  font-weight: 700;
}
.verified-badge svg { width: 0.9rem; height: 0.9rem; }
.profile-location { margin: var(--space-2) 0 0; color: var(--color-text-soft); }
.profile-rating { display: inline-flex; align-items: center; gap: 0.4rem; margin-top: var(--space-3); color: var(--color-text); font-weight: 700; }
.profile-rating svg { color: var(--color-accent); width: 1.1rem; height: 1.1rem; }
.profile-rating__val { font-weight: 700; }
.profile-rating .muted-label { font-weight: 400; color: var(--color-text-soft); margin-left: 0.2rem; }
.profile-desc { margin: var(--space-4) 0 0; color: var(--color-text); line-height: 1.6; }
.profile-interests { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-4); padding: 0; list-style: none; }
.profile-interests li {
  padding: 0.3em 0.9em;
  border-radius: var(--radius-pill);
  background: var(--color-surface-soft);
  color: var(--color-text);
  font-size: 0.85rem;
}
.profile-section {
  margin-top: var(--space-10);
  padding-top: var(--space-8);
  border-top: 1px solid color-mix(in srgb, var(--color-text) 10%, transparent);
}
.profile-section__heading {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-primary);
  margin: 0 0 var(--space-4);
}
.profile-review {
  padding: var(--space-6);
  background: var(--color-surface-soft);
  border-radius: var(--radius-md);
}
.profile-review__stars { display: flex; gap: 0.15rem; color: var(--color-accent); margin-bottom: var(--space-3); }
.profile-review__text { margin: 0; font-size: 1.05rem; font-style: italic; }
.profile-review__meta { margin: var(--space-3) 0 0; color: var(--color-text-soft); font-size: 0.85rem; }
.profile-review--narrow { max-width: var(--content-prose); margin-inline: auto; }
.related-grid { display: flex; flex-direction: column; gap: var(--space-3); }
.related-card {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--radius-md);
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.15s ease;
}
.related-card:hover { box-shadow: var(--shadow-sm); }
.related-card__avatar {
  width: 3rem;
  height: 3rem;
  border-radius: 50%;
  object-fit: cover;
  object-position: center top;
  flex: none;
  background: var(--color-surface-soft);
}
.related-card__avatar.avatar-silhouette { display: flex; align-items: center; justify-content: center; color: var(--color-text-soft); }
.related-card__avatar.avatar-silhouette svg { width: 1.6rem; height: 1.6rem; }
.related-card__info { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.related-card__name { font-weight: 700; color: var(--color-text); }
.related-card__meta { color: var(--color-text-soft); font-size: 0.88rem; }
.profile-back { margin-top: var(--space-5); }
.section-head--spaced { margin-top: var(--space-10); }

/* ===== §10 Image card blocks =====
   Photo-paired blocks mirroring the image-card pattern used throughout
   matelocal.com: a hero photo beside the intro copy, a photo beside the
   closing CTA, and a 3-col photo card grid. Real Couples/Singles
   photography from build-assets/0-the-brand/Images. */

/* Hero with photo — .location-hero + this modifier, used on pricing,
   features, safety, community. Photo sits beside the existing
   breadcrumb/eyebrow/h1/intro copy (unchanged markup), each wrapped in
   .hero-media-copy / .hero-media-photo. */
.location-hero--media .container {
  display: grid;
  gap: var(--space-8);
  align-items: center;
}
@media (min-width: 768px) {
  .location-hero--media .container {
    grid-template-columns: minmax(0, 1fr) minmax(0, 380px);
    gap: var(--space-10);
  }
}
.hero-media-photo img {
  width: 100%;
  height: 240px;
  object-fit: cover;
  object-position: center 30%;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}
@media (min-width: 768px) {
  .hero-media-photo img { height: 320px; }
}

/* CTA band with photo — .cta-band + this modifier, used on pricing,
   features, community. Copy stays centered on mobile; photo sits beside
   it from tablet up. */
.cta-band--media .container {
  display: grid;
  gap: var(--space-8);
  align-items: center;
}
@media (min-width: 768px) {
  .cta-band--media .container {
    grid-template-columns: minmax(0, 340px) minmax(0, 1fr);
    text-align: left;
  }
  .cta-band--media .cta-media-copy p { margin-left: 0; margin-right: 0; }
  .cta-band--media .cta-media-photo { order: -1; }
}
.cta-media-photo img {
  width: 100%;
  height: 220px;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
@media (min-width: 768px) {
  .cta-media-photo img { height: 260px; }
}
/* Per-photo crop override for the homepage-v2.html closing CTA photo — the
   two subjects sit left-of-centre in the source with empty couch/room to
   the right, so a plain center crop wastes room on that empty side. Pulls
   the crop window left and slightly up. Chained with the base
   ".cta-media-photo img" element+class above (not a standalone class) so it
   actually out-specifies that rule's default (centered) object-position —
   same fix pattern as .section-head-photo-img--top earlier in this file. */
.cta-media-photo img.cta-media-photo-img--left { object-position: 12% 35%; }

/* 3-col image card grid — homepage "community built around real
   connection" pattern: photo + eyebrow + heading + stat line, linking
   out to another page on the site. */
.img-card-grid {
  display: grid;
  gap: var(--space-6);
  margin-top: var(--space-8);
}
@media (min-width: 768px) {
  .img-card-grid { grid-template-columns: repeat(3, 1fr); }
  /* v2-only modifier: homepage-v2.html's 4th card (Features) doesn't divide
     evenly into 3 columns, so this compound selector overrides to a 2x2
     layout instead — only on markup carrying both classes together.
     homepage.html's plain .img-card-grid (3 cards, no modifier) is
     untouched and keeps the 3-column layout above. */
  .img-card-grid.img-card-grid--v2-4up { grid-template-columns: repeat(2, 1fr); }
}
.img-card {
  display: block;
  border-radius: var(--card-radius);
  overflow: hidden;
  box-shadow: var(--card-shadow);
  border: var(--card-border);
  background: var(--color-surface);
  transition: box-shadow var(--transition-base), transform var(--transition-base);
}
.img-card:hover, .img-card:focus-visible {
  box-shadow: var(--shadow-md);
  transform: translateY(-3px);
}
.img-card img {
  width: 100%;
  height: 190px;
  object-fit: cover;
  object-position: center;
}
/* Per-photo vertical crop override — same idea as .hero-banner-photo--high/--low
   above. The default center crop splits the crop evenly top/bottom, which cuts
   into a subject whose head sits close to the top of the frame. This pulls the
   crop window up so the full head stays in view, at the cost of a little more
   crop off the bottom (couch/legs, not the point of the photo). Add alongside
   the img's own class inside .img-card. */
.img-card img.img-card-photo--top { object-position: center 12%; }
.img-card-body { padding: var(--space-5) var(--space-6) var(--space-6); }
.img-card-eyebrow {
  display: block;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-primary);
  margin-bottom: var(--space-2);
}
.img-card-body h3 { margin: 0 0 var(--space-2); font-size: 1.15rem; }
.img-card-stat {
  margin: 0;
  font-size: 0.88rem;
  color: var(--color-text-soft);
  font-weight: 600;
}

/* Content split — alternating photo + copy block, built for the About
   page so it can break up prose without re-explaining features/safety/
   pricing. Two children in source order: put the photo first for a
   left-photo layout, or the copy first for a right-photo layout — the
   HTML order carries the alternation, no --reverse modifier needed. */
.content-split { padding-block: var(--space-12); }
.content-split--soft { background: var(--color-surface-soft); }
.content-split .container {
  display: grid;
  gap: var(--space-8);
  align-items: center;
}
@media (min-width: 900px) {
  .content-split .container { grid-template-columns: 1fr 1fr; gap: var(--space-12); }
}
.content-split-photo img {
  width: 100%;
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
  display: block;
}
.content-split-copy h2 { margin-top: 0; }
.content-split-copy p { color: var(--color-text-soft); }
.content-split-copy .btn { margin-top: var(--space-2); }

/* Article-style prose body — used by comparison pages (and reusable for
   future articles) where the content is genuinely long-form: headings,
   paragraphs and a table mixed together, rather than the card/grid
   sections used elsewhere on the site. */
.article-body {
  max-width: var(--content-prose);
  margin-inline: auto;
}
.article-body h2 { margin: var(--space-10) 0 var(--space-3); font-size: 1.4rem; }
.article-body h2:first-child { margin-top: 0; }
.article-body h3 { margin: var(--space-6) 0 var(--space-2); font-size: 1.12rem; }
.article-body p { color: var(--color-text-soft); }
.article-body a { color: var(--color-primary); font-weight: 600; }
.article-body ul, .article-body ol { color: var(--color-text-soft); padding-left: 1.25em; margin: var(--space-3) 0; }
.article-body li { margin-bottom: var(--space-2); }
.article-body blockquote {
  margin: var(--space-6) 0;
  padding: var(--space-4) var(--space-6);
  border-left: 3px solid var(--color-primary);
  background: var(--color-surface-soft);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-style: italic;
  color: var(--color-text);
}
.article-body strong { color: var(--color-text); }

/* Article byline — site/articles/<slug>.html hero. Text-only for the brand
   byline fallback (no avatar/profile link); avatar + name link when a real
   author is registered. Centered to match the centered article hero. */
.article-byline {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
  color: var(--color-text-soft);
  font-size: 0.92rem;
}
.article-byline-avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; object-position: center top; }
.article-byline-name { color: var(--color-text); font-weight: 700; }
.article-byline-dot { opacity: 0.5; }

/* Article tag chips — visual only, no filtering (see article.md "What NOT
   to build"). Deliberately lighter/smaller than `.location-chip`, which is
   an interactive nav element elsewhere on the site. */
.article-tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-10);
  max-width: var(--content-prose);
  margin-inline: auto;
}
.tag-chip {
  display: inline-block;
  padding: 0.35rem 0.85rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface-soft);
  color: var(--color-text-soft);
  font-size: 0.82rem;
  font-weight: 600;
}

/* Article card grid — site/articles.html landing + "related articles"
   blocks on individual article pages. Same auto-fit/centered pattern as
   `.comparison-card-grid`, wrapping the same `.img-card` component. */
.article-card-grid {
  display: grid;
  gap: var(--space-6);
  margin-top: var(--space-8);
  grid-template-columns: 1fr;
}
@media (min-width: 640px) {
  .article-card-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 340px)); justify-content: center; }
}

/* Compare panels — a reusable 2-up card pair for a comparison page's
   "X vs Y" style sections (what each app is, why each approach works,
   who each is best for). Reused 3x on the Grindr comparison rather than
   inventing a distinct component per section. */
.compare-panels { display: grid; gap: var(--space-6); margin-top: var(--space-8); }
@media (min-width: 768px) { .compare-panels { grid-template-columns: 1fr 1fr; } }
.compare-panel {
  padding: var(--space-6);
  background: var(--color-surface);
  border: var(--card-border);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
}
.compare-panel--accent { background: var(--color-surface-soft); }
.compare-panel__label {
  display: block;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-primary);
  margin-bottom: var(--space-2);
}
.compare-panel h3 { margin: 0 0 var(--space-3); }
.compare-panel p { color: var(--color-text-soft); }
.compare-panel .tier-features { margin-bottom: 0; }
.compare-panels + .article-body--follow-panels { margin-top: var(--space-8); }

/* 2-column variant of the existing checkmark list (`.tier-features`,
   pricing.html) — same list, just laid out in 2 columns on wider
   screens where there are enough items to benefit from it. */
@media (min-width: 600px) {
  .tier-features--grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-2) var(--space-6); }
}

/* Comparison "at a glance" table — built for site/comparisons/<slug>.html.
   Deliberately a separate component from `.comparison-table` above: that
   one is tuned for narrow fixed-width tick columns (pricing's feature
   grid), this one holds full sentences per cell, so it needs normal
   left-aligned, non-fixed columns instead. */
.compare-table-wrap {
  overflow-x: auto;
  margin-top: var(--space-8);
  border-radius: var(--radius-md);
  border: var(--card-border);
}
.compare-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; background: var(--color-surface); }
.compare-table th, .compare-table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid color-mix(in srgb, var(--color-text) 8%, transparent);
}
.compare-table thead th { background: var(--color-surface-soft); font-family: var(--font-heading); }
.compare-table th[scope="row"] { font-weight: 600; white-space: nowrap; }
.compare-table tbody tr:last-child th, .compare-table tbody tr:last-child td { border-bottom: none; }

/* Gaydar/Grindr toggle for the at-a-glance table on mobile — CSS-only,
   same radio-input technique as the header's nav-toggle checkbox. Below
   700px, a 3-column table of full sentences forces a horizontal scroll
   to read even one row, so instead: each dimension becomes its own
   stacked card, and a pill switcher flips every card between showing
   Gaydar's answer or Grindr's answer, rather than showing both stacked
   underneath each other. Desktop is untouched — the toggle and its
   radios stay hidden and the full 3-column table renders as normal.
   Markup contract: `#compare-toggle-gaydar`/`#compare-toggle-grindr`
   and `.compare-toggle` must be siblings of `.compare-table-wrap`
   (all direct children of the same container) for the `~` selectors
   below to reach the table. */
.compare-toggle-input { position: absolute; opacity: 0; pointer-events: none; }
.compare-toggle { display: none; }
@media (max-width: 700px) {
  .compare-table-wrap { overflow-x: visible; border: none; margin-top: var(--space-6); }
  .compare-table thead { display: none; }
  .compare-table, .compare-table tbody, .compare-table tr, .compare-table th, .compare-table td { display: block; width: auto; }
  .compare-toggle {
    display: flex;
    gap: 4px;
    background: var(--color-surface-soft);
    padding: 4px;
    border-radius: var(--radius-pill);
    margin-top: var(--space-6);
    margin-bottom: var(--space-4);
  }
  .compare-toggle label {
    flex: 1;
    text-align: center;
    padding: 0.6em 1em;
    border-radius: var(--radius-pill);
    font-family: var(--font-heading);
    font-weight: 700;
    font-size: 0.88rem;
    cursor: pointer;
    color: var(--color-text-soft);
    transition: background var(--transition-fast), color var(--transition-fast);
  }
  #compare-toggle-gaydar:checked ~ .compare-toggle label[for="compare-toggle-gaydar"],
  #compare-toggle-grindr:checked ~ .compare-toggle label[for="compare-toggle-grindr"] {
    background: var(--color-primary);
    color: #fff;
  }
  /* Each dimension is still its own white card (the section behind it
     is soft-tinted, so the card needs real contrast — a shadow, not
     just a border, or it would blend into the section). Only the
     toggled-off app's cell is hidden, so each card shows one answer. */
  .compare-table tr {
    margin-bottom: var(--space-3);
    background: var(--color-surface);
    border-radius: var(--card-radius);
    box-shadow: var(--card-shadow);
    overflow: hidden;
  }
  .compare-table tr:last-child { margin-bottom: 0; }
  .compare-table th[scope="row"] {
    display: block;
    font-family: var(--font-heading);
    font-size: 0.95rem;
    padding: var(--space-3) var(--space-4);
  }
  .compare-table td {
    padding: 0 var(--space-4) var(--space-3);
    font-size: 0.9rem;
    color: var(--color-text-soft);
  }
  #compare-toggle-gaydar:checked ~ .compare-table-wrap td[data-label="Grindr"] { display: none; }
  #compare-toggle-grindr:checked ~ .compare-table-wrap td[data-label="Gaydar"] { display: none; }
}

/* Comparisons landing card grid — wraps `.img-card` (reused as-is) rather
   than a new card component. Centers 1-3 cards in a single row and only
   becomes a proper multi-row grid once there are enough cards to need
   one, per comparisons-landing.md's volume rule — one `auto-fit` rule
   handles both cases without a separate few-vs-many modifier class. */
.comparison-card-grid {
  display: grid;
  gap: var(--space-6);
  margin-top: var(--space-8);
  grid-template-columns: 1fr;
}
@media (min-width: 640px) {
  .comparison-card-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 340px)); justify-content: center; }
}
