/* ──────────────────────────────────────────────
   LANDING PAGE — minimal editorial
────────────────────────────────────────────── */

.landing-page { padding-bottom: 72px; }

/* ── Single-frame app layout: masthead + one section + tab bar + footer,
   all fitting in one viewport, no page scroll. Sections slide horizontally
   when the tab bar switches between them. ──
   .landing-page-framed overrides the plain-flow padding above; kept as a
   separate class rather than folding into .landing-page so a future
   non-framed variant of the page could still reuse the base rules. */
body:has(.landing-page-framed) {
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
/* .page-wrapper (base.html) normally has min-height:100vh for the scrolling
   flow every other page uses — override to a flex child that shrinks to
   its content instead, so the footer (a sibling of .page-wrapper) has room
   to sit right below it within the same fixed-height viewport. */
body:has(.landing-page-framed) .page-wrapper {
  min-height: 0;
  flex: 1 1 auto;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
body:has(.landing-page-framed) .site-footer {
  flex: 0 0 auto;
}
.landing-page.landing-page-framed {
  padding-bottom: 0;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.landing-page-framed .landing-masthead {
  flex: 0 0 auto;
}

/* Wraps the frame (card) + tab bar as one group, centered in the space
   between masthead and footer. A moderate, capped card size (below) plus
   this centering is what keeps the "hell lot of empty space" complaint
   from an earlier, oversized attempt at this fix from recurring — that
   attempt made the card stretch to fill the entire available height
   (flex:1 on .landing-frame), which on some real browser viewports (with
   actual browser chrome reducing usable height beyond what headless
   Chrome reports) pushed total content past 100dvh and reintroduced the
   very outer page scrollbar this single-frame layout exists to avoid.
   Do not make .landing-frame flex-grow again. */
.landing-middle {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.landing-frame {
  /* Target height sized to comfortably fit the tallest tab's real content
     (the hero, with its pill kicker + 44px headline + highlighter marks +
     CTA + store badges) — 620px is a ceiling, not a fixed size: `92vh` was
     measured against the FULL viewport, not the actual space left after
     the masthead+footer, so on a short window the frame could still be
     taller than what .landing-middle actually had room for. Since
     .landing-page-framed has overflow:hidden, that overflow was being
     silently clipped off the TOP of the frame (title + tab bar cut off,
     no visible scrollbar) rather than falling back to the card's own
     internal scroll like it should. flex-shrink lets the flex box itself
     enforce the real available height instead of guessing with vh. */
  flex: 1 1 620px;
  min-height: 0;
  max-height: 620px;
  position: relative;
  padding: 8px 24px;
}

/* Sections stack in the same place (position: absolute) — only the
   .is-active one is interactive/visible; others sit inert until tossed in
   or out. */
.landing-frame-section {
  position: absolute;
  inset: 20px 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  transform: translateX(0) rotate(0deg) scale(0.98);
  transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.5s ease;
}
.landing-frame-section.is-active {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(0) rotate(0deg) scale(1);
  z-index: 2;
}
/* Outgoing card: tossed away like a dismissed flashcard — same shape of
   motion as the flashcard game's slideLeft/slideRight keyframes. Travels
   130% of its own width so it fully clears the visible frame (not just a
   small nudge) before it's done fading, and runs slower than the incoming
   card's settle so the toss reads as a deliberate motion, not a snap.
   will-change hints the browser to composite this on its own GPU layer —
   without it, a full-size shadowed card animating transform+opacity can
   force an expensive repaint every frame and make the page feel briefly
   unresponsive to clicks even though pointer-events is correctly "none"
   here the whole time. */
.landing-frame-section.is-tossing-left,
.landing-frame-section.is-tossing-right {
  will-change: transform, opacity;
}
.landing-frame-section.is-tossing-left {
  opacity: 0;
  transform: translateX(-130%) rotate(-10deg) scale(0.94);
  transition: transform 0.85s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.7s ease-in;
  z-index: 1;
}
.landing-frame-section.is-tossing-right {
  opacity: 0;
  transform: translateX(130%) rotate(10deg) scale(0.94);
  transition: transform 0.85s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.7s ease-in;
  z-index: 1;
}
/* Incoming card's starting position, set before .is-active is applied so
   the transition has somewhere to animate FROM (see the forced-reflow
   comment in landing.html's activate()). */
.landing-frame-section.is-entering:not(.is-active) {
  transform: translateX(0) rotate(0deg) scale(0.96);
  opacity: 0;
}

.landing-frame-card {
  width: 100%;
  /* Fills its parent .landing-frame, which is itself a fixed height sized
     to fit real content (see above) — every tab renders inside this same
     box. Do NOT make this content-driven (auto height) — one shared box
     shape across all 4 tabs is the point; shorter tabs (Pricing) just
     center vertically inside it via .landing-frame-card-inner. */
  height: 100%;
  max-width: 760px;
  border-radius: 22px;
  /* Same treatment as the dashboard's stat cards (.glass-card): white at
     45% opacity over the paper background, not solid white — pure white
     was the only place in the whole app that colour appeared as one giant
     panel rather than a small raised tile. */
  background: rgba(255, 255, 255, 0.45);
  backdrop-filter: blur(22px) saturate(150%);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
  border: 1px solid var(--lp-rule, var(--border-subtle));
  /* A layered, deliberately deep shadow: a soft warm ambient shadow, a
     tighter contact shadow, and a very faint colored glow in the accent
     hue underneath — the combination is what gives the card real "lifted
     off the page" depth instead of a flat drop-shadow. */
  box-shadow:
    0 32px 70px -14px rgba(28, 23, 14, 0.32),
    0 8px 20px -4px rgba(28, 23, 14, 0.14),
    0 0 0 1px rgba(28, 23, 14, 0.03),
    0 40px 90px -30px rgba(var(--lp-accent-rgb, 67, 56, 202), 0.22);
  /* overflow + scroll live on an INNER wrapper (.landing-frame-card-inner),
     not here. Putting overflow-y:auto directly on a flex container that's
     also doing align-items:center to centre its child is a known CSS trap:
     the browser can report a scrollable overflow even when the content
     visibly fits, because centring interacts badly with the scrollport's
     size calculation. Splitting "fixed size + visual card chrome" (this
     rule) from "scroll if content overflows, centre otherwise" (the inner
     wrapper below) avoids that entirely — confirmed the scrollbar the user
     saw despite empty space below the content was exactly this bug. */
  overflow: hidden;
}
.landing-frame-card-inner {
  width: 100%;
  height: 100%;
  overflow-y: auto;
  display: flex;
  /* "safe center": centers the content when it fits, but behaves like
     flex-start once it overflows — plain align-items:center on an
     overflowing flex container centers the SYMMETRIC overflow (content
     bleeds equally above and below), so scrollTop:0 doesn't actually show
     the real top of the content, it shows partway into the header. That
     silently clipped the hero's kicker/title on short viewports even
     though the scrollbar itself was functional. `safe center` is the
     standard fix; Firefox/Safari/Chrome all support it. */
  align-items: safe center;
  justify-content: center;
  /* Fallback for short viewports (per user's choice): if content
     genuinely doesn't fit the fixed card height, THIS scrolls internally
     rather than the whole page, or content being forced illegibly small —
     but only when content actually overflows, not before. */
  /* Scroll stays functional as a safety net, but its track/thumb chrome
     is hidden — a few tabs run 50-90px over the fixed card height by
     design (real content, not a bug), and a visible scrollbar there reads
     as broken chrome on an otherwise "app-like" fixed frame. */
  scrollbar-width: none;
}
.landing-frame-card-inner::-webkit-scrollbar { display: none; }

@media (prefers-reduced-motion: reduce) {
  .landing-frame-section,
  .landing-frame-section.is-tossing-left,
  .landing-frame-section.is-tossing-right {
    transition: opacity 0.2s ease;
    transform: none;
  }
}

/* ── Tab bar — same shape language as the flashcard game's 4-button rating
   row (static/flashcards/css/flashcards.css .rating-btns/.rating-btn):
   a 4-column grid of bordered, tinted rectangles, not pills. Sits with its
   own margin above the footer instead of flush against it, so it doesn't
   read as stuck to the footer. Colors here are the landing page's current
   per-hue tokens — meant as the reference the flashcard buttons get
   updated to match later, not the other way around. */
.landing-tabbar {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  max-width: 760px;
  width: 100%;
  /* Symmetric top/bottom margin (was 0 top / 18px bottom) — matched to
     .landing-frame's own 8px top/bottom padding above the card, so the
     masthead-to-card gap and the tabbar-to-footer gap read as the same
     rhythm instead of visibly uneven amounts. */
  margin: 10px auto;
  padding: 0 24px;
}
.landing-tab {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.2;
  color: var(--lp-ink, var(--text-primary));
  background: none;
  border: 2px solid var(--lp-rule-strong, var(--border-subtle));
  border-radius: 14px;
  /* More top/bottom padding so these read as real buttons (matching the
     weight of the Flashcard Duel-style CTAs) rather than thin chips. */
  padding: 18px 8px;
  cursor: pointer;
  transition: transform var(--transition-fast), filter var(--transition-fast),
              background var(--transition-fast), border-color var(--transition-fast),
              color var(--transition-fast), box-shadow var(--transition-fast);
}
.landing-tab:hover { transform: translateY(-3px); filter: brightness(1.06); }
.landing-tab:active { transform: translateY(-1px); }

.landing-tab:nth-child(1) {
  background: rgba(var(--indigo-rgb), 0.12);
  border-color: rgba(var(--indigo-rgb), 0.4);
}
.landing-tab:nth-child(2) {
  background: rgba(var(--gold-rgb), 0.12);
  border-color: rgba(var(--gold-rgb), 0.4);
}
.landing-tab:nth-child(3) {
  background: rgba(var(--emerald-rgb), 0.12);
  border-color: rgba(var(--emerald-rgb), 0.4);
}
.landing-tab:nth-child(4) {
  background: rgba(var(--rose-rgb), 0.12);
  border-color: rgba(var(--rose-rgb), 0.4);
}
/* Active state was a solid full-strength fill + white text — right next
   to three pastel-outline inactive tabs it read as a different component
   entirely rather than the same tab selected. Active now stays the same
   outline-chip family, just with a stronger tint and border so it's
   clearly selected without breaking the set. */
.landing-tab.is-active {
  color: var(--lp-ink, var(--text-primary));
  font-weight: 800;
}
.landing-tab:nth-child(1).is-active { background: rgba(var(--indigo-rgb), 0.20); border-color: var(--indigo); box-shadow: 0 6px 16px -6px rgba(var(--indigo-rgb), 0.4); }
.landing-tab:nth-child(2).is-active { background: rgba(var(--gold-rgb), 0.20);   border-color: var(--gold);   box-shadow: 0 6px 16px -6px rgba(var(--gold-rgb), 0.4); }
.landing-tab:nth-child(3).is-active { background: rgba(var(--emerald-rgb), 0.20); border-color: var(--emerald); box-shadow: 0 6px 16px -6px rgba(var(--emerald-rgb), 0.4); }
.landing-tab:nth-child(4).is-active { background: rgba(var(--rose-rgb), 0.20);   border-color: var(--rose);   box-shadow: 0 6px 16px -6px rgba(var(--rose-rgb), 0.4); }

/* ── Placeholder sections (Pricing) ── */
.landing-placeholder {
  text-align: center;
  padding: 24px;
}
.placeholder-title {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 34px;
  font-weight: 700;
  margin: 10px 0 8px;
  color: var(--lp-ink, var(--text-primary));
}
.placeholder-sub {
  color: var(--lp-ink-soft, var(--text-secondary));
  font-size: 15px;
}

/* ── Features list — same numbered-row idiom as the Knock Knock! game's
   category list (order number + name + one-line descriptor per row), just
   laid out for a static marketing list instead of a sortable/filterable
   game data table. Lists what the app does beyond the 6 games already
   shown under "Chapters to Play" (XP, streaks, leaderboard, planner, test
   series), so it stays additive rather than repeating that section. */
.landing-features {
  width: 100%;
  max-width: 640px;
  padding: 16px 24px;
}
.features-list {
  display: flex;
  flex-direction: column;
}
.feature-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  /* Reduced vertical padding — was 14px, needed to fit 6 rows without
     scrolling. Icon size (44px) and heading size (19px) are unchanged,
     per feedback: only tighten spacing/description, never shrink the
     icons or the name text. */
  padding: 4px 4px;
  border-bottom: 1px solid var(--lp-rule, var(--border-subtle));
}
.feature-row:last-child { border-bottom: none; }
.feature-row-index {
  flex: 0 0 auto;
  font-family: 'Space Grotesk', sans-serif;
  font-size: 13px;
  font-weight: 700;
  color: var(--lp-ink-muted, var(--text-muted));
  padding-top: 14px;
  min-width: 20px;
}
.feature-row-icon {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid;
}
.feature-row-icon .icon { width: 22px; height: 22px; }
.feature-row-icon.indigo  { background: rgba(var(--indigo-rgb), 0.1);  border-color: rgba(var(--indigo-rgb), 0.28);  color: var(--indigo); }
.feature-row-icon.gold    { background: rgba(var(--gold-rgb), 0.1);    border-color: rgba(var(--gold-rgb), 0.28);    color: var(--gold); }
.feature-row-icon.emerald { background: rgba(var(--emerald-rgb), 0.1); border-color: rgba(var(--emerald-rgb), 0.28); color: var(--emerald); }
.feature-row-icon.purple  { background: rgba(var(--purple-rgb), 0.1);  border-color: rgba(var(--purple-rgb), 0.28);  color: var(--purple); }
.feature-row-icon.rose    { background: rgba(var(--rose-rgb), 0.1);    border-color: rgba(var(--rose-rgb), 0.28);    color: var(--rose); }
.feature-row-body { flex: 1 1 auto; min-width: 0; padding-top: 2px; }
.feature-row-name {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 19px;
  font-weight: 700;
  color: var(--lp-ink, var(--text-primary));
  margin-bottom: 4px;
}
.feature-row-desc {
  font-size: 13px;
  line-height: 1.4;
  color: var(--lp-ink-soft, var(--text-secondary));
}
/* "More coming" previously had no icon chip at all, so it sat ~40px out of
   alignment with rows 01-05. It gets the same 44px chip (a plus glyph) so
   every row shares one grid. */
.feature-row-more .feature-row-index {
  font-size: 13px;
  color: var(--lp-accent, #4338CA);
}
.feature-row-more .feature-row-icon {
  font-size: 22px;
  font-weight: 700;
  line-height: 1;
  color: var(--lp-accent, #4338CA);
  background: rgba(var(--indigo-rgb), 0.1);
  border-color: rgba(var(--indigo-rgb), 0.28);
}
.feature-row-more .feature-row-name { color: var(--lp-accent, #4338CA); }

@media (max-width: 640px) {
  .feature-row-desc { display: none; }
  /* Was 12px — bigger than the desktop base (4px) it's supposedly
     compacting, which silently undid the fit-without-scrolling fix on
     mobile specifically. Icon (44px) and name (19px) stay full size per
     feedback; only spacing tightens. */
  .feature-row { padding: 6px 4px; gap: 12px; }
  .editorial-section-header { margin-bottom: 10px; }
  .features-header-text { font-size: 22px; }
}

/* ── Pricing — same visual language as the Match Maker game's paired-row
   cards (match_maker/static/match_maker/css/match_maker.css .mm-pair-row/
   .mm-left-item/.mm-drop-zone): a colored left-border accent + a background
   lightly tinted toward that same color, one hue per row/card. Translated
   here from "row" to "card" since 3 pricing plans read better side by side
   than stacked, but the border-accent + tint recipe is the same. Real plan
   data from subscriptions/management/commands/seed_plans.py (Pro Monthly
   ₹299, Pro Annual ₹2,499/recommended, Pro Monthly recurring ₹299) — not
   invented numbers. */
/* Rebuilt: the old 640px-capped container inside a 760px card was most of
   the ~320px dead space above and ~260px below the cards. Now uses the
   card's full width, and the three plans get a deliberate hue hierarchy
   instead of three equal-weight colours competing — indigo/gold recede to
   neutral so the emerald "Best Value" card is the only saturated one and
   reads as the actual recommendation. */
.landing-pricing {
  width: 100%;
  max-width: 100%;
  padding: 4px 8px;
}
/* Header: same badge + heading as the in-app Plans page — a rounded-rect
   gamepad icon (matching the masthead's own .logo-home-btn) over "Go Pro
   on theupscale.app", centred. Replaces the plain "Go Pro" label + hr,
   which didn't match the rest of the app's Pro-upsell surfaces at all. */
.pricing-header { text-align: center; margin-bottom: 18px; }
.pricing-header-badge {
  width: 40px;
  height: 40px;
  margin: 0 auto 10px;
  border-radius: var(--radius-md);
  border: 1px solid var(--lp-rule-strong, var(--border-rule));
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--lp-accent, var(--accent));
}
.pricing-header-badge .icon-lg { width: 20px; height: 20px; }
.pricing-header-title {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 22px;
  font-weight: 800;
  color: var(--lp-ink, var(--text-primary));
}

.pricing-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  align-items: stretch;
}
.pricing-card {
  position: relative;
  display: flex;
  flex-direction: column;
  /* Smaller, tighter padding than before (was 26px/22px) — the cards
     were oversized relative to how little content each holds. */
  padding: 18px 16px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--lp-rule, var(--border-subtle));
  background: var(--lp-paper-raised, var(--bg-surface));
}

/* "Best Value" now uses the brand accent (indigo) instead of emerald —
   emerald is a semantic "success" colour elsewhere in the app (correct
   answers, completed chapters); using it here for "the plan we'd like
   you to pick" was inconsistent with what that hue means everywhere
   else. Indigo is the actual brand/CTA colour, which is what a
   recommended-plan highlight should be. */
.pricing-card-recommended {
  border-color: var(--lp-accent, var(--accent));
  background: rgba(var(--lp-accent-rgb, 67, 56, 202), 0.05);
  box-shadow: 0 12px 26px -12px rgba(var(--lp-accent-rgb, 67, 56, 202), 0.35), 0 0 0 1.5px var(--lp-accent, var(--accent));
  transform: translateY(-4px);
}
.pricing-card-badge {
  position: absolute;
  top: -11px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--lp-accent, var(--accent));
  color: #fff;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  padding: 4px 12px;
  border-radius: 999px;
  white-space: nowrap;
  box-shadow: 0 4px 10px -2px rgba(var(--lp-accent-rgb, 67, 56, 202), 0.5);
}
.pricing-card-name {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 16px;
  font-weight: 700;
  color: var(--lp-ink, var(--text-primary));
}
.pricing-card-tag {
  font-size: 11px;
  font-weight: 600;
  color: var(--lp-ink-muted, var(--text-muted));
  text-transform: uppercase;
  letter-spacing: 0.4px;
  margin-bottom: 10px;
}
.pricing-card-price {
  font-family: 'Space Grotesk', sans-serif;
  /* Was 32px — reduced, and the cards now fit both breakpoints without
     the type itself needing to carry all the visual weight. */
  font-size: 24px;
  font-weight: 800;
  color: var(--lp-ink, var(--text-primary));
  margin-bottom: 4px;
}
.pricing-card-price span {
  font-size: 12px;
  font-weight: 600;
  color: var(--lp-ink-muted, var(--text-muted));
}
.pricing-card-save {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--lp-accent, var(--accent));
  margin-bottom: 8px;
}
.pricing-card-desc {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--lp-ink-soft, var(--text-secondary));
  flex: 1;
}
.pricing-card-cta {
  display: block;
  margin-top: 14px;
  padding: 9px 14px;
  border-radius: var(--radius-md);
  font-family: 'Space Grotesk', sans-serif;
  font-size: 13px;
  font-weight: 700;
  text-align: center;
  text-decoration: none;
  border: 1px solid var(--lp-rule-strong, var(--border-rule));
  color: var(--lp-ink, var(--text-primary));
  background: transparent;
  transition: background 160ms ease, border-color 160ms ease, transform 160ms ease;
}
.pricing-card-cta:hover { transform: translateY(-1px); }
.pricing-card-recommended .pricing-card-cta {
  border-color: var(--lp-accent, var(--accent));
  background: var(--lp-accent, var(--accent));
  color: #fff;
}
.pricing-card-recommended .pricing-card-cta:hover { background: var(--lp-accent-dark, var(--accent-hover)); }

.pricing-footnote {
  margin-top: 18px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--lp-ink-muted, var(--text-muted));
  text-align: center;
}

@media (max-width: 640px) {
  /* Was single-column with full-size cards — three stacked cards each
     carrying their long description needed ~1080px, more than double
     what the card actually had room for (~570px), so the panel always
     needed a big internal scroll. Kept the SAME 3-column layout as
     desktop instead, just compacted: hide the description (the single
     biggest height cost, and "Monthly / One-time / ₹299 / Choose ->" is
     legible on its own without it) and tighten padding/type further.
     Confirmed this fits without scrolling — see verification. */
  .pricing-header-title { font-size: 18px; }
  .pricing-grid { grid-template-columns: repeat(3, 1fr); gap: 8px; }
  .pricing-card { padding: 12px 8px; }
  .pricing-card-recommended { transform: none; }
  .pricing-card-name { font-size: 13px; }
  .pricing-card-tag { font-size: 9px; margin-bottom: 8px; }
  .pricing-card-price { font-size: 17px; }
  .pricing-card-price span { font-size: 10px; }
  .pricing-card-save { font-size: 9.5px; }
  .pricing-card-desc { display: none; }
  .pricing-card-cta { font-size: 11px; padding: 8px 6px; margin-top: 10px; }
  .pricing-card-badge { font-size: 8.5px; padding: 3px 8px; top: -9px; }
  .pricing-footnote { font-size: 11px; margin-top: 14px; }
}

@media (max-width: 640px) {
  .landing-tab { font-size: 12px; padding: 16px 10px; }
  /* Was shrunk to 6px/11.5px here — much smaller than the app's own
     .btn-sm (8px 16px / 13px) — which made it look squashed/undersized
     next to the wordmark rather than like a normal button. Keep the
     real .btn-sm size; only let it wrap instead of forcing one wide
     pill if a narrow phone genuinely can't fit it on one line. */
  .landing-masthead-actions .btn-sm {
    white-space: normal;
    text-align: center;
    line-height: 1.3;
  }
}

/* ── Masthead — padding matched to the dashboard/app navbar's own
   `.navbar { padding: 14px 0 }` (static/css/main.css) so both read as the
   same height (66px: 14px + 38px logo-svg + 14px) across the app. ── */
.landing-masthead {
  border-bottom: 1px solid var(--border-subtle);
  padding: 14px 0;
}
.landing-masthead-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.landing-masthead-actions {
  display: flex;
  align-items: center;
  gap: 18px;
}

/* ── Hero ── */
.landing-hero {
  text-align: center;
  padding: 40px 24px 36px;
  max-width: 780px;
}

.hero-kicker {
  margin-bottom: 20px;
  color: var(--accent-hover);
  letter-spacing: 1.2px;
}

.hero-title {
  font-size: 40px;
  letter-spacing: -0.02em;
  line-height: 1.08;
  margin-bottom: 18px;
}

.hero-accent { color: var(--text-accent); }

.hero-sub {
  font-size: 16px;
  color: var(--text-secondary);
  line-height: 1.7;
  margin: 0 auto 22px;
  max-width: 560px;
}

/* Highlighter-marker look built as a background-image (not an absolutely
   positioned ::after) so it correctly respects line wraps — a phrase that
   breaks across 2 lines gets one bar PER visual line, each clipped to
   that line's own box, via box-decoration-break: clone. An absolutely
   positioned ::after can't do this: its box is the union of every line
   fragment (including the empty gap between wrapped lines), which drew a
   stray vertical bar bridging the two lines. */
.hero-mark {
  position: relative;
  display: inline;
  color: var(--lp-ink);
  font-weight: 700;
  /* Low-alpha bar (not a solid fill) so dark ink text always stays legible
     over it — a solid full-strength accent bar under accent-colored text
     elsewhere on the page was a real red-on-red contrast failure. */
  background-image: linear-gradient(rgba(var(--lp-accent-rgb), 0.24), rgba(var(--lp-accent-rgb), 0.24));
  background-repeat: no-repeat;
  background-position: 0 92%;
  background-size: 0% 42%;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  animation: heroMarkSwipe 0.6s cubic-bezier(0.65, 0, 0.35, 1) forwards;
  animation-delay: var(--mark-delay, 0.2s);
}
.hero-mark:nth-of-type(1) { --mark-delay: 0.5s; }
.hero-mark:nth-of-type(2) { --mark-delay: 0.9s; }
.hero-mark:nth-of-type(3) { --mark-delay: 1.3s; }
@keyframes heroMarkSwipe {
  to { background-size: 100% 42%; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-mark {
    animation: none;
    background-size: 100% 42%;
  }
}

.hero-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  flex-wrap: wrap;
}
/* Bigger, bolder than the site-wide .btn-lg — this is the single most
   important click target on the page, it should look like it. Only on
   wider screens — at mobile width this made a full-width single button
   (the logged-in "Go to Dashboard" case, no second button to share the
   row with) look oversized/bloated rather than confident. */
@media (min-width: 641px) {
  .hero-cta .btn-lg {
    padding: 17px 32px;
    font-size: 16.5px;
  }
}
@media (max-width: 640px) {
  .hero-cta .btn-lg { padding: 12px 22px; font-size: 14px; }
}

.store-badges-label {
  font-size: 11px;
  color: var(--text-muted);
  letter-spacing: 0.4px;
  text-transform: uppercase;
  margin: 22px 0 10px;
}

.store-badges {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  flex-wrap: wrap;
}

.store-badge {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border: 1px solid var(--border-subtle);
  border-radius: 10px;
  opacity: 0.75;
  cursor: pointer;
  text-decoration: none;
  user-select: none;
  -webkit-user-select: none;
  transition: opacity 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.store-badge:hover {
  opacity: 1;
  border-color: var(--text-muted);
  background: var(--border-subtle);
}

.store-badge:active {
  opacity: 0.85;
}

.store-badge-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  color: var(--text-secondary);
}

.store-badge-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

/* ── Games section ── */
.landing-games { padding-top: 8px; }

.editorial-section-header { margin-bottom: 14px; }
.editorial-section-header .section-label { margin-bottom: 8px; }
/* "Beyond the Games" — centred, same typographic voice as the hero's
   "Method over motivation." emphasis (.hero-accent / .pin-detail-title):
   Playfair Display italic, indigo-dark, slight rotation, low-alpha
   skewed underline bar. */
.features-header { text-align: center; }
.features-header .section-label { display: block; }
.features-header-text {
  display: inline-block;
  font-family: 'Playfair Display', serif;
  font-style: italic;
  font-size: 26px;
  font-weight: 800;
  color: var(--lp-accent-dark);
  transform: rotate(-1.2deg);
  position: relative;
  isolation: isolate;
}
.features-header-text::after {
  content: '';
  position: absolute;
  left: -8px;
  right: -8px;
  bottom: 4px;
  top: 72%;
  background: var(--lp-accent-light, #6366F1);
  opacity: 0.22;
  z-index: -1;
  border-radius: 3px;
  transform: skewX(-8deg);
}

.games-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-bottom: 8px;
}

.game-card {
  position: relative;
  /* Dashboard cards are naturally taller thanks to the .game-play-btn
     (removed here, no purpose on a marketing page) and count badge —
     extra vertical padding restores the same overall card height/shape
     without bringing that content back. */
  padding-top: 26px;
  padding-bottom: 30px;
}

.game-card-index {
  position: absolute;
  top: 16px;
  right: 18px;
  font-family: 'Space Grotesk', sans-serif;
  font-size: 22px;
  font-weight: 700;
  color: var(--border-rule);
  line-height: 1;
}

.game-card { cursor: pointer; }
.game-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ── Game explainer modal ── */
.game-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 200;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.game-modal.open { display: flex; }

.game-modal-backdrop {
  position: absolute;
  inset: 0;
  background: var(--scrim-bg);
  opacity: 0;
  transition: opacity var(--transition-base);
}
.game-modal.open .game-modal-backdrop { opacity: 1; }

.game-modal-panel {
  position: relative;
  width: min(440px, 100%);
  max-height: 85vh;
  overflow-y: auto;
  background: var(--nav-panel-bg);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card-hover);
  padding: 32px 28px;
  transform: scale(0.96);
  opacity: 0;
  transition: transform var(--transition-slow), opacity var(--transition-slow);
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.game-modal.open .game-modal-panel {
  transform: scale(1);
  opacity: 1;
}
.game-modal-panel[data-tint="indigo"]  { background: linear-gradient(160deg, rgba(var(--indigo-rgb), 0.16) 0%, transparent 55%), var(--nav-panel-bg);  border-color: rgba(var(--indigo-rgb), 0.3);  box-shadow: var(--shadow-card-hover), 0 0 48px -16px rgba(var(--indigo-rgb), 0.4); }
.game-modal-panel[data-tint="gold"]    { background: linear-gradient(160deg, rgba(var(--gold-rgb), 0.16) 0%, transparent 55%), var(--nav-panel-bg);    border-color: rgba(var(--gold-rgb), 0.3);    box-shadow: var(--shadow-card-hover), 0 0 48px -16px rgba(var(--gold-rgb), 0.4); }
.game-modal-panel[data-tint="emerald"] { background: linear-gradient(160deg, rgba(var(--emerald-rgb), 0.16) 0%, transparent 55%), var(--nav-panel-bg); border-color: rgba(var(--emerald-rgb), 0.3); box-shadow: var(--shadow-card-hover), 0 0 48px -16px rgba(var(--emerald-rgb), 0.4); }
.game-modal-panel[data-tint="purple"]  { background: linear-gradient(160deg, rgba(var(--purple-rgb), 0.16) 0%, transparent 55%), var(--nav-panel-bg);  border-color: rgba(var(--purple-rgb), 0.3);  box-shadow: var(--shadow-card-hover), 0 0 48px -16px rgba(var(--purple-rgb), 0.4); }
.game-modal-panel[data-tint="rose"]    { background: linear-gradient(160deg, rgba(var(--rose-rgb), 0.16) 0%, transparent 55%), var(--nav-panel-bg);    border-color: rgba(var(--rose-rgb), 0.3);    box-shadow: var(--shadow-card-hover), 0 0 48px -16px rgba(var(--rose-rgb), 0.4); }
.game-modal-panel[data-tint="amber"]   { background: linear-gradient(160deg, rgba(var(--gold-rgb), 0.16) 0%, transparent 55%), var(--nav-panel-bg);    border-color: rgba(var(--gold-rgb), 0.3);    box-shadow: var(--shadow-card-hover), 0 0 48px -16px rgba(var(--gold-rgb), 0.4); }

.game-modal-close {
  position: absolute;
  top: 16px;
  right: 16px;
  width: 32px;
  height: 32px;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.game-modal-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-md);
  background: var(--bg-card);
  margin-bottom: 16px;
}
.game-modal-icon svg { color: currentColor; }
.game-modal-icon.gold    { color: var(--gold);    box-shadow: inset 0 0 0 1px rgba(var(--gold-rgb), 0.35); }
.game-modal-icon.emerald { color: var(--emerald); box-shadow: inset 0 0 0 1px rgba(var(--emerald-rgb), 0.35); }
.game-modal-icon.purple  { color: var(--purple);  box-shadow: inset 0 0 0 1px rgba(var(--purple-rgb), 0.35); }
.game-modal-icon.rose    { color: var(--rose);    box-shadow: inset 0 0 0 1px rgba(var(--rose-rgb), 0.35); }
.game-modal-icon.amber   { color: var(--gold);    box-shadow: inset 0 0 0 1px rgba(var(--gold-rgb), 0.35); }
.game-modal-icon.indigo  { color: var(--indigo);  box-shadow: inset 0 0 0 1px rgba(var(--indigo-rgb), 0.35); }

.game-modal-title {
  font-family: 'Space Grotesk', sans-serif;
  font-size: 22px;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 20px;
}

.game-modal-section-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.game-modal-steps {
  margin: 0 0 20px;
  padding-left: 20px;
  color: var(--text-secondary);
  font-size: 14px;
  line-height: 1.7;
}
.game-modal-steps li { margin-bottom: 4px; }

.game-modal-science {
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-secondary);
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
}
.game-modal-science strong { color: var(--text-primary); }

/* ──────────────────────────────────────────────
   RESPONSIVE
────────────────────────────────────────────── */
@media (max-width: 900px) {
  .games-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 640px) {
  .hero-title { font-size: 26px; }
  .hero-sub   { font-size: 14px; }
  .landing-hero { padding: 40px 12px 44px; }
  .games-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .game-card  { padding: 16px 14px; }
  .game-card-index { font-size: 14px; top: 10px; right: 10px; }
  .landing-masthead-actions .btn-text { display: none; }
  /* The card was a fixed 620px on every viewport, vertically centering
     whatever content it held. Desktop's hero roughly fills that; mobile's
     smaller hero text does not, which is the dead space above/below the
     card in the small-screen screenshot.
     Do NOT make .landing-frame-card height:auto — its content is a stack
     of position:absolute .landing-frame-section panels (so all 4 tabs
     share one box shape and cross-fade instead of resizing/reflowing).
     An absolutely positioned child contributes nothing to a height:auto
     parent, so that approach collapsed .landing-frame to ~16px while the
     card kept rendering at its old height outside the flex flow — which
     visually overlapped and hid the tab bar underneath it.
     Shrinking the fixed height instead keeps the same architecture, just
     sized to what mobile's shorter hero content actually needs (~564px at
     390px width) rather than desktop's 620px ceiling. A first attempt at
     420px was too small and clipped every tab's content with a hidden
     (non-obvious) scrollbar — measured against real content before
     picking this number. */
  .landing-frame { max-height: 628px; }
}

/* ──────────────────────────────────────────────
   STORYBOOK EDITORIAL v2 — light, glossy-book premium
   Scoped entirely to .landing-page so no other
   page/template is affected by this pass.

   Reference: oddtables.co screenshots, translated to a
   light/bright base — original Space Grotesk font/logo
   kept (not swapped), plus a serif for editorial voice,
   a warm energetic accent used sparingly (not as flat
   full-card fills), and richer glossy-book gradients.
────────────────────────────────────────────── */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,600;0,700;1,500;1,600&display=swap');

.landing-page {
  /* Matches main.css's light theme exactly — the landing page is the first
     thing an aspirant sees, so it must not run its own palette. Paper is
     #F3EEEA so raised cards read at 1.15:1 rather than 1.05:1, and ink is
     12.1:1 rather than 15.9:1 (halation over long reading). */
  --lp-paper:        #F3EEEA;
  --lp-paper-raised: #FFFFFF;
  --lp-ink:           #39322B;
  --lp-ink-soft:      #5C554F;
  --lp-ink-muted:     #706B65;
  --lp-rule:          rgba(34, 30, 24, 0.10);
  --lp-rule-strong:   rgba(34, 30, 24, 0.20);

  /* Signature accent — indigo, matching the app-wide brand accent (reverted
     from an earlier vermillion pass — vermillion changed dark theme's
     accent too, and indigo is the color that reads well on both dark and
     light without a hue shift between themes). Darker shade for hover/
     emphasis on this light paper background, same convention main.css's
     own light theme uses. */
  --lp-accent:       #4338CA;
  --lp-accent-light: #6366F1;
  --lp-accent-dark:  #372F9E;
  --lp-accent-rgb:   67, 56, 202;

  background: var(--lp-paper);
  color: var(--lp-ink);
  position: relative;
  isolation: isolate;
}

/* Navbar matches the rest of the app's navbar exactly (--navbar-bg is
   near-white in light theme, same token the dashboard uses) — it was
   previously tinted to match the page background, which made it a
   different colour from every other navbar in the app. Footer stays a
   touch darker than the page for its own visual "frame" at the bottom. */
.landing-page .landing-masthead {
  background: var(--navbar-bg);
}
body:has(.landing-page) {
  background: #F0EDE6;
}
body:has(.landing-page) .site-footer {
  background: #F0EDE6;
  border-top: 1px solid var(--lp-rule);
}
body:has(.landing-page) .landing-masthead {
  background: var(--navbar-bg);
  border-bottom-color: var(--lp-rule);
}
body:has(.landing-page) .footer-links a,
body:has(.landing-page) .footer-link-btn {
  color: var(--lp-ink-muted, #8D8779);
}
body:has(.landing-page) .footer-links a:hover,
body:has(.landing-page) .footer-link-btn:hover {
  color: var(--lp-accent, #4338CA);
}

/* Subtle "high quality book" scrollbar — thin, warm, rounded */
body:has(.landing-page) {
  scrollbar-width: thin;
  scrollbar-color: rgba(34, 30, 24, 0.22) transparent;
}
body:has(.landing-page)::-webkit-scrollbar { width: 10px; }
body:has(.landing-page)::-webkit-scrollbar-track { background: transparent; }
body:has(.landing-page)::-webkit-scrollbar-thumb {
  background: rgba(34, 30, 24, 0.18);
  border-radius: 8px;
  border: 2px solid var(--lp-paper, #FAFAF8);
}
body:has(.landing-page)::-webkit-scrollbar-thumb:hover {
  background: rgba(67, 56, 202, 0.35);
}

.landing-page,
.landing-page .hero-sub,
.landing-page .store-badge-name,
.landing-page .game-desc {
  font-family: 'Inter', sans-serif;
}

/* Editorial serif — used only for the display headline's emphasis phrase
   and section kickers, mixed with the original Space Grotesk (untouched
   for wordmark, .hero-title base weight, card index numbers) */
.landing-page .hero-accent,
.landing-page .section-label em,
.landing-page .lp-quote {
  font-family: 'Playfair Display', serif;
  font-style: italic;
}

.landing-page .hero-title {
  color: var(--lp-ink);
  font-weight: 800;
}
.landing-page .hero-accent {
  color: var(--lp-accent-dark);
  font-weight: 800;
  display: inline-block;
  transform: rotate(-1.2deg);
  position: relative;
  isolation: isolate;
}
/* This bar's comment (and its old orange/peach opacity) predates the
   switch to indigo as the single brand accent — at 0.55 opacity, indigo
   text on an indigo bar read as a heavy lavender smear rather than
   emphasis. Lowered to a thin low-alpha band, matching the muted
   .hero-mark treatment used in the paragraph below it. */
.landing-page .hero-accent::after {
  content: '';
  position: absolute;
  left: -6px;
  right: -6px;
  bottom: 2px;
  top: 72%;
  background: var(--lp-accent-light, #6366F1);
  opacity: 0.22;
  z-index: -1;
  border-radius: 3px;
  transform: skewX(-8deg);
}
.landing-page .hero-sub { color: var(--lp-ink-soft); }

.landing-page .section-label {
  font-weight: 700;
  font-size: 20px;
  color: var(--lp-ink);
  letter-spacing: 0;
  text-transform: none;
}

/* Comes after .section-label so its chip look wins over that rule's
   ink/size — .hero-kicker markup always carries .section-label too.
   Was a hardcoded near-black chip, disconnected from the indigo brand —
   now a quiet indigo-tinted pill so it reads as part of the same system
   as the buttons/links around it, not an unrelated dark badge. */
.landing-page .hero-kicker {
  color: var(--lp-accent, var(--accent));
  background: rgba(var(--lp-accent-rgb), 0.08);
  border: 1px solid rgba(var(--lp-accent-rgb), 0.22);
  font-family: 'Space Grotesk', sans-serif;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.4px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 16px 7px 12px;
  border-radius: 999px;
}
.landing-page .hero-kicker::before {
  content: '';
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--lp-accent, var(--accent));
  box-shadow: 0 0 0 3px rgba(var(--lp-accent-rgb), 0.28);
}

.landing-page .rule { border-color: var(--lp-rule); }
.landing-page .landing-masthead { border-color: var(--lp-rule); }
.landing-page .logo-svg { color: var(--lp-ink) !important; }

/* ── Glossy-book light sweep — replaces the particle/orb background ── */
.landing-page .paper-sheen {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 60% 40% at 15% 0%, rgba(var(--lp-accent-rgb), 0.08) 0%, transparent 70%),
    radial-gradient(ellipse 50% 35% at 100% 30%, rgba(99, 102, 241, 0.06) 0%, transparent 70%),
    linear-gradient(120deg, transparent 0%, transparent 35%, rgba(255,255,255,0.55) 48%, rgba(255,255,255,0.85) 50%, rgba(255,255,255,0.55) 52%, transparent 65%, transparent 100%);
  background-size: auto, auto, 260% 260%;
  background-position: 0 0, 0 0, 0% 0%;
  mix-blend-mode: multiply;
  animation: paperSweep 22s ease-in-out infinite;
}
@keyframes paperSweep {
  0%   { background-position: 0 0, 0 0, 0% 0%; }
  50%  { background-position: 0 0, 0 0, 100% 60%; }
  100% { background-position: 0 0, 0 0, 0% 0%; }
}
@media (prefers-reduced-motion: reduce) {
  .landing-page .paper-sheen { animation: none; }
}

/* ── Chapters to Play: taped sticky notes, no thread ──
   The thread never actually touched the tape, so it was pure decoration
   with nothing visually hanging from it — removed, along with both pins.
   Notes now sit flush against the TOP EDGE of the .pinboard container
   itself: the tape overlaps that edge (--pinboard-top-inset below), so
   each note reads as a piece of paper taped down right at the board's
   border, peeking over it — the "3D, pasted at the edge" look — instead
   of floating in empty space under an unconnected thread.
   Row wraps with flex + centered alignment (not a stretched grid) so a
   second row of leftover notes stays centered like the original design,
   and on narrow screens the browser wraps to more, smaller rows instead
   of shrinking every note's type to force one line. */
.landing-page .landing-games {
  /* Same column width as the hero text above it, so the whole page reads
     as one centered column instead of the games grid being wider. */
  max-width: 760px;
  margin: 0 auto;
  padding-left: 24px;
  padding-right: 24px;
  /* Own the full card height so .pin-detail can take what's left instead
     of being hard-locked to 260px while the card sat 83px empty top and
     bottom. */
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  padding-top: 18px;
  padding-bottom: 18px;
}

.landing-page .pinboard {
  /* Dropped the "tape half outside the card" idea entirely — anything
     past the card's edge just gets visually lost, it never read as
     intentional. Tape now sits fully ON the note, like real cello tape
     stuck down over a piece of paper. Generous, explicit top padding so
     the first row has real breathing room from the card's own edge
     (not fighting a second, stacked offset for tape math that no longer
     exists). */
  position: relative;
  flex: none;
  padding-top: 22px;
}
.landing-page .pinboard-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  /* Bigger ROW gap than column gap: a second row of notes needs clear
     separation from the first, not just the same spacing as the gap
     between notes sitting side by side. */
  row-gap: 30px;
  column-gap: 14px;
}

.landing-page .pin-note {
  position: relative;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-family: inherit;
  min-width: 0;
}
.landing-page .pin-note-name {
  display: block;
  padding: 14px 14px 10px;
  border-radius: 2px;
  font-family: 'Space Grotesk', sans-serif;
  font-size: 12.5px;
  font-weight: 700;
  text-align: center;
  line-height: 1.3;
  color: var(--lp-ink-soft, var(--text-secondary));
  border: none;
  background-color: var(--lp-paper-raised, #fff);
  box-shadow:
    0 1px 2px rgba(28, 23, 14, 0.12),
    0 8px 16px -6px rgba(28, 23, 14, 0.22);
  position: relative;
  transition: transform var(--transition-fast), box-shadow var(--transition-fast),
              border-color var(--transition-fast), color var(--transition-fast),
              background var(--transition-fast);
}
/* Tape-corner sticker, sized to overlap exactly --pinboard-top-inset above
   the note — since every note sits at the same padding-top from the
   board's own top edge (see .pinboard above), this tape always lands
   right on that edge, on every row, not just the first. That's the "tape
   pasted at the edge" look: the note visually starts at the board's
   border, not floating below it. */
.landing-page .pin-note-name::before {
  content: '';
  position: absolute;
  /* Tape sits fully ON the note now — half above its top edge (clear of
     the paper), half overlapping down onto it, the way real tape is
     stuck down over the top of a piece of paper. Nothing floats above
     the container edge; this is entirely within the note's own box, so
     there is no ambiguity from ancestor layout (safe-center, padding
     chains, etc.) to get wrong a third time. */
  top: -7px;
  left: 50%;
  transform: translateX(-50%) rotate(-3deg);
  width: 34px;
  height: 16px;
  /* Glassy look: translucent white + blur, so the note's colour shows
     faintly through the tape (like real translucent cello tape), plus a
     soft highlight/shadow pair for a slight sheen instead of a flat chip. */
  background: rgba(255, 255, 255, 0.38);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  border: 1px solid rgba(255, 255, 255, 0.5);
  box-shadow:
    0 1px 2px rgba(28, 23, 14, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
  pointer-events: none;
  z-index: 4;
}
.landing-page .pin-note:nth-child(7n+1) .pin-note-name { transform: rotate(-3deg); }
.landing-page .pin-note:nth-child(7n+2) .pin-note-name { transform: rotate(2.2deg); }
.landing-page .pin-note:nth-child(7n+3) .pin-note-name { transform: rotate(-1.6deg); }
.landing-page .pin-note:nth-child(7n+4) .pin-note-name { transform: rotate(2.8deg); }
.landing-page .pin-note:nth-child(7n+5) .pin-note-name { transform: rotate(-2.2deg); }
.landing-page .pin-note:nth-child(7n+6) .pin-note-name { transform: rotate(1.6deg); }
.landing-page .pin-note:nth-child(7n+7) .pin-note-name { transform: rotate(-1.2deg); }
.landing-page .pin-note:hover .pin-note-name,
.landing-page .pin-note.is-active .pin-note-name {
  transform: rotate(0deg) translateY(-3px) scale(1.04);
  box-shadow:
    0 2px 4px rgba(28, 23, 14, 0.14),
    0 14px 28px -8px rgba(28, 23, 14, 0.3);
}
/* Per-hue tint on the note at rest, stepping up through active. Uses the
   current semantic/game-accent tokens (not the old byte-identical hues). */
.landing-page .pin-note.indigo  .pin-note-name { background: rgba(var(--indigo-rgb), 0.07); }
.landing-page .pin-note.gold    .pin-note-name { background: rgba(var(--gold-rgb), 0.07); }
.landing-page .pin-note.emerald .pin-note-name { background: rgba(var(--emerald-rgb), 0.07); }
.landing-page .pin-note.purple  .pin-note-name { background: rgba(var(--purple-rgb), 0.07); }
.landing-page .pin-note.rose    .pin-note-name { background: rgba(var(--rose-rgb), 0.07); }
.landing-page .pin-note.amber   .pin-note-name { background: rgba(var(--amber-rgb), 0.07); }

.landing-page .pin-note.indigo.is-active  .pin-note-name { background: rgba(var(--indigo-rgb), 0.14);  color: var(--lp-ink); }
.landing-page .pin-note.gold.is-active    .pin-note-name { background: rgba(var(--gold-rgb), 0.14);    color: var(--lp-ink); }
.landing-page .pin-note.emerald.is-active .pin-note-name { background: rgba(var(--emerald-rgb), 0.14); color: var(--lp-ink); }
.landing-page .pin-note.purple.is-active  .pin-note-name { background: rgba(var(--purple-rgb), 0.14);  color: var(--lp-ink); }
.landing-page .pin-note.rose.is-active    .pin-note-name { background: rgba(var(--rose-rgb), 0.14);    color: var(--lp-ink); }
.landing-page .pin-note.amber.is-active   .pin-note-name { background: rgba(var(--amber-rgb), 0.14);   color: var(--lp-ink); }

/* "...and more games" — distinct/neutral, not tied to any game hue. */
.landing-page .pin-note-more .pin-note-name {
  background-color: var(--lp-paper, #F3EEEA);
  outline: 1.5px dashed var(--lp-rule-strong, rgba(34,30,24,0.24));
  outline-offset: -5px;
  color: var(--lp-ink-muted, #706B65);
  font-style: italic;
  font-weight: 600;
}
.landing-page .pin-note-more.is-active .pin-note-name,
.landing-page .pin-note-more:hover .pin-note-name {
  background-color: rgba(34, 30, 24, 0.05);
  outline-color: var(--lp-ink-muted);
  color: var(--lp-ink);
}

/* Settle-in animation: notes drop onto the thread one at a time */
.landing-page .pin-note {
  opacity: 0;
  transform: translateY(-14px);
  transition: transform var(--transition-slow), opacity var(--transition-base);
}
.landing-page .pin-note.lp-in { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  .landing-page .pin-note { opacity: 1; transform: none; }
}

/* ── Detail panel below the thread — replaces the old popup modal. Plain
   paper, no card chrome of its own, just a top rule to separate it from
   the pinboard above. TRUE fixed height (not min-height, which is only a
   floor and still let taller bodies like Map Master grow the box past a
   shorter one like Story Mode) — content that doesn't fit scrolls inside
   this panel instead of resizing it, so the box is bit-for-bit identical
   no matter which of the 7 notes is active. */
.landing-page .pin-detail {
  margin-top: 18px;
  padding-top: 22px;
  position: relative;
  text-align: left;
  /* Take the card's remaining height instead of a hard-locked 260px. That
     fixed height was the single cause of BOTH reported problems: ~83px of
     dead space above and below on short tabs, and taller panels clipping
     invisibly (overflow was scrollable but its scrollbar was hidden, so
     there was no affordance that content continued). */
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
/* If a panel genuinely overflows, show a real scrollbar — the previous
   `scrollbar-width: none` is what made the hidden content unfindable. */
.landing-page .pin-detail { scrollbar-width: thin; }
.landing-page .pin-detail::-webkit-scrollbar { width: 6px; }
.landing-page .pin-detail::-webkit-scrollbar-thumb {
  background: var(--lp-rule-strong, var(--border-rule));
  border-radius: 999px;
}
/* Replaces the old plain 1px hairline: a full-width accent rule that
   fades out at both ends, rather than a flat grey line or a short solid
   bar. Previous version used a dot sitting partly ABOVE the element's own
   top edge (negative offset) — .pin-detail scrolls (overflow-y: auto), so
   that negative-offset sliver was getting clipped by the scroll
   container, which is why only half of it ever showed. This version has
   no negative offsets: the whole rule lives inside the element's own top
   padding, so there's nothing for the scroll clip to cut off. */
.landing-page .pin-detail::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  background: linear-gradient(
    to right,
    transparent 0%,
    rgba(var(--lp-accent-rgb, 67, 56, 202), 0.35) 20%,
    rgba(var(--lp-accent-rgb, 67, 56, 202), 0.35) 80%,
    transparent 100%
  );
}
.landing-page .pin-detail::-webkit-scrollbar { display: none; }
/* Icon chip removed per feedback — the game name alone is the heading. */
.landing-page .pin-detail-icon { display: none; }
/* One heading, centred over the whole two-column block (not one per
   column — a title on each side was never the ask, it just duplicated
   the game name for no reason). Lives outside .pin-detail-body as its
   own row so it sits above both the text and the preview equally. */
/* Same treatment as the hero's "Method over motivation." emphasis
   (.hero-accent): Playfair Display italic, indigo-dark, a slight
   rotation, and a low-alpha skewed underline bar — so a game name here
   reads as the same typographic voice as the landing page's headline
   accent, not a plain bold sans heading. The div stays full-width/block
   (text-align: center lays out its inline-block child), and the actual
   rotated "chip" is an inner span so the rotation/underline don't need
   fragile self-centering math. */
.landing-page .pin-detail-title {
  text-align: center;
  margin-bottom: 24px;
}
.landing-page .pin-detail-title-text {
  display: inline-block;
  font-family: 'Playfair Display', serif;
  font-style: italic;
  font-size: 26px;
  font-weight: 800;
  color: var(--lp-accent-dark);
  transform: rotate(-1.2deg);
  position: relative;
  isolation: isolate;
}
.landing-page .pin-detail-title-text::after {
  content: '';
  position: absolute;
  left: -8px;
  right: -8px;
  bottom: 4px;
  top: 72%;
  background: var(--lp-accent-light, #6366F1);
  opacity: 0.22;
  z-index: -1;
  border-radius: 3px;
  transform: skewX(-8deg);
}
.landing-page .pin-detail-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--lp-ink-muted, var(--text-muted));
  margin-bottom: 8px;
}
/* Same size/line-height as .hero-sub (the "theupscale.app turns UPSC…"
   paragraph on the first card) — was 13px/1.4, noticeably smaller than
   that reference; matched to 16px/1.7. */
.landing-page .pin-detail-steps {
  margin: 0;
  /* Was 20px — at 16px numbered markers ("1.", "2.", "3."), that leaves
     almost no clearance against the panel's own left edge on a narrower
     column, so the number visually crowds/clips against the card
     border. list-style-position: outside (the default) plus a bit more
     padding gives the marker real breathing room instead. */
  padding-left: 26px;
  list-style-position: outside;
  color: var(--lp-ink-soft, var(--text-secondary));
  font-size: 16px;
  line-height: 1.7;
}
.landing-page .pin-detail-steps li { margin-bottom: 6px; padding-left: 2px; }
.landing-page .pin-detail-science {
  font-size: 16px;
  line-height: 1.7;
  color: var(--lp-ink-soft, var(--text-secondary));
  margin: 0;
}

/* Two-column body: description on the left, a small looping game-specific
   preview on the right — hidden on mobile (see breakpoint below), where
   the panel collapses back to description-only. */
.landing-page .pin-detail-body {
  /* One heading row (see .pin-detail-title above), then the symmetrical
     two columns beneath it: explanation left, animation right. */
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}
.landing-page .pin-detail-body[hidden] { display: none; }
.landing-page .pin-detail-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: stretch;
  gap: 28px;
  flex: 1;
  min-height: 0;
}
/* Right column (the CSS/SVG preview animation) temporarily hidden per
   feedback — it has open visual issues to fix later. Description column
   takes the full width in the meantime. Markup/CSS for the previews is
   left intact (not deleted) so this is a one-line revert once fixed:
   just remove this rule and the two lines below it. */
.landing-page .pin-detail-columns { grid-template-columns: 1fr; }
.landing-page .pin-detail-text {
  min-width: 0;
  /* With the preview column hidden, the description now has the whole
     card width — rather than stretch one text block edge to edge (hard
     to read at that line length), split "How it works" and "Why it
     works" into their own side-by-side panels. */
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 36px;
  max-width: 900px;
  margin: 0 auto;
}
.landing-page .pin-detail-how,
.landing-page .pin-detail-why {
  display: flex;
  flex-direction: column;
}
.landing-page .pin-detail-why {
  padding-left: 36px;
  border-left: 1px solid var(--lp-rule, var(--border-subtle));
}
/* "...and more games" has only a "why"-style paragraph, no "how it
   works" steps — the two-column grid it inherits from the other 6 games
   left an empty left column and pushed its text to one side. Collapse
   to a single, centred column for this one panel specifically. */
.landing-page .pin-detail-text-more {
  grid-template-columns: 1fr;
  max-width: 640px;
  text-align: center;
}
.landing-page .pin-detail-text-more .pin-detail-why {
  padding-left: 0;
  border-left: none;
}
.landing-page .pin-detail-right {
  min-width: 0;
  display: flex;
  flex-direction: column;
}
/* Placed AFTER the block above (not before) — an earlier `display: none`
   here was overridden by .pin-detail-right's own `display: flex` rule
   two lines below it, since it comes later in the cascade at equal
   specificity. The preview rendered anyway, spilling out of the card. */
.landing-page .pin-detail-right { display: none; }
.landing-page .pin-detail-preview {
  /* Fills its whole column (was 220x130 — a small tile in the corner). */
  width: 100%;
  height: 100%;
  min-height: 300px;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 14px;
  background: rgba(var(--lp-accent-rgb, 67, 56, 202), 0.03);
  border: 1px solid var(--lp-rule, var(--border-subtle));
  overflow: hidden;
}

@media (max-width: 640px) {
  .landing-page .landing-games { padding-left: 14px; padding-right: 14px; }
  .landing-page .pinboard { padding-top: 18px; }
  .landing-page .pinboard-row { row-gap: 24px; }
  /* No font-shrink hack: same note size as desktop, the flex-wrap row
     just puts fewer per line on a narrow phone (naturally 2-3 instead of
     6) rather than squeezing every note's type down to fit one row. */
  .landing-page .pinboard-row { gap: 12px 8px; }
  /* Single column on phones: the preview stacks under the text rather than
     being hidden, so the animation is still part of the story. */
  .landing-page .pin-detail-body {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  .landing-page .pin-detail-body[hidden] { display: none; }
  .landing-page .pin-detail-preview {
    width: 100%;
    height: 200px;
  }
  /* How/Why side-by-side (desktop) becomes two narrow, unreadable
     columns on a phone — stack them instead. Text stays at the full
     16px/1.7 size; only if that still doesn't fit does the size step
     down (see the two rules below), and actual scrolling is the last
     resort under that, not the first response to a narrow screen. */
  .landing-page .pin-detail-text {
    grid-template-columns: 1fr;
    gap: 20px;
    max-width: none;
  }
  .landing-page .pin-detail-why {
    padding-left: 0;
    border-left: none;
    padding-top: 16px;
    border-top: 1px solid var(--lp-rule, var(--border-subtle));
  }
}

/* Fallback ONLY: if 16px/1.7 still doesn't fit a short phone viewport,
   step the size down before ever resorting to scroll. JS toggles this
   class on .pin-detail once per active game, comparing scrollHeight to
   clientHeight — see the toggleDetailOverflow() function below. */
.landing-page .pin-detail.is-tight .pin-detail-steps,
.landing-page .pin-detail.is-tight .pin-detail-science {
  font-size: 14px;
  line-height: 1.5;
}
.landing-page .pin-detail.is-tight .pin-detail-steps { padding-left: 22px; }
.landing-page .pin-detail.is-tight .pin-detail-text { gap: 12px; }
.landing-page .pin-detail.is-tight .pin-detail-why { padding-top: 12px; }

/* ── Pin-detail preview animations — one small, self-looping, pure-CSS
   demo per game, evoking that game's real mechanic. No images, no JS.
   All six share a common footprint via .pin-detail-preview (220×130,
   set above) so every game's animation occupies the same box. ── */

/* Story Mode (indigo) — the cover opens once to a comfortable ~100deg
   (a book propped open, readable), holds, then a "+10 XP" toast lands and
   the loop resets. Deliberately simple per feedback: no page-turning, no
   closing — the previous version rotated the cover to -165deg (almost a
   full flip edge-on to the camera), so screenshots caught it mid-spin
   showing only the book's paper-thin side profile instead of the cover. */
.landing-page .lp-preview-book {
  width: 130px; height: 150px; position: relative; perspective: 900px;
  margin: 0 auto;
}
.landing-page .lp-book-spine {
  position: absolute; left: 8px; top: 6px; bottom: 6px; width: 9px;
  background: var(--indigo-dark, #4338CA); border-radius: 2px 0 0 2px;
  box-shadow: 2px 0 5px rgba(28,23,14,0.2);
}
.landing-page .lp-book-page {
  position: absolute; left: 16px; right: 6px; top: 6px; bottom: 6px;
  background: #fdfcf8; border: 1px solid rgba(28,23,14,0.1);
  border-radius: 0 3px 3px 0;
}
.landing-page .lp-book-page::after {
  content: ''; position: absolute; inset: 12px 10px auto 10px; height: 62%;
  background: repeating-linear-gradient(
    to bottom,
    rgba(28,23,14,0.13) 0 1px,
    transparent 1px 8px);
  border-radius: 2px;
}
.landing-page .lp-book-page-1 { box-shadow: 1px 0 3px rgba(28,23,14,0.10); }
.landing-page .lp-book-page-2 { background: #f7f5ee; }
.landing-page .lp-book-cover {
  position: absolute; left: 8px; right: 6px; top: 6px; bottom: 6px;
  background: linear-gradient(135deg, var(--indigo, #6366F1), var(--indigo-dark, #4338CA));
  border-radius: 2px 3px 3px 2px;
  transform-origin: left center;
  box-shadow: 0 6px 16px -4px rgba(var(--indigo-rgb), 0.5);
  animation: lpBookCoverOpen 4s ease-in-out infinite;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
}
.landing-page .lp-book-title-mark { width: 40px; height: 3px; border-radius: 2px; background: rgba(255,255,255,0.82); }
.landing-page .lp-book-title-mark-2 { width: 26px; }
.landing-page .lp-book-xp {
  position: absolute; top: 4px; right: -8px; font-size: 12px; font-weight: 800;
  color: var(--emerald, #00754A);
  background: var(--lp-paper-raised, #fff); border-radius: 999px; padding: 3px 10px;
  box-shadow: 0 3px 10px rgba(28,23,14,0.18); opacity: 0;
  animation: lpBookXp 4s ease-in-out infinite;
}
/* Opens to -100deg (a book propped open, cover still readable at an
   angle) rather than -165deg (almost fully flipped, edge-on to camera). */
@keyframes lpBookCoverOpen {
  0%, 15%   { transform: rotateY(0deg); }
  40%, 100% { transform: rotateY(-100deg); }
}
@keyframes lpBookXp {
  0%, 48%   { opacity: 0; transform: translateY(5px) scale(0.85); }
  58%, 92%  { opacity: 1; transform: translateY(0) scale(1); }
  100%      { opacity: 0; transform: translateY(-5px) scale(0.9); }
}

/* Flashcard Duel (gold) — the real 3D-flip recipe, plus the actual
   4-button rating row underneath (Again/Hard/Good/Easy), one of which
   lights up right as the card settles on its answer face — echoes the
   real in-app flashcard interaction end-to-end, not just the card. */
.landing-page .lp-preview-flip { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px; }
.landing-page .lp-flip-scene { width: 268px; height: 168px; perspective: 1400px; }
.landing-page .lp-flip-card {
  width: 100%; height: 100%; position: relative; transform-style: preserve-3d;
  border-radius: 14px; animation: lpFlipLoop 4.2s cubic-bezier(0.4, 0.2, 0.2, 1) infinite;
}
.landing-page .lp-flip-face {
  position: absolute; inset: 0; border-radius: 14px;
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; padding: 18px; text-align: center;
  background: var(--lp-paper-raised, #fff); border: 1px solid rgba(var(--gold-rgb), 0.28);
  box-shadow: 0 8px 18px rgba(28, 23, 14, 0.14);
}
.landing-page .lp-flip-back { transform: rotateY(180deg); background: rgba(var(--gold-rgb), 0.09); }
.landing-page .lp-flip-label { font-size: 11px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--gold); }
.landing-page .lp-flip-text { font-family: 'Space Grotesk', sans-serif; font-size: 19px; font-weight: 700; line-height: 1.3; color: var(--lp-ink); }
@keyframes lpFlipLoop {
  0%, 35%  { transform: rotateY(0deg); }
  48%, 88% { transform: rotateY(180deg); }
  100%     { transform: rotateY(360deg); }
}
.landing-page .lp-flip-ratings { display: flex; gap: 8px; }
.landing-page .lp-flip-rating-btn {
  font-size: 11px; font-weight: 700; padding: 7px 13px; border-radius: 7px;
  border: 1px solid var(--lp-rule-strong, rgba(34,30,24,0.24)); color: var(--lp-ink-muted, #8D8779);
  transition: none;
}
.landing-page .lp-flip-rating-again { color: #fff; border-color: var(--rose); background: var(--rose); }
.landing-page .lp-flip-rating-good  { color: #fff; border-color: var(--emerald); background: var(--emerald); }
.landing-page .lp-flip-rating-again,
.landing-page .lp-flip-rating-good { opacity: 0.25; animation: lpFlipRatingPulse 4.2s ease-in-out infinite; }
.landing-page .lp-flip-rating-good { animation-delay: 0.05s; }
@keyframes lpFlipRatingPulse {
  0%, 48%, 88%, 100% { opacity: 0.25; transform: scale(1); box-shadow: none; }
  58%, 78%           { opacity: 1; transform: scale(1.08); box-shadow: 0 4px 10px -2px currentColor; }
}

/* Knock Knock! (emerald) — a door that knocks, then swings open on a 3D
   hinge to reveal the ANSWER as a real illustration (the Red Fort, matching
   the clue chips below), while the clues stack in one at a time. The reveal
   used to be an anonymous grey blob, which gave the mechanic no payoff. */
.landing-page .lp-preview-door {
  width: 100%; height: 100%; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 16px;
}
.landing-page .lp-door-scene { width: 190px; height: 226px; perspective: 760px; }
.landing-page .lp-door-frame {
  width: 100%; height: 100%; border: 5px solid var(--emerald, #00754A);
  border-radius: 10px 10px 2px 2px; position: relative; overflow: hidden;
  background: rgba(var(--emerald-rgb), 0.06);
}
/* The answer behind the door: the generated Red Fort line art. It is a
   transparent PNG, so `currentColor` via the mask keeps it theme-aware
   instead of baking in a stroke colour. */
.landing-page .lp-door-silhouette {
  position: absolute; left: 50%; top: 50%; width: 150px; height: 62px;
  transform: translate(-50%, -50%);
  background-color: var(--lp-ink, var(--text-primary));
  -webkit-mask: url("/static/img/illustrations/red-fort.0cbcdd597c08.png") center / contain no-repeat;
  mask: url("/static/img/illustrations/red-fort.0cbcdd597c08.png") center / contain no-repeat;
  opacity: 0;
  animation: lpDoorRevealIn 4.4s ease-in-out infinite;
  border-radius: 0;
}
@keyframes lpDoorRevealIn {
  0%, 26%   { opacity: 0; transform: translate(-50%, -46%) scale(0.96); }
  38%, 76%  { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  88%, 100% { opacity: 0; transform: translate(-50%, -46%) scale(0.96); }
}
.landing-page .lp-door {
  width: 100%; height: 100%;
  background: linear-gradient(135deg, var(--emerald, #00754A), var(--emerald-light, #00754A));
  border-right: 3px solid rgba(0,0,0,0.2); transform-origin: left center;
  position: relative; animation: lpDoorKnock 4.4s ease-in-out infinite;
  box-shadow: 5px 0 18px rgba(28,23,14,0.22);
}
/* Two recessed panels, so the door reads as a door rather than a slab. */
.landing-page .lp-door::before,
.landing-page .lp-door::after {
  content: ''; position: absolute; left: 16px; right: 26px;
  border: 2px solid rgba(255, 255, 255, 0.22); border-radius: 3px;
}
.landing-page .lp-door::before { top: 18px; height: 38%; }
.landing-page .lp-door::after  { bottom: 18px; height: 30%; }
.landing-page .lp-door-knob {
  position: absolute; right: 12px; top: 50%; width: 11px; height: 11px; border-radius: 50%;
  background: #fff9e8; transform: translateY(-50%); box-shadow: 0 0 0 2px rgba(28,23,14,0.2);
  z-index: 1;
}
@keyframes lpDoorKnock {
  0%, 6%, 12%   { transform: rotateY(0deg) translateX(0); }
  3%, 9%        { transform: rotateY(0deg) translateX(5px); }
  30%, 78%      { transform: rotateY(-105deg); }
  92%, 100%     { transform: rotateY(0deg); }
}
.landing-page .lp-door-clues { display: flex; flex-direction: column; gap: 6px; width: 190px; }
.landing-page .lp-door-clue {
  font-size: 12px; font-weight: 600; color: var(--lp-ink-soft, var(--text-secondary));
  background: rgba(var(--emerald-rgb), 0.09); border-left: 3px solid var(--emerald);
  border-radius: 0 6px 6px 0; padding: 6px 11px; opacity: 0; transform: translateX(-8px);
  animation: lpDoorClueIn 4.4s ease-in-out infinite;
}
.landing-page .lp-door-clue-1 { animation-delay: 0.1s; }
.landing-page .lp-door-clue-2 { animation-delay: 0.5s; }
.landing-page .lp-door-clue-3 { animation-delay: 0.9s; font-weight: 800; color: var(--lp-ink); }
@keyframes lpDoorClueIn {
  0%, 2%    { opacity: 0; transform: translateX(-8px); }
  10%, 88%  { opacity: 1; transform: translateX(0); }
  96%, 100% { opacity: 0; transform: translateX(-8px); }
}

/* Map Master (purple) — the real India silhouette (simplified from the
   app's own india_states.geojson used by the actual Map Master game),
   with a location pin dropping onto New Delhi.

   Two bugs fixed here: the "pin" was #icon-map, the FOLDED-MAP glyph (a
   map pointing at a map), now #icon-pin; and the pin, its ripple and its
   label sat at three different `top` values (6% / 24% / 30%) so nothing
   lined up. All three now hang off ONE anchor (--lp-map-x/--lp-map-y) and
   are positioned relative to it, so they cannot drift apart again. */
.landing-page .lp-preview-map {
  /* New Delhi's position within the India viewBox. */
  --lp-map-x: 38%;
  --lp-map-y: 27%;
  width: 100%; height: 100%; max-width: 300px; max-height: 300px;
  position: relative;
}
.landing-page .lp-map-india {
  position: absolute; inset: 0; width: 100%; height: 100%;
  filter: drop-shadow(0 6px 14px rgba(var(--purple-rgb), 0.20));
}
/* The shared anchor point every marker is measured from. */
.landing-page .lp-map-pin,
.landing-page .lp-map-ripple,
.landing-page .lp-map-label {
  position: absolute;
  left: var(--lp-map-x);
  top: var(--lp-map-y);
}
.landing-page .lp-map-pin {
  width: 34px; height: 34px; color: var(--purple);
  /* translate(-50%,-100%) puts the pin's TIP on the anchor, which is what
     a location pin means — the old rule centred the whole glyph on it. */
  margin: 0; transform: translate(-50%, -100%);
  animation: lpMapDrop 3.6s ease-in-out infinite;
  filter: drop-shadow(0 4px 6px rgba(28,23,14,0.3));
}
.landing-page .lp-map-pin svg { width: 100%; height: 100%; display: block; }
.landing-page .lp-map-ripple {
  width: 26px; height: 10px; border-radius: 50%;
  background: transparent; box-shadow: 0 0 0 2px rgba(var(--purple-rgb), 0.45);
  transform: translate(-50%, -50%) scale(0);
  animation: lpMapRipple 3.6s ease-in-out infinite;
}
.landing-page .lp-map-label {
  transform: translate(-50%, 14px);
  font-size: 12px; font-weight: 800; color: #fff; background: var(--purple);
  border-radius: 999px; padding: 4px 12px; white-space: nowrap;
  opacity: 0; animation: lpMapLabel 3.6s ease-in-out infinite;
  box-shadow: 0 4px 12px rgba(var(--purple-rgb), 0.4);
}
@keyframes lpMapDrop {
  0%, 15%   { transform: translate(-50%, calc(-100% - 42px)); }
  42%       { transform: translate(-50%, calc(-100% + 6px)); }
  50%       { transform: translate(-50%, calc(-100% - 5px)); }
  58%, 100% { transform: translate(-50%, -100%); }
}
@keyframes lpMapRipple {
  0%, 40%   { opacity: 0; transform: translate(-50%, -50%) scale(0); }
  48%       { opacity: 1; transform: translate(-50%, -50%) scale(1); }
  75%, 100% { opacity: 0; transform: translate(-50%, -50%) scale(2.4); }
}
@keyframes lpMapLabel {
  0%, 44%   { opacity: 0; transform: translate(-50%, 20px); }
  55%, 92%  { opacity: 1; transform: translate(-50%, 14px); }
  100%      { opacity: 0; transform: translate(-50%, 20px); }
}

/* 3 Truths & 1 Lie (rose) — four real statement cards (not abstract
   dots), the false one pulsing and marked with an X on a loop, matching
   the actual "spot the false statement" mechanic. */
.landing-page .lp-preview-truths { display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 320px; }
.landing-page .lp-truth-card {
  display: flex; align-items: center; gap: 12px;
  font-size: 15px; font-weight: 600; color: var(--lp-ink-soft, var(--text-secondary));
  background: rgba(var(--rose-rgb), 0.06); border: 1px solid rgba(var(--rose-rgb), 0.18);
  border-radius: 10px; padding: 13px 16px; position: relative;
}
.landing-page .lp-truth-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--rose); flex: 0 0 auto; }
.landing-page .lp-truth-lie { animation: lpTruthPulse 3.6s ease-in-out infinite; }
.landing-page .lp-truth-x {
  margin-left: auto; color: #fff; background: var(--rose); border-radius: 50%;
  width: 22px; height: 22px; font-size: 13px; line-height: 22px; text-align: center; font-weight: 800;
  opacity: 0; transform: scale(0.5); animation: lpTruthX 3.6s ease-in-out infinite;
}
@keyframes lpTruthPulse {
  0%, 50%   { border-color: rgba(var(--rose-rgb), 0.18); box-shadow: none; }
  65%, 90%  { border-color: var(--rose); box-shadow: 0 4px 12px -3px rgba(var(--rose-rgb), 0.5); }
  100%      { border-color: rgba(var(--rose-rgb), 0.18); box-shadow: none; }
}
@keyframes lpTruthX {
  0%, 55%   { opacity: 0; transform: scale(0.5); }
  68%, 88%  { opacity: 1; transform: scale(1); }
  100%      { opacity: 0; transform: scale(0.5); }
}

/* Match Maker (amber) — two columns of real term/definition tiles with an
   SVG connector drawing itself between each matched pair.

   The pairs were factually WRONG: the connectors joined Article 21 to Free
   Speech and Article 32 to Right to Life, and the CSS pulse agreed with
   that (a1+b1, a2+b2). Correct constitutional pairs, now used by both the
   connectors and the pulse timing:
     Article 21 -> Right to Life  (row 1 -> row 1)
     Article 32 -> Remedies       (row 2 -> row 3)
     Article 19 -> Free Speech    (row 3 -> row 2)   */
.landing-page .lp-preview-match {
  width: 100%; height: 100%; position: relative; display: grid;
  grid-template-columns: 150px 60px 150px;
  justify-content: center; align-items: center; gap: 0;
}
.landing-page .lp-match-col { display: flex; flex-direction: column; gap: 18px; min-width: 0; width: 150px; }
.landing-page .lp-match-col-right { align-items: stretch; }
.landing-page .lp-match-tile {
  font-size: 13px; font-weight: 700; color: var(--lp-ink-soft, var(--text-secondary));
  background: rgba(var(--amber-rgb), 0.09); border: 1px solid rgba(var(--amber-rgb), 0.28);
  border-radius: 9px; padding: 11px 10px; white-space: nowrap; width: 100%; box-sizing: border-box;
  overflow: hidden; text-overflow: ellipsis; text-align: center;
}
/* Pair 1: Article 21 (a1) with Right to Life (b1). */
.landing-page .lp-match-tile-a1,
.landing-page .lp-match-tile-b1 { animation: lpMatchTilePulse 3.8s ease-in-out infinite; }
/* Pair 2: Article 32 (a2) with Remedies (b3) — NOT b2. */
.landing-page .lp-match-tile-a2,
.landing-page .lp-match-tile-b3 { animation: lpMatchTilePulse 3.8s ease-in-out infinite 1.3s; }
@keyframes lpMatchTilePulse {
  0%, 6%, 40%, 100% { border-color: rgba(var(--amber-rgb), 0.28); background: rgba(var(--amber-rgb), 0.09); }
  16%, 30%          { border-color: var(--amber); background: rgba(var(--amber-rgb), 0.22); box-shadow: 0 5px 14px -3px rgba(var(--amber-rgb), 0.5); }
}
.landing-page .lp-match-lines { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.landing-page .lp-match-line {
  fill: none; stroke: var(--amber, #A65706); stroke-width: 2.5; stroke-linecap: round;
  stroke-dasharray: 120; stroke-dashoffset: 120; opacity: 0;
}
.landing-page .lp-match-line-1 { animation: lpMatchLineDraw 3.8s ease-in-out infinite; }
.landing-page .lp-match-line-2 { animation: lpMatchLineDraw 3.8s ease-in-out infinite 1.3s; }
@keyframes lpMatchLineDraw {
  0%, 8%   { opacity: 0; stroke-dashoffset: 120; }
  20%, 34% { opacity: 1; stroke-dashoffset: 0; }
  42%, 100%{ opacity: 0; stroke-dashoffset: 0; }
}

/* "...and more games" preview + icon styling (pin-note itself is styled
   near the other pin-note hue rules above). */
.landing-page .pin-detail-icon-more { background: rgba(34, 30, 24, 0.06); color: var(--lp-ink-muted, #8D8779); }
.landing-page .pin-detail-preview-more { display: flex; align-items: center; gap: 6px; }
.landing-page .lp-more-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--lp-ink-muted, #8D8779);
  animation: lpMoreDotPulse 1.4s ease-in-out infinite;
}
.landing-page .lp-more-dot:nth-child(2) { animation-delay: 0.2s; }
.landing-page .lp-more-dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes lpMoreDotPulse { 0%, 100% { opacity: 0.25; transform: scale(0.8); } 50% { opacity: 1; transform: scale(1); } }

@media (prefers-reduced-motion: reduce) {
  .landing-page .lp-book-cover, .landing-page .lp-book-xp,
  .landing-page .lp-flip-card, .landing-page .lp-flip-rating-again, .landing-page .lp-flip-rating-good,
  .landing-page .lp-door, .landing-page .lp-door-clue,
  .landing-page .lp-map-pin, .landing-page .lp-map-ripple, .landing-page .lp-map-label,
  .landing-page .lp-truth-lie, .landing-page .lp-truth-x,
  .landing-page .lp-match-tile-a1, .landing-page .lp-match-tile-b1, .landing-page .lp-match-tile-a2, .landing-page .lp-match-tile-b2,
  .landing-page .lp-match-line, .landing-page .lp-more-dot {
    animation: none;
  }
}

/* ── Buttons — bold, high-contrast, real depth: a saturated gradient fill
   with a strong ambient glow in the accent hue, not just a thin drop
   shadow, so the CTA reads as the clear, confident focal point. ── */
.landing-page .btn {
  border-radius: 10px;
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 700;
  letter-spacing: 0.1px;
}

.landing-page .btn-primary {
  background: linear-gradient(160deg, var(--lp-accent-light) 0%, var(--lp-accent) 55%, var(--lp-accent-dark) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    inset 0 -2px 0 rgba(0, 0, 0, 0.16),
    0 10px 24px -6px rgba(var(--lp-accent-rgb), 0.55),
    0 2px 8px rgba(var(--lp-accent-rgb), 0.35);
  transition: transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.18s ease;
}
.landing-page .btn-primary:hover {
  background: linear-gradient(160deg, #7C7BFA 0%, var(--lp-accent-light) 55%, var(--lp-accent) 100%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 -2px 0 rgba(0, 0, 0, 0.16),
    0 16px 36px -6px rgba(var(--lp-accent-rgb), 0.6),
    0 4px 12px rgba(var(--lp-accent-rgb), 0.4);
  transform: translateY(-2px) scale(1.015);
}
.landing-page .btn-primary:active {
  transform: translateY(0) scale(0.98);
}

.landing-page .btn-ghost {
  background: var(--lp-paper-raised);
  color: var(--lp-ink);
  border: 2px solid var(--lp-rule-strong);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6);
  transition: transform 0.18s ease, border-color 0.18s ease;
}
.landing-page .btn-ghost:hover {
  background: var(--lp-paper-raised);
  border-color: var(--lp-ink);
  transform: translateY(-2px);
}
.landing-page .btn-ghost:active {
  transform: translateY(0);
}

.landing-page .btn-text { color: var(--lp-accent-dark); }
.landing-page .btn-text:hover { color: var(--lp-accent); }

/* ── Store badges — spine-label chip ── */
.landing-page .store-badge {
  background: var(--lp-paper-raised);
  border: 1.5px solid var(--lp-rule);
  border-radius: 6px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 1px 2px rgba(36, 28, 16, 0.06);
  opacity: 0.85;
}
.landing-page .store-badge:hover {
  opacity: 1;
  border-color: var(--lp-ink-muted);
  background: var(--lp-paper-raised);
}
.landing-page .store-badge-icon,
.landing-page .store-badge-name { color: var(--lp-ink-soft); }
.landing-page .store-badges-label { color: var(--lp-ink-muted); }

/* ── Game explainer modal — flat paper panel, no blur/glow ── */
.landing-page .game-modal-panel {
  background: var(--lp-paper-raised);
  border: 1.5px solid var(--lp-rule);
  box-shadow: 0 12px 32px rgba(36, 28, 16, 0.18);
}
.landing-page .game-modal-panel[data-tint] { background: var(--lp-paper-raised); }
.landing-page .game-modal-title {
  color: var(--lp-ink);
  font-family: 'Space Grotesk', sans-serif;
}
.landing-page .game-modal-section-label { color: var(--lp-ink-muted); }
.landing-page .game-modal-steps { color: var(--lp-ink-soft); }
.landing-page .game-modal-science {
  color: var(--lp-ink-soft);
  border-color: var(--lp-rule);
}
.landing-page .game-modal-science strong { color: var(--lp-ink); }
.landing-page .game-modal-close {
  background: var(--lp-paper);
  border-color: var(--lp-rule);
  color: var(--lp-ink-soft);
}

