/* =====================================================================
 * page-character.css  (r20)
 *
 * Per-page hero variants and accent colors for the Level Up Hobbies CMS.
 * Loaded after styles.css so the variant rules can extend (not override)
 * the base .subhero typography and tokens.
 *
 * All variants stay inside the LUH palette (--ink #0A1F4D, --red #E53935,
 * --yellow #FFC93C, --cream #F2EBD8, --paper #FFF8E5) and use the existing
 * display font (--ff-dis), spacing scale (--s-*), and container width.
 * The same dot-pattern halftone, same eyebrow style, same lede style,
 * same pill row. Only the layout, size, and accent change per page so
 * the site reads as one coherent visual system with distinct chapters.
 *
 * Accent color is delivered via the --page-accent custom property. Set
 * on body.page-{slug} by an inline style block in template.php; consumed
 * here by the accent <em> inside .subhero__h1 and a couple of borders.
 * Default falls back to --red so pages without an accent set look
 * identical to pre-r20.
 *
 * Variant classes:
 *   .subhero--banner     massive single-word display headline
 *   .subhero--numbered   editorial chapter style with a numeral pillar
 *   .subhero--photo      full-bleed bg image with dark scrim
 *   .subhero--split      text left + image card right grid
 *   .subhero--minimal    tight legal-page hero
 * ===================================================================== */

/* -------------------------------------------------------------------
 * Accent color override.
 *
 * Existing rule: .subhero__h1 em { color: var(--red); }
 * r20 supersedes with the per-page custom property; default = red so
 * pages that haven't set an accent render identically to before.
 * ------------------------------------------------------------------- */

.subhero__h1 em {
  color: var(--page-accent, var(--red));
}

/* r21: per-page accent flows through every section, not just the hero.
 * The kit's section partials use <em> for the italic accent word in
 * their h2 titles (e.g. "Three things we care about." has <em>care
 * about.</em>). On pages with accent_color set, that whole-page italic
 * accent should follow the page's chosen color so the accent reads as
 * a continuous voice rather than only happening in the hero. */
body[data-page-accent] section h2 em,
body[data-page-accent] section h3 em,
body[data-page-accent] section .head__h2 em,
body[data-page-accent] section .h2-display em,
body[data-page-accent] .pill-row__dot {
  color: var(--page-accent);
}
body[data-page-accent] .pill-row__dot {
  background: var(--page-accent);
}

/* Eyebrow gets a small underline tinted in the page accent. Subtle but
   reinforces the per-page identity without changing brand colors. */
.subhero .head__eyebrow {
  display: inline-block;
  border-bottom: 2px solid var(--page-accent, var(--red));
  padding-bottom: 3px;
}

/* Pills border picks up the accent so they read as part of the page voice. */
.subhero .pill-row__dot {
  background: var(--page-accent, var(--red));
}

/* -------------------------------------------------------------------
 * Variant: banner
 *
 * Use for pages with very short titles (FAQ, Terms, Privacy, "Sell").
 * The hero becomes a chunky single statement, weight 900, with
 * line-height tightened so the word fills the viewport. Eyebrow stays
 * compact above; lede sits below at normal size. Halftone preserved.
 * ------------------------------------------------------------------- */

.subhero--banner {
  padding: clamp(64px, 9vw, 128px) 0 clamp(56px, 7vw, 96px);
}

.subhero--banner .subhero__wrap {
  max-width: 1200px;
}

.subhero--banner .subhero__h1 {
  font-size: clamp(56px, 11vw, 144px);
  font-weight: 900;
  line-height: 0.88;
  letter-spacing: -0.04em;
  margin: var(--s-3) 0 var(--s-4);
  max-width: 22ch;
}

.subhero--banner .subhero__lede {
  font-size: clamp(18px, 1.7vw, 22px);
  max-width: 56ch;
}

/* -------------------------------------------------------------------
 * Variant: numbered
 *
 * Editorial chapter style. A numeral pillar (e.g. "No. 04", "II", "FAQ")
 * sits above the eyebrow in an italic serif display treatment. Reads
 * like a chapter heading in a typeset book and pairs well with editorial
 * content (About, history, longer-form pages).
 * ------------------------------------------------------------------- */

.subhero--numbered .subhero__numeral {
  font-family: Georgia, "Iowan Old Style", "Source Serif Pro", serif;
  font-style: italic;
  font-weight: 400;
  font-size: clamp(48px, 7vw, 96px);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--page-accent, var(--red));
  margin: 0 0 var(--s-4);
  /* Outline-style: gives the numeral weight without solid color overpower */
  -webkit-text-stroke: 1px currentColor;
  text-shadow: 4px 4px 0 rgba(10, 31, 77, 0.06);
  opacity: 0.92;
}

.subhero--numbered .subhero__h1 {
  margin-top: var(--s-2);
}

/* -------------------------------------------------------------------
 * Variant: photo
 *
 * Full-bleed background image with a dark scrim gradient layered on top
 * for legibility. Inherits hero_bg_url, hero_bg_scrim_top, and
 * hero_bg_scrim_bot from the page-level fields; the scrim itself is
 * applied by the inline #r19-page-hero-bg style block emitted in
 * template.php's <head>. This variant just locks in the foreground
 * styling so eyebrow/h1/lede stay legible over photography.
 * ------------------------------------------------------------------- */

.subhero--photo {
  background-color: var(--ink);
  color: #fff;
  padding: clamp(96px, 12vw, 168px) 0 clamp(80px, 10vw, 144px);
  min-height: clamp(360px, 38vw, 560px);
  display: flex;
  align-items: center;
}

.subhero--photo .subhero__halftone {
  /* Halftone disappears over a photo - the dark scrim does the heavy
     lifting and a dot pattern would just add visual noise. */
  display: none;
}

.subhero--photo .head__eyebrow {
  color: var(--page-accent, var(--yellow));
  border-bottom-color: var(--page-accent, var(--yellow));
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

.subhero--photo .subhero__h1 {
  color: #fff;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
}

.subhero--photo .subhero__h1 em {
  /* Over a photo, the accent word reads better in cream or yellow than
     red. If the page set accent=red explicitly, fall back to yellow on
     photo variant to keep legibility against any image. */
  color: var(--page-accent-on-photo, var(--yellow));
}

.subhero--photo .subhero__lede {
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  max-width: 56ch;
}

.subhero--photo .page-crumb,
.subhero--photo .page-crumb a {
  color: rgba(255, 255, 255, 0.78);
}

.subhero--photo .page-crumb a:hover {
  color: var(--page-accent, var(--yellow));
}

/* Pills on a photo bg: the default cream pill backgrounds with navy text
   work but the white-paper fill fights the photo and reads as a stock
   chip. Tint them transparent-on-dark so they sit on the photo like
   light embossed buttons instead of separate cards. */
.subhero--photo .pill-row__item {
  background: rgba(10, 31, 77, 0.55);
  border-color: rgba(255, 255, 255, 0.35);
  color: #fff;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.subhero--photo .pill-row__item span {
  color: #fff;
}

/* A thin accent stripe down the left side reinforces the page accent
   without adding a heavy decorative element. Subtle but recognizable. */
.subhero--photo {
  border-left: 4px solid var(--page-accent, var(--yellow));
}

/* -------------------------------------------------------------------
 * Variant: split
 *
 * Two-column grid at desktop: text on the left (1.4fr), photo card on
 * the right (1fr). Photo card has a subtle inset border in the page
 * accent so it doesn't look like a stock-photo dropped onto the layout.
 * Collapses to single column under 880px so the photo isn't crushed.
 * ------------------------------------------------------------------- */

.subhero--split {
  padding: clamp(64px, 7vw, 96px) 0 clamp(48px, 6vw, 80px);
}

.subhero--split .subhero__wrap.subhero__split-grid {
  max-width: 1240px;
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: clamp(28px, 4vw, 64px);
  align-items: center;
}

.subhero--split .subhero__split-photo {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  min-height: clamp(280px, 32vw, 440px);
  border-radius: 6px;
  border: 3px solid var(--page-accent, var(--red));
  /* Slight outset shadow gives the card a print-block weight, in keeping
     with LUH's hand-drawn aesthetic. */
  box-shadow: 8px 8px 0 var(--ink);
  position: relative;
}

@media (max-width: 880px) {
  .subhero--split .subhero__wrap.subhero__split-grid {
    grid-template-columns: 1fr;
  }
  .subhero--split .subhero__split-photo {
    min-height: 240px;
    box-shadow: 6px 6px 0 var(--ink);
  }
}

/* -------------------------------------------------------------------
 * Variant: minimal
 *
 * Tight hero for legal pages (Terms, Privacy) where the content below
 * is what matters. Halftone removed, padding tightened. H1 is kept
 * meaningfully large so it doesn't read as smaller than the content
 * section h2s further down the page (which would invert hierarchy).
 * ------------------------------------------------------------------- */

.subhero--minimal {
  padding: clamp(48px, 6vw, 80px) 0 clamp(32px, 4vw, 56px);
}

.subhero--minimal .subhero__halftone {
  display: none;
}

.subhero--minimal .subhero__h1 {
  font-size: clamp(36px, 4.6vw, 64px);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -0.025em;
  margin: var(--s-3) 0 var(--s-3);
}

.subhero--minimal .subhero__lede {
  font-size: 17px;
  max-width: 62ch;
}

/* -------------------------------------------------------------------
 * Per-page accent palette
 *
 * Maps the accent_color page field to a concrete value. Set on body via
 * an inline style block from template.php using the .page-{slug} class
 * so the accent only applies to pages that opt in. Default (no opt-in)
 * leaves --page-accent unset and the base --red fallback applies.
 * ------------------------------------------------------------------- */

body[data-page-accent="red"]    { --page-accent: var(--red);    --page-accent-on-photo: var(--yellow); }
body[data-page-accent="yellow"] { --page-accent: var(--yellow); --page-accent-on-photo: var(--yellow); }
body[data-page-accent="navy"]   { --page-accent: var(--ink);    --page-accent-on-photo: var(--cream);  }
body[data-page-accent="cream"]  { --page-accent: var(--cream);  --page-accent-on-photo: var(--cream);  }


/* =====================================================================
 * Section dividers  (r20.1)
 *
 * Page-level decorative dividers between top-level sections. Applied via
 * body[data-section-divider="..."] so they cascade across every section
 * on the page but don't touch pages that didn't opt in.
 *
 * Each divider style picks up var(--page-accent) where possible so the
 * divider reads as a continuation of the page's character, not a generic
 * separator. Each is purely decorative; layout dimensions don't shift,
 * the divider sits in extra margin added at the top of every section
 * after the first.
 *
 * The selector `body[data-section-divider] main > section:not(:first-of-type)`
 * targets adjacent siblings inside main, which matches the kit's slot
 * layout (cms_render_insertion_slot emits each instance as a direct
 * child <section>).
 *
 * Variants:
 *   sawtooth   classic gaming/arcade zigzag, ink-colored
 *   dots       row of evenly-spaced accent dots, subtle continuation of halftone
 *   wave       gentle sine wave, the calmest option
 *   ticket     perforated edge like a torn ticket stub, retro-arcade
 * ===================================================================== */

body[data-section-divider] main > section:not(:first-of-type),
body[data-section-divider] > section:not(:first-of-type) {
  position: relative;
}

/* --- Sawtooth ----------------------------------------------------- */
body[data-section-divider="sawtooth"] main > section:not(:first-of-type),
body[data-section-divider="sawtooth"] > section:not(:first-of-type) {
  margin-top: 36px;
}
body[data-section-divider="sawtooth"] main > section:not(:first-of-type)::before,
body[data-section-divider="sawtooth"] > section:not(:first-of-type)::before {
  content: '';
  position: absolute;
  top: -32px;
  left: 0;
  right: 0;
  height: 18px;
  pointer-events: none;
  /* Single zigzag row: alternating triangles built with clip-path on a
     repeating linear-gradient. Simpler and more reliable than the
     two-layer overlay. */
  background-image: linear-gradient(135deg, var(--page-accent, var(--ink)) 25%, transparent 25%),
                    linear-gradient(225deg, var(--page-accent, var(--ink)) 25%, transparent 25%);
  background-position: 0 0, 0 0;
  background-size: 18px 18px;
  background-repeat: repeat-x;
  opacity: 0.32;
}

/* --- Dots --------------------------------------------------------- */
body[data-section-divider="dots"] main > section:not(:first-of-type),
body[data-section-divider="dots"] > section:not(:first-of-type) {
  margin-top: 32px;
}
body[data-section-divider="dots"] main > section:not(:first-of-type)::before,
body[data-section-divider="dots"] > section:not(:first-of-type)::before {
  content: '';
  position: absolute;
  top: -20px;
  left: 50%;
  transform: translateX(-50%);
  width: clamp(120px, 18vw, 220px);
  height: 8px;
  pointer-events: none;
  background-image: radial-gradient(circle, var(--page-accent, var(--ink)) 1.6px, transparent 2px);
  background-size: 16px 8px;
  background-repeat: repeat-x;
  background-position: center;
  opacity: 0.55;
}

/* --- Wave --------------------------------------------------------- */
body[data-section-divider="wave"] main > section:not(:first-of-type),
body[data-section-divider="wave"] > section:not(:first-of-type) {
  margin-top: 36px;
}
body[data-section-divider="wave"] main > section:not(:first-of-type)::before,
body[data-section-divider="wave"] > section:not(:first-of-type)::before {
  content: '';
  position: absolute;
  top: -36px;
  left: 0;
  right: 0;
  height: 36px;
  pointer-events: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 24'><path d='M0,12 Q20,0 40,12 T80,12' fill='none' stroke='%230A1F4D' stroke-width='2.5' stroke-linecap='round'/></svg>");
  background-size: 80px 24px;
  background-repeat: repeat-x;
  background-position: center;
  opacity: 0.32;
}

/* --- Ticket stub -------------------------------------------------- */
/* Perforated-edge look, evokes arcade-ticket / boarding-pass tear lines.
   Strongest LUH-vibe option of the four. */
body[data-section-divider="ticket"] main > section:not(:first-of-type),
body[data-section-divider="ticket"] > section:not(:first-of-type) {
  margin-top: 22px;
}
body[data-section-divider="ticket"] main > section:not(:first-of-type)::before,
body[data-section-divider="ticket"] > section:not(:first-of-type)::before {
  content: '';
  position: absolute;
  top: -22px;
  left: 0;
  right: 0;
  height: 22px;
  pointer-events: none;
  background-image:
    /* dashed center-line */
    repeating-linear-gradient(90deg, var(--page-accent, var(--ink)) 0 12px, transparent 12px 22px),
    /* notch row top + bottom */
    radial-gradient(circle at 11px 0, transparent 5px, transparent 5.5px),
    radial-gradient(circle at 11px 22px, transparent 5px, transparent 5.5px);
  background-size: 100% 2px, 22px 11px, 22px 11px;
  background-position: 0 50%, 0 0, 0 100%;
  background-repeat: repeat-x;
  opacity: 0.38;
}

/* In admin preview mode we add a small floating indicator so editors
   remember the page is rendering under override params, not persisted
   state. Hides cleanly in public mode (no `is-preview` class). */
body.is-preview::before {
  content: 'Preview mode';
  position: fixed;
  top: 80px;
  right: 20px;
  z-index: 50;
  background: var(--page-accent, var(--yellow));
  color: var(--ink);
  font-family: monospace, "JetBrains Mono", "Menlo", "Courier New";
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 8px 14px;
  border-radius: 4px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
  pointer-events: none;
}



/* =====================================================================
 * r21 MAXIMUM OPTIONS  (cohesive but combinable)
 *
 * Five new page-level dimensions on top of the existing variant/accent/
 * numeral/divider system. Each is optional and falls through cleanly,
 * so pages with no opt-in render exactly as before.
 *
 * Grouping (mirrors the admin form):
 *
 *   HERO        corner_watermark, hero_pattern
 *   SECTIONS    section_accent_stripe, section_rhythm
 *   TEXTURE     page_grain
 *
 * All values are gated through allowlists in template.php so unknown
 * strings can never reach the CSS attribute selector.
 * ===================================================================== */

/* -------------------------------------------------------------------
 * HERO: corner watermark
 *
 * Decorative element rendered in the top-right of the hero on any
 * variant. Picks up --page-accent at low opacity so it reads as a
 * stamp rather than a label. Hidden under header chrome and admin
 * toolbar where they overlap.
 * ------------------------------------------------------------------- */

.subhero__watermark {
  position: absolute;
  top: clamp(20px, 3vw, 36px);
  right: clamp(20px, 4vw, 60px);
  font-family: Georgia, "Iowan Old Style", "Source Serif Pro", serif;
  font-style: italic;
  font-weight: 400;
  font-size: clamp(40px, 5.5vw, 80px);
  line-height: 0.9;
  letter-spacing: -0.01em;
  color: var(--page-accent, var(--red));
  opacity: 0.28;
  -webkit-text-stroke: 1px currentColor;
  text-shadow: 4px 4px 0 rgba(10, 31, 77, 0.05);
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
  z-index: 1;
}

.subhero--photo .subhero__watermark {
  /* Over a photo, the cream/yellow accent reads better than red. */
  color: var(--page-accent-on-photo, var(--yellow));
  opacity: 0.4;
  text-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
}

.subhero--minimal .subhero__watermark,
.subhero--banner .subhero__watermark {
  /* Smaller-scale watermark on quieter variants so it stays subordinate. */
  font-size: clamp(28px, 4vw, 56px);
}

/* -------------------------------------------------------------------
 * HERO: pattern overlay
 *
 * Additional pattern layered behind the existing halftone. The halftone
 * stays in place; pattern overlays at low opacity for texture variety.
 *
 * Implemented via a SECOND ::after pseudo-element on .subhero so the
 * existing .subhero__halftone div is undisturbed. Z-order keeps both
 * behind the wrap content.
 *
 * The pattern is suppressed on photo and split variants where it would
 * fight the photo. Banner/numbered/minimal/default all support it.
 * ------------------------------------------------------------------- */

body[data-hero-pattern] .subhero:not(.subhero--photo):not(.subhero--split)::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}
body[data-hero-pattern] .subhero__wrap {
  position: relative;
  z-index: 2;
}

body[data-hero-pattern="dots-dense"] .subhero:not(.subhero--photo):not(.subhero--split)::after {
  background-image: radial-gradient(circle, var(--ink) 0.7px, transparent 1.1px);
  background-size: 8px 8px;
  opacity: 0.18;
  mask-image: linear-gradient(180deg, transparent, black 18%, black 82%, transparent);
  -webkit-mask-image: linear-gradient(180deg, transparent, black 18%, black 82%, transparent);
}

body[data-hero-pattern="diagonals"] .subhero:not(.subhero--photo):not(.subhero--split)::after {
  background-image: repeating-linear-gradient(45deg, var(--ink) 0 1.2px, transparent 1.2px 14px);
  opacity: 0.12;
  mask-image: linear-gradient(180deg, transparent, black 18%, black 82%, transparent);
  -webkit-mask-image: linear-gradient(180deg, transparent, black 18%, black 82%, transparent);
}

body[data-hero-pattern="cross-stitch"] .subhero:not(.subhero--photo):not(.subhero--split)::after {
  background-image:
    linear-gradient(var(--ink), var(--ink)),
    linear-gradient(var(--ink), var(--ink));
  background-size: 8px 1.2px, 1.2px 8px;
  background-position: center, center;
  background-repeat: repeat;
  opacity: 0.16;
  mask-image: linear-gradient(180deg, transparent, black 18%, black 82%, transparent);
  -webkit-mask-image: linear-gradient(180deg, transparent, black 18%, black 82%, transparent);
}

body[data-hero-pattern="halftone-bold"] .subhero:not(.subhero--photo):not(.subhero--split)::after {
  background-image: radial-gradient(circle, var(--ink) 2.2px, transparent 2.6px);
  background-size: 24px 24px;
  opacity: 0.18;
  mask-image: linear-gradient(180deg, transparent, black 16%, black 84%, transparent);
  -webkit-mask-image: linear-gradient(180deg, transparent, black 16%, black 84%, transparent);
}

/* -------------------------------------------------------------------
 * SECTIONS: left-edge accent stripe
 *
 * A vertical accent stripe on the left edge of every section after the
 * subhero. Two thicknesses: thin (3px) and thick (6px with a soft
 * inset highlight). Picks up --page-accent.
 *
 * Same selector trick as the section dividers: section:not(:first-of-type)
 * to skip the subhero and avoid the anchor-span sibling issue.
 * ------------------------------------------------------------------- */

body[data-section-stripe="thin"] main > section:not(:first-of-type) {
  border-left: 3px solid var(--page-accent, var(--red));
}

body[data-section-stripe="thick"] main > section:not(:first-of-type) {
  border-left: 6px solid var(--page-accent, var(--red));
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.35);
}

/* The first section after subhero needs an extra top-padding adjustment
   so the stripe lines up cleanly with the section's own padding. */
body[data-section-stripe] main > section:not(:first-of-type) {
  padding-left: clamp(16px, 2.5vw, 32px);
  padding-right: clamp(16px, 2.5vw, 32px);
}

/* -------------------------------------------------------------------
 * SECTIONS: rhythm tier
 *
 * Per-page vertical rhythm override. Default keeps each section's own
 * padding. Tight reduces by ~25%, spacious increases by ~25%. Applied
 * via custom-property scale that the kit's own section padding can
 * read where they use --section-y-*, but also as a margin override
 * for sections that hardcode their padding.
 * ------------------------------------------------------------------- */

body[data-section-rhythm="tight"] main > section:not(:first-of-type) {
  padding-top: calc(0.75 * var(--section-y, clamp(72px, 8vw, 112px)));
  padding-bottom: calc(0.75 * var(--section-y, clamp(72px, 8vw, 112px)));
}

body[data-section-rhythm="spacious"] main > section:not(:first-of-type) {
  padding-top: calc(1.25 * var(--section-y, clamp(72px, 8vw, 112px)));
  padding-bottom: calc(1.25 * var(--section-y, clamp(72px, 8vw, 112px)));
}

/* -------------------------------------------------------------------
 * TEXTURE: page grain
 *
 * Subtle texture overlay across the entire page. Sits at the body level
 * behind all content via position:fixed pseudo-element. Three options:
 *
 *   paper       very soft tan-on-cream paper grain via SVG noise
 *   noise       fine random-ish dot noise
 *   dots-faint  ultra-faint dot pattern, lower contrast than halftone
 * ------------------------------------------------------------------- */

body[data-page-grain]::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}

body[data-page-grain="paper"]::after {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0.04 0 0 0 0 0.12 0 0 0 0 0.30 0 0 0 0.4 0'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.6'/></svg>");
  background-size: 200px 200px;
  background-repeat: repeat;
  opacity: 0.06;
  mix-blend-mode: multiply;
}

body[data-page-grain="noise"]::after {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='2.5' numOctaves='1' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)' opacity='0.4'/></svg>");
  background-size: 100px 100px;
  background-repeat: repeat;
  opacity: 0.08;
  mix-blend-mode: multiply;
}

body[data-page-grain="dots-faint"]::after {
  background-image: radial-gradient(circle, rgba(10, 31, 77, 0.4) 0.5px, transparent 1px);
  background-size: 22px 22px;
  opacity: 0.18;
}

/* Keep the grain layer behind everything but section content. Footer,
   header, and toolbar should never be tinted; the grain overlays the
   page surface only. */
body.is-admin[data-page-grain]::after {
  /* Don't grain the admin toolbar; offset the start. */
  inset: 64px 0 0 0;
}

/* ====================================================================
   r23 PATCHES - appended additions that don't override existing rules
   ====================================================================
   These three rule blocks were added in r23 to fix specific reported
   issues without touching the kit's main styles.css. Each block is
   defensive (won't break anything if the targeted markup is absent)
   and scoped tightly so they cannot leak. Remove with confidence if
   the kit ever absorbs equivalent rules.
*/

/* ----- r23: defensive socials centering ----------------------------
   Reported: the .socials row in luh-subscribe was rendering offset
   to the right in some viewports / parent contexts. The base rule
   (in styles.css line ~1267) is `display: grid; grid-template-columns:
   repeat(4, 1fr); gap: var(--s-5)` which centers correctly when the
   parent is a centered container with 4 items, but can offset when
   the item count differs from 4, or when the parent flexes its width.

   This rule is purely additive: it keeps the kit's grid + responsive
   breakpoints, and adds three safety nets:
     1. margin-inline: auto         - center the container if its
        intrinsic width is less than its parent.
     2. justify-content: center     - center the column track if grid
        items don't fill all columns (e.g., 3 socials in a 4-column
        grid would otherwise pile up on the left).
     3. justify-items: center       - center each badge inside its
        own column cell (handles slight width variance from rotation
        + handle text length).

   `justify-content` interacts with `grid-template-columns: repeat(N, 1fr)`
   only when the grid TRACK is narrower than the container. With
   1fr columns the track fills the container, so this only kicks in
   when there are fewer items than columns. Safe to layer over the
   existing rule. */
.socials {
  margin-inline: auto;
  justify-content: center;
  justify-items: center;
}

/* ----- r23: /party hero background texture scaffold ----------------
   When Joe drops party-hero-bg.svg into /assets/img/uploads/, the
   /party page hero gets a textured background (d20s + confetti) that
   layers OVER the existing navy backdrop. Until the SVG file exists,
   the browser silently 404s the URL and the existing navy
   background-color shows through unchanged - so this rule is harmless
   to ship now.

   Scope: body.page-party only. Cannot leak to other pages because:
     - body class is page-<slug> (template.php emits e.g.
       `<body class="page-party">` for /party, `page-home` for /home)
     - the selector requires page-party EXACTLY
     - if an editor renames the slug, the rule deactivates safely
   The hero target is `.subhero` - that's the actual rendered class
   from partials/sections/luh-subhero.php (NOT `.luh-subhero` which is
   a section_type identifier but never makes it into the DOM as a
   class name). `.subhero` is also used on /sell, /about, /faq, etc,
   but the body.page-party prefix restricts this rule to /party only.
   Selector specificity: 0,1,1 - enough to win against the kit's
   default `.subhero` rules (specificity 0,1,0) but easy to override
   with a more specific selector if needed. */
body.page-party .subhero {
  /* Layer the SVG ON TOP of whatever background-color the hero
     already has. The SVG is transparent on purpose; the underlying
     navy stays visible. */
  background-image: url('/assets/img/uploads/party-hero-bg.svg');
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
  /* Preserve any background-color set by the hero variant. We do NOT
     set background-color here - that's the hero's job. */
}

/* ----- r23: luh-ladder count-adaptive grid -------------------------
   The kit's `.pts-ladder` uses `grid-template-columns: repeat(4,
   minmax(0, 1fr))`, which fills 4 columns no matter the item count.
   With 4 items that's correct (the original design). With 3 items
   (section_pty00009 on /party - "Private rooms, by the slot"), the
   3 cards take columns 1-3 and column 4 stays empty, pushing the
   row visibly off-center to the left. With 2 items it's worse, with
   1 the card sits in column 1 alone.

   The fix is pure CSS via :has() to detect the populated child
   count and swap the column template to match. No PHP change, no
   content edit. Cards stay the same width-budget they would in the
   4-card layout but the row centers as a group.

   :has() browser support (May 2026): Chrome 105+, Safari 15.4+,
   Firefox 121+ - all stable for ~30 months by now. Older browsers
   degrade to the kit's default 4-column grid (the current state),
   which is a layout regression for those users only, not a break.

   Why we don't use auto-fit: auto-fit with minmax(220px, 1fr) would
   produce 5 columns at 1240px wide and break the 4-card design when
   4 items are present. Explicit count-by-count rules preserve every
   intended layout. */
.pts-ladder:has(> .ladder-card:nth-child(3):last-child) {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
.pts-ladder:has(> .ladder-card:nth-child(2):last-child) {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  max-width: 760px;
  margin-inline: auto;
}
.pts-ladder:has(> .ladder-card:only-child) {
  grid-template-columns: 1fr;
  max-width: 360px;
  margin-inline: auto;
}
/* Responsive: at the 920px breakpoint where the kit drops to 2 columns,
   a 3-item ladder would split 2+1 (with the lone card left-aligned in
   column 1). Override to single-column for cleaner mobile-tablet
   stacking. The kit's <540px rule already handles full-width single
   column so we don't need a further override there. */
@media (max-width: 920px) {
  .pts-ladder:has(> .ladder-card:nth-child(3):last-child) {
    grid-template-columns: 1fr;
    max-width: 480px;
    margin-inline: auto;
  }
}
