/* ============================================================================
   CheapSelfHelp (CSH) — site.css
   Single drop-in vanilla stylesheet. No frameworks, no utilities.
   Dark-first. Warm/muted terracotta–plum palette. Urbanist throughout.

   Ported from the Claude Design handoff bundle (design_tokens/handoff/css/site.css).
   Sections 1–18 are the design system verbatim. Sections 19–20 add the
   app-specific hooks the Razor pages need (gallery/topics, ASP.NET validation
   integration) and the admin area, all built on the same tokens.
   ========================================================================== */

/* ----------------------------------------------------------------------------
   0. FONTS — Urbanist, self-hosted (no Google Fonts <link>, no CDN).
   One variable file covers weights 400–900 via the wght axis (see FONTS.md).
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Urbanist";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("/fonts/Urbanist-VariableFont_wght.woff2") format("woff2-variations");
}

/* ----------------------------------------------------------------------------
   1. DESIGN TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* --- Color: base surfaces (dark-first) --- */
  --bg: #15100c;              /* page background — near-black warm charcoal */
  --bg-2: #100c09;            /* deeper wells: player rail, footer          */
  --surface: #211913;         /* cards, inputs, panels                      */
  --surface-2: #2b2119;       /* raised chips, thumb fallbacks, hover       */

  /* --- Color: text (semantic) --- */
  --ink: #f3ece3;             /* primary text / headings                    */
  --muted: #c3b6a8;           /* secondary text, body copy on dark          */
  --faint: #8f8377;           /* tertiary: metadata, placeholders, captions */

  /* --- Color: brand accents (warm, muted — NOT saturated red/pink) --- */
  --primary: #cf8262;         /* terracotta — CTAs, active, links           */
  --primary-hover: #d99f8f;   /* blush — link/hover lift                    */
  --primary-ink: #1b140f;     /* text/icon color ON a --primary fill        */
  --plum: #9c6f86;            /* secondary accent — gradients, detail hero  */
  --gold: #c8a06a;            /* ratings / stars only                       */

  /* --- Color: lines & feedback --- */
  --border: rgba(243, 236, 227, 0.10);  /* hairline dividers, card edges    */
  --border-strong: rgba(243, 236, 227, 0.20);
  --danger: #d9736b;          /* remove / destructive / validation error    */
  --danger-bg: rgba(217, 115, 107, 0.12);
  --success: #cf8262;         /* completion ticks reuse terracotta          */
  --focus-ring: rgba(207, 130, 98, 0.45);

  /* --- Typography --- */
  --font-sans: "Urbanist", system-ui, -apple-system, "Segoe UI", Roboto,
               Helvetica, Arial, "Apple Color Emoji", sans-serif;
  --font-mono: ui-monospace, "SF Mono", "SFMono-Regular", Menlo, Consolas, monospace;

  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;
  --fw-extrabold: 800;
  --fw-black: 900;

  /* Type scale (px shown for reference; rem used so it respects user zoom) */
  --fs-xs: 0.75rem;    /* 12px — badges, captions            */
  --fs-sm: 0.8125rem;  /* 13px — metadata, small labels       */
  --fs-base: 0.9375rem;/* 15px — UI text, card titles         */
  --fs-md: 1.0625rem;  /* 17px — body / lede                  */
  --fs-lg: 1.4375rem;  /* 23px — section titles               */
  --fs-xl: 1.5625rem;  /* 25px — sub-hero / player title      */
  --fs-2xl: 2.25rem;   /* 36px — CTA band heading             */
  --fs-3xl: 2.875rem;  /* 46px — home hero                    */
  --fs-4xl: 3.25rem;   /* 52px — detail hero                  */

  --lh-tight: 1.03;
  --lh-snug: 1.25;
  --lh-normal: 1.55;
  --lh-body: 1.62;

  /* --- Spacing scale (4px base) --- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --space-16: 64px;

  /* --- Radii --- */
  --radius-sm: 8px;    /* inputs, small chips inner            */
  --radius: 12px;      /* default: buttons, list rows, cards    */
  --radius-md: 15px;   /* thumbnails                            */
  --radius-lg: 20px;   /* panels, buy card                      */
  --radius-xl: 26px;   /* hero / CTA band                       */
  --radius-pill: 999px;

  /* --- Borders --- */
  --border-width: 1px;
  --hairline: var(--border-width) solid var(--border);

  /* --- Shadows --- */
  --shadow-card: 0 12px 30px rgba(0, 0, 0, 0.36);
  --shadow-lift: 0 14px 34px rgba(0, 0, 0, 0.42);
  --shadow-panel: 0 20px 50px rgba(0, 0, 0, 0.40);
  --shadow-hero: 0 18px 44px rgba(0, 0, 0, 0.40);

  /* --- Layout --- */
  /* ONE width token for the whole site. There used to be two (--maxw 1200 for content, --maxw-wide
     1400 for the header/breadcrumb/admin shell), which meant the logo and nav sat 100px further out
     than every piece of content beneath them, and the detail breadcrumb overhung its own page body.
     1400 wins because the header was already drawing the intended content edge: at a 1700px viewport
     the logo starts at x=185 and so, now, does the content. A second width token must not come back —
     surfaces that need a narrower measure set their own (.static-page 68ch, .auth-shell 460px),
     which is a reading measure, not a page clamp. */
  --maxw: 1400px;            /* the one page container width     */
  --gutter: 40px;            /* page side padding (desktop)      */
  --gutter-mobile: 20px;
  /* The content rectangle every surface lines up to: --maxw minus a gutter each side. Elements that
     carry their own background or scrollbar (.hero, .shelf) size their BOX to this, so their edges
     match the text of elements that instead use max-width:--maxw + padding-inline (.page-head,
     .section__head, .course-grid...). Mixing the two is what made the shelves look 40px too wide. */
  --content-w: calc(var(--maxw) - (2 * var(--gutter)));
  --header-h: 62px;

  /* --- Category thumbnail art (soft warm gradients; swap for photography) - */
  --art-bundle:    radial-gradient(135% 135% at 22% 16%, oklch(0.52 0.10 36)  0%, oklch(0.34 0.07 12)  46%, oklch(0.19 0.03 346) 100%);
  --art-seduction: radial-gradient(135% 135% at 30% 16%, oklch(0.50 0.10 30)  0%, oklch(0.34 0.07 10)  46%, oklch(0.19 0.03 352) 100%);
  --art-sexed:     radial-gradient(135% 135% at 26% 16%, oklch(0.50 0.10 18)  0%, oklch(0.34 0.07 358) 46%, oklch(0.19 0.03 340) 100%);
  --art-body:      radial-gradient(135% 135% at 40% 16%, oklch(0.52 0.09 56)  0%, oklch(0.36 0.07 44)  46%, oklch(0.19 0.03 32)  100%);
  --art-health:    radial-gradient(135% 135% at 44% 16%, oklch(0.54 0.09 74)  0%, oklch(0.38 0.07 60)  46%, oklch(0.20 0.03 46)  100%);
  --art-self:      radial-gradient(135% 135% at 34% 16%, oklch(0.52 0.10 44)  0%, oklch(0.35 0.07 32)  46%, oklch(0.19 0.03 22)  100%);
}

/* Optional light-mode toggle (secondary; dark is the design priority). */
[data-theme="light"] {
  --bg: #f6f0e8;
  --bg-2: #efe7dc;
  --surface: #ffffff;
  --surface-2: #f1e9de;
  --ink: #201812;
  --muted: #5f5449;
  --faint: #8a7d6f;
  --border: rgba(32, 24, 18, 0.12);
  --border-strong: rgba(32, 24, 18, 0.22);
  --primary-ink: #ffffff;
  --shadow-card: 0 10px 26px rgba(60, 40, 25, 0.12);
  --shadow-panel: 0 18px 44px rgba(60, 40, 25, 0.16);
}

/* ----------------------------------------------------------------------------
   2. RESET / BASE
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 { margin: 0; letter-spacing: -0.02em; font-weight: var(--fw-extrabold); }
p { margin: 0; }
img { max-width: 100%; display: block; }

a { color: var(--primary); text-decoration: none; transition: color 0.15s ease; }
a:hover { color: var(--primary-hover); }
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

::selection { background: var(--primary); color: var(--primary-ink); }
::placeholder { color: var(--faint); opacity: 1; }

::-webkit-scrollbar { height: 10px; width: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: #332720; border-radius: var(--radius-sm);
  border: 2px solid transparent; background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: #463629; background-clip: padding-box; }

/* ----------------------------------------------------------------------------
   3. LAYOUT
   -------------------------------------------------------------------------- */
.container { max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.main-content { display: block; min-height: 60vh; }

/* .bleed is the legacy full-bleed helper. Both browse surfaces are now clamped to --maxw instead —
   the catalog grid and the home shelves — so every page shares one left edge. */
.bleed { padding-inline: var(--gutter); }

.section { padding: 6px 0 30px; }
.section__head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--space-4); padding-inline: var(--gutter); margin-bottom: 15px;
  max-width: var(--maxw); margin-inline: auto;
}
.section__title { font-size: var(--fs-lg); font-weight: var(--fw-extrabold); }
.section__blurb { color: var(--faint); font-size: var(--fs-base); margin-top: 2px; }
.section__link {
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: inherit; font-size: 0.875rem; font-weight: var(--fw-bold);
  color: var(--primary);
}

/* ----------------------------------------------------------------------------
   4. SITE HEADER
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky; top: 0; z-index: 40;
  background: linear-gradient(to bottom, rgba(21,16,12,0.97), rgba(21,16,12,0.70));
  backdrop-filter: blur(14px);
  border-bottom: var(--hairline);
}
.header-inner {
  max-width: var(--maxw); margin-inline: auto;
  display: flex; align-items: center; gap: var(--space-6);
  padding: 15px var(--gutter); min-height: var(--header-h);
}
.brand {
  display: inline-flex; align-items: flex-end; gap: 4px;
  font-size: 1.5625rem; font-weight: var(--fw-black);
  letter-spacing: -0.035em; color: var(--ink); line-height: 1;
}
.brand:hover { color: var(--ink); }
.brand__dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--primary); margin-bottom: 3px;
}
.site-nav { display: flex; gap: var(--space-6); align-items: center; }
/* Desktop shows the labels; the icons only exist for the mobile header below. */
.site-nav__icon { display: none; }
.site-nav__count { display: none; }
.site-nav a {
  font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--muted);
}
.site-nav a:hover { color: var(--ink); }
/* [aria-current] unqualified on purpose: an exact page match emits "page", while a link standing for
   a whole section (Browse, Admin) emits "true" when you are somewhere inside it. Matching only
   ="page" is what left every non-index page with no highlighted nav item. */
.site-nav a[aria-current] { color: var(--ink); }

.header-search {
  display: flex; align-items: center; gap: 10px; width: 320px;
  margin-left: auto;
  background: var(--surface); border: var(--hairline);
  border-radius: var(--radius-pill); padding: 9px 16px; color: var(--faint);
}
.header-search svg { flex: 0 0 auto; }
.header-search input {
  border: none; background: none; outline: none; width: 100%;
  color: var(--ink); font-family: inherit; font-size: 0.875rem;
}
.header-actions { display: flex; align-items: center; gap: var(--space-4); }
/* The single mobile account entry point; desktop keeps the avatar + Log out pair instead. */
/* Replaced the initial-letter avatar (a 38px gradient-filled circle) at every width. The gradient
   moved onto the icon's own strokes — see the SVG's linearGradient in _Layout — so the brand colour
   is still there without a letterform standing in for a person. */
.account-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: var(--radius-pill);
  transition: background 0.15s ease;
}
.account-icon svg { width: 25px; height: 25px; }
.account-icon:hover { background: rgba(207, 130, 98, 0.14); }
.header-actions--guest .account-icon { display: none; }
/* When there is no search bar, actions still sit at the far right. */
.site-nav + .header-actions { margin-left: auto; }

/* ----------------------------------------------------------------------------
   5. BUTTONS & LINK-BUTTONS
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: inherit; font-weight: var(--fw-extrabold); font-size: var(--fs-base);
  padding: 11px 20px; border-radius: var(--radius-pill);
  border: 1px solid transparent; cursor: pointer;
  background: var(--primary); color: var(--primary-ink);
  transition: filter 0.15s ease, background 0.15s ease, transform 0.1s ease;
}
.btn:hover { filter: brightness(1.06); color: var(--primary-ink); }
.btn:active { transform: translateY(1px); }
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45; cursor: not-allowed; filter: none; transform: none;
}
.btn--block { width: 100%; }
.btn--lg { font-size: var(--fs-md); padding: 15px 30px; }
.btn--sm, .btn--small { font-size: var(--fs-sm); padding: 8px 14px; }
.btn--square { border-radius: var(--radius); }   /* buy card CTA uses 12px */

.btn--ghost {
  background: rgba(255, 255, 255, 0.05);
  color: var(--ink); border-color: var(--border);
}
.btn--ghost:hover { background: rgba(255, 255, 255, 0.09); filter: none; color: var(--ink); }

/* Text-style action buttons (remove, watch preview, "see all") */
.linkbutton {
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: inherit; font-size: 0.875rem; font-weight: var(--fw-bold);
  color: var(--primary); display: inline-flex; align-items: center; gap: 6px;
}
.linkbutton:hover { color: var(--primary-hover); }
.linkbutton--danger { color: var(--danger); }
.linkbutton--danger:hover { color: #e88a82; }

/* A form that should not introduce block flow around an inline link-button. */
.inline-form { display: inline; margin: 0; }

/* ----------------------------------------------------------------------------
   6. HERO + SEARCH (home & detail)
   -------------------------------------------------------------------------- */
.hero {
  position: relative; overflow: hidden;
  border-radius: var(--radius-xl); min-height: 460px;
  display: flex; align-items: center;
  margin: 22px auto 6px;
  width: calc(100% - (2 * var(--gutter))); max-width: var(--content-w);
}
.hero__art {
  position: absolute; inset: 0;
  background:
    radial-gradient(130% 120% at 82% 22%, oklch(0.46 0.10 22) 0%, transparent 52%),
    repeating-linear-gradient(135deg, #251d16 0, #251d16 13px, #1d1610 13px, #1d1610 26px);
}
.hero__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(90deg,
    rgba(15,11,8,0.97) 0%, rgba(15,11,8,0.82) 44%, rgba(15,11,8,0.28) 100%);
}
.hero__note {
  position: absolute; top: 16px; right: 22px;
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: rgba(243, 236, 227, 0.40); letter-spacing: 0.04em;
}
.hero__body { position: relative; max-width: 640px; padding: 54px 46px; }
.hero__eyebrow {
  font-size: var(--fs-base); font-weight: var(--fw-bold);
  color: var(--primary); margin-bottom: 14px;
}
.hero__eyebrow--brand {
  font-size: var(--fs-sm); font-weight: var(--fw-extrabold);
  letter-spacing: 0.14em; text-transform: uppercase;
}
.hero h1 {
  font-size: var(--fs-3xl); line-height: var(--lh-tight);
  font-weight: var(--fw-extrabold); letter-spacing: -0.025em;
  margin-bottom: 16px; text-wrap: balance;
}
.lede {
  color: var(--muted); font-size: var(--fs-md); line-height: 1.5;
  max-width: 480px; margin-bottom: 26px; text-wrap: pretty;
}

.searchbar {
  display: flex; align-items: center; gap: 12px; max-width: 540px;
  background: rgba(20, 14, 10, 0.90); border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill); padding: 13px 14px 13px 20px;
  backdrop-filter: blur(8px); box-shadow: var(--shadow-hero);
}
.searchbar:focus-within { border-color: var(--primary); }
.searchbar svg { flex: 0 0 auto; color: var(--primary); }
.searchbar input {
  flex: 1; border: none; background: none; outline: none;
  color: var(--ink); font-size: var(--fs-md); font-family: inherit;
}

/* Intent chips ("find a course for X") + filter chips */
.chip-list { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 18px; max-width: 560px; list-style: none; padding: 0; }
.chip {
  /* inline-flex, not the default inline. A chip has 8px of VERTICAL padding, and vertical padding on
     an inline element does not grow its line box — it just paints outside it. Where chips are direct
     children of the flex .chip-list (home hero, catalog filters) they get blockified and look right,
     which is why this never showed there. On the course page they are wrapped in <li>, so the <li> is
     the flex item and the chip stayed inline: the li measured 23px while the chip painted 35px, so
     row gap was computed on the wrong box and pills overlapped their neighbours by 3px. */
  display: inline-flex; align-items: center;
  font-size: 0.84375rem; font-weight: var(--fw-semibold);
  padding: 8px 14px; border-radius: var(--radius-pill);
  border: var(--hairline); color: var(--muted);
  background: rgba(255, 255, 255, 0.04); cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.chip:hover,
.chip[aria-pressed="true"],
.chip--active { color: var(--ink); border-color: var(--border-strong); }
.chip[aria-pressed="true"],
.chip--active {
  background: rgba(207, 130, 98, 0.14); border-color: var(--primary); color: var(--ink);
}

/* Value-prop cards (visitor home) */
.valueprops { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; padding: 30px var(--gutter) 12px; }
.valueprop { background: var(--surface); border: var(--hairline); border-radius: var(--radius-lg); padding: 26px; }
.valueprop__icon { width: 42px; height: 42px; border-radius: var(--radius); background: linear-gradient(135deg, var(--primary), var(--plum)); margin-bottom: 16px; }
.valueprop__title { font-weight: var(--fw-extrabold); font-size: 1.125rem; margin-bottom: 7px; }
.valueprop__body { color: var(--muted); font-size: 0.90625rem; line-height: var(--lh-normal); text-wrap: pretty; }

/* CTA band (visitor home) */
.cta-band {
  border-radius: var(--radius-xl); padding: 56px 44px; text-align: center;
  border: var(--hairline); margin: 12px var(--gutter) 30px;
  background: radial-gradient(120% 150% at 12% 0%, oklch(0.44 0.10 24) 0%, var(--surface) 62%);
}
.cta-band h2 { font-size: var(--fs-2xl); margin-bottom: 12px; letter-spacing: -0.025em; }
.cta-band .lede { max-width: 540px; margin: 0 auto 28px; }
.cta-band__actions { display: flex; justify-content: center; gap: 14px; flex-wrap: wrap; }

/* ----------------------------------------------------------------------------
   7. SHELVES (horizontal scroll rows) + COURSE CARDS
   -------------------------------------------------------------------------- */
/* Clamped like .course-grid so the home shelves start at the same left edge as their headings and
   the rest of the site. The row still scrolls horizontally — the clamp bounds the viewport it
   scrolls inside, it does not stop the overflow. */
.shelf {
  display: flex; gap: 18px; overflow-x: auto; padding: 6px 0 14px;
  scroll-snap-type: x proximity;
  width: calc(100% - (2 * var(--gutter))); max-width: var(--content-w); margin-inline: auto;
  /* No inline padding on purpose. It used to sit inside the scrollport, which put the box 40px wider
     than the hero on each side (the scrollbar and half-scrolled cards visibly overshot), and scroll
     snapping then resolved to the first card's snap-align:start INSIDE that padding — the browser
     silently scrolled by exactly var(--gutter) at load. Sizing the scrollport to --content-w instead
     fixes both: cards start on the shared left edge and clip on the shared right edge. */
}
.shelf > * { scroll-snap-align: start; }

/* Edge fade. Cards used to end in a hard vertical cut at the content edge, which reads as a clipping
   artifact rather than "there is more this way". The fade is driven by the shelf's OWN scroll
   position, so it only ever appears on a side that actually has content behind it: no left fade at
   rest, no right fade once you reach the end, and — because a scroll timeline on a container with no
   scrollable overflow is INACTIVE and applies no effect — no fade at all on a short row that fits.
   That inactive-timeline behaviour is also the whole fallback story: a browser without scroll-driven
   animations simply keeps today's hard edge. Pure CSS on purpose; this site ships no JavaScript. */
@supports (animation-timeline: scroll(self inline)) {
  .shelf {
    --shelf-fade: 56px;
    animation-name: shelf-edge-fade;
    animation-timeline: scroll(self inline);
    animation-fill-mode: both;
    animation-timing-function: linear;
  }
}

/* Every keyframe must declare the SAME four colour stops in the same units — a linear-gradient only
   interpolates against another with matching structure, so varying the stop count would make the
   mask jump instead of fade. Only the alpha of the two outer stops changes. */
@keyframes shelf-edge-fade {
  0% {
    mask-image: linear-gradient(90deg,
      rgb(0 0 0 / 1) 0px, rgb(0 0 0 / 1) var(--shelf-fade),
      rgb(0 0 0 / 1) calc(100% - var(--shelf-fade)), rgb(0 0 0 / 0) 100%);
  }
  6%, 94% {
    mask-image: linear-gradient(90deg,
      rgb(0 0 0 / 0) 0px, rgb(0 0 0 / 1) var(--shelf-fade),
      rgb(0 0 0 / 1) calc(100% - var(--shelf-fade)), rgb(0 0 0 / 0) 100%);
  }
  100% {
    mask-image: linear-gradient(90deg,
      rgb(0 0 0 / 0) 0px, rgb(0 0 0 / 1) var(--shelf-fade),
      rgb(0 0 0 / 1) calc(100% - var(--shelf-fade)), rgb(0 0 0 / 1) 100%);
  }
}

/* Grid variant (catalog listing) */
/* Clamped to --maxw so the grid's left edge lines up with .page-head above it. Without this the
   heading indents on a wide screen while the cards run to the viewport edges. */
.course-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 22px 18px; padding: 6px var(--gutter) 20px;
  max-width: var(--maxw); margin-inline: auto;
}

/* A column, so the cover / title / author / price stack predictably and the price can be pinned to
   the bottom. The card stretches to its row height (grid items stretch by default; shelf cards
   stretch as flex items), which is what makes prices line up across a row. */
.course-card {
  display: flex; flex-direction: column; text-align: left; background: none; border: none; padding: 0;
  cursor: pointer; font-family: inherit; color: inherit; width: 100%;
  transition: transform 0.18s ease;
}
.shelf .course-card { flex: 0 0 auto; width: 234px; }
.course-card:hover { transform: translateY(-5px); }
.course-card:hover .course-card__title { color: var(--primary-hover); }

.course-card__thumb {
  position: relative; display: block; width: 100%; aspect-ratio: 234 / 150;
  border-radius: var(--radius-md); overflow: hidden;
  background: var(--surface-2); box-shadow: var(--shadow-card);
}
.shelf .course-card__thumb { height: 150px; aspect-ratio: auto; }
/* Cover layers on top of the always-present placeholder; onerror hides it → placeholder shows. */
.course-card__thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; }
.course-card__thumb::after {   /* readability scrim for future photography */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(10,7,5,0.5), transparent 62%);
}
.course-card__placeholder { position: absolute; inset: 0; background: var(--art-self); }
.course-card__placeholder.ph--bundle    { background: var(--art-bundle); }
.course-card__placeholder.ph--seduction { background: var(--art-seduction); }
.course-card__placeholder.ph--sexed     { background: var(--art-sexed); }
.course-card__placeholder.ph--body      { background: var(--art-body); }
.course-card__placeholder.ph--health    { background: var(--art-health); }
.course-card__placeholder.ph--self      { background: var(--art-self); }

.course-card__badge {
  position: absolute; top: 10px; left: 10px; z-index: 1;
  font-size: 0.625rem; font-weight: var(--fw-extrabold); letter-spacing: 0.07em;
  text-transform: uppercase; padding: 5px 9px; border-radius: var(--radius-pill);
  background: rgba(20, 14, 10, 0.72); backdrop-filter: blur(4px); color: var(--ink);
}
.course-card__duration {
  position: absolute; bottom: 9px; right: 10px; z-index: 1;
  font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--ink);
  background: rgba(20, 14, 10, 0.62); padding: 3px 8px; border-radius: 6px;
}

/* These four were <span>s with no display set, so they were INLINE: the margin-top values below did
   nothing and the title, author and price ran together as one wrapped text blob — visibly ragged once
   titles differed in length. They are blocks now. */
.course-card__body { display: flex; flex-direction: column; flex: 1 1 auto; padding-top: 11px; }
.course-card__title {
  font-weight: var(--fw-bold); font-size: var(--fs-base); line-height: var(--lh-snug);
  text-wrap: pretty; transition: color 0.15s ease;
  /* Clamp long titles to two lines. Deliberately NO min-height: reserving two lines unconditionally
     left a dead gap in rows where every title is short (the bundles shelf). Instead the cards stretch
     to the tallest in their row and the price is pinned to the bottom, so prices line up in mixed
     rows and uniform rows stay compact. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.course-card__title a { color: inherit; }
.course-card__title a:hover { color: var(--primary-hover); }
/* One line, ellipsised — author names vary wildly in length and must not reflow the card. */
.course-card__author {
  display: block; color: var(--faint); font-size: var(--fs-sm); margin-top: 3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* margin-top:auto pins the price to the bottom of the stretched card, so prices align in a row. */
.course-card__price {
  display: block; color: var(--ink); font-size: var(--fs-base); font-weight: var(--fw-bold);
  margin-top: auto; padding-top: 6px;
}
.course-card__price .was { color: var(--faint); font-weight: var(--fw-regular); text-decoration: line-through; margin-left: 8px; }

/* ----------------------------------------------------------------------------
   8. CATALOG LISTING: filters, result count, pagination
   -------------------------------------------------------------------------- */
.filter-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 6px var(--gutter) 4px;
  max-width: var(--maxw); margin-inline: auto;
}
.filter-bar .chip-list { margin-top: 0; max-width: none; }
/* The catalog's search field is the page's primary control, not a hero accent, so it gets half again
   the shared .searchbar measure (540px -> 810px). The hero one stays 540px: it sits in the hero's
   narrower text column and would otherwise run under the artwork. */
.filter-bar .searchbar { max-width: 810px; width: 100%; }
.filter-group { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.filter-group__label {
  color: var(--faint); font-size: 0.75rem; font-weight: var(--fw-extrabold);
  text-transform: uppercase; letter-spacing: 0.08em; margin-right: 2px;
}
.result-count {
  color: var(--faint); font-size: 0.875rem; font-weight: var(--fw-semibold);
  padding: 4px var(--gutter) 0;
  max-width: var(--maxw); margin-inline: auto;
}
.listing-toolbar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 18px var(--gutter) 0; flex-wrap: wrap;
}

.pagination {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 20px var(--gutter) 56px; list-style: none;
  margin: 0 auto; max-width: var(--maxw);
}
.pagination a, .pagination span {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; height: 40px; padding: 0 12px;
  border-radius: var(--radius); border: var(--hairline);
  font-size: 0.875rem; font-weight: var(--fw-bold); color: var(--muted);
  background: var(--surface);
}
.pagination a:hover { color: var(--ink); border-color: var(--border-strong); }
.pagination a[aria-current="page"] {
  background: var(--primary); color: var(--primary-ink); border-color: var(--primary);
}
.pagination .is-disabled { opacity: 0.4; pointer-events: none; }
.pagination__status { color: var(--faint); font-size: 0.875rem; margin: 0 10px; border: none; background: none; }

/* ----------------------------------------------------------------------------
   9. COURSE DETAIL
   -------------------------------------------------------------------------- */
.breadcrumb {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  /* Symmetric on purpose. The bottom was 0, so the breadcrumb text sat flush against the top of the
     full-bleed hero while carrying 18px above it — the band read as broken rather than as a gutter. */
  padding: 18px var(--gutter); color: var(--faint); font-size: 0.875rem;
  max-width: var(--maxw); margin-inline: auto;
}
.breadcrumb a { color: var(--muted); font-weight: var(--fw-semibold); }
.breadcrumb a:hover { color: var(--ink); }
.breadcrumb__sep { color: var(--faint); }

.course-detail { display: block; }
.course-detail__hero {
  position: relative; min-height: 440px; display: flex; align-items: flex-end;
  overflow: hidden;
}
.course-detail__hero .hero__art {
  background:
    radial-gradient(120% 120% at 78% 8%, oklch(0.44 0.09 348) 0%, transparent 55%),
    repeating-linear-gradient(135deg, #251d16 0, #251d16 13px, #1d1610 13px, #1d1610 26px);
}
.course-detail__hero .hero__art img { width: 100%; height: 100%; object-fit: cover; }
.course-detail__hero .hero__scrim {
  background: linear-gradient(to top,
    rgba(15,11,8,0.99) 6%, rgba(15,11,8,0.78) 40%, rgba(15,11,8,0.35) 100%);
}
.course-detail__hero-body { position: relative; padding: 0 var(--gutter) 44px; max-width: 900px; margin-inline: auto; width: 100%; }
.course-detail__eyebrow {
  font-size: var(--fs-sm); font-weight: var(--fw-extrabold); color: var(--primary);
  letter-spacing: 0.14em; text-transform: uppercase; margin-bottom: 12px;
}
.course-detail__hero-body h1 {
  font-size: var(--fs-4xl); line-height: 1.02; font-weight: var(--fw-black);
  letter-spacing: -0.03em; margin-bottom: 14px; text-wrap: balance;
}
.course-detail__lede {
  font-size: 1.1875rem; color: var(--muted); line-height: 1.45;
  margin-bottom: 18px; max-width: 640px; text-wrap: pretty;
}
.course-detail__meta {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  color: var(--muted); font-size: 0.90625rem; font-weight: var(--fw-semibold);
}
.course-detail__meta .dot { width: 4px; height: 4px; border-radius: 50%; background: var(--faint); }
.course-detail__rating { color: var(--gold); font-weight: var(--fw-extrabold); }
.course-detail__author { color: var(--muted); }

.course-detail__grid {
  display: grid; grid-template-columns: 1fr 360px; gap: 48px;
  padding: 44px var(--gutter) 60px; max-width: var(--maxw); margin-inline: auto;
  align-items: start;
}
/* The aside is first in SOURCE order so the stacked layout leads with the buy card. Explicit
   placement — not `order` — puts the two columns back the right way round on desktop, which keeps
   the wide layout byte-identical to before while the narrow one follows source order for free. */
.course-detail__summary { grid-column: 1; grid-row: 1; }
.course-detail__aside { grid-column: 2; grid-row: 1; }
/* Sticky moved from .buycard to the whole aside. With only the card sticky, the tags panel beneath it
   stayed put in flow while the card slid down over it as you scrolled — sticky does not reserve the
   space it moves through. Sticking the column keeps card and tags together as one unit. */
.course-detail__aside { position: sticky; top: calc(var(--header-h) + 26px); }
.course-detail__summary h2,
.course-detail__summary h3 { font-size: var(--fs-lg); margin-bottom: 16px; margin-top: 34px; }
.course-detail__summary > :first-child { margin-top: 0; }
.course-detail__description p,
.course-detail__description {
  color: var(--muted); font-size: 1.03125rem; line-height: var(--lh-body);
  margin-bottom: 16px; text-wrap: pretty; white-space: pre-line;
}

.callout {
  background: var(--surface); border: var(--hairline);
  border-radius: var(--radius-md); padding: 22px; margin: 22px 0;
}
.callout__label {
  font-size: var(--fs-xs); font-weight: var(--fw-extrabold); letter-spacing: 0.10em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 8px;
}
.callout__body { color: var(--muted); font-size: var(--fs-base); line-height: var(--lh-normal); text-wrap: pretty; }
.callout--bonus {
  display: flex; align-items: center; gap: 14px; margin-bottom: 36px;
  background: linear-gradient(100deg, rgba(207,130,98,0.14), rgba(156,111,134,0.10));
  border-color: rgba(207, 130, 98, 0.28);
}
.callout--bonus .callout__label { color: var(--primary); margin-bottom: 0; }
.callout--bonus strong { font-size: var(--fs-md); font-weight: var(--fw-bold); }

/* "What you'll learn" topic list (detail) */
.topic-list { list-style: none; margin: 0 0 8px; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 12px 28px; }
.topic-list__item {
  position: relative; padding-left: 30px; color: var(--muted);
  font-size: 0.96875rem; line-height: 1.45;
}
.topic-list__item::before {
  content: ""; position: absolute; left: 0; top: 2px;
  width: 18px; height: 18px; border-radius: 50%;
  background: rgba(207, 130, 98, 0.16);
  /* terracotta check mark */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23cf8262' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: center;
}

/* Screenshot / gallery grid (detail) */
.gallery { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; }
.gallery__item { margin: 0; }
.gallery__item a { display: block; border-radius: var(--radius-md); overflow: hidden; border: var(--hairline); background: var(--surface-2); box-shadow: var(--shadow-card); }
.gallery__item img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; transition: transform 0.2s ease; }
.gallery__item a:hover img { transform: scale(1.04); }

/* ----------------------------------------------------------------------------
   10. FILE LIST + BADGES  (curriculum / "what's included" / library files)
   -------------------------------------------------------------------------- */
.file-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.file-list__item {
  display: flex; align-items: center; gap: 16px;
  padding: 15px 18px; border: var(--hairline); border-radius: var(--radius);
  background: var(--surface);
}
.file-list__num { font-family: var(--font-mono); color: var(--faint); font-size: 0.8125rem; }
/* min-width:0 is the fix, not a tidy-up. `flex: 1` is `flex: 1 1 0%`, but min-width resolves to
   `auto`, so a flex item cannot shrink below its MIN-CONTENT width — and for a filename with no
   break opportunity ("DVD1-SquirtingOrgasmMastery.mp4", 127 such files in production) min-content is
   the whole string. Measured on a 375px screen: the row's scrollWidth blew out to 746px inside a
   291px box and pushed the Download button 454px past the item's right edge. overflow-wrap:anywhere
   gives long runs somewhere to break so the name wraps instead of overflowing. */
.file-list__title {
  flex: 1; min-width: 0; overflow-wrap: anywhere;
  font-weight: var(--fw-semibold); font-size: 0.96875rem;
}
/* The action must never be the thing that gets squeezed. */
.file-list__item > .btn { flex: 0 0 auto; }
.download-btn { display: inline-flex; align-items: center; gap: 8px; }
.download-btn__icon { display: none; }
.file-list__meta { color: var(--faint); font-size: 0.8125rem; }
.file-list__lock { color: var(--faint); display: inline-flex; }

/* Bundle contents — same card rhythm as .file-list, but each row is a link to a real course. */
.bundle-list { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.bundle-list__item {
  display: flex; align-items: center; gap: 16px;
  padding: 15px 18px; border: var(--hairline); border-radius: var(--radius);
  background: var(--surface);
}
.bundle-list__title { flex: 1; font-weight: var(--fw-semibold); font-size: 0.96875rem; color: var(--ink); text-decoration: none; }
.bundle-list__title:hover { color: var(--primary); text-decoration: underline; }
.bundle-list__meta { color: var(--faint); font-size: 0.8125rem; white-space: nowrap; }

.badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 0.6875rem; font-weight: var(--fw-extrabold); letter-spacing: 0.04em;
  text-transform: uppercase; padding: 4px 9px; border-radius: var(--radius-pill);
  border: 1px solid transparent; white-space: nowrap;
}
.badge--video { color: var(--primary); border-color: rgba(207, 130, 98, 0.40); background: rgba(207, 130, 98, 0.10); }
.badge--pdf   { color: var(--plum);    border-color: rgba(156, 111, 134, 0.45); background: rgba(156, 111, 134, 0.12); }
.badge--audio { color: var(--gold);    border-color: rgba(200, 160, 106, 0.45); background: rgba(200, 160, 106, 0.12); }
/* Admin-only badges: bundle marks a course that delivers other courses, warn flags a child a
   bundle can no longer deliver (unpublished, deleted, or with no files of its own). */
.badge--bundle { background: rgba(156, 111, 134, 0.18); color: var(--plum); }
.badge--warn { background: rgba(217, 115, 107, 0.16); color: var(--danger); }
.badge--other { color: var(--muted);   border-color: var(--border); background: rgba(255, 255, 255, 0.04); }
.badge--free  { color: var(--primary); border-color: rgba(207, 130, 98, 0.40); }
.badge--new   { color: var(--ink); background: rgba(20, 14, 10, 0.72); }

/* ----------------------------------------------------------------------------
   11. BUY / PURCHASE CARD (detail aside)  + owned state
   -------------------------------------------------------------------------- */
.buycard {
  background: var(--surface); border: var(--hairline);
  border-radius: var(--radius-lg); padding: 26px; box-shadow: var(--shadow-panel);
}
.buycard__media {
  position: relative; height: 150px; border-radius: var(--radius-md);
  overflow: hidden; margin-bottom: 22px;
}
.buycard__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1; }
.buycard__media .course-card__placeholder { border-radius: inherit; }
.buycard__row {
  display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 4px;
}
.buycard__label {
  font-size: 0.8125rem; font-weight: var(--fw-bold); color: var(--faint);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.buycard__price { font-size: 1.875rem; font-weight: var(--fw-black); letter-spacing: -0.02em; }
.buycard__price .was { color: var(--faint); font-weight: var(--fw-regular); font-size: 1.1rem; text-decoration: line-through; margin-left: 10px; }
.buycard .btn { margin-top: 12px; }
.buycard__note { color: var(--faint); font-size: 0.8125rem; margin-top: 14px; text-align: center; }
.buycard__or {
  display: flex; align-items: center; gap: 14px; margin: 22px 0;
  color: var(--faint); font-size: 0.8125rem; font-weight: var(--fw-bold);
}
.buycard__or::before, .buycard__or::after {
  content: ""; flex: 1; height: 1px; background: var(--border);
}
.buycard__perks { margin-top: 18px; display: flex; flex-direction: column; gap: 10px; }
.buycard__perk { display: flex; align-items: center; gap: 10px; color: var(--muted); font-size: 0.875rem; }
.buycard__perk svg { color: var(--primary); flex: 0 0 auto; }
.buycard__preview { width: 100%; justify-content: center; padding: 16px 0 4px; }

/* Companion panel under the buy card. Quieter than the card on purpose — the card stays the focal
   point; these are a way onward, not a second call to action. */
.detail-tags { margin-top: 18px; padding: 18px 20px; border: var(--hairline); border-radius: var(--radius-lg); }
.detail-tags__title {
  font-size: 0.75rem; font-weight: var(--fw-extrabold); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint); margin-bottom: 12px;
}
.detail-tags .chip-list { margin-top: 0; max-width: none; gap: 8px; }

/* Owned / already-purchased state */
.owned-banner {
  display: flex; align-items: center; gap: 12px;
  background: rgba(207, 130, 98, 0.12); border: 1px solid rgba(207, 130, 98, 0.30);
  border-radius: var(--radius); padding: 14px 16px; margin-bottom: 18px;
  color: var(--ink); font-weight: var(--fw-bold); font-size: 0.9375rem;
}
.owned-banner svg { color: var(--primary); flex: 0 0 auto; }

/* ----------------------------------------------------------------------------
   13. CART / CHECKOUT
   -------------------------------------------------------------------------- */
.page-head { padding: 28px var(--gutter) 8px; max-width: var(--maxw); margin-inline: auto; }
.page-head h1 { font-size: var(--fs-2xl); }
.page-head p { color: var(--muted); margin-top: 6px; }

.cart-wrap { max-width: var(--maxw); margin-inline: auto; padding: 0 var(--gutter) 20px; }

.cart-table { width: 100%; border-collapse: collapse; }
.cart-table caption { text-align: left; }
.cart-table th {
  text-align: left; color: var(--faint); font-size: 0.75rem; font-weight: var(--fw-extrabold);
  text-transform: uppercase; letter-spacing: 0.08em; padding: 0 0 12px; border-bottom: var(--hairline);
}
.cart-table td { padding: 18px 0; border-bottom: var(--hairline); vertical-align: middle; }
.cart-table__item { display: flex; align-items: center; gap: 16px; }
.cart-table__thumb {
  width: 96px; height: 60px; border-radius: var(--radius-sm); overflow: hidden;
  flex: 0 0 auto; position: relative; background: var(--surface-2);
}
.cart-table__thumb img { width: 100%; height: 100%; object-fit: cover; }
.cart-table__title { font-weight: var(--fw-bold); font-size: 0.96875rem; }
.cart-table__title a { color: var(--ink); }
.cart-table__title a:hover { color: var(--primary-hover); }
.cart-table__sub { color: var(--faint); font-size: 0.8125rem; margin-top: 2px; }
.cart-table__price { text-align: right; font-weight: var(--fw-bold); font-size: var(--fs-md); white-space: nowrap; }
.cart-table__action { text-align: right; white-space: nowrap; }
.cart-actions { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 24px 0 56px; flex-wrap: wrap; }

/* Order summary (checkout aside + cart totals) */
.summary {
  background: var(--surface); border: var(--hairline);
  border-radius: var(--radius-lg); padding: 24px;
}
.summary h2 { font-size: 1.1875rem; margin-bottom: 16px; }
.summary__row { display: flex; align-items: baseline; justify-content: space-between; padding: 9px 0; color: var(--muted); font-size: 0.9375rem; }
.summary__row--total {
  border-top: var(--hairline); margin-top: 8px; padding-top: 16px;
  color: var(--ink); font-weight: var(--fw-extrabold); font-size: var(--fs-md);
}
.summary__row--total .amount { font-size: 1.375rem; font-weight: var(--fw-black); }
.summary .btn { margin-top: 18px; }
.summary__note { color: var(--faint); font-size: 0.8125rem; margin-top: 14px; text-align: center; }

.checkout-grid { display: grid; grid-template-columns: 1fr 380px; gap: 40px; padding: 20px var(--gutter) 60px; max-width: var(--maxw); margin-inline: auto; align-items: start; }
.checkout-grid .summary { position: sticky; top: calc(var(--header-h) + 20px); }

/* ----------------------------------------------------------------------------
   14. FORMS  (login / register / checkout / contact)
   -------------------------------------------------------------------------- */
.auth-shell { max-width: 460px; margin: 40px auto; padding: 0 var(--gutter-mobile); }
.auth-shell--wide { max-width: 720px; }
.auth-card { background: var(--surface); border: var(--hairline); border-radius: var(--radius-lg); padding: 36px; box-shadow: var(--shadow-panel); }
.auth-card h1 { font-size: var(--fs-xl); margin-bottom: 6px; }
.auth-card__sub { color: var(--muted); margin-bottom: 26px; }
.auth-card__foot { text-align: center; color: var(--muted); font-size: 0.9375rem; margin-top: 22px; }
.auth-card__foot a { font-weight: var(--fw-semibold); }

.form { display: flex; flex-direction: column; gap: 18px; }
.form--wide .field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

.field { display: flex; flex-direction: column; gap: 7px; }
.field > label { font-size: 0.875rem; font-weight: var(--fw-bold); color: var(--ink); }
.field .hint { font-size: 0.8125rem; color: var(--faint); }
.field input,
.field select,
.field textarea {
  width: 100%; font-family: inherit; font-size: var(--fs-base); color: var(--ink);
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 12px 14px; transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.field textarea { resize: vertical; min-height: 120px; }
/* Native select chrome is the one control that still looked like the host OS rather than the site.
   appearance:none removes it; the caret is an inline data-URI SVG so no asset request is added, and
   padding-right keeps long option text clear of it. currentColor is not available inside a
   background image, hence the literal --faint value. */
.field select {
  appearance: none; -webkit-appearance: none;
  padding-right: 40px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%238f8377' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
}
.field select:hover { border-color: var(--border-strong); }

/* The option LIST of a native <select> is drawn by the OS, so no amount of CSS on the element itself
   restyles it — that is why Category/Author still popped a macOS (or Windows) chooser while the
   closed control already matched the site. `appearance: base-select` hands the whole control,
   including its picker, to CSS, which is the only way to get a site-styled dropdown without
   shipping a JS combobox. Progressive enhancement: browsers without it keep the native picker and
   the appearance:none styling above, so nothing regresses.
   Everything below is inside @supports for that reason — base-select must not be declared where the
   ::picker rules cannot follow it, or the control would render unstyled. */
@supports (appearance: base-select) {
  .field select,
  .field select::picker(select) { appearance: base-select; }

  .field select {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    /* base-select drops the background-image caret with the native chrome, so draw one as content. */
    background-image: none; padding-right: 14px;
  }
  .field select::picker-icon {
    color: var(--faint); transition: transform 0.15s ease;
  }
  .field select:open::picker-icon { transform: rotate(180deg); }

  .field select::picker(select) {
    background: var(--surface); border: 1px solid var(--border-strong);
    border-radius: var(--radius); box-shadow: var(--shadow-panel);
    padding: 6px; margin-top: 6px;
    max-height: 320px; overflow-y: auto;
    /* The picker sits in the top layer, so it needs its own colours — it inherits nothing useful. */
    color: var(--ink); font-family: var(--font-sans); font-size: var(--fs-base);
  }
  .field select option {
    display: flex; align-items: center; gap: 10px;
    padding: 9px 12px; border-radius: var(--radius-sm);
    background: none; color: var(--muted); cursor: pointer;
  }
  .field select option:hover,
  .field select option:focus { background: var(--surface-2); color: var(--ink); outline: none; }
  .field select option:checked { color: var(--primary); font-weight: var(--fw-semibold); }
  /* The UA draws its own tick for the selected option; ours is the colour above, so drop it. */
  .field select option::checkmark { display: none; }
}
.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--border-strong); }
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--focus-ring);
}
.field input:disabled { opacity: 0.5; cursor: not-allowed; }

/* Error / validation styling — bundle hooks + ASP.NET-generated classes (§19). */
.field--error input,
.field--error select,
.field--error textarea { border-color: var(--danger); }
.field--error input:focus { box-shadow: 0 0 0 3px var(--danger-bg); }
.field__error { color: var(--danger); font-size: 0.8125rem; font-weight: var(--fw-semibold); }

.field--checkbox { flex-direction: row; align-items: flex-start; gap: 12px; }
/* Label rows put the input inside the <label>, so the label is the flex item and has to lay its own
   contents out; without this the 20px control and its text sit on the baseline unevenly. */
.field--checkbox label { display: inline-flex; align-items: center; gap: 10px; }
/* The type selector is deliberate. This control used to be class-only, and the single place that
   used the class put it on the <label> instead of the input — so the label was squeezed to 20px wide
   (which is why tag names wrapped under their tick) while every real checkbox in the app fell back
   to system chrome. Styling the element type means a new checkbox is correct by default and cannot
   silently regress; the class is kept for the explicit case. */
input[type="checkbox"],
.checkbox {
  appearance: none; -webkit-appearance: none; flex: 0 0 auto;
  /* padding:0 is load-bearing. `.field input` sets `padding: 12px 14px` at equal specificity, and
     with border-box that padding is a floor on the box: a 20px square came out 31x27. */
  width: 20px; height: 20px; padding: 0; margin-top: 1px;
  border: 1.5px solid var(--border-strong); border-radius: 6px;
  background: var(--bg); cursor: pointer; position: relative;
  transition: background 0.12s ease, border-color 0.12s ease;
}
input[type="checkbox"]:checked,
.checkbox:checked { background: var(--primary); border-color: var(--primary); }
input[type="checkbox"]:checked::after,
.checkbox:checked::after {
  content: ""; position: absolute; left: 6px; top: 2px;
  width: 5px; height: 10px; border: solid var(--primary-ink);
  border-width: 0 2.5px 2.5px 0; transform: rotate(45deg);
}
input[type="checkbox"]:focus-visible,
.checkbox:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.field--checkbox label { font-size: 0.9375rem; color: var(--muted); font-weight: var(--fw-medium); line-height: 1.4; }
.field--checkbox a { font-weight: var(--fw-semibold); }

.checkbox-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.checkbox-grid .field--checkbox {
  align-items: center; background: var(--bg); border: var(--hairline);
  border-radius: var(--radius); padding: 12px 14px; gap: 10px;
}

/* Tag entry: one input with native datalist suggestions, and the attached tags as pills below it.
   Replaces a two-column grid of every tag in the catalog as checkboxes. */
/* A fieldset draws a native border and notches its legend into it, which made the tag block look
   like a different kind of control from every other field on the form. Strip both and render the
   legend exactly like a .field label. */
.tag-field { gap: 10px; border: none; padding: 0; margin: 0; min-inline-size: 0; }
.tag-field > legend {
  padding: 0; float: left; width: 100%;
  font-size: 0.875rem; font-weight: var(--fw-bold); color: var(--ink); margin-bottom: 7px;
}
.tag-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.tag-pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 14px 7px 10px; border-radius: var(--radius-pill);
  border: var(--hairline); background: var(--surface-2);
  font-size: 0.84375rem; font-weight: var(--fw-semibold); color: var(--ink);
  cursor: pointer; user-select: none;
  transition: opacity 0.12s ease, border-color 0.12s ease;
}
.tag-pill input[type="checkbox"] { width: 16px; height: 16px; border-radius: 5px; margin: 0; }
.tag-pill input[type="checkbox"]:checked::after { left: 5px; top: 1px; width: 4px; height: 8px; border-width: 0 2px 2px 0; }
/* Unticked = staged for removal on save. Say so visually rather than leaving an identical pill. */
.tag-pill:has(input:not(:checked)) { opacity: 0.45; }
.tag-pill:has(input:not(:checked)) > span { text-decoration: line-through; }

/* Notices / validation summary / empty states */
.validation-summary {
  background: var(--danger-bg); border: 1px solid rgba(217, 115, 107, 0.4);
  border-radius: var(--radius); padding: 14px 16px; color: #f0b9b3;
}
.validation-summary strong { color: var(--danger); display: block; margin-bottom: 6px; }
.validation-summary ul { margin: 0; padding-left: 18px; font-size: 0.875rem; }

.notice {
  background: var(--surface); border: var(--hairline); border-left: 3px solid var(--primary);
  border-radius: var(--radius); padding: 14px 16px; color: var(--muted); font-size: 0.9375rem;
}
.notice--success { border-left-color: var(--primary); }
.notice--error { border-left-color: var(--danger); color: #f0b9b3; }

.muted { color: var(--muted); }
.faint { color: var(--faint); }

/* Accessible-but-hidden text (screen-reader-only labels). */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Spam honeypot: kept in the DOM (bots fill it) but off-screen and out of the tab order. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Skip-to-content link: off-screen until keyboard-focused, then pinned top-left. */
.skip-link {
  position: absolute; left: 8px; top: -48px; z-index: 100;
  background: var(--primary); color: var(--primary-ink);
  font-weight: var(--fw-bold); padding: 10px 16px; border-radius: var(--radius);
  transition: top 0.15s ease;
}
.skip-link:focus { top: 8px; color: var(--primary-ink); }
main:focus { outline: none; }

/* Owns a background, so it sizes its BOX to the content rectangle — the same idiom as .hero and
   .shelf. It used to be `margin: 8px var(--gutter)` with no clamp at all, which made it span the
   whole viewport minus one gutter: on a 1700px screen the "No courses found" panel was 1620px wide
   while the course grid it replaces was 1120px. Cart and My Courses share the class and had it too. */
.empty-state {
  text-align: center; padding: 64px 24px; color: var(--muted);
  border: 1px dashed var(--border-strong); border-radius: var(--radius-lg);
  background: var(--surface); margin: 8px auto;
  width: calc(100% - (2 * var(--gutter))); max-width: var(--content-w);
}
.empty-state h2 { font-size: var(--fs-lg); color: var(--ink); margin-bottom: 8px; }
.empty-state p { max-width: 400px; margin: 0 auto 20px; }

/* 404 — a way onward, not an apology. Sized to the content rectangle like every other
   surface, and the oversized numeral is decorative only (aria-hidden), so a screen reader
   reads the heading rather than "four zero four". */
.notfound {
  max-width: var(--maxw); margin-inline: auto;
  padding: 64px var(--gutter) 72px; text-align: center;
}
.notfound__code {
  font-size: clamp(4.5rem, 14vw, 9rem); font-weight: var(--fw-black);
  line-height: 0.9; letter-spacing: -0.04em; margin-bottom: 8px;
  /* Gradient numeral, clipped to the glyphs — the one place a flourish earns its keep. */
  background: linear-gradient(135deg, var(--primary), var(--plum));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.notfound h1 { font-size: var(--fs-2xl); margin-bottom: 12px; }
.notfound__lede {
  color: var(--muted); font-size: var(--fs-md); line-height: var(--lh-body);
  max-width: 46ch; margin: 0 auto 28px;
}
/* The search field is the primary action here, so it is centred and given room. */
.notfound .searchbar { margin: 0 auto 22px; max-width: 560px; text-align: left; }
.notfound__actions {
  display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 8px;
}
.notfound__suggest { margin-top: 56px; text-align: left; }
.notfound__suggest h2 {
  font-size: 0.75rem; font-weight: var(--fw-extrabold); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--faint);
  padding-inline: 0; margin-bottom: 16px; text-align: center;
}
/* The grid already clamps itself; inside .notfound the gutter is applied by the parent. */
.notfound__suggest .course-grid { padding-inline: 0; max-width: none; }

@media (max-width: 560px) {
  .notfound { padding: 40px var(--gutter) 48px; }
  .notfound__actions .btn { width: 100%; }
}

/* ----------------------------------------------------------------------------
   15. LIBRARY / MY COURSES  (expand to files with per-file download)
   -------------------------------------------------------------------------- */
.library-list { display: flex; flex-direction: column; gap: 14px; padding: 8px var(--gutter) 56px; max-width: var(--maxw); margin-inline: auto; }
.library-item { background: var(--surface); border: var(--hairline); border-radius: var(--radius-md); overflow: hidden; }
.library-item > summary {
  display: flex; align-items: center; gap: 18px; padding: 16px 20px;
  cursor: pointer; list-style: none; user-select: none;
}
.library-item > summary::-webkit-details-marker { display: none; }
.library-item__thumb { width: 120px; height: 72px; border-radius: var(--radius-sm); overflow: hidden; flex: 0 0 auto; position: relative; background: var(--surface-2); }
.library-item__thumb img { width: 100%; height: 100%; object-fit: cover; }
.library-item__info { flex: 1; min-width: 0; }
.library-item__title { font-weight: var(--fw-bold); font-size: var(--fs-md); }
.library-item__author { color: var(--faint); font-size: 0.875rem; margin-top: 2px; }
.library-item__meta { color: var(--faint); font-size: 0.8125rem; margin-top: 6px; }
.library-item__chevron { color: var(--faint); transition: transform 0.2s ease; flex: 0 0 auto; }
.library-item[open] .library-item__chevron { transform: rotate(180deg); }
.library-item__files { padding: 4px 20px 20px; border-top: var(--hairline); margin-top: 4px; }
.library-item__files .file-list { padding-top: 16px; }
.library-item__files .file-list__item { justify-content: space-between; }

/* ----------------------------------------------------------------------------
   16. SITE FOOTER
   -------------------------------------------------------------------------- */
.site-footer { background: var(--bg-2); border-top: var(--hairline); margin-top: 40px; }
.site-footer__inner {
  max-width: var(--maxw); margin-inline: auto; padding: 48px var(--gutter) 40px;
  display: grid; grid-template-columns: 1.4fr repeat(3, 1fr); gap: 32px;
}
.site-footer__col h4 { font-size: 0.8125rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--faint); margin-bottom: 14px; font-weight: var(--fw-extrabold); }
.site-footer__col ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.site-footer__col a { color: var(--muted); font-size: 0.9375rem; }
.site-footer__col a:hover { color: var(--ink); }
.site-footer__brandcol p { color: var(--faint); font-size: 0.875rem; line-height: 1.6; margin-top: 14px; max-width: 280px; }
.site-footer__legal {
  max-width: var(--maxw); margin-inline: auto; padding: 20px var(--gutter);
  border-top: var(--hairline); color: var(--faint); font-size: 0.8125rem;
  display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
}

/* ----------------------------------------------------------------------------
   18. RESPONSIVE BREAKPOINTS
   Values: 1024px (tablet-landscape) · 768px (tablet) · 560px (mobile)
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  .course-detail__grid { grid-template-columns: 1fr; }
  /* Single column: drop the placement so the grid falls back to source order — aside (buy card +
     tags) first, then the description. Leaving grid-column: 2 here would conjure a second column. */
  .course-detail__summary,
  .course-detail__aside { grid-column: auto; grid-row: auto; }
  .course-detail__aside { position: static; }
  .checkout-grid { grid-template-columns: 1fr; }
  .checkout-grid .summary { position: static; }
  .site-footer__inner { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 768px) {
  :root { --gutter: var(--gutter-mobile); }

  /* The mobile header used to be `.site-nav, .header-search { display: none }` with nothing put in
     their place: Browse, Cart and My Courses survived only in the page footer, and the cart count was
     invisible entirely. Now it is three columns — brand, icon nav, account — and the 1fr outer
     columns are what makes the centre group sit dead centre on any screen width while only the gaps
     either side change, rather than the nav drifting with the brand's text width. */
  .header-inner { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--space-3); }
  .brand { justify-self: start; }
  .header-actions { justify-self: end; }
  .header-search { display: none; }

  .site-nav { justify-self: center; gap: var(--space-6); }
  .site-nav__label { display: none; }
  .site-nav__icon { display: block; }
  .site-nav__link {
    display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; border-radius: var(--radius); color: var(--muted);
  }
  /* Only Browse and Cart are icons; My Courses and Admin are reached via the account icon. This has
     to come AFTER the .site-nav__link rule above — same specificity, so source order decides, and
     with it first the base rule won and both text links stayed in the mobile header. */
  .site-nav__link--wide { display: none; }
  .site-nav__link[aria-current] { color: var(--primary); background: rgba(207, 130, 98, 0.12); }
  .site-nav__iconwrap { position: relative; display: inline-flex; }
  .site-nav__count {
    display: block; position: absolute; top: -5px; right: -7px;
    min-width: 16px; height: 16px; padding: 0 4px; border-radius: var(--radius-pill);
    background: var(--primary); color: var(--primary-ink);
    font-size: 0.625rem; font-weight: var(--fw-extrabold); line-height: 16px; text-align: center;
  }

  /* Log out is a POST form and must not become a GET link, so on mobile it drops to the account page
     and the footer; the icon is the whole of the header's account UI. */
  /* On mobile the icon is the whole account UI, signed in or out — the Log in / Create account
     buttons are hidden here, so the guest state needs it back. */
  .header-actions--guest .account-icon { display: inline-flex; }
  .account-icon { width: 40px; height: 40px; color: var(--muted); }
  .account-icon svg { width: 22px; height: 22px; }
  /* Back to currentColor here: the gradient makes sense as the one accent among desktop text links,
     but in the mobile row it would be the single coloured icon next to a muted shop and cart. A CSS
     `stroke` beats the SVG's presentation attribute, so this override needs nothing in the markup. */
  .account-icon svg { stroke: currentColor; }
  .account-icon:hover { color: var(--ink); background: none; }
  .header-actions .inline-form,
  .header-actions .linkbutton,
  .header-actions .btn { display: none; }
  .hero { min-height: 380px; }
  .hero h1 { font-size: 2.25rem; }
  .course-detail__hero-body h1 { font-size: 2.5rem; }
  .valueprops { grid-template-columns: 1fr; }
  .form--wide .field-row { grid-template-columns: 1fr; }
  .checkbox-grid { grid-template-columns: 1fr; }
  .topic-list { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  .hero__body { padding: 32px 24px; }
  .hero h1 { font-size: 1.875rem; }

  /* The hero search used to wrap, dropping its button onto a second line and making the control two
     rows tall on every phone. Keeping one line needs three things: no wrap, a shrinkable input
     (min-width:0 — a flex item will not shrink below its intrinsic input width otherwise, which is
     what forced the wrap), and tighter padding so the button still has room. */
  .searchbar { flex-wrap: nowrap; padding: 9px 10px 9px 14px; gap: 8px; }
  .searchbar input { min-width: 0; font-size: var(--fs-base); }
  .searchbar .btn { padding: 9px 16px; flex: 0 0 auto; }
  .searchbar svg { width: 17px; height: 17px; }

  /* Smaller cards on a phone: the shelf shows more of the next card as a scroll cue, and the catalog
     fits two per row instead of one, which halves the scrolling to reach anything. 150px minimum is
     the largest that still fits two columns plus the 18px gap inside a 335px content rectangle. */
  .shelf .course-card { width: 190px; }
  .shelf .course-card__thumb { height: 122px; }
  .course-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 18px 14px; }
  .cart-table thead { display: none; }
  .cart-table td { display: block; border: none; padding: 6px 0; }
  .cart-table tr { display: block; border-bottom: var(--hairline); padding: 14px 0; }
  .cart-table__price, .cart-table__action { text-align: left; }
  /* The totals row is a th + td pair, which the `td { display: block }` rule above does not touch,
     so the label and the amount ended up butted together as "TOTAL$90.00". Lay that row out
     explicitly instead of relying on table spacing that no longer applies. */
  .cart-table tfoot tr { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
  .cart-table tfoot th, .cart-table tfoot td { display: block; padding: 6px 0; text-align: left; }
  /* The row carries a third, empty cell (the action column). In a flex row with space-between that
     empty cell becomes a third slot and drags the amount into the middle, so drop it. */
  .cart-table tfoot th:empty, .cart-table tfoot td:empty { display: none; }

  /* Download drops to icon-only: it frees ~74px per row for the filename, which is what pushed the
     button out of the block on long names in the first place. */
  .download-btn__label { display: none; }
  .download-btn__icon { display: block; }
  .download-btn { padding: 9px 11px; }
  .site-footer__inner { grid-template-columns: 1fr; }
  .library-item > summary { flex-wrap: wrap; }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0.001ms !important; animation-duration: 0.001ms !important; }
  .course-card:hover { transform: none; }
  /* The shelf edge fade is scroll-POSITION driven, not time driven — nothing moves on its own, so
     it is not the kind of animation this block is meant to suppress. It must keep duration:auto:
     collapsing a scroll-timeline animation to 0.001ms snaps it straight to its final keyframe, which
     would pin the fade to the left edge of every shelf regardless of where the row is scrolled. */
  .shelf { animation-duration: auto !important; }
}

/* ============================================================================
   19. ASP.NET INTEGRATION — map framework-generated classes onto the design.
   The tag helpers emit these class names; without rules the validation summary
   would render as an empty red box when valid, and field errors would be unstyled.
   ========================================================================== */
/* A valid (error-free) summary/field must be invisible — the framework leaves the
   element in the DOM with a *-valid class and (for the summary) an empty <ul>. */
.validation-summary-valid { display: none; }
.field-validation-valid { display: none; }

/* Field-level error text (asp-validation-for span). */
.field-validation-error,
span.field-validation-error {
  color: var(--danger); font-size: 0.8125rem; font-weight: var(--fw-semibold);
  display: block; margin-top: 2px;
}
/* Inputs the framework flags as invalid pick up the danger border. */
.input-validation-error {
  border-color: var(--danger) !important;
}
.input-validation-error:focus { box-shadow: 0 0 0 3px var(--danger-bg) !important; }

/* ============================================================================
   20. ADMIN AREA — built on the same dark tokens. Functional, dense, legible.
   ========================================================================== */
.admin-shell { max-width: var(--maxw); margin-inline: auto; padding: 0 var(--gutter); }
.admin-topbar {
  display: flex; align-items: center; gap: var(--space-4);
  padding: 16px 0; border-bottom: var(--hairline);
}
.admin-topbar .brand { font-size: 1.25rem; }
.admin-topbar__tag {
  font-size: 0.6875rem; font-weight: var(--fw-extrabold); letter-spacing: 0.10em;
  text-transform: uppercase; color: var(--primary-ink); background: var(--primary);
  padding: 3px 8px; border-radius: var(--radius-pill);
}
.admin-topbar__spacer { margin-left: auto; }
.admin-topbar a { color: var(--muted); font-weight: var(--fw-semibold); font-size: var(--fs-sm); }
.admin-topbar a:hover { color: var(--ink); }

.admin-nav {
  display: flex; flex-wrap: wrap; gap: var(--space-5);
  padding: 14px 0; margin-bottom: var(--space-6); border-bottom: var(--hairline);
}
.admin-nav a {
  color: var(--muted); font-weight: var(--fw-semibold); font-size: var(--fs-base);
  padding-bottom: 12px; margin-bottom: -13px; border-bottom: 2px solid transparent;
}
.admin-nav a:hover { color: var(--ink); }
/* The markup never set aria-current here at all, so this rule existed but could never match and the
   panel had no active-section marker. A colour shift alone is too quiet against --muted, so the
   active section also carries the terracotta rule that sits on the nav's own bottom hairline. */
.admin-nav a[aria-current] { color: var(--ink); border-bottom-color: var(--primary); }

.admin-main { padding-bottom: var(--space-16); }
.admin-main > h1 { font-size: var(--fs-xl); margin-bottom: var(--space-5); }
.admin-main .section__title { font-size: var(--fs-lg); margin: var(--space-8) 0 var(--space-4); }
.admin-main .form, .admin-main .form--wide { max-width: 720px; }
.admin-main .form .field, .admin-main .form--wide .field { gap: 6px; }
.admin-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-5); }
/* Same head layout for a section heading further down a page, without the h1 top spacing. */
.admin-head--section { margin-top: var(--space-8); }
.admin-head--section .section__title { margin: 0; }
.admin-head h1 { font-size: var(--fs-xl); }

.admin-stats { list-style: none; padding: 0; margin: var(--space-4) 0 var(--space-8); display: grid; gap: var(--space-4); grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.admin-stats li { background: var(--surface); border: var(--hairline); border-radius: var(--radius-lg); padding: 20px 22px; }
.admin-stats__num { display: block; font-size: 1.75rem; font-weight: var(--fw-black); letter-spacing: -0.02em; }
.admin-stats__label { color: var(--faint); font-size: var(--fs-sm); font-weight: var(--fw-semibold); margin-top: 2px; }

.admin-table { width: 100%; border-collapse: collapse; margin: var(--space-4) 0 var(--space-8); }
.admin-table th, .admin-table td { padding: 12px 12px; border-bottom: var(--hairline); text-align: left; vertical-align: top; }
.admin-table th {
  color: var(--faint); font-size: 0.75rem; font-weight: var(--fw-extrabold);
  text-transform: uppercase; letter-spacing: 0.06em;
}
.admin-table tbody tr:hover, .admin-table tr:hover td { background: rgba(255,255,255,0.02); }
/* A td must keep display:table-cell. Setting `display:flex` on it — which this rule used to do —
   replaces the cell box, so the browser wraps it in an anonymous cell and the flex box sizes to its
   own content instead of stretching to the row: measured 48px in a 165px row, with vertical-align
   silently inert. The visible tell is the row divider, since border-bottom then gets painted 48px
   down rather than at the row's baseline, putting a step in every separator under the actions column.
   The children are laid out inline instead, which needs no wrapper element and so fixes all ten
   admin listings at once. */
.admin-table__actions { white-space: nowrap; }
.admin-table__actions > * { display: inline-flex; align-items: center; vertical-align: middle; }
.admin-table__actions > * + * { margin-left: var(--space-4); }
.admin-row--deleted td { opacity: .5; }
.admin-nowrap { white-space: nowrap; }

.admin-filter { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: 0 0 var(--space-4); align-items: center; }
.admin-filter a { color: var(--muted); text-transform: capitalize; font-weight: var(--fw-semibold); font-size: var(--fs-sm); }
.admin-filter a.is-active { color: var(--ink); }
.admin-search { display: flex; gap: var(--space-2); max-width: 420px; margin: 0 0 var(--space-4); }
.admin-search input[type="search"] {
  flex: 1; font-family: inherit; font-size: var(--fs-base); color: var(--ink);
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px;
}
.admin-search input[type="search"]:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--focus-ring); }

.admin-detail { display: grid; grid-template-columns: max-content 1fr; gap: 8px 24px; margin: var(--space-4) 0 var(--space-8); }
.admin-detail dt { color: var(--faint); font-weight: var(--fw-semibold); }
.admin-detail dd { margin: 0; color: var(--ink); }

.admin-card { background: var(--surface); border: var(--hairline); border-radius: var(--radius-lg); padding: 24px; margin-bottom: var(--space-5); }

/* Add-a-thing panel for the taxonomy listings. Uses the native popover API — the trigger is a plain
   button with popovertarget, and the browser gives us the top layer, Esc-to-close and light dismiss
   for free. That is the only way to get a real modal here without shipping JavaScript.
   ::backdrop and [popover]'s UA defaults both need overriding: the UA stylesheet sets a white
   background, a black border and margin:auto, none of which belong in this palette. */
.admin-popover {
  width: min(520px, calc(100vw - 2 * var(--gutter)));
  max-height: min(80vh, 720px); overflow-y: auto;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--border-strong); border-radius: var(--radius-lg);
  padding: 24px; box-shadow: var(--shadow-panel);
  margin: auto;   /* centres it in the viewport-sized top-layer box */
}
.admin-popover::backdrop { background: rgba(10, 7, 5, 0.62); backdrop-filter: blur(2px); }
.admin-popover__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4); margin-bottom: var(--space-5);
}
.admin-popover__head h2 { font-size: var(--fs-lg); }
.admin-popover__close {
  appearance: none; background: none; border: none; cursor: pointer;
  font-family: inherit; font-size: 1.5rem; line-height: 1;
  color: var(--faint); padding: 0 4px;
}
.admin-popover__close:hover { color: var(--ink); }
/* The three taxonomy forms are narrow, so their submit button should not stretch the panel width. */
.admin-popover .form > .btn { align-self: flex-start; }
.admin-form { max-width: 720px; }
.admin-form .field-row { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.admin-form .field-row .field { flex: 1 1 12rem; }
.admin-form .field { margin: 0 0 var(--space-4); }

.message-body { white-space: pre-line; border-left: 3px solid var(--border); margin: 0 0 var(--space-6); padding: 4px 0 4px 16px; color: var(--muted); }
.admin-analytics-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-6); }
@media (max-width: 768px) { .admin-analytics-grid { grid-template-columns: 1fr; } }

.static-page { max-width: 68ch; margin-inline: auto; padding: var(--space-8) 0; }
.static-page h1 { font-size: var(--fs-2xl); margin-bottom: var(--space-5); }
.static-page h2 { font-size: var(--fs-lg); margin: var(--space-6) 0 var(--space-3); }
.static-page p { color: var(--muted); line-height: var(--lh-body); margin-bottom: var(--space-4); }
.static-page ul { color: var(--muted); line-height: var(--lh-body); margin: 0 0 var(--space-4); padding-left: 1.3rem; }
.static-page li { margin-bottom: var(--space-2); }
.static-page a { font-weight: var(--fw-semibold); }
.static-page strong { color: var(--ink); }

/* Status / message badges (admin lists) reuse the pill badge shape. */
.badge--status-paid { color: #7fd0a0; border-color: rgba(127,208,160,.4); background: rgba(127,208,160,.12); }
.badge--status-pending { color: var(--gold); border-color: rgba(200,160,106,.4); background: rgba(200,160,106,.12); }
.badge--status-failed { color: var(--danger); border-color: rgba(217,115,107,.4); background: var(--danger-bg); }
.badge--status-refunded { color: var(--faint); border-color: var(--border); background: rgba(255,255,255,.04); }
.badge--msg-new { color: var(--primary); border-color: rgba(207,130,98,.4); background: rgba(207,130,98,.10); }
.badge--msg-read { color: var(--faint); border-color: var(--border); background: rgba(255,255,255,.04); }
.badge--msg-replied { color: #7fd0a0; border-color: rgba(127,208,160,.4); background: rgba(127,208,160,.12); }
.badge--msg-archived { color: var(--faint); border-color: var(--border); background: rgba(255,255,255,.02); }

/* --- Button variants the admin markup already uses (aliases + destructive) --- */
/* Placed after .btn so equal-specificity overrides of background/color win. */
.btn--secondary {
  background: rgba(255, 255, 255, 0.05); color: var(--ink); border-color: var(--border);
}
.btn--secondary:hover { background: rgba(255, 255, 255, 0.09); filter: none; color: var(--ink); }
.btn--danger { background: var(--danger); color: #1b0f0e; }
.btn--danger:hover { filter: brightness(1.06); color: #1b0f0e; }
/* Text-style destructive button that still carries the .btn class in markup. */
.btn--link-danger {
  background: none; border: none; padding: 0; border-radius: 0;
  color: var(--danger); font-weight: var(--fw-bold); font-size: 0.875rem;
}
.btn--link-danger:hover { filter: none; color: #e88a82; }

/* --- Admin misc layout hooks --- */
.admin-notice {
  background: var(--surface); border: var(--hairline); border-left: 3px solid var(--primary);
  border-radius: var(--radius); padding: 12px 16px; color: var(--muted);
  font-size: 0.9375rem; margin: 0 0 var(--space-5);
}
.admin-actions, .admin-danger-form { display: flex; flex-wrap: wrap; gap: var(--space-3); align-items: center; margin: 0 0 var(--space-5); }
.admin-inline-form { display: inline; margin: 0; }
/* The admin listings now use the storefront .pagination component (the old .admin-pager is gone).
   It only needs its page-level insets neutralised: the admin shell already applies the gutter, and
   the shell is the width clamp, so re-applying both here would inset it against its own table. */
.admin-main .pagination {
  padding: var(--space-2) 0 var(--space-8);
  max-width: none; margin-inline: 0;
}

/* Generic field-row (non-wide admin forms): responsive two-up field grouping. */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
@media (max-width: 768px) { .field-row { grid-template-columns: 1fr; } }
