*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

  :root {
    --gold: #C9A84C;
    --gold-light: #E8C97A;
    --cream: #F5EDD8;
    --ink: #0E0C0A;
    --ink-mid: #1C1A16;
    --ink-soft: rgba(245,237,216,0.55);
    --ink-muted: rgba(245,237,216,0.35);
    --error: #C4695A;
    /* Classic-mode emerald accent (chat 2.7) — migrated here from
       index.php's inline <style> block (see CURRENT.md Suggested Next
       Steps #9) so any page reusing .mode-btn-classic renders it
       identically, not just the homepage. Scoped to .mode-btn-classic
       only, below — does not touch any existing .mode-btn rule. */
    --emerald: #2F5D4F;
    --emerald-light: #6FA98F;
    /* Print-mode ink-blue accent (chat 2.28) — third homepage mode card,
       .mode-btn-print below. Chosen from copper/oxblood/ink blue/
       parchment side-by-side mockups: distinct from gold and emerald,
       and a nod to pen and paper. */
    --ink-blue-light: #8FA9CC;
  }

  html, body { height: 100%; font-family: 'DM Sans', sans-serif; background: var(--ink); color: var(--cream); overflow-x: hidden; }

  .hero {
    position: relative;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    overflow: hidden;
  }

  .hero-bg {
    position: absolute;
    inset: 0;
    background-image: url('/images/library.jpg');
    background-size: cover;
    background-position: center 30%;
    background-attachment: fixed;
    filter: brightness(0.55) saturate(1.1);
    z-index: 0;
    pointer-events: none;
  }

  .hero-bg::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, rgba(14,12,10,0.3) 0%, rgba(14,12,10,0.0) 40%, rgba(14,12,10,0.7) 100%);
    pointer-events: none;
  }

  nav:not(.site-nav-minimal) {
    position: absolute;
    top: 0; left: 0; right: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1.25rem 1.5rem;
  }

  .nav-brand {
    display: inline-flex;
    align-items: center;
    gap: 0;
    text-decoration: none;
    z-index: 21;
    position: relative;
    line-height: 1;
  }

  .nav-brand-text {
    font-family: 'Playfair Display', serif;
    font-size: 1.15rem;
    letter-spacing: 0.06em;
    color: var(--cream);
  }

  .nav-brand svg { display: block; }

  .nav-hamburger {
    background: none;
    border: none;
    cursor: pointer;
    z-index: 21;
    position: relative;
    padding: 0.25rem;
    display: flex;
    flex-direction: column;
    gap: 5px;
    align-items: flex-end;
  }

  .nav-hamburger span {
    display: block;
    height: 1.5px;
    background: var(--cream);
    border-radius: 2px;
    transition: all 0.3s ease;
  }

  .nav-hamburger span:nth-child(1) { width: 24px; }
  .nav-hamburger span:nth-child(2) { width: 18px; }
  .nav-hamburger span:nth-child(3) { width: 24px; }

  .nav-hamburger.open span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); width: 24px; }
  .nav-hamburger.open span:nth-child(2) { opacity: 0; width: 0; }
  .nav-hamburger.open span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); width: 24px; }

  .nav-overlay {
    position: fixed;
    inset: 0;
    z-index: 15;
    background: rgba(14,12,10,0.97);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.35s ease;
  }

  .nav-overlay.open { opacity: 1; pointer-events: all; }

  .nav-overlay a {
    font-family: 'Playfair Display', serif;
    font-size: clamp(1.6rem, 6vw, 2.2rem);
    font-weight: 400;
    color: var(--ink-soft);
    text-decoration: none;
    letter-spacing: 0.04em;
    padding: 0.6rem 0;
    transition: color 0.2s;
    opacity: 0;
    transform: translateY(12px);
    transition: color 0.2s, opacity 0.35s ease, transform 0.35s ease;
  }

  .nav-overlay.open a { opacity: 1; transform: translateY(0); }
  .nav-overlay.open a:nth-child(1) { transition-delay: 0.05s; }
  .nav-overlay.open a:nth-child(2) { transition-delay: 0.1s; }
  .nav-overlay.open a:nth-child(3) { transition-delay: 0.15s; }
  .nav-overlay.open a:nth-child(4) { transition-delay: 0.2s; }
  .nav-overlay.open a:nth-child(5) { transition-delay: 0.25s; }

  .nav-overlay a:hover { color: var(--cream); }

  .nav-overlay .nav-divider {
    width: 1px;
    height: 2rem;
    background: rgba(245,237,216,0.15);
    margin: 0.5rem 0;
    opacity: 0;
    transition: opacity 0.35s ease 0.2s;
  }

  .nav-overlay.open .nav-divider { opacity: 1; }

  .nav-overlay .nav-signin-overlay {
    font-family: 'DM Sans', sans-serif;
    font-size: 0.78rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--gold);
    border: 1px solid rgba(201,168,76,0.4);
    padding: 0.7rem 2rem;
    border-radius: 2px;
    margin-top: 0.5rem;
  }

  .nav-desktop { display: none; }

  @media (min-width: 760px) {
    nav:not(.site-nav-minimal) { padding: 1.5rem 3rem; }

    body:has(.nav-desktop) .nav-hamburger { display: none; }
    body:has(.nav-desktop) .nav-overlay { display: none; }

    .nav-desktop {
      display: flex;
      align-items: center;
      gap: 2rem;
      list-style: none;
    }

    .nav-desktop a {
      font-size: 0.78rem;
      letter-spacing: 0.12em;
      text-transform: uppercase;
      color: var(--ink-soft);
      text-decoration: none;
      transition: color 0.2s;
    }

    .nav-desktop a:hover { color: var(--cream); }

    .nav-desktop .nav-signin {
      color: var(--gold);
      border: 1px solid rgba(201,168,76,0.4);
      padding: 0.45rem 1.1rem;
      border-radius: 2px;
      transition: all 0.2s;
    }

    .nav-desktop .nav-signin:hover { background: rgba(201,168,76,0.1); border-color: var(--gold); }

    /* Second context for .nav-desktop (chat 2.15 continued) — dropped
       directly into a centered page header (dashboard.php/builder.php/
       print-builder.php/category.php's picker) rather than living
       inside index.php's own <nav>, which supplies the space-between
       positioning that pushes it right in that original context. Here
       there's no such flex parent — .nav-desktop.header-nav-desktop
       switches to inline-flex so its own width shrinks to its content,
       letting the header's existing text-align:center actually center
       it, and adds real spacing above/below since these headers have
       no <nav> padding to borrow from. Compound selector (both classes
       on the same element) rather than relying on source order to win
       over the base .nav-desktop rule above. */
    .nav-desktop.header-nav-desktop {
      display: flex;
      justify-content: center;
      margin: 0 0 1.4rem;
    }
  }

  .hero-content {
    position: relative;
    z-index: 5;
    text-align: center;
    padding: 6rem 1.5rem 2rem;
    max-width: 780px;
    width: 100%;
  }

  @media (min-width: 760px) {
    .hero-content { padding: 5rem 1.5rem 2rem; }
  }

  .eyebrow {
    font-size: 0.72rem;
    letter-spacing: 0.25em;
    text-transform: uppercase;
    color: var(--gold);
    margin-bottom: 1.5rem;
    opacity: 0.9;
    display: none;
  }

  @media (min-width: 760px) { .eyebrow { display: block; } }

  h1 {
    font-family: 'Playfair Display', serif;
    font-size: clamp(2.8rem, 7vw, 5rem);
    font-weight: 400;
    line-height: 1.1;
    color: var(--cream);
    margin-bottom: 1.25rem;
    letter-spacing: -0.01em;
  }

  h1 em {
    font-style: italic;
    color: var(--gold-light);
  }

  .hero-sub {
    font-size: 1rem;
    font-weight: 300;
    color: var(--ink-soft);
    line-height: 1.7;
    max-width: 480px;
    margin: 0 auto 3rem;
    letter-spacing: 0.02em;
  }

  .mode-buttons {
    display: flex;
    gap: 1rem;
    justify-content: center;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
  }

  .mode-btn {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    width: 220px;
    padding: 1.5rem 1.75rem;
    border: 1px solid rgba(245,237,216,0.2);
    background: rgba(10,8,6,0.78);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border-radius: 4px;
    cursor: pointer;
    text-decoration: none;
    text-align: left;
    transition: all 0.25s;
    overflow: hidden;
  }

  .mode-btn::before {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(201,168,76,0.0);
    transition: background 0.25s;
  }

  .mode-btn:hover { border-color: rgba(201,168,76,0.5); transform: translateY(-2px); }
  .mode-btn:hover::before { background: rgba(201,168,76,0.06); }

  .mode-btn.featured {
    border-color: rgba(245,237,216,0.2);
    background: rgba(10,8,6,0.78);
  }

  .mode-btn.featured:hover {
    border-color: rgba(201,168,76,0.5);
    transform: translateY(-2px);
  }

  .mode-btn.featured:hover::before {
    background: rgba(201,168,76,0.06);
  }

  .mode-tag {
    font-size: 0.65rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--gold);
    margin-bottom: 0.6rem;
  }

  .mode-title {
    font-family: 'Playfair Display', serif;
    font-size: 1.4rem;
    font-weight: 400;
    color: var(--cream);
    margin-bottom: 0.5rem;
    line-height: 1.2;
  }

  .mode-desc {
    font-size: 0.78rem;
    font-weight: 300;
    color: var(--ink-muted);
    line-height: 1.5;
  }

  .mode-arrow {
    position: absolute;
    bottom: 1.25rem;
    right: 1.5rem;
    font-size: 1rem;
    color: var(--gold);
    opacity: 0.6;
    transition: opacity 0.2s, transform 0.2s;
  }

  .mode-btn:hover .mode-arrow { opacity: 1; transform: translateX(3px); }

  /* Classic-mode emerald accent — migrated verbatim from index.php's
     inline <style> block (chat 2.7 original), see the --emerald token
     comment above. */
  .mode-btn-classic { border-color: rgba(111,169,143,0.35); }
  .mode-btn-classic:hover { border-color: var(--emerald-light); }
  .mode-btn-classic .mode-tag { color: var(--emerald-light); }
  .mode-btn-classic .mode-arrow { color: var(--emerald-light); }

  /* Print-mode ink-blue accent (chat 2.28) — same pattern as
     .mode-btn-classic directly above. */
  .mode-btn-print { border-color: rgba(143,169,204,0.35); }
  .mode-btn-print:hover { border-color: var(--ink-blue-light); }
  .mode-btn-print .mode-tag { color: var(--ink-blue-light); }
  .mode-btn-print .mode-arrow { color: var(--ink-blue-light); }

  .guest-note {
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    color: var(--ink-muted);
    text-transform: uppercase;
  }

  .guest-note a { color: var(--ink-soft); text-decoration: underline; text-underline-offset: 3px; }

  .scroll-hint {
    position: absolute;
    bottom: 2rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 5;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
    opacity: 0.35;
  }

  .scroll-hint span { font-size: 0.65rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--cream); }

  .scroll-line {
    width: 1px;
    height: 40px;
    background: linear-gradient(to bottom, var(--cream), transparent);
    animation: scrollpulse 2s ease-in-out infinite;
  }

  @keyframes scrollpulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.8; } }

  /* Mobile: put the Explore hint back in normal flow (chat 2.28).
     Pinned absolutely 2rem from the bottom of the 100vh hero, it had
     no reserved space — on phones the hero content fills the whole
     section, so "Explore" and its line landed on top of the Classic
     card and the "Create a free account" note. In flow it simply sits
     below the guest note. Desktop keeps the pinned version. */
  @media (max-width: 759px) {
    .scroll-hint {
      position: static;
      transform: none;
      margin: 0.5rem 0 2rem;
    }
  }

  .categories-section {
    background: var(--ink-mid);
    padding: 3.5rem 1.25rem;
    border-top: 1px solid rgba(245,237,216,0.06);
  }

  @media (min-width: 760px) { .categories-section { padding: 5rem 3rem; } }

  .section-label {
    font-size: 0.7rem;
    letter-spacing: 0.25em;
    text-transform: uppercase;
    color: var(--gold);
    text-align: center;
    margin-bottom: 0.75rem;
  }

  .section-title {
    font-family: 'Playfair Display', serif;
    font-size: clamp(1.5rem, 5vw, 2.6rem);
    font-weight: 400;
    text-align: center;
    color: var(--cream);
    margin-bottom: 2rem;
    line-height: 1.2;
  }

  @media (min-width: 760px) { .section-title { margin-bottom: 3rem; } }

  .categories-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1px;
    max-width: 900px;
    margin: 0 auto;
    background: rgba(245,237,216,0.07);
    border: 1px solid rgba(245,237,216,0.07);
    border-radius: 4px;
    overflow: hidden;
  }

  @media (min-width: 480px) { .categories-grid { grid-template-columns: repeat(3, 1fr); } }
  @media (min-width: 760px) { .categories-grid { grid-template-columns: repeat(5, 1fr); } }

  .cat-card {
    padding: 1.25rem 1rem;
    background: var(--ink-mid);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.5rem;
    cursor: pointer;
    transition: background 0.2s;
    text-decoration: none;
  }

  @media (min-width: 760px) { .cat-card { padding: 1.75rem 1.25rem; } }

  .cat-card:hover { background: rgba(201,168,76,0.08); }

  .cat-icon {
    font-size: 1.1rem;
    line-height: 1;
    margin-bottom: 0.2rem;
  }

  .cat-name { font-size: 0.83rem; font-weight: 500; color: var(--cream); letter-spacing: 0.02em; line-height: 1.3; }

  .cat-count { font-size: 0.68rem; color: var(--ink-muted); font-weight: 300; }

  .daily-section {
    background: var(--ink);
    padding: 5rem 3rem;
    border-top: 1px solid rgba(245,237,216,0.06);
  }

  .daily-inner { max-width: 600px; margin: 0 auto; text-align: center; }

  .daily-badge {
    display: inline-block;
    font-size: 0.65rem;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--gold);
    border: 1px solid rgba(201,168,76,0.35);
    padding: 0.35rem 1rem;
    border-radius: 2px;
    margin-bottom: 1.5rem;
  }

  .daily-title {
    font-family: 'Playfair Display', serif;
    font-size: clamp(1.6rem, 3.5vw, 2.2rem);
    font-weight: 400;
    color: var(--cream);
    margin-bottom: 1rem;
    line-height: 1.25;
  }

  .daily-desc {
    font-size: 0.9rem;
    font-weight: 300;
    color: var(--ink-soft);
    line-height: 1.7;
    margin-bottom: 2rem;
  }

  .streak-preview {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin-bottom: 2rem;
  }

  .streak-day {
    width: 32px;
    height: 32px;
    border-radius: 2px;
    border: 1px solid rgba(201,168,76,0.2);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.6rem;
    letter-spacing: 0.05em;
    color: var(--ink-muted);
    text-transform: uppercase;
  }

  .streak-day.done { background: rgba(201,168,76,0.15); border-color: rgba(201,168,76,0.5); color: var(--gold); }
  .streak-day.today { background: var(--gold); border-color: var(--gold); color: var(--ink); font-weight: 500; }

  .btn-primary {
    display: inline-block;
    font-size: 0.8rem;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--ink);
    background: var(--gold);
    padding: 0.9rem 2.5rem;
    border-radius: 2px;
    text-decoration: none;
    transition: background 0.2s;
    font-weight: 500;
  }

  .btn-primary:hover { background: var(--gold-light); }

  .account-section {
    background: var(--ink-mid);
    padding: 5rem 3rem;
    border-top: 1px solid rgba(245,237,216,0.06);
  }

  .account-inner {
    max-width: 860px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4rem;
    align-items: center;
  }

  .account-visual {
    position: relative;
    height: 280px;
    border: 1px solid rgba(245,237,216,0.08);
    border-radius: 4px;
    overflow: hidden;
    background: rgba(245,237,216,0.02);
  }

  .av-photo {
    position: absolute;
    inset: 0;
    background-image: url('/images/library.jpg');
    background-size: cover;
    background-position: center 30%;
    filter: brightness(0.55) saturate(1.1);
  }

  .av-mark {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
  }

  .av-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    padding: 1.5rem;
    background: linear-gradient(to top, rgba(14,12,10,0.8) 0%, transparent 60%);
  }

  .av-name { font-family: 'Playfair Display', serif; font-size: 1.1rem; color: var(--cream); margin-bottom: 0.3rem; }
  .av-streak { font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--gold); }

  .av-puzzle {
    position: absolute;
    top: 1rem;
    right: 1rem;
    background: rgba(14,12,10,0.7);
    border: 1px solid rgba(201,168,76,0.3);
    border-radius: 2px;
    padding: 0.5rem 0.75rem;
    font-size: 0.7rem;
    color: var(--cream);
    letter-spacing: 0.05em;
  }

  .av-puzzle strong { color: var(--gold); display: block; font-size: 1rem; font-weight: 500; }

  .account-text .section-label { text-align: left; margin-bottom: 0.75rem; }

  .account-text h2 {
    font-family: 'Playfair Display', serif;
    font-size: clamp(1.5rem, 3vw, 2rem);
    font-weight: 400;
    color: var(--cream);
    line-height: 1.25;
    margin-bottom: 1rem;
  }

  .account-text p {
    font-size: 0.88rem;
    font-weight: 300;
    color: var(--ink-soft);
    line-height: 1.75;
    margin-bottom: 1.5rem;
  }

  .feature-list { list-style: none; margin-bottom: 2rem; }

  .feature-list li {
    font-size: 0.82rem;
    color: var(--ink-soft);
    font-weight: 300;
    padding: 0.4rem 0;
    padding-left: 1.25rem;
    position: relative;
    letter-spacing: 0.01em;
  }

  .feature-list li::before { content: '—'; position: absolute; left: 0; color: var(--gold); font-size: 0.7rem; }

  .btn-ghost-wrap { text-align: center; }
  @media (min-width: 760px) { .btn-ghost-wrap { text-align: left; } }

  .btn-ghost {
    display: inline-block;
    font-size: 0.78rem;
    letter-spacing: 0.15em;
    text-transform: uppercase;
    color: var(--gold);
    border: 1px solid rgba(201,168,76,0.4);
    padding: 0.8rem 2rem;
    border-radius: 2px;
    text-decoration: none;
    transition: all 0.2s;
  }

  .btn-ghost:hover { background: rgba(201,168,76,0.08); border-color: var(--gold); }

  footer {
    background: var(--ink);
    border-top: 1px solid rgba(245,237,216,0.06);
    padding: 2.5rem 3rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .footer-brand { font-family: 'Playfair Display', serif; font-size: 0.9rem; color: var(--ink-muted); letter-spacing: 0.05em; }
  .footer-brand span { color: var(--gold); }

  .footer-links { display: flex; gap: 1.5rem; list-style: none; }

  .footer-links a {
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-muted);
    text-decoration: none;
  }

  @media (max-width: 680px) {
    nav { padding: 1.25rem 1.5rem; }
    .nav-links { display: none; }
    .mode-buttons { flex-direction: column; align-items: center; }
    .mode-btn { width: 100%; max-width: 340px; }
    .categories-section, .daily-section, .account-section { padding: 3.5rem 1.25rem; }
    .account-inner { grid-template-columns: 1fr; gap: 2rem; }
    .account-visual { height: 220px; }
    footer { flex-direction: column; gap: 1rem; text-align: center; padding: 2rem 1.25rem; }
    .footer-links { justify-content: center; }
    .section-label { margin-bottom: 0.5rem; }
  }

  .fade-in { opacity: 0; transform: translateY(16px); animation: fadein 0.7s ease forwards; }
  .fade-in:nth-child(1) { animation-delay: 0.1s; }
  .fade-in:nth-child(2) { animation-delay: 0.25s; }
  .fade-in:nth-child(3) { animation-delay: 0.4s; }
  .fade-in:nth-child(4) { animation-delay: 0.55s; }
  .fade-in:nth-child(5) { animation-delay: 0.65s; }

  @keyframes fadein { to { opacity: 1; transform: translateY(0); } }

  /* ── DAILY STRIP ── */
  .daily-strip {
    display: inline-flex;
    align-items: center;
    gap: 1rem;
    background: rgba(201,168,76,0.15);
    border: 1px solid rgba(201,168,76,0.5);
    border-radius: 50px;
    padding: 0.85rem 1.75rem;
    text-decoration: none;
    margin-bottom: 2rem;
    transition: background 0.2s, border-color 0.2s;
    white-space: nowrap;
    width: auto;
    max-width: fit-content;
  }

  .daily-strip:hover {
    background: rgba(201,168,76,0.25);
    border-color: rgba(201,168,76,0.8);
  }

  .daily-strip-text {
    font-size: 0.88rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--cream);
    font-weight: 400;
    white-space: nowrap;
  }

  .daily-strip-text strong {
    color: var(--gold-light);
    font-weight: 600;
    font-size: 0.95rem;
  }

  .daily-strip-arrow {
    font-size: 0.9rem;
    color: var(--gold);
    opacity: 0.9;
    flex-shrink: 0;
  }

  /* ── DEMO SECTION — mobile first ── */
  .demo-section {
    background: var(--ink);
    padding: 3.5rem 1.25rem 4rem;
    border-top: 1px solid rgba(245,237,216,0.06);
  }

  .demo-inner {
    max-width: 960px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 2rem;
  }

  .demo-header { text-align: center; }
  .demo-header .section-label { margin-bottom: 0.6rem; }

  .demo-header h2 {
    font-family: 'Playfair Display', serif;
    font-size: clamp(1.6rem, 5vw, 2.4rem);
    font-weight: 400;
    color: var(--cream);
    line-height: 1.2;
    margin-bottom: 0.75rem;
  }

  .demo-header p {
    font-size: 0.88rem;
    font-weight: 300;
    color: var(--ink-soft);
    line-height: 1.8;
    max-width: 480px;
    margin: 0 auto;
  }

  /* Desktop: side by side — grid left, clues + CTA right */
  @media (min-width: 760px) {
    .demo-section { padding: 5rem 3rem 6rem; }

    .demo-inner {
      display: grid;
      grid-template-columns: 1fr 1fr;
      grid-template-rows: auto auto;
      gap: 0 4rem;
      align-items: start;
    }

    .demo-header {
      grid-column: 1 / -1;
      text-align: left;
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 0 4rem;
      margin-bottom: 1.5rem;
    }

    .demo-header .section-label { grid-column: 1; }
    .demo-header h2 { grid-column: 1; text-align: center; }
    .demo-header p { grid-column: 2; margin: 0; align-self: end; padding-bottom: 0.25rem; }
  }
  /* ============================================================
     Auth pages (register.php, login.php, forgot-password.php,
     reset-password.php) — chat 2.9. New block, appended; nothing
     above this point was modified beyond the --error token added
     to :root. No form/input styling existed anywhere on the site
     before this — these are new base classes, not overrides.
     ============================================================ */

  .auth-page {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 3rem 1.25rem;
    background: var(--ink);
  }

  .auth-card {
    width: 100%;
    max-width: 420px;
    background: var(--ink-mid);
    border: 1px solid rgba(245,237,216,0.08);
    border-radius: 6px;
    padding: 2.5rem 2rem;
  }

  @media (min-width: 480px) { .auth-card { padding: 3rem 2.5rem; } }

  .auth-logo-wrap {
    display: flex;
    justify-content: center;
    margin-bottom: 1.75rem;
    text-decoration: none;
  }

  .auth-title {
    font-family: 'Playfair Display', serif;
    font-size: 1.6rem;
    font-weight: 400;
    color: var(--cream);
    text-align: center;
    margin-bottom: 0.5rem;
    line-height: 1.25;
  }

  .auth-sub {
    font-size: 0.85rem;
    font-weight: 300;
    color: var(--ink-soft);
    text-align: center;
    margin-bottom: 2rem;
    line-height: 1.65;
  }

  .field-group { margin-bottom: 1.25rem; }

  .field-label {
    display: block;
    font-size: 0.7rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: 0.5rem;
  }

  .field-input {
    display: block;
    width: 100%;
    background: rgba(245,237,216,0.04);
    border: 1px solid rgba(245,237,216,0.14);
    border-radius: 3px;
    padding: 0.8rem 0.95rem;
    font-family: 'DM Sans', sans-serif;
    font-size: 0.9rem;
    color: var(--cream);
    transition: border-color 0.2s, background 0.2s;
  }

  .field-input::placeholder { color: var(--ink-muted); }

  .field-input:focus {
    outline: none;
    border-color: var(--gold);
    background: rgba(245,237,216,0.06);
  }

  .field-hint {
    font-size: 0.72rem;
    font-weight: 300;
    color: var(--ink-muted);
    margin-top: 0.5rem;
    line-height: 1.5;
  }

  .btn-primary.btn-block {
    display: block;
    width: 100%;
    text-align: center;
    border: none;
    cursor: pointer;
    font-family: 'DM Sans', sans-serif;
  }

  .btn-primary.btn-block:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .form-alert {
    font-size: 0.82rem;
    font-weight: 300;
    padding: 0.85rem 1rem;
    border-radius: 3px;
    margin-bottom: 1.5rem;
    line-height: 1.55;
  }

  .form-alert-error {
    background: rgba(196,105,90,0.12);
    border: 1px solid rgba(196,105,90,0.35);
    color: #E8A99C;
  }

  .form-alert-success {
    background: rgba(201,168,76,0.1);
    border: 1px solid rgba(201,168,76,0.35);
    color: var(--gold-light);
  }

  .auth-footer-link {
    text-align: center;
    margin-top: 1.75rem;
    font-size: 0.82rem;
    font-weight: 300;
    color: var(--ink-soft);
  }

  .auth-footer-link a {
    color: var(--gold);
    text-decoration: underline;
    text-underline-offset: 3px;
  }

  .auth-footer-link a:hover { color: var(--gold-light); }

  .auth-toast {
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    background: rgba(28,26,22,0.96);
    color: var(--cream);
    border: 1px solid rgba(201,168,76,0.4);
    padding: 0.7rem 1.4rem;
    border-radius: 4px;
    font-family: 'DM Sans', sans-serif;
    font-size: 0.8rem;
    letter-spacing: 0.03em;
    z-index: 999;
    opacity: 0;
    transition: opacity 0.3s ease;
    pointer-events: none;
  }

  .auth-toast.visible { opacity: 1; }

  /* ============================================================
     Dashboard page (dashboard.php) — visual redesign per the approved
     mockup (dashboard-mockup.html, DECISIONS.md #133), built into real
     code this session. Reuses .win-btn/.form-alert/.section-label from
     elsewhere rather than duplicating button styling, and reuses
     .mode-buttons/.mode-btn/.mode-btn-classic verbatim for Play — the
     direct fix the redesign was for — so .dashboard-quick-link/
     -quick-links/-quick-link-classic and the old plain .dashboard-list/
     -card* row styling are removed below, no longer used by the
     rebuilt markup (confirmed via grep before deleting, same discipline
     as DECISIONS.md #132's .saved removal).
     ============================================================ */

  .dashboard-page {
    position: relative;
    min-height: 100vh;
    background: var(--ink);
    /* Top padding raised chat 2.24 from 3rem to 6rem to clear the
       absolutely-positioned .dashboard-nav-bar above the content —
       same mechanism index.php uses (.hero-content's own top padding
       clears its nav), not a margin hack on an inner element. */
    padding: 6rem 1.25rem 4rem;
    overflow: hidden;
  }

  /* Same atmospheric photo treatment as .hero-bg on index.php (same
     image, filter, and gradient overlay) — direct request for visual
     consistency between the homepage and the account's own landing
     page, rather than the dashboard reading as a flatter, separate
     part of the site. */
  .dashboard-page-bg {
    position: absolute;
    inset: 0;
    background-image: url('/images/library.jpg');
    background-size: cover;
    background-position: center 30%;
    background-attachment: fixed;
    filter: brightness(0.55) saturate(1.1);
    z-index: 0;
    pointer-events: none;
  }

  .dashboard-page-bg::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(to bottom, rgba(14,12,10,0.3) 0%, rgba(14,12,10,0.0) 40%, rgba(14,12,10,0.7) 100%);
    pointer-events: none;
  }

  /* ── Touch devices: steady background photo (chat 2.28) ──────────
     .hero-bg (homepage) and .dashboard-page-bg (dashboard, FAQ,
     contact, legal, topic pages...) use background-attachment:fixed,
     which phones handle badly: Android Chrome re-scales the photo every
     time the address bar slides in/out, and iOS Safari ignores fixed
     entirely. Desktop (mouse/trackpad) keeps it, untouched.

     1) FALLBACK, every touch device: the photo scrolls normally, sized
        to one full screen (100lvh — the largest viewport height, which
        doesn't change as the address bar moves, so nothing re-scales),
        fading out at the bottom into the page's --ink background. A
        mask rather than an overlay fading to --ink, because this
        layer's brightness filter would darken an --ink stop and leave
        a faint line.

     2) PINNED, touch devices that support :has() (iOS 15.4+, Chrome
        105+ — the same era as lvh): the owner's preferred look, photo
        stays put while the page scrolls over it, drawn by a body::before
        layer identical to category.php's (see the block below for why).
        Sections with their own solid backgrounds scroll over it; the
        homepage's topics strip has none of its own, so it's given --ink
        to keep looking exactly as before, and .dashboard-page's own
        --ink background is made transparent so the photo shows. No
        clip-path is used anywhere — it would clip the mobile nav
        overlay, which lives inside these sections. */
  @media (hover: none) and (pointer: coarse) {
    .hero-bg,
    .dashboard-page-bg {
      bottom: auto;
      height: 100vh;
      height: 100lvh;
      background-attachment: scroll;
      -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
      mask-image: linear-gradient(to bottom, #000 70%, transparent 100%);
    }
  }
  @supports selector(body:has(.hero)) {
    @media (hover: none) and (pointer: coarse) {
      /* The photo is drawn by body::before — the EXACT element and
         settings of category.php's own layer, which the owner confirmed
         looks right on their phone (sizing/anchoring differs — see
         below). Earlier attempts
         (chat 2.28) pinned the pages' own .hero-bg/.dashboard-page-bg
         boxes instead and still jerked slightly on the homepage, even
         after removing the card blur and the photo filter; rather than
         keep tuning a different element, these pages now use the one
         proven to work. Darkness is baked into the layer (45% black +
         the same overlay gradient), no filter. The pages' own photo
         boxes are hidden on these devices. */
      body:has(.hero),
      body:has(.dashboard-page) {
        position: relative;
        z-index: 0;
      }
      body:has(.hero)::before,
      body:has(.dashboard-page)::before {
        content: '';
        position: fixed;
        /* Fixed HEIGHT (100lvh, the full-screen height, which never
           changes) anchored to the screen edge that stays put when the
           address bar hides — so the layer never resizes and never
           moves. Android Chrome's bar is at the top and slides up, so
           the BOTTOM edge is steady; iOS Safari is the reverse (below).
           NOT inset:0: attached to all four edges, the layer resizes
           when the bar hides and the cover-sized photo visibly
           re-scales. category.php looked perfect with inset:0 only
           because its address bar never hides at all (owner's
           observation, chat 2.28). */
        left: 0;
        right: 0;
        top: auto;
        bottom: 0;
        height: 100vh;
        height: 100lvh;
        z-index: -1;
        background-image:
          linear-gradient(rgba(0,0,0,0.45), rgba(0,0,0,0.45)),
          linear-gradient(to bottom, rgba(14,12,10,0.3) 0%, rgba(14,12,10,0.0) 40%, rgba(14,12,10,0.7) 100%),
          url('/images/library.jpg');
        background-size: cover;
        background-position: center 30%;
        pointer-events: none;
      }
      /* iOS Safari: address bar at the BOTTOM by default, shrinking
         downward — the top edge is the steady one there.
         -webkit-touch-callout is supported only by iOS WebKit, a
         reliable iOS-only test. */
      @supports (-webkit-touch-callout: none) {
        body:has(.hero)::before,
        body:has(.dashboard-page)::before {
          top: 0;
          bottom: auto;
        }
      }
      .hero-bg,
      .dashboard-page-bg {
        display: none;
      }
      .dashboard-page {
        background: transparent;
      }
      /* Mode cards on phones (chat 2.28): the frosted blur is kept —
         it was switched off while chasing the scroll jerk, which turned
         out to be the address bar alone (fixed above). Background
         lightened 0.78 -> 0.60 at the owner's request ("pretty dark"),
         so more of the pinned photo shows through. .featured repeats
         the base background at higher specificity, so it's listed too.
         Tune the 0.60 on-device if needed. If any scroll stutter ever
         returns over the cards, turning the blur back off here is the
         first thing to try. */
      .mode-btn,
      .mode-btn.featured {
        background: rgba(10,8,6,0.60);
      }
      body:has(.hero) .topic-tabs-section {
        background: var(--ink);
      }
    }
  }

  .dashboard-inner {
    position: relative;
    z-index: 1;
    max-width: 1000px;
    margin: 0 auto;
  }

  /* Desktop nav bar — chat 2.23, corrected chat 2.24. Now a direct
     child of .dashboard-page (full-width, position:relative), so plain
     position:absolute + left:0/right:0 anchors to the true page width —
     the exact same mechanism index.php's own <nav> uses inside its
     full-width .hero. The earlier viewport-breakout variant is gone;
     every page's nav bar now uses this identical rule. */
  .dashboard-nav-bar {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 20;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding: 1.25rem 1.5rem;
  }
  @media (min-width: 760px) {
    .dashboard-nav-bar { padding: 1.5rem 3rem; }
  }

  .dashboard-header {
    text-align: center;
    margin-bottom: 2.5rem;
  }

  .dashboard-logo {
    display: inline-flex;
    justify-content: center;
    margin-bottom: 1.5rem;
    text-decoration: none;
  }

  .dashboard-title {
    font-family: 'Playfair Display', serif;
    font-size: clamp(1.6rem, 5vw, 2.2rem);
    font-weight: 400;
    color: var(--cream);
    margin: 0.4rem 0 1.5rem;
  }

  /* Dashboard header's Sign Out link (chat 2.14) — real gap, this page
     previously had no way to sign out at all. Plain inline text link,
     not a button, so a genuine underline is the correct affordance
     here (color:inherit picks up .section-label's own gold rather
     than guessing at whatever the global anchor default is). */
  .dashboard-signout-link {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
  }
  .dashboard-signout-link:hover {
    color: var(--gold-light);
  }

  .dashboard-section { margin-bottom: 3rem; }

  /* Saved Puzzles + My Puzzles side by side on desktop (chat 2.11).
     Plain flex row: when My Puzzles isn't rendered at all (no builder
     puzzles yet), Saved Puzzles is the only flex item and naturally
     fills the full width — no separate "solo" layout needed. */
  .dashboard-columns {
    display: flex;
    gap: 2rem;
    align-items: flex-start;
    margin-bottom: 3rem;
  }
  .dashboard-columns .dashboard-section {
    flex: 1;
    min-width: 0;
    margin-bottom: 0;
  }
  @media (max-width: 760px) {
    .dashboard-columns { flex-direction: column; gap: 3rem; }
  }

  /* Mobile-only tab bar (Saved Puzzles / My Puzzles) — direct feedback
     that two full sections stacked one after another on mobile,
     each already a tile grid of its own, was too much to scroll
     through at once. Only rendered by dashboard.php when both
     sections actually exist (a lone Saved Puzzles section with
     nothing to switch to has no tabs at all, see dashboard.php).
     Desktop is completely unaffected: .dashboard-mobile-tabs stays
     display:none outside this media query, and .dashboard-collapsible
     has no hiding rule at all outside it — its normal .dashboard-
     section/.dashboard-columns layout applies untouched. */
  .dashboard-mobile-tabs { display: none; }
  @media (max-width: 760px) {
    .dashboard-mobile-tabs {
      display: flex;
      gap: 0.6rem;
      margin-bottom: 1.5rem;
    }
    .dashboard-mobile-tab-btn {
      flex: 1;
      padding: 0.7rem 0.5rem;
      background: rgba(245,237,216,0.02);
      border: 1px solid rgba(201,168,76,0.25);
      border-radius: 8px;
      color: var(--ink-soft);
      font-family: 'DM Sans', sans-serif;
      font-size: 0.78rem;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      cursor: pointer;
      transition: background .2s, border-color .2s, color .2s;
    }
    .dashboard-mobile-tab-btn.active {
      background: rgba(201,168,76,0.12);
      border-color: var(--gold);
      color: var(--gold);
    }

    /* Both panels closed by default (no .mobile-open on load);
       selecting a tab opens exactly one. Only applies to sections
       marked .dashboard-collapsible — dashboard.php only adds that
       class when a real tab bar exists to control it. */
    .dashboard-collapsible { display: none; }
    .dashboard-collapsible.mobile-open { display: block; }
  }
  .dashboard-section-head { text-align: center; margin-bottom: 1.25rem; }

  .dashboard-empty {
    text-align: center;
    padding: 3rem 1.5rem;
    border: 1px solid rgba(245,237,216,0.08);
    border-radius: 8px;
    background: rgba(245,237,216,0.02);
  }
  .dashboard-empty p {
    font-size: 0.95rem;
    color: var(--cream);
    margin-bottom: 0.5rem;
  }
  .dashboard-empty-sub {
    font-size: 0.82rem !important;
    font-weight: 300;
    color: var(--ink-soft) !important;
  }

  /* Saved Puzzles — card grid, same visual family as .cat-card (thin
     hairline grid, dark fill) rather than an invented style, per the
     approved mockup. */
  /* ============================================================
     Bulk-select tile grid (chat 2.11) — replaces the old one-per-row
     .dashboard-saved-card layout entirely: direct feedback that a
     full-width row per puzzle was a poor use of space and made
     deleting many puzzles tedious (one confirm-click each). Smaller
     tiles, several per row, a checkbox per tile instead of an
     individual Remove button, one Select All + Delete Selected pair
     per section (.dashboard-bulk-form). Used identically by both
     Saved Puzzles and My Puzzles.

     .dashboard-panel wraps each section in a real contained card —
     direct feedback that two lists floating loose over the busy
     background photo, with no defined edge, read as messy. Same
     subtle background/border treatment already established by
     .dashboard-empty, reused here as the container for the whole
     section rather than just the empty state.
     ============================================================ */
  .dashboard-panel {
    background: rgba(245,237,216,0.02);
    border: 1px solid rgba(201,168,76,0.25);
    border-radius: 10px;
    padding: 1.5rem;
  }
  .dashboard-panel-head { text-align: center; margin-bottom: 1.25rem; }
  .dashboard-panel-title {
    font-family: 'Playfair Display', serif;
    font-size: 1.3rem;
    font-weight: 400;
    color: var(--cream);
  }

  .dashboard-tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    gap: 0.6rem;
  }

  .dashboard-tile-card {
    background: var(--ink-mid);
    border: 1px solid rgba(245,237,216,0.1);
    border-radius: 6px;
    padding: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
  }

  /* Highlights whichever tile matches the puzzle currently open for
     editing on print-builder.php (chat 2.14 continued) — direct
     request, following directly from removing that puzzle's earlier
     exclusion from this same list (it used to just disappear from
     here while being edited, which read as a bug). Gold border/glow
     matches the site's existing "this is the active one" language
     (e.g. the mode-toggle's own active state) rather than inventing a
     new visual treatment. */
  .dashboard-tile-card.is-current {
    border-color: rgba(201,168,76,0.5);
    box-shadow: 0 0 0 1px rgba(201,168,76,0.25), 0 0 16px rgba(201,168,76,0.12);
  }
  .dashboard-tile-current-badge {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.05rem 0.5rem;
    border-radius: 50px;
    background: rgba(201,168,76,0.15);
    border: 1px solid rgba(201,168,76,0.4);
    color: var(--gold);
    font-size: 0.6rem;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    vertical-align: middle;
  }
  .win-btn.dashboard-tile-current-label {
    flex: 1;
    text-align: center;
    color: var(--ink-muted);
    border-color: rgba(245,237,216,0.1);
    cursor: default;
  }
  .win-btn.dashboard-tile-current-label:hover { background: transparent; color: var(--ink-muted); }

  /* Real mini-grid thumbnail — actual letters, actual found-word
     highlight colors, rendered server-side by dashboard.php's
     dashboard_render_thumb() from the puzzle's real grid_data/
     owner_progress. This is the owner's own private dashboard, so
     showing the real (if illegible-at-this-size) puzzle state carries
     none of the spoiler risk a public/shared view would have.

     Fixed square, NOT the puzzle's real aspect ratio (unlike the
     actual grid on puzzle.php, where preserving it matters for real
     play) — direct feedback that a 10×15 puzzle next to a 14×19 one
     rendered at different heights in the same row, reading as
     "funny"/uneven. At this illegible decorative scale, uniform tile
     shape matters more than exactly preserving the true ratio. */
  .dashboard-tile-thumb {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    background: rgba(0,0,0,0.25);
    border: 1px solid rgba(245,237,216,0.08);
    border-radius: 4px;
  }
  .dashboard-saved-thumb-grid {
    display: grid;
    width: 100%;
    height: 100%;
  }
  .dashboard-saved-thumb-grid span {
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    font-family: 'DM Sans', sans-serif;
    font-size: 4px;
    line-height: 1;
    color: var(--ink-muted);
    white-space: nowrap;
  }

  /* Checkbox overlay, top-right of the thumbnail — replaces the old
     per-card Remove button entirely. Native input hidden but still
     focusable/clickable (opacity:0, not display:none), a sibling span
     drawn as the visible box so the checked state and focus ring both
     stay real browser behavior, not simulated. */
  .dashboard-tile-check {
    position: absolute;
    top: 0.35rem;
    right: 0.35rem;
    z-index: 2;
    display: flex;
    cursor: pointer;
  }
  .dashboard-tile-check-input {
    position: absolute;
    inset: 0;
    width: 20px;
    height: 20px;
    margin: 0;
    opacity: 0;
    cursor: pointer;
  }
  .dashboard-tile-check-box {
    width: 20px;
    height: 20px;
    border: 1.5px solid rgba(245,237,216,0.45);
    border-radius: 4px;
    background: rgba(14,12,10,0.75);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background .15s, border-color .15s;
  }
  .dashboard-tile-check-input:checked + .dashboard-tile-check-box {
    background: var(--gold);
    border-color: var(--gold);
  }
  .dashboard-tile-check-input:checked + .dashboard-tile-check-box::after {
    content: '';
    width: 5px;
    height: 9px;
    border: solid var(--ink);
    border-width: 0 2px 2px 0;
    transform: rotate(45deg) translate(-1px, -1px);
  }
  .dashboard-tile-check-input:focus-visible + .dashboard-tile-check-box {
    outline: 2px solid var(--gold);
    outline-offset: 2px;
  }

  .dashboard-tile-body { min-width: 0; }
  .dashboard-tile-mode { display: block; font-size: 0.6rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--gold); margin-bottom: 0.25rem; }
  .dashboard-tile-card.classic .dashboard-tile-mode { color: var(--emerald-light); }
  .dashboard-tile-title {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    font-family: 'Playfair Display', serif;
    font-size: 0.9rem;
    color: var(--cream);
    line-height: 1.25;
    margin-bottom: 0.2rem;
    overflow: hidden;
    /* Always reserves 2 lines' worth of height, even when the title
       only needs 1 — direct feedback that a short title made its own
       card shorter, pushing its Play/Edit buttons up out of alignment
       with neighboring cards whose titles actually wrapped. */
    min-height: 2.25rem;
  }
  .dashboard-tile-meta { display: block; font-size: 0.65rem; color: var(--ink-soft); margin-bottom: 0.5rem; }
  .dashboard-tile-actions { display: flex; gap: 0.35rem; }
  /* .win-btn's base border-radius (puzzle.css) is 50px — a real pill
     shape at the size it was designed for, but at this tile's tiny
     scale that radius exceeds the button's own half-height, over-
     rounding into something closer to a circle/blob with a rough-
     looking edge (direct feedback: "border bleeding" / "circle
     looking"). Scaled down to a radius proportionate to a small
     button's real height — still a pill, just not oversized for it. */
  .dashboard-tile-actions .win-btn {
    flex: 1 1 0;
    font-size: 0.6rem;
    padding: 0.32rem 0.3rem;
    border-radius: 16px;
    /* text-decoration/centering were previously patched here as a
       defensive, context-local fix (before puzzle.css was available
       to diagnose the real cause). Removed now that the actual root
       cause is fixed at the source: the base .win-btn rule
       (puzzle.css) never set text-decoration/text-align at all — it
       silently relied on <button>'s own browser-default centering
       and no-underline behavior everywhere else it was used. Since
       Play/Edit here are real <a> links (unlike Select All/Delete
       Selected, which are <button>s), they had neither default,
       which is what was actually showing through. Fixed generally in
       puzzle.css instead of patched per-context again. */
  }

  /* Select All + Delete Selected as a matched pair, side by side —
     direct feedback that a lone checkbox up top and a full-width
     button down below read as disconnected. Select All toggles its
     own label between "Select All"/"Deselect All" (js/dashboard.js)
     so it always states what pressing it will do. Delete Selected
     keeps the same warning/error look (same tone as .form-alert-error)
     already established for the old per-card Remove button. Two
     classes (.win-btn.dashboard-bulk-delete-btn) beats puzzle.css's
     single .win-btn on specificity regardless of stylesheet load
     order — same reasoning as the original Remove fix, not the
     equal-specificity trap documented elsewhere. Always enabled —
     clicking with nothing checked shows .dashboard-bulk-warning
     instead of the button being greyed out (js/dashboard.js). */
  .dashboard-bulk-actions {
    display: flex;
    gap: 0.6rem;
    margin-top: 1.25rem;
  }
  .dashboard-bulk-actions .win-btn { flex: 1 1 0; }
  .dashboard-bulk-warning {
    margin-top: 0.6rem;
    text-align: center;
    font-size: 0.78rem;
    color: var(--error);
  }
  .dashboard-bulk-warning[hidden] { display: none; }
  .win-btn.dashboard-bulk-delete-btn {
    background: rgba(196,105,90,0.12);
    border-color: rgba(196,105,90,0.35);
    color: #E8A99C;
  }
  .win-btn.dashboard-bulk-delete-btn:hover {
    background: rgba(196,105,90,0.22);
    border-color: var(--error);
    color: var(--error);
  }


  /* Site nav access on category.php/puzzle.php (chat 2.9 continued) —
     just positions the hamburger trigger; .nav-hamburger/.nav-overlay's
     own styling above already covers everything else. Right-aligned,
     matching index.php's own hamburger position (top-right is the
     established convention here, not left — corrected after live
     feedback flagged the initial left placement as unconventional). */
  .site-nav-minimal {
    position: fixed;
    top: 1rem;
    right: 1rem;
    z-index: 500;
  }


  /* "Every clue is a door" — homepage section after the demo (chat 2.30).
     Three steps styled like found answers in the game's question list:
     a colored left accent, a faint border and an answer pill, each in a
     color from puzzle.js's COLOR_PALETTE (emerald, sapphire, amethyst).
     --found / --found-light are "r,g,b" triples so one value drives the
     border, tint and text. */
  .discover-section {
    background: var(--ink);
    padding: 3.5rem 1.25rem 4rem;
    border-top: 1px solid rgba(245,237,216,0.06);
  }
  @media (min-width: 760px) { .discover-section { padding: 5rem 3rem 5.5rem; } }

  .discover-inner { max-width: 1100px; margin: 0 auto; }

  .discover-header { text-align: center; margin-bottom: 2.25rem; }
  @media (min-width: 760px) { .discover-header { margin-bottom: 3rem; } }
  .discover-header .section-label { margin-bottom: 0.6rem; }

  .discover-title {
    font-family: 'Playfair Display', serif;
    font-size: clamp(1.7rem, 5vw, 2.6rem);
    font-weight: 400;
    color: var(--cream);
    line-height: 1.2;
    margin-bottom: 1rem;
  }
  .discover-title em { font-style: italic; color: var(--gold-light); }

  .discover-intro {
    font-size: 0.95rem;
    font-weight: 300;
    color: rgba(245,237,216,0.72);
    line-height: 1.8;
    max-width: 540px;
    margin: 0 auto;
  }

  .discover-steps {
    list-style: none;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.9rem;
    max-width: 30rem;
    margin: 0 auto;
  }
  @media (min-width: 760px) {
    .discover-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1.25rem; max-width: none; }
  }

  .discover-card {
    --found: 201,168,76;
    --found-light: 232,201,122;
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    padding: 1.1rem 1.4rem 1.5rem 1.5rem;
    border-radius: 12px;
    background: rgba(245,237,216,0.05);
    border: 1px solid rgba(var(--found),0.35);
    box-shadow: inset 3px 0 0 rgb(var(--found));
  }
  .discover-card--solve    { --found: 82,166,117;  --found-light: 151,201,172; }
  .discover-card--wonder   { --found: 107,155,209; --found-light: 166,195,227; }
  .discover-card--discover { --found: 139,111,201; --found-light: 185,168,222; }

  .discover-num { align-self: flex-start; font-size: 0.8rem; color: var(--ink-muted); }

  .discover-pill {
    align-self: center;
    padding: 0.5rem 1.6rem;
    border: 1px solid rgb(var(--found));
    border-radius: 999px;
    background: rgba(var(--found),0.10);
    font-size: 1rem;
    letter-spacing: 0.2em;
    color: rgb(var(--found-light));
  }

  .discover-text {
    font-size: 0.95rem;
    line-height: 1.6;
    text-align: center;
    color: rgba(245,237,216,0.88);
  }

  .discover-coda {
    font-family: 'Playfair Display', serif;
    font-style: italic;
    font-size: clamp(1.1rem, 2.6vw, 1.4rem);
    line-height: 1.5;
    text-align: center;
    color: var(--gold-light);
    margin-top: 2.25rem;
  }
  @media (min-width: 760px) { .discover-coda { margin-top: 3rem; } }

  /* Quiet rise (index.php script adds .js-rise, then .is-visible once the
     cards scroll into view). will-change keeps each card on one render
     layer the whole time, so the pill's thin outline and text don't
     "click" brighter when the animation ends. */
  .discover-section.js-rise .discover-card { opacity: 0; transform: translateY(14px); }
  .discover-section.js-rise.is-visible .discover-card {
    will-change: opacity, transform;
    animation: discoverRise 0.7s cubic-bezier(.2,.7,.2,1) both;
  }
  .discover-section.js-rise.is-visible .discover-card:nth-child(1) { animation-delay: 0.15s; }
  .discover-section.js-rise.is-visible .discover-card:nth-child(2) { animation-delay: 0.27s; }
  .discover-section.js-rise.is-visible .discover-card:nth-child(3) { animation-delay: 0.39s; }
  @keyframes discoverRise {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  @media (prefers-reduced-motion: reduce) {
    .discover-section.js-rise .discover-card { opacity: 1; transform: none; animation: none; will-change: auto; }
  }

  /* ── Printables menu (chat 2.32) ──
     Desktop: a "Printables" item in .nav-desktop whose panel lists the
     maker and the audience doors (js/nav.js toggles .open on click; hover
     opens it on devices with a mouse). Phones: the same links grouped
     under a small gold heading inside .nav-overlay. */
  .nav-sub { position: relative; }
  .nav-sub-toggle {
    background: none;
    border: 0;
    padding: 0;
    cursor: pointer;
    font-family: 'DM Sans', sans-serif;
    font-size: 0.78rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-soft);
    transition: color 0.2s;
  }
  .nav-sub-toggle::after {
    content: '';
    display: inline-block;
    width: 0.36rem;
    height: 0.36rem;
    margin-left: 0.45rem;
    border-right: 1px solid currentColor;
    border-bottom: 1px solid currentColor;
    transform: translateY(-0.18rem) rotate(45deg);
    transition: transform 0.2s;
  }
  .nav-sub-toggle:hover,
  .nav-sub.open .nav-sub-toggle { color: var(--cream); }
  .nav-sub.open .nav-sub-toggle::after { transform: translateY(0.02rem) rotate(225deg); }
  .nav-sub-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 4px; }

  .nav-submenu {
    display: none;
    position: absolute;
    top: calc(100% + 0.9rem);
    left: -1.1rem;
    z-index: 30;
    min-width: 15rem;
    list-style: none;
    margin: 0;
    padding: 0.55rem 0;
    background: #0E0C0A;
    border: 1px solid rgba(201,168,76,0.28);
    border-radius: 4px;
    box-shadow: 0 14px 30px rgba(0,0,0,0.5);
    text-align: left;
  }
  /* An invisible bridge across the gap, so the mouse can travel from the
     word down to the panel without it closing. */
  .nav-submenu::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: -0.9rem;
    height: 0.9rem;
  }
  .nav-sub.open .nav-submenu { display: block; }
  @media (hover: hover) {
    .nav-sub:hover .nav-submenu { display: block; }
    .nav-sub:hover .nav-sub-toggle { color: var(--cream); }
  }
  .nav-desktop .nav-submenu a {
    display: block;
    padding: 0.6rem 1.1rem;
    font-size: 0.84rem;
    letter-spacing: 0.03em;
    text-transform: none;
    color: var(--ink-soft);
    white-space: nowrap;
  }
  .nav-desktop .nav-submenu a:hover,
  .nav-desktop .nav-submenu a:focus-visible { color: var(--cream); background: rgba(201,168,76,0.07); outline: none; }
  .nav-submenu-sep { height: 1px; background: rgba(245,237,216,0.08); margin: 0.45rem 1.1rem; }
  .nav-submenu-label {
    font-size: 0.64rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--ink-muted);
    padding: 0.3rem 1.1rem 0.15rem;
  }

  .nav-overlay-group {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  /* "Printables" looks like every other menu item, with a chevron that
     turns when its list is open (accordion — the mobile standard). */
  .nav-overlay-group-toggle {
    background: none;
    border: 0;
    cursor: pointer;
    font-family: 'Playfair Display', serif;
    font-size: clamp(1.6rem, 6vw, 2.2rem);
    font-weight: 400;
    color: var(--ink-soft);
    letter-spacing: 0.04em;
    padding: 0.6rem 0;
    opacity: 0;
    transform: translateY(12px);
    transition: color 0.2s, opacity 0.35s ease 0.15s, transform 0.35s ease 0.15s;
  }
  .nav-overlay.open .nav-overlay-group-toggle { opacity: 1; transform: translateY(0); }
  .nav-overlay-group-toggle:hover,
  .nav-overlay-group-toggle[aria-expanded="true"] { color: var(--cream); }
  .nav-overlay-group-toggle::after {
    content: '';
    display: inline-block;
    width: 0.45rem;
    height: 0.45rem;
    margin-left: 0.6rem;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: translateY(-0.3rem) rotate(45deg);
    transition: transform 0.2s;
  }
  .nav-overlay-group-toggle[aria-expanded="true"]::after { transform: translateY(0) rotate(225deg); }
  .nav-overlay-group-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 4px; }
  .nav-overlay-group-list {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1rem;
    margin: 0.1rem 0 0.6rem;
    padding: 0.4rem 1.6rem 0.5rem;
    border-left: 1px solid rgba(201,168,76,0.3);
    border-right: 1px solid rgba(201,168,76,0.3);
  }
  .nav-overlay-group-list[hidden] { display: none; }
  .nav-overlay a.nav-overlay-sub {
    font-family: 'DM Sans', sans-serif;
    font-size: 1rem;
    letter-spacing: 0.02em;
    padding: 0.45rem 0;
  }
  /* With the extra links, a short phone could run out of room: let the
     menu scroll instead of clipping, still centred when it fits. */
  .nav-overlay { overflow-y: auto; justify-content: safe center; padding: 4.5rem 0 2rem; }

  /* ── Homepage "Made for…" doors (chat 2.32, option B) ── */
  .home-doors {
    background: var(--ink-mid);
    padding: 4.5rem 1.5rem;
    border-top: 1px solid rgba(245,237,216,0.06);
  }
  .home-doors-title {
    font-family: 'Playfair Display', serif;
    font-weight: 400;
    font-size: clamp(1.6rem, 4.5vw, 2.4rem);
    text-align: center;
    color: var(--cream);
    margin-bottom: 0.6rem;
  }
  .home-doors-sub {
    text-align: center;
    color: var(--ink-soft);
    font-size: 0.95rem;
    margin-bottom: 2.4rem;
  }
  .home-doors-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
    max-width: 1000px;
    margin: 0 auto;
  }
  @media (min-width: 760px) {
    .home-doors { padding: 5.5rem 3rem; }
    .home-doors-grid { grid-template-columns: repeat(2, 1fr); gap: 1.5rem; max-width: 760px; }
  }
  .home-door {
    display: flex;
    flex-direction: column;
    text-decoration: none;
    background: rgba(10,8,6,0.55);
    border: 1px solid rgba(245,237,216,0.12);
    border-radius: 4px;
    overflow: hidden;
    transition: border-color 0.2s, transform 0.2s;
  }
  .home-door:hover { border-color: var(--ink-blue-light); }
  .home-door:focus-visible { outline: 2px solid var(--ink-blue-light); outline-offset: 3px; }
  .home-door-art {
    display: flex;
    justify-content: center;
    height: 160px;
    padding-top: 20px;
    overflow: hidden;
    background: #1c1a16;
  }
  .home-door-art img {
    width: 62%;
    height: auto;
    align-self: flex-start;
    box-shadow: 0 10px 24px rgba(0,0,0,0.5);
    transform: rotate(-2deg);
    transition: transform 0.3s;
  }
  .home-door:hover .home-door-art img { transform: rotate(-2deg) translateY(-4px); }
  @media (prefers-reduced-motion: reduce) {
    .home-door:hover .home-door-art img { transform: rotate(-2deg); }
  }
  .home-door-body { display: flex; flex-direction: column; padding: 1.15rem 1.3rem 1.35rem; flex: 1; }
  .home-door-label {
    font-family: 'Playfair Display', serif;
    font-style: italic;
    font-size: 1.35rem;
    color: var(--ink-blue-light);
    margin-bottom: 0.35rem;
  }
  .home-door-text { font-size: 0.9rem; line-height: 1.55; color: var(--ink-soft); margin-bottom: 0.9rem; flex: 1; }
  .home-door-go { font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--gold); }
  /* Four doors (chat 2.32, faith added): two by two on tablets, a row of
     four on wide screens. */
  @media (min-width: 1100px) {
    .home-doors-grid { grid-template-columns: repeat(4, 1fr); max-width: 1180px; }
  }


  /* ── Printable Library, /printables (chat 2.32) ── */
  .lib .visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .lib-title {
    font-family: 'Playfair Display', serif;
    font-weight: 400;
    font-size: clamp(1.8rem, 5vw, 2.6rem);
    text-align: center;
    color: var(--cream);
    margin: 0 0 0.5rem;
  }
  .lib-sub { text-align: center; color: var(--ink-soft); font-size: 0.98rem; line-height: 1.6; margin: 0 auto 2rem; max-width: 36rem; }
  .lib-tools { display: flex; flex-wrap: wrap; gap: 0.9rem 1rem; align-items: center; margin-bottom: 2.2rem; }
  .lib-search-wrap { flex: 1 1 16rem; }
  .lib-search {
    width: 100%;
    box-sizing: border-box;
    background: rgba(0,0,0,0.3);
    border: 1px solid rgba(245,237,216,0.15);
    border-radius: 4px;
    padding: 0.75rem 0.95rem;
    color: var(--cream);
    font-family: 'DM Sans', sans-serif;
    font-size: 0.95rem;
  }
  .lib-search:focus { outline: none; border-color: var(--gold); }
  .lib-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; }
  .lib-chip {
    border: 1px solid rgba(201,168,76,0.35);
    border-radius: 999px;
    padding: 0.45rem 1rem;
    font-size: 0.82rem;
    color: var(--ink-soft);
    text-decoration: none;
    transition: border-color 0.2s, color 0.2s;
  }
  .lib-chip:hover { color: var(--cream); border-color: var(--gold); }
  .lib-chip.is-on { background: rgba(201,168,76,0.18); color: var(--gold-light); border-color: var(--gold); }
  .lib-group { margin-bottom: 2.4rem; }
  .lib-group-head {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    border-bottom: 1px solid rgba(201,168,76,0.18);
    padding-bottom: 0.55rem;
    margin-bottom: 1rem;
  }
  .lib-group-title { font-family: 'Playfair Display', serif; font-weight: 400; font-size: 1.4rem; color: var(--cream); margin: 0; }
  .lib-group-count { font-size: 0.8rem; color: var(--ink-muted); }
  .lib-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr; gap: 0.8rem; }
  @media (min-width: 640px) { .lib-cards { grid-template-columns: repeat(2, 1fr); } }
  @media (min-width: 960px) { .lib-cards { grid-template-columns: repeat(3, 1fr); } }
  .lib-card {
    display: block;
    height: 100%;
    box-sizing: border-box;
    background: rgba(28,26,22,0.8);
    border: 1px solid rgba(245,237,216,0.1);
    border-radius: 4px;
    padding: 0.95rem 1.05rem;
    text-decoration: none;
    transition: border-color 0.2s;
  }
  .lib-card:hover, .lib-card:focus-visible { border-color: var(--gold); outline: none; }
  .lib-card-title { display: block; font-family: 'Playfair Display', serif; font-size: 1.06rem; line-height: 1.3; color: var(--cream); margin-bottom: 0.3rem; }
  .lib-card-meta { font-size: 0.78rem; color: var(--ink-muted); }
  .lib-tag {
    display: inline-block;
    margin-left: 0.45rem;
    font-size: 0.64rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-blue-light);
    border: 1px solid rgba(143,169,204,0.4);
    border-radius: 999px;
    padding: 0.08rem 0.5rem;
  }
  /* Six per group until "Show all" (only with the script running); a
     search shows every match. */
  .lib-js .lib-group:not(.lib-open) .lib-extra { display: none; }
  .lib-searching .lib-group .lib-extra { display: block; }
  .lib-card-item.lib-miss { display: none !important; }
  .lib-searching .lib-more { display: none; }
  .lib-more {
    margin-top: 0.8rem;
    background: none;
    border: 0;
    padding: 0.2rem 0;
    cursor: pointer;
    font-family: 'DM Sans', sans-serif;
    font-size: 0.85rem;
    color: var(--gold);
  }
  .lib-more:hover { color: var(--gold-light); }
  .lib-empty, .lib-nomatch { text-align: center; color: var(--ink-soft); line-height: 1.7; margin: 2rem 0; }
  .lib-empty a, .lib-nomatch a, .lib-own a { color: var(--gold-light); }
  .lib-own { text-align: center; color: var(--ink-soft); margin-top: 2.5rem; font-size: 0.95rem; }
