/* ============================================================
   SITE.CSS — Gridpanions marketing / account pages
   Shared by welcome.html, login.html, signup.html, join.html.
   Standalone — not shared with the in-game screens (style.css /
   theme*.css), so game theming can never leak into these pages.
   ============================================================ */

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

:root {
  --site-bg:        #f0f4f8;
  --site-surface:   #ffffff;
  --site-border:    #d1dbe8;
  --site-text:      #1a2332;
  --site-muted:     #5a6a7e;
  --site-radius:    14px;
  --site-shadow:    0 4px 20px rgba(0,0,0,0.10);
  --site-accent:    #BA7517;
  --site-accent-dark: #8A5010;
  --site-danger:    #c0392b;
  --site-success:   #1D9E75;
}

body {
  font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
  background: var(--site-bg);
  color: var(--site-text);
  min-height: 100vh;
}

a { color: inherit; }

/* ── HERO ─────────────────────────────────────────────────── */

.hero {
  /* Branded header gradient — deep navy into grid blue into a
     purple-blue, echoing the coordinate-grid theme of the game itself
     rather than a generic dark blue. Shared with home.css's .hero for
     a consistent look across both of the site's main brand pages. */
  background: linear-gradient(135deg, #102A43 0%, #244A8F 55%, #493D9E 100%);
  color: #fff;
  padding: 2.5rem 1.5rem 2.2rem;
  text-align: center;
  position: relative;
  overflow: hidden;
}

/* A faint square coordinate-grid overlay (nodding at Gridpanions' own
   coordinate-game concept without competing with the logo) plus a
   handful of small, low-opacity gold sparkle accents kept toward the
   banner's outer edges — never inside the vertical strip the logo/
   headline actually sit in. */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect x='0.5' y='0.5' width='39' height='39' fill='none' stroke='%23ffffff' stroke-opacity='0.07' stroke-width='1'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22'%3E%3Cpath d='M11,0 L13,9 L22,11 L13,13 L11,22 L9,13 L0,11 L9,9 Z' fill='%23F4B942' fill-opacity='0.5'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16'%3E%3Cpath d='M8,0 L10,6 L16,8 L10,10 L8,16 L6,10 L0,8 L6,6 Z' fill='%23F4B942' fill-opacity='0.4'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14'%3E%3Cpath d='M7,0 L9,5 L14,7 L9,9 L7,14 L5,9 L0,7 L5,5 Z' fill='%23F4B942' fill-opacity='0.45'/%3E%3C/svg%3E");
  background-repeat: repeat, no-repeat, no-repeat, no-repeat;
  background-position: 0 0, 6% 14%, 93% 20%, 90% 82%;
  background-size: 40px 40px, 22px 22px, 16px 16px, 14px 14px;
  /* Purely decorative — must never intercept taps meant for the
     account-bar's Log out link (or anything else) layered on top of it. */
  pointer-events: none;
}

/* Soft teal/cyan glow centred behind the logo, so it lifts off the
   darker gradient instead of blending into it. */
.hero::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 55% 42% at 50% 28%, rgba(100,204,197,0.32), transparent 70%);
  pointer-events: none;
}

.hero-inner {
  max-width: 820px;
  margin: 0 auto;
  position: relative;
  /* Above the ::before/::after decorative layers (grid pattern, sparkles,
     teal glow) — those are both absolutely-positioned with z-index:auto,
     which without this would paint ::after (the glow) on top of the
     logo/text instead of behind it. */
  z-index: 1;
}

.site-title {
  font-size: clamp(2.1rem, 6vw, 3.2rem);
  font-weight: 800;
  letter-spacing: -0.5px;
  margin-bottom: 0.6rem;
}

/* logos/logo1.png — transparent-background wordmark + mascot lockup,
   used in place of the plain text title. Width-clamped rather than a
   fixed size so it scales with the viewport like .site-title did. */
.site-logo {
  display: block;
  width: clamp(220px, 42vw, 480px);
  height: auto;
  margin: 0 auto 0.6rem;
}

.site-tagline {
  font-size: 1.1rem;
  opacity: 0.85;
  margin-bottom: 2.2rem;
  max-width: 520px;
  margin-left: auto;
  margin-right: auto;
}

/* The brand slogan — sits above the descriptive tagline, so it gets its
   own margin (not the shared 2.2rem below the descriptive line) and a
   bolder look to read as the headline, not body copy. */
.site-slogan {
  font-size: 1.3rem;
  font-weight: 700;
  opacity: 1;
  margin-bottom: 0.6rem;
  color: #ffd98a;
}

/* ── CTA BUTTON ROW ───────────────────────────────────────── */

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  justify-content: center;
  margin-bottom: 1.2rem;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  /* Fluid via clamp() (min, preferred-with-vw, max) rather than a fixed
     rem size + breakpoint jumps — scales smoothly from a small phone up
     to a wide desktop instead of snapping between a couple of fixed
     sizes, so welcome/login/signup/join's buttons never look cramped on
     a narrow screen or tiny relative to the space on a big one. */
  padding: clamp(0.7rem, 0.55rem + 0.6vw, 0.95rem) clamp(1.2rem, 0.9rem + 1.2vw, 1.7rem);
  border-radius: 100px;
  font-size: clamp(0.92rem, 0.85rem + 0.25vw, 1.05rem);
  font-weight: 700;
  border: 2px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.12s ease, box-shadow 0.12s ease, background 0.12s ease;
}
.btn:hover { transform: translateY(-1px); }

/* welcome.html's hero row (Join a Game / Log In / Sign Up / Host a
   Game) — bigger than the shared .btn base so login.html/signup.html/
   join.html's own buttons (same .btn/.btn-gold classes) aren't affected.
   Same fluid clamp() approach, just with a bigger range. */
.cta-row .btn {
  padding: clamp(0.85rem, 0.6rem + 1vw, 1.15rem) clamp(1.5rem, 1rem + 2vw, 2.3rem);
  font-size: clamp(1rem, 0.85rem + 0.6vw, 1.3rem);
}

.btn-gold {
  background: linear-gradient(135deg, var(--site-accent), var(--site-accent-dark));
  color: #fff;
  box-shadow: 0 3px 12px rgba(0,0,0,0.25);
}
.btn-gold:hover { background: linear-gradient(135deg, var(--site-accent-dark), var(--site-accent-dark)); }

.btn-light {
  background: rgba(255,255,255,0.12);
  border-color: rgba(255,255,255,0.55);
  color: #fff;
}
.btn-light:hover { background: rgba(255,255,255,0.22); }

/* Brand-blue login CTA — a fixed colour (not tied to --site-accent, which
   varies per page/theme) so "Log In" always reads the same regardless of
   which page it appears on. Same navy/grid-blue/purple gradient +
   coordinate-grid pattern as the hero banner, instead of a flat two-tone
   blue, so it reads as cut from the same branded surface. */
.btn-login-accent {
  background-color: #244A8F;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Crect x='0.5' y='0.5' width='19' height='19' fill='none' stroke='%23ffffff' stroke-opacity='0.18' stroke-width='1'/%3E%3C/svg%3E"),
    linear-gradient(135deg, #102A43 0%, #244A8F 55%, #493D9E 100%);
  background-repeat: repeat, no-repeat;
  background-size: 20px 20px, 100% 100%;
  border-color: transparent;
  color: #fff;
  box-shadow: 0 3px 12px rgba(62,117,173,0.4);
}
.btn-login-accent:hover {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Crect x='0.5' y='0.5' width='19' height='19' fill='none' stroke='%23ffffff' stroke-opacity='0.18' stroke-width='1'/%3E%3C/svg%3E"),
    linear-gradient(135deg, #0c2032 0%, #1c3c74 55%, #402f85 100%);
  background-repeat: repeat, no-repeat;
  background-size: 20px 20px, 100% 100%;
}

.btn-outline-light {
  background: transparent;
  border-color: rgba(255,255,255,0.55);
  color: #fff;
}
.btn-outline-light:hover { background: rgba(255,255,255,0.14); }

/* Same idea as .btn-outline-light but for use on a light card background
   (e.g. upgrade.html's "continue with Free" link). */
.btn-outline {
  background: transparent;
  border-color: var(--site-border);
  color: var(--site-text);
}
.btn-outline:hover { background: #f3f3f3; }

.secondary-link-row {
  font-size: 0.85rem;
  opacity: 0.75;
}
.secondary-link-row a { text-decoration: underline; text-underline-offset: 2px; }
.secondary-link-row a:hover { opacity: 0.9; }

/* ── TEACHER SIDE PANEL (welcome.html, signed-in teachers only) ──
   Fixed sidebar ≥900px wide viewports; a slide-in drawer (toggle +
   backdrop) below that, since a fixed 220px sidebar would eat too
   much of a phone/tablet screen to stay permanently visible. */

.teacher-side-panel {
  /* Same navy → grid-blue → purple-blue gradient as the main hero
     banner (.hero), so the side menu reads as the same brand surface
     rather than a plain dark sidebar. */
  background: linear-gradient(135deg, #102A43 0%, #244A8F 55%, #493D9E 100%);
  position: fixed;
  top: 0; left: 0; bottom: 0;
  width: 260px;
  color: #fff;
  padding: 1.6rem 1.25rem;
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  overflow-y: auto;
  transform: translateX(-100%);
  transition: transform 0.25s ease;
}
.teacher-side-panel.open { transform: translateX(0); }
/* A faint echo of the hero banner's coordinate-grid overlay — kept to
   z-index:-1 so it sits behind the panel's own links/buttons (which are
   plain in-flow content, not individually positioned) instead of
   covering them. */
.teacher-side-panel::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Crect x='0.5' y='0.5' width='39' height='39' fill='none' stroke='%23ffffff' stroke-opacity='0.06' stroke-width='1'/%3E%3C/svg%3E");
  pointer-events: none;
}

.teacher-panel-signedin {
  font-size: 0.92rem;
  opacity: 0.85;
  margin-bottom: 1.1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid rgba(255,255,255,0.15);
}
.teacher-panel-signedin strong { font-weight: 700; }

.teacher-panel-heading {
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  opacity: 0.55;
  margin-bottom: 0.5rem;
}

.teacher-panel-link {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.8rem 0.75rem;
  border-radius: 8px;
  color: #fff;
  text-decoration: none;
  font-size: 1.02rem;
  font-weight: 600;
  background: rgba(255,255,255,0.06);
}
.teacher-panel-link:hover { background: rgba(255,255,255,0.14); }
/* Pins Log out to the bottom of the panel (a flex column — see
   .teacher-side-panel above) regardless of how many links sit above it,
   reading as a clearly separate, deliberate exit action rather than
   just the next item in the list. */
.teacher-panel-link[data-account-logout] { margin-top: auto; }

.teacher-panel-close {
  align-self: flex-end;
  background: none;
  border: none;
  color: #fff;
  opacity: 0.7;
  font-size: 1.25rem;
  cursor: pointer;
  padding: 0.3rem;
  margin-bottom: 0.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.teacher-panel-close:hover { opacity: 1; }

.teacher-panel-toggle {
  position: fixed;
  top: 1rem;
  left: 1rem;
  z-index: 190;
  background: rgba(20,40,65,0.85);
  border: 1px solid rgba(255,255,255,0.3);
  color: #fff;
  border-radius: 999px;
  width: 50px;
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.4rem;
  cursor: pointer;
}

.teacher-panel-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  z-index: 150;
}
.teacher-panel-backdrop.open { display: block; }

@media (min-width: 900px) {
  .teacher-side-panel { transform: translateX(0); box-shadow: 2px 0 14px rgba(0,0,0,0.18); }
  .teacher-panel-close, .teacher-panel-toggle, .teacher-panel-backdrop { display: none !important; }
  body.has-teacher-panel { margin-left: 260px; }
}

/* ── ACCOUNT BAR (signed-in state, top of screen) ─────────────── */

.is-hidden { display: none !important; }

.account-bar {
  position: absolute;
  top: 1rem;
  right: 1.5rem;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 0.82rem;
  background: rgba(255,255,255,0.12);
  border: 1px solid rgba(255,255,255,0.28);
  border-radius: 100px;
  padding: 0.45rem 1rem 0.45rem 1.1rem;
  z-index: 5;
}
.account-bar strong { font-weight: 700; }
.account-bar a {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 2px;
  opacity: 0.85;
  display: inline-block;
  /* Larger tap target than the bare underlined text — negative margin
     offsets the padding so it doesn't shift the pill's visible size. */
  padding: 0.5rem 0.3rem;
  margin: -0.5rem -0.3rem;
}
.account-bar a:hover { opacity: 1; }
@media (max-width: 560px) {
  .account-bar { position: static; margin: 0 auto 1.25rem; width: fit-content; }
}

/* ── SCROLL-DOWN HINT ─────────────────────────────────────── */

.scroll-hint {
  margin-top: 2rem;
  font-size: 0.78rem;
  opacity: 0.6;
  letter-spacing: 0.5px;
}
.scroll-hint span { display: inline-block; animation: bob 1.6s ease-in-out infinite; }
@keyframes bob { 0%,100% { transform: translateY(0); } 50% { transform: translateY(4px); } }

/* ── INFO SECTIONS (below the fold) ───────────────────────── */

.info-section {
  max-width: 1100px;
  margin: 0 auto;
  padding: 3rem 1.5rem;
}

.info-block {
  background: var(--site-surface);
  border: 1px solid var(--site-border);
  border-radius: var(--site-radius);
  box-shadow: var(--site-shadow);
  padding: 1.75rem 2rem;
  margin-bottom: 2rem;
}

.info-block h2 {
  font-size: 1.3rem;
  margin-bottom: 0.9rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.info-block p { color: var(--site-muted); line-height: 1.6; margin-bottom: 0.8rem; }
.info-block p:last-child { margin-bottom: 0; }

.curriculum-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.9rem 0;
}
/* Blue-grey (not the brand blue used on actual buttons) — these are
   plain informational tags, never clickable. */
.curr-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  background: rgba(109,149,191,0.14);
  border: 1px solid #6d95bf;
  color: #3f5872;
  border-radius: 100px;
  padding: 0.3rem 0.75rem;
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
}
a.curr-tag { color: inherit; text-decoration: none; }
a.curr-tag:hover { filter: brightness(1.15); }

.acara-note {
  font-size: 0.78rem;
  color: var(--site-muted);
  border-top: 1px dashed var(--site-border);
  padding-top: 0.8rem;
  margin-top: 0.4rem;
}

/* Button to alignment.html — sits on welcome.html's curriculum
   info-block, centred and matching .info-block h2's font size so it
   reads as prominently as the box's own title. (home.css gives
   home.html's curriculum-block its own dark-hero .curr-toggle variant.) */
.alignment-link-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  width: fit-content;
  margin: 0.9rem auto 0;
  /* Same navy/grid-blue/purple gradient + coordinate-grid pattern as the
     hero banner (see .btn-login-accent in this file) instead of a flat
     accent fill, so it reads as cut from the same branded surface. */
  background-color: #244A8F;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Crect x='0.5' y='0.5' width='19' height='19' fill='none' stroke='%23ffffff' stroke-opacity='0.18' stroke-width='1'/%3E%3C/svg%3E"),
    linear-gradient(135deg, #102A43 0%, #244A8F 55%, #493D9E 100%);
  background-repeat: repeat, no-repeat;
  background-size: 20px 20px, 100% 100%;
  color: #fff;
  font-size: 1.3rem;
  font-weight: 700;
  text-decoration: none;
  padding: 0.6rem 1.4rem;
  border-radius: 100px;
}
.alignment-link-btn:hover {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20'%3E%3Crect x='0.5' y='0.5' width='19' height='19' fill='none' stroke='%23ffffff' stroke-opacity='0.18' stroke-width='1'/%3E%3C/svg%3E"),
    linear-gradient(135deg, #0c2032 0%, #1c3c74 55%, #402f85 100%);
  background-repeat: repeat, no-repeat;
  background-size: 20px 20px, 100% 100%;
}

/* ── SCREENSHOT GALLERY (welcome.html "Maths practice…") ────────────
   Six real screenshots (each a composite of that theme's teacher +
   student screen, mid-game — see screenshots/*-mid-game.png and the
   worksheet composite) laid out as an overlapping, slightly-rotated
   fan — like a spread of brochure pages — rather than a plain row, so
   the whole set reads as one "here's the product" gesture instead of
   six separate thumbnails. The Pirate card (the default/free theme)
   is deliberately the star: unrotated, a touch larger, and given the
   highest z-index so it's the one card guaranteed to sit on top of
   its neighbours regardless of DOM/paint order.
   Cards overlap horizontally via negative margin (not absolute
   positioning), so the row's height is still driven by normal flex
   layout — no fixed-height wrapper to keep in sync with content. */
.screenshot-gallery {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  align-items: center;
  margin: 2rem 0 1.2rem;
  padding: 0 0.5rem;
}
.screenshot-card {
  position: relative;
  flex: 0 0 auto;
  width: 190px;
  background: var(--site-surface);
  border: 1px solid var(--site-border);
  border-radius: 10px;
  padding: 0.5rem 0.5rem 0.7rem;
  text-align: center;
  margin: 0 -18px;
  box-shadow: 0 6px 16px rgba(20, 30, 50, 0.16);
  transition: transform 0.18s ease, box-shadow 0.18s ease, z-index 0.18s;
}
.screenshot-card:hover,
.screenshot-card:focus-within {
  transform: translateY(-10px) rotate(0deg) scale(1.04) !important;
  box-shadow: 0 14px 28px rgba(20, 30, 50, 0.26);
  z-index: 20 !important;
}
.screenshot-card img {
  display: block;
  width: 100%;
  aspect-ratio: 1.9 / 1;
  object-fit: contain;
  background: #fff;
  border-radius: 6px;
  border: 1px solid var(--site-border);
}
.screenshot-caption {
  margin-top: 0.55rem;
  font-size: 0.8rem;
  font-weight: 700;
}
.screenshot-caption-sub {
  font-size: 0.68rem;
  color: var(--site-muted);
  font-weight: 400;
  margin-top: 0.1rem;
  line-height: 1.35;
}
.screenshot-badge {
  display: inline-block;
  color: #fff;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.3px;
  border-radius: 100px;
  padding: 0.12rem 0.5rem;
  margin-bottom: 0.4rem;
}
.screenshot-badge-free { background: var(--site-success); }
.screenshot-badge-pdf  { background: var(--site-accent); }

/* Fan the six cards out with alternating rotation + a slight vertical
   stagger. nth-child order here is the HTML's own reading order
   (Pirate, Maths, Science, Halloween, Christmas, Worksheet) — the
   Pirate card's z-index is set well above the rest below, independent
   of this rotation/stagger so it stays frontmost no matter where it
   falls visually. */
.screenshot-card:nth-child(1) { transform: rotate(0deg)    translateY(-14px); z-index: 4; }
.screenshot-card:nth-child(2) { transform: rotate(-4deg)   translateY(6px);   z-index: 2; }
.screenshot-card:nth-child(3) { transform: rotate(3deg)    translateY(10px);  z-index: 3; }
.screenshot-card:nth-child(4) { transform: rotate(-3deg)   translateY(4px);   z-index: 2; }
.screenshot-card:nth-child(5) { transform: rotate(4deg)    translateY(12px);  z-index: 1; }
.screenshot-card:nth-child(6) { transform: rotate(-2deg)   translateY(2px);   z-index: 1; }

/* Pirate card: the star of the stack — bigger, upright, always on top. */
.screenshot-card-pirate {
  width: 220px;
  z-index: 10 !important;
}

/* ── Below ~860px there isn't room to fan six overlapping cards
   legibly — collapse to a plain horizontally-scrollable strip (no
   rotation/overlap) instead, same convention as other wide content on
   this page (.align-table-wrap / .stats-table-wrap use overflow-x for
   the same reason). Snap-scrolling makes "swipe to the next card" the
   obvious mobile gesture. ── */
@media (max-width: 860px) {
  .screenshot-gallery {
    justify-content: flex-start;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding-bottom: 0.6rem;
    margin: 1.2rem -0.5rem 0.6rem;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
  .screenshot-card,
  .screenshot-card-pirate,
  .screenshot-card:nth-child(1),
  .screenshot-card:nth-child(2),
  .screenshot-card:nth-child(3),
  .screenshot-card:nth-child(4),
  .screenshot-card:nth-child(5),
  .screenshot-card:nth-child(6) {
    transform: none;
    width: min(78vw, 260px);
    margin: 0 0.55rem 0 0;
    scroll-snap-align: start;
  }
  .screenshot-card:hover,
  .screenshot-card:focus-within { transform: none !important; }
  .screenshot-card:first-child { margin-left: 0; }
}

/* ── ALIGNMENT PAGE (alignment.html) — ACARA table + General
   Capabilities, light-card styling to match .info-block. ────────── */

.align-table-wrap { overflow-x: auto; margin-top: 0.9rem; }
.align-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  min-width: 560px;
}
.align-table th, .align-table td {
  text-align: left;
  padding: 0.65rem 0.8rem;
  border-bottom: 1px solid var(--site-border);
  vertical-align: top;
}
.align-table th {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--site-muted);
  background: var(--site-bg);
}
.align-table tr:last-child td { border-bottom: none; }
.align-table .ac-code {
  font-family: monospace;
  background: var(--site-bg);
  border: 1px solid var(--site-border);
  border-radius: 4px;
  padding: 1px 6px;
  white-space: nowrap;
}

.capability-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1rem;
  margin-top: 1rem;
}
.capability-card {
  background: var(--site-bg);
  border: 1px solid var(--site-border);
  border-radius: 10px;
  padding: 1rem 1.1rem;
}
.capability-card h3 {
  font-size: 0.95rem;
  font-weight: 700;
  margin-bottom: 0.5rem;
  color: var(--site-accent-dark);
}
.capability-card ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.capability-card li {
  font-size: 0.82rem;
  color: var(--site-text);
  padding-left: 1.1rem;
  position: relative;
  line-height: 1.4;
}
.capability-card li::before {
  content: '•';
  position: absolute;
  left: 0.2rem;
  color: var(--site-accent);
  font-weight: 800;
}

/* ── PLANS TABLE ───────────────────────────────────────────── */

/* Compact "what Pro includes" strip shown above the plan cards — see
   renderPricingPlans() in pricing.js. */
.plan-benefits-strip {
  border: 1px solid var(--site-border);
  border-radius: 12px;
  background: linear-gradient(180deg, #fff8ec, #fff);
  padding: 0.6rem 1rem;
  margin-top: 1rem;
  text-align: center;
}
.plan-benefits-title { font-size: 0.85rem; font-weight: 800; color: var(--site-accent-dark); }
.plan-benefits-items { font-size: 0.78rem; color: var(--site-muted); margin-top: 0.15rem; }
@media (max-width: 560px) { .plan-benefits-items { line-height: 1.5; } }

.plans-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.25rem;
  margin-top: 1rem;
}
@media (max-width: 560px) { .plans-grid { grid-template-columns: 1fr; } }

/* Free / Pro / Pro Pass — see pricing.js's renderPricingPlans() */
.plans-grid-3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 860px) { .plans-grid-3 { grid-template-columns: 1fr; } }

/* Free / Month / Term Pass / Annual — see pricing.js's renderPricingPlans().
   Laid out 2x2 (rather than 4-in-a-row) so each card gets enough width
   to breathe — matches the reference mock's spacing. */
.plans-grid-4 { grid-template-columns: repeat(2, 1fr); gap: 1.5rem; }
@media (max-width: 560px) { .plans-grid-4 { grid-template-columns: 1fr; } }

/* Every plan tile carries a subtle wash of both blues (the button blue
   and the tag blue-grey) rather than a plain white card — the paid
   tiers get a stronger version of the same gradient to still read as
   the "upgrade" tier next to the free one. */
.plan-card {
  border: 1px solid var(--site-border);
  border-radius: 12px;
  padding: 1.25rem 1.4rem;
  position: relative;
  display: flex;
  flex-direction: column;
  background: linear-gradient(160deg, rgba(83,156,231,0.10), rgba(109,149,191,0.05) 55%, #fff 100%);
}
.plan-card.plan-paid {
  border-color: var(--site-accent);
  background: linear-gradient(160deg, rgba(83,156,231,0.24), rgba(109,149,191,0.10) 55%, #fff 100%);
}
/* Whole card is a click target routing to signup or Stripe checkout —
   see renderPricingPlans() in pricing.js. */
.plan-card.plan-clickable { cursor: pointer; transition: transform 0.12s ease, box-shadow 0.12s ease; }
.plan-card.plan-clickable:hover,
.plan-card.plan-clickable:focus-visible {
  transform: translateY(-3px);
  box-shadow: 0 8px 22px rgba(0,0,0,0.12);
}
.plan-card.plan-clickable:focus-visible { outline: 2px solid var(--site-accent); outline-offset: 2px; }
.plan-cta { margin-top: 1rem; }
.plan-badge-save {
  position: absolute;
  top: -0.6rem;
  right: 1rem;
  background: var(--site-danger);
  color: #fff;
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  padding: 0.3rem 0.6rem;
  border-radius: 999px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}

/* Pinned to the opposite (top-left) corner from .plan-badge-save so the
   Annual card can show "Best Value" and the 50% off banner at once. Below
   ~480px the card isn't wide enough for both badges side by side, so the
   media query further down drops .plan-badge-save to a second row instead
   of letting the two overlap — see planCard() in pricing.js. */
.plan-badge-best {
  position: absolute;
  top: -0.6rem;
  left: 1rem;
  background: var(--site-accent-dark);
  color: #fff;
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  padding: 0.3rem 0.6rem;
  border-radius: 999px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}

/* The signed-in teacher's actual plan (see renderPricingPlans()'s
   currentPlan param) — a badge instead of a checkout button, since
   clicking to "upgrade" to the plan you're already on doesn't make sense. */
.plan-card.plan-current { border-color: var(--site-success); background: #f2fbf7; }
.plan-badge-current {
  position: absolute;
  top: -0.6rem;
  right: 1rem;
  background: var(--site-success);
  color: #fff;
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  padding: 0.3rem 0.6rem;
  border-radius: 999px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}

/* Too narrow for "Best Value" and "50% off" side by side (e.g. the
   Annual card on a phone) — stack the save badge under the best-value
   one instead of letting them collide, and give the card room for both. */
@media (max-width: 480px) {
  .plan-card:has(.plan-badge-best) { padding-top: 3rem; }
  .plan-badge-best + .plan-badge-save { top: 1.5rem; }
}

/* Shown on the Month/Term/Annual cards for a teacher currently on the
   'promo' plan (see renderPricingPlans()'s promoEndsAt param) — a
   time-boxed grant outside Stripe that already includes everything any
   one of these three tiers offers, so all three get the same banner
   rather than picking one as "current". Same blue as upgrade.html's
   "Have an access code?" box, so the two read as one connected idea. */
.plan-promo-banner {
  background: #086ABE;
  color: #fff;
  font-size: 0.74rem;
  font-weight: 700;
  text-align: center;
  line-height: 1.3;
  padding: 0.4rem 0.7rem;
  border-radius: 8px;
  margin-bottom: 0.7rem;
}
.plan-name {
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: 0;
  text-transform: none;
  color: var(--site-text);
  line-height: 1.25;
  margin-bottom: 0.6rem;
}
.plan-card.plan-paid .plan-name { color: var(--site-accent-dark); }
.plan-price { display: flex; align-items: baseline; flex-wrap: wrap; column-gap: 0.5rem; margin-bottom: 0.3rem; }
.plan-price-old { font-size: 1rem; font-weight: 600; text-decoration: line-through; color: var(--site-muted); }
.plan-price-new { font-size: 2.15rem; font-weight: 900; line-height: 1; color: var(--site-accent-dark); }
.plan-price-unit { font-size: 0.95rem; font-weight: 600; color: var(--site-muted); }
.plan-price-permonth { font-size: 0.9rem; font-weight: 800; color: var(--site-accent-dark); margin-bottom: 0.5rem; }
.plan-price-note { font-size: 0.72rem; color: var(--site-muted); margin-bottom: 0.8rem; }
.plan-list { list-style: none; display: flex; flex-direction: column; gap: 0.5rem; flex: 1; }
.plan-list li { font-size: 0.88rem; color: var(--site-text); padding-left: 1.4rem; position: relative; }
.plan-list li::before { content: '✓'; position: absolute; left: 0; color: var(--site-success); font-weight: 800; }
.plan-list li.plan-list-note { color: var(--site-muted); font-style: italic; }
.plan-list li.plan-list-note::before { content: none; }
.plan-note { font-size: 0.76rem; color: var(--site-muted); margin-top: 0.9rem; }

/* ── STUDENT POINTS &amp; GRIDPANIONS PANEL (welcome.html, signed-in students) ── */

.student-panel {
  max-width: 1400px;
  margin: -1.5rem auto 0;
  padding: 0 1.5rem;
  position: relative;
  z-index: 2;
}

.student-panel-inner {
  background: var(--site-surface);
  border: 1px solid var(--site-border);
  border-radius: var(--site-radius);
  box-shadow: var(--site-shadow);
  padding: 1.75rem 2rem;
}

.student-panel-inner h2 {
  font-size: 1.3rem;
  margin-bottom: 1rem;
}

.stat-chip-row {
  display: flex;
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.stat-chip {
  flex: 1;
  background: #fff8ec;
  border: 1px solid #f0deb0;
  border-radius: 10px;
  padding: 0.9rem 1rem;
  text-align: center;
}

.stat-chip-val {
  font-size: 1.6rem;
  font-weight: 800;
  color: var(--site-accent-dark);
}

.stat-chip-lbl {
  font-size: 1.1rem;
  color: var(--site-muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  margin-top: 0.2rem;
}

/* Offer of 3 random eggs (see eggMilestonesEarned() in xp-system.js) —
   shown above the pets grid whenever student_stats.pending_egg_choices
   is non-empty. */
.egg-choice-hint { font-size: 0.85rem; font-weight: 700; margin-bottom: 0.7rem; }

.egg-choice-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 0.7rem;
  margin-bottom: 1.5rem;
}

.egg-choice-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  background: #fff;
  border: 1.5px solid var(--site-border);
  border-radius: 10px;
  padding: 1rem 0.5rem;
  cursor: pointer;
  font-family: inherit;
  transition: border-color 0.12s, background 0.12s;
}
.egg-choice-card:hover { border-color: var(--site-accent); background: #fff8ec; }
.egg-choice-icon { font-size: 2rem; }
.egg-choice-icon-img { width: 100%; max-width: 84px; object-fit: contain; }
.egg-choice-label { font-size: 0.8rem; font-weight: 700; }

/* Rarity treatment on egg-choice cards (see GRIDPANION_RARITIES in
   xp-system.js) — border/glow/background tint wraps the card only, the
   egg artwork itself is never recolored. Named egg-card-rarity-* (not
   pet-card-epic-style names) since "epic" already means "fully evolved
   stage" elsewhere in this file (.pet-card-epic) — a different concept
   from the Epic rarity tier, so the class names are kept deliberately
   distinct to avoid confusing the two. */
.egg-card-rarity-common {
  border-color: #3f7d43;
  box-shadow: 0 0 0 3px rgba(63, 125, 67, 0.12);
}
.egg-card-rarity-rare {
  border-color: #1c6fd6;
  box-shadow: 0 0 0 3px rgba(28, 111, 214, 0.14);
}
.egg-card-rarity-epic {
  border-color: #7b3fd6;
  box-shadow: 0 0 0 3px rgba(123, 63, 214, 0.16);
}
/* Legendary gets a noticeably bigger treatment — thicker gold border,
   stronger glow, sparkle accents and a slow shimmer sweep — so it reads
   as more exciting than the other three at a glance, with no separate
   popup needed. */
.egg-card-rarity-legendary {
  border: 2px solid #c8720f;
  box-shadow: 0 0 0 4px rgba(200, 114, 15, 0.16), 0 2px 14px rgba(200, 114, 15, 0.28);
  background: linear-gradient(120deg, #fff 0%, #fff8ec 45%, #fdeee0 55%, #fff 100%);
  background-size: 220% 100%;
  animation: egg-legendary-shimmer 3.2s ease-in-out infinite;
}
.egg-card-rarity-legendary::before,
.egg-card-rarity-legendary::after {
  content: "✨";
  position: absolute;
  font-size: 0.9rem;
  opacity: 0.85;
  pointer-events: none;
}
.egg-card-rarity-legendary::before { top: 6px; right: 10px; }
.egg-card-rarity-legendary::after  { bottom: 6px; left: 10px; font-size: 0.7rem; opacity: 0.65; }
@keyframes egg-legendary-shimmer {
  0%, 100% { background-position: 0% 0%; }
  50%      { background-position: 100% 0%; }
}
@media (prefers-reduced-motion: reduce) {
  .egg-card-rarity-legendary { animation: none; }
}

/* One card per owned pet — full growth-stage illustration (see
   PET_SPECIES's `sheet` in xp-system.js) plus a progress bar toward the
   next stage's cumulative feed cost (PET_STAGES) and a row of food
   buttons (PET_FOOD) to feed it with. */
.pets-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1rem;
}

/* Featured Gridpanions (welcome.html's white student-panel-inner box —
   up to 3 pets, #pets-grid specifically, not the "see all" modal's full-
   collection #pets-modal-grid above) — a flex row with a FIXED card
   width instead of the shared .pets-grid's auto-fill/1fr grid, so a
   card is the same size whether there's 1, 2 or 3 of them (1fr used to
   stretch a lone card to fill the whole row). justify-content:
   space-evenly puts equal space around every card including the outer
   edges, which reads as centered with one card, symmetric around the
   middle with two, and spread edge-to-edge (inside the box's own
   padding) with three — one rule, every count, no JS needed. */
#pets-grid, #tp-pets-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-evenly;
  align-items: stretch;
  gap: 1.25rem 1rem;
}
#pets-grid .pet-card, #tp-pets-grid .pet-card { flex: 0 1 264px; width: 264px; max-width: 100%; }
#pets-grid .pet-card-art, #tp-pets-grid .pet-card-art { max-height: 150px; }
/* A fully evolved Gridpanion has no food buttons, so its art takes that
   space instead: the tile stays the same size as its neighbours (cards
   stretch to one height) and the Gridpanion shows bigger. */
.pet-card-epic .pet-card-art,
#pets-grid .pet-card-epic .pet-card-art, #tp-pets-grid .pet-card-epic .pet-card-art { flex: 1 1 auto; min-height: 0; max-height: 280px; }
.pet-card-epic .pet-food-row { display: none; }

.pet-card {
  --stage-color: var(--site-accent);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2rem;
  background: #fff;
  border: 1.5px solid var(--site-border);
  border-radius: 12px;
  padding: 0.7rem 0.9rem 0.9rem;
  min-height: 300px;
}

/* Epic (fully-evolved) pets get a gold border/glow + a couple of small
   sparkle accents — deliberately just an outline treatment, not a
   background change, so the card stays in the same clean/flat style
   as every other stage. */
.pet-card-epic {
  border: 2px solid #D4A72C;
  box-shadow: 0 0 0 3px rgba(212, 167, 44, 0.14), 0 2px 10px rgba(212, 167, 44, 0.18);
}
.pet-card-epic::before,
.pet-card-epic::after {
  content: "✨";
  position: absolute;
  font-size: 0.85rem;
  opacity: 0.8;
  pointer-events: none;
}
.pet-card-epic::before { top: 6px; right: 10px; }
.pet-card-epic::after  { bottom: 8px; left: 10px; font-size: 0.65rem; opacity: 0.6; }

.pet-card-art { width: 100%; max-height: 132px; object-fit: contain; }

.pet-card-species {
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--stage-color);
  text-align: center;
}
.pet-card-title {
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--site-text);
  text-align: center;
  margin-top: -0.15rem;
}

.pet-progress-bar {
  width: 100%;
  background: var(--site-bg);
  border: 1px solid var(--site-border);
  border-radius: 999px;
  height: 10px;
  overflow: hidden;
  margin-top: 0.2rem;
}
.pet-progress-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--stage-color), color-mix(in srgb, var(--stage-color) 70%, black));
  border-radius: 999px;
  transition: width 0.2s ease;
}
.pet-progress-label { font-size: 1rem; font-weight: 600; color: var(--site-muted); }

.pet-food-row { display: flex; gap: 0.4rem; margin-top: 0.3rem; flex-wrap: wrap; justify-content: center; min-height: 48px; }

.pet-food-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  background: #fffaf0;
  border: 1.5px solid #e8c775;
  border-radius: 8px;
  padding: 0.4rem 0.65rem;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  font-family: inherit;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
  transition: border-color 0.12s, background 0.12s, transform 0.12s;
}
.pet-food-btn:hover:not(:disabled) { border-color: var(--site-accent); background: #fff3da; transform: translateY(-1px); }
.pet-food-btn.locked { opacity: 0.4; cursor: default; box-shadow: none; }
.pet-food-btn.locked:hover { transform: none; }
.pet-food-cost { font-size: 0.85rem; font-weight: 600; color: var(--site-muted); }

/* ── FEATURED GRIDPANIONS PICKER (only 3 show on the profile itself —
   see renderPetsGrid()/openPetsModal() in welcome.html) ──────────── */

.pets-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 25, 35, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  z-index: 500;
}
.pets-modal {
  background: #fff;
  border-radius: var(--site-radius);
  padding: 1.5rem;
  max-width: 720px;
  width: 100%;
  max-height: 85vh;
  overflow-y: auto;
  box-shadow: var(--site-shadow);
}
.pets-modal-hint { color: var(--site-muted); font-size: 0.88rem; margin-bottom: 0.6rem; }
.pets-modal-count { font-size: 0.82rem; font-weight: 700; margin-bottom: 0.8rem; }
.pets-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
  margin-top: 1.2rem;
}
.pets-modal-grid-card {
  cursor: pointer;
  transition: border-color 0.12s, box-shadow 0.12s;
}
.pets-modal-grid-card.selected {
  border-color: var(--site-accent);
  box-shadow: 0 0 0 2px var(--site-accent);
}
.pets-modal-grid-card.selected::after {
  content: '✓ Featured';
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--site-accent-dark);
}

/* ── LEVEL BAR ─────────────────────────────────────────────── */

.level-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 1.2rem;
}

.level-badge {
  flex-shrink: 0;
  background: var(--site-accent);
  color: #fff;
  font-weight: 800;
  font-size: 0.9rem;
  padding: 0.5rem 0.9rem;
  border-radius: 999px;
  white-space: nowrap;
}

.level-bar-wrap { flex: 1; min-width: 0; }

.level-bar {
  background: var(--site-bg);
  border: 1px solid var(--site-border);
  border-radius: 999px;
  height: 10px;
  overflow: hidden;
}

.level-bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--site-accent), var(--site-accent-dark));
  border-radius: 999px;
  transition: width 0.2s ease;
}

.level-bar-label {
  font-size: 0.72rem;
  color: var(--site-muted);
  margin-top: 0.3rem;
}

/* ── PANEL SUBSECTIONS (stats table, badges) ─────────────────── */

.panel-subheading {
  font-size: 1.05rem;
  margin: 1.75rem 0 0.9rem;
}

/* Bumped to match .panel-subheading ("🏅 Badges" above it) per direct
   request — was noticeably smaller than the section heading it sits
   under. */
.shop-theme-heading {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--site-muted);
  margin-bottom: 0.5rem;
}

.rarity-tag {
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  padding: 0.1rem 0.4rem;
  border-radius: 999px;
}
/* Gridpanion rarity tiers (see GRIDPANION_RARITIES in xp-system.js) —
   common=green, rare=blue, epic=purple, legendary=gold, matching the
   egg-choice-card border/glow treatment below (.egg-card-rarity-*).
   uncommon/mythic are left defined but unused — no such tiers exist
   today, only common/rare/epic/legendary. */
/* Small "active Gridpanion" avatar strip (see renderActiveTeacherPets()
   in xp-system.js) — dropped into a teacher's dashboard/lobby/live/
   podium screens. Kept deliberately small and corner-friendly so it
   never competes with the actual gameplay for attention. */
.teacher-pet-strip {
  display: inline-flex;
  gap: 0.3rem;
  align-items: center;
  justify-content: center;
}
.teacher-pet-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--site-border);
  object-fit: contain;
  padding: 3px;
  box-sizing: border-box;
}

.rarity-common    { background: #e6f3e8; color: #3f7d43; }
.rarity-uncommon  { background: #e3f5ec; color: #1D9E75; }
.rarity-rare      { background: #e6f0fb; color: #1c6fd6; }
.rarity-epic      { background: #f2e8fb; color: #7b3fd6; }
.rarity-legendary { background: #fdeee0; color: #c8720f; }
.rarity-mythic    { background: #fdeaea; color: #c0392b; }

.stats-table-wrap { overflow-x: auto; }

.stats-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 1.1rem;
  min-width: 480px;
}

.stats-table th, .stats-table td {
  padding: 0.55rem 0.7rem;
  text-align: center;
  border-bottom: 1px solid var(--site-border);
  white-space: nowrap;
}

.stats-table th:first-child, .stats-table td:first-child {
  text-align: left;
  color: var(--site-muted);
  font-weight: 600;
}

.stats-table thead th {
  font-weight: 700;
  color: var(--site-text);
  background: var(--site-bg);
}

/* Grid Strike/Grid Sprint's own stat tables — BADGE_ONLY_THEMES
   (xp-system.js), can't join the main multi-column Statistics table
   since their stat shape is unrelated to the Pirate-family one it
   reads (see renderExtraStatsTables() in welcome.html) — each gets its
   own small 2-column (Statistic/Value) table instead, stacked below
   it. */
.extra-stats-table { margin-top: 1.1rem; overflow-x: auto; }
.extra-stats-table .stats-table { min-width: 0; max-width: 420px; }
.extra-stats-table .stats-table td:last-child { text-align: right; font-weight: 700; }

/* ── Levelled badges (renderBadges() in welcome.html, teacher badges
   in teachers-pet.html; data from TRACK_DEFS/trackView() in
   xp-system.js). Each badge grows Egg → Hatchling → Pathfinder → Trailblazer → Legend. */
.bt-intro { margin: 0.2rem 0 0.8rem; color: var(--site-muted); line-height: 1.5; }
.bt-heading { margin: 1.4rem 0 0.2rem; font-size: 1.25rem; }
.bt-summary { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 0.4rem; }
.bt-summary-chip { background: var(--site-surface); border: 1px solid var(--site-border); border-radius: 999px; padding: 0.35rem 0.9rem; color: var(--site-muted); }
.bt-summary-chip strong { color: var(--site-text); font-size: 1.1rem; }
.bt-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.8rem; }
@media (max-width: 900px) { .bt-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .bt-grid { grid-template-columns: minmax(0, 1fr); } }
.bt-card { background: var(--site-surface); border: 1px solid var(--site-border); border-radius: 14px; overflow: hidden; display: flex; flex-direction: column; }
.bt-card.is-legend { border: 2px solid #e0a82e; box-shadow: 0 0 0 4px #fff4d6; }
.bt-card.is-soon { opacity: 0.6; }
.bt-stripe { height: 7px; }
.bt-card-body { padding: 0.9rem 1rem; display: flex; flex-direction: column; gap: 0.6rem; }
.bt-head { display: flex; align-items: center; gap: 0.8rem; }
.bt-art { width: 64px; height: 64px; border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.bt-art img { width: 54px; height: 54px; object-fit: contain; }
.bt-art img.is-locked { filter: grayscale(1); opacity: 0.35; }
.bt-art-sm { width: 50px; height: 50px; }
.bt-art-sm img { width: 42px; height: 42px; }
.bt-title { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.bt-name { font-weight: 800; font-size: 1.05rem; line-height: 1.2; }
.bt-subtitle { font-weight: 700; font-size: 1rem; }
.bt-chip { align-self: flex-start; font-size: 0.72rem; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; padding: 0.15rem 0.6rem; border-radius: 999px; color: #fff; border: 1px solid transparent; white-space: nowrap; }
.bt-chip-empty { background: #fff; color: #5f6b80; border-color: #b8c6d9; }
.bt-what { font-size: 0.92rem; color: var(--site-muted); }
.bt-bar { height: 10px; border-radius: 999px; background: #e6ecf4; overflow: hidden; }
.bt-bar-sm { height: 8px; }
.bt-bar-fill { height: 100%; border-radius: 999px; }
.bt-foot { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }
.bt-next { font-size: 0.92rem; font-weight: 700; }
.bt-source { font-size: 0.8rem; font-weight: 700; color: var(--site-muted); text-align: right; }
.bt-dots { display: flex; gap: 4px; flex-shrink: 0; }
.bt-dot { width: 10px; height: 10px; border-radius: 50%; background: #dbe3ee; }
.bt-game-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.8rem; }
@media (max-width: 900px) { .bt-game-grid { grid-template-columns: minmax(0, 1fr); } }
.bt-game { background: var(--site-surface); border: 1px solid var(--site-border); border-radius: 14px; overflow: hidden; }
/* Fixed height so the three banners (and the rows under them) line up. */
.bt-game-head { color: #fff; padding: 0.8rem 1rem; min-height: 4.9rem; box-sizing: border-box; text-shadow: 0 1px 2px rgba(0,0,0,0.25); }
.bt-game-name { font-weight: 800; font-size: 1.15rem; }
.bt-game-sub { font-size: 0.82rem; opacity: 0.9; }
.bt-game-rows { padding: 0.2rem 1rem 0.6rem; }
.bt-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.7rem 0; border-bottom: 1px solid #e6ecf4; }
.bt-row:last-child { border-bottom: 0; }
.bt-row-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.bt-row-top { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 0.3rem 0.5rem; }
.bt-row-what { font-size: 0.85rem; color: var(--site-muted); }
.bt-row-what strong { color: var(--site-text); }
.bt-reward-note { margin-top: 1.2rem; display: flex; gap: 0.9rem; align-items: center; background: var(--site-surface); border: 1px dashed #b8c6d9; border-radius: 14px; padding: 0.9rem 1rem; color: var(--site-muted); line-height: 1.5; }
.bt-reward-note img { width: 44px; height: 44px; object-fit: contain; flex-shrink: 0; }
.bt-reward-note strong { color: var(--site-text); }

footer.site-footer {
  text-align: center;
  padding: 2rem 1rem 3rem;
  font-size: 0.8rem;
  color: var(--site-muted);
}
/* Larger tap target than the bare underlined text (negative margin
   offsets the padding so it doesn't shift the visible layout) — the
   footer's Log in/Sign up/Log out links were easy to mis-tap on a
   phone otherwise. */
footer.site-footer a {
  text-decoration: underline;
  text-underline-offset: 2px;
  display: inline-block;
  padding: 0.5rem 0.3rem;
  margin: -0.5rem -0.3rem;
}

/* ── FALLING EGGS (login / signup / join backgrounds) ─────────
   Continuous-loop decorative background — see initFallingEggs() in
   falling-eggs.js. Fixed + negative z-index so it paints behind the
   normal page flow (the account card, header link, etc.) without
   needing position/z-index changes on every other element. */

.falling-eggs-bg {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: -1;
}

.falling-egg {
  position: absolute;
  top: -110px;
  height: auto;
  animation-name: egg-fall;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  will-change: transform, opacity;
  filter: drop-shadow(0 4px 8px rgba(0,0,0,0.15));
}

@keyframes egg-fall {
  0%   { transform: translate(0, 0) rotate(0deg); }
  100% { transform: translate(var(--egg-sway), 125vh) rotate(var(--egg-rot)); }
}

@media (prefers-reduced-motion: reduce) {
  .falling-eggs-bg { display: none; }
}

/* Egg icon standing in for an emoji at inline text size — used to swap
   the 🎯 on "Join a Game" buttons for a shrunk egg (see welcome.html). */
.join-icon-egg {
  height: 1.15em;
  width: auto;
  vertical-align: -0.2em;
}

/* ── ACCOUNT CARDS (login / signup / join) ───────────────────── */

.account-wrap {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1rem;
}

.account-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  background: var(--site-surface);
  border: 1px solid var(--site-border);
  border-radius: var(--site-radius);
  box-shadow: var(--site-shadow);
  padding: 2rem 1.9rem 1.8rem;
}

.account-back {
  display: block;
  font-size: 0.8rem;
  color: var(--site-muted);
  text-decoration: none;
  margin-bottom: 1rem;
}
.account-back:hover { text-decoration: underline; }

.account-title {
  font-size: 1.5rem;
  font-weight: 800;
  margin-bottom: 0.3rem;
  text-align: center;
}
.account-sub {
  font-size: 0.88rem;
  color: var(--site-muted);
  text-align: center;
  margin-bottom: 1.5rem;
}

/* logos/logo1.png on the sign-up card — smaller than welcome.html's
   hero-sized .site-logo since it's sitting on a narrow white card, not
   a full-width dark hero. */
.signup-logo {
  display: block;
  width: min(240px, 70%);
  height: auto;
  margin: 0 auto 0.4rem;
}

/* Small pet chips shown next to a signed-in student's name on the
   join-a-game screen — one emoji chip per owned pet (species + stage). */
.join-pets-wrap { text-align: center; margin-bottom: 0.6rem; }
.join-pets-row { display: flex; justify-content: center; gap: 0.4rem; flex-wrap: wrap; }
.join-pet-chip {
  font-size: 1.4rem;
  background: var(--site-bg);
  border: 1.5px solid var(--site-border);
  border-radius: 999px;
  padding: 0.25rem 0.5rem;
}

/* A pet stage's reference image (PET_STAGES' `art`, see xp-system.js),
   dropped in wherever a stage used to be shown as a bare `icon` emoji —
   sized in em so it always matches whatever font-size the emoji it
   replaced would have rendered at in that spot (1.4rem in .join-pet-chip,
   4rem in .stage-celebration-icon), with no separate size rule needed
   per call site. */
.stage-icon-img {
  width: 1em;
  height: 1em;
  vertical-align: middle;
  object-fit: contain;
}

/* Role toggle (teacher / student) on signup */
.role-toggle {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
  margin-bottom: 1.4rem;
}
.role-btn {
  /* Same fluid clamp() approach as .btn above — see signup.html's
     Teacher/Student toggle. */
  padding: clamp(0.75rem, 0.6rem + 0.6vw, 1rem) clamp(0.4rem, 0.25rem + 0.6vw, 0.6rem);
  border: 2px solid var(--site-border);
  border-radius: 10px;
  background: #fff;
  cursor: pointer;
  text-align: center;
  font-weight: 700;
  font-size: clamp(0.85rem, 0.8rem + 0.2vw, 0.95rem);
  color: var(--site-muted);
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.role-btn .role-emoji { display: block; font-size: clamp(1.2rem, 1rem + 0.6vw, 1.5rem); margin-bottom: 0.25rem; }
/* Student toggle's egg — a real (shrunk) Gridpanion galah egg image
   standing in for an emoji, sized to match .role-emoji's footprint. */
.role-btn .role-emoji-img { display: block; width: clamp(1.3rem, 1.1rem + 0.6vw, 1.6rem); height: auto; margin: 0 auto 0.25rem; }
.role-btn.active {
  border-color: var(--site-accent);
  color: var(--site-accent-dark);
  background: #fff8ec;
}

.field { margin-bottom: 1rem; }
.field label {
  display: block;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--site-muted);
  margin-bottom: 0.35rem;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.field input, .field select {
  width: 100%;
  padding: 0.7rem 0.85rem;
  border: 1.5px solid var(--site-border);
  border-radius: 8px;
  font-size: 0.95rem;
  font-family: inherit;
  color: var(--site-text);
  background: #fff;
}
.field input:focus, .field select:focus {
  outline: none;
  border-color: var(--site-accent);
}

/* Eye toggle on a password field — reveals/hides the typed password.
   See togglePasswordVisibility() in auth.js. Declared after
   ".field input" so its extra right padding (room for the icon) wins
   the equal-specificity tie instead of being overwritten by it. */
.password-field { position: relative; }
.password-field input { padding-right: 2.6rem; }
.password-toggle {
  position: absolute;
  top: 50%;
  right: 0.6rem;
  transform: translateY(-50%);
  background: none;
  border: none;
  cursor: pointer;
  font-size: 1.05rem;
  line-height: 1;
  padding: 0.3rem;
  opacity: 0.6;
}

.password-toggle:hover { opacity: 1; }

/* Cloudflare Turnstile widget containers (login.html/signup.html) — the
   Cloudflare script that actually renders the widget loads async and
   often takes a second or so, so these divs sit empty at first.
   Without a reserved height, the widget popping in shoves the submit
   button (and everything below it) down the form right as someone's
   reading it. 65px matches Turnstile's standard "normal" widget
   height; the "compact" layout it auto-switches to on a narrow phone
   screen is taller, hence the bump below.
   #l-turnstile (login's main widget) is deliberately excluded: it uses
   appearance:'interaction-only' (see renderTurnstile() in auth.js) and
   stays genuinely zero-height unless Cloudflare's own risk signals
   decide a challenge is needed — reserving space for it here would
   just add a permanent blank gap to the login form for no reason. */
#s-turnstile, #fp-turnstile, #v-turnstile { min-height: 65px; }
@media (max-width: 420px) {
  #s-turnstile, #fp-turnstile, #v-turnstile { min-height: 140px; }
}

/* Live password-requirements checklist — see wirePasswordChecklist() in
   auth.js. Each requirement starts muted/red-dot and turns green with a
   checkmark once the typed password satisfies it. */
.pw-requirements {
  list-style: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 0.8rem;
  margin: 0.35rem 0 1rem;
  padding: 0;
  font-size: 0.78rem;
  color: var(--site-muted);
}
@media (max-width: 380px) { .pw-requirements { grid-template-columns: 1fr; } }
.pw-requirements li {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.15rem 0;
  transition: color 0.15s;
}
.pw-requirements li::before {
  content: '○';
  color: var(--site-muted);
  font-size: 0.7rem;
}
.pw-requirements li.met {
  color: var(--site-success);
  font-weight: 600;
}
.pw-requirements li.met::before {
  content: '✓';
  color: var(--site-success);
  font-size: 0.85rem;
}

/* Short explanatory text placed directly under a form field — e.g. the
   year-level question on signup.html — separate from .field label
   (bold/uppercase) since this is a sentence meant to be read, not a
   field name. Positive top margin (not pulled up against the field
   above it) so it never overlaps a <select>'s own box. */
.field-hint {
  font-size: 0.78rem;
  color: var(--site-muted);
  line-height: 1.4;
  margin: 0.45rem 0 0;
}
.field-hint a { color: var(--site-accent-dark); }

/* Pairs two short single-line .field blocks side by side (e.g.
   signup.html's Username + School email) so a form with several fields
   fits without as much vertical scrolling — collapses back to one
   column on narrow screens. */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 0.9rem; }
.field-row .field { min-width: 0; }
@media (max-width: 480px) { .field-row { grid-template-columns: 1fr; } }

.form-err {
  color: var(--site-danger);
  font-size: 0.82rem;
  min-height: 1.1rem;
  margin-bottom: 0.6rem;
}
.form-msg {
  color: var(--site-success);
  font-size: 0.85rem;
  background: #e6f7f1;
  border: 1px solid #b7e6d5;
  border-radius: 8px;
  padding: 0.7rem 0.9rem;
  margin-bottom: 1rem;
  line-height: 1.5;
}

.btn-block { width: 100%; justify-content: center; }

.account-foot {
  text-align: center;
  font-size: 0.85rem;
  color: var(--site-muted);
  margin-top: 1.2rem;
}
.account-foot a { color: var(--site-accent-dark); font-weight: 700; text-decoration: none; }
.account-foot a:hover { text-decoration: underline; }

.plain-btn {
  background: none;
  border: none;
  color: var(--site-accent-dark);
  font-weight: 700;
  cursor: pointer;
  font-size: 0.85rem;
  text-decoration: underline;
  text-underline-offset: 2px;
  padding: 0;
}

.join-code-input {
  text-align: center;
  font-size: 1.3rem;
  letter-spacing: 4px;
  text-transform: uppercase;
}

/* ── PET STAGE-UP CELEBRATION (welcome.html) ─────────────────── */
/* Popped by celebrateStageUp() when feeding a pet crosses into a new
   PET_STAGES tier — a pulsing light behind the new stage's icon, plus a
   confetti burst (fireCelebrationConfetti(), mirrors style.css's
   window.fireConfetti() on the game pages — welcome.html doesn't load
   theme-loader.js, so it gets its own small copy of the same effect). */

.stage-celebration-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 14, 4, 0.55);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1300;
  cursor: pointer;
  animation: stage-celebration-fade-in 0.25s ease;
}

.stage-celebration-card {
  position: relative;
  background: var(--site-surface);
  border-radius: var(--site-radius);
  border: 1px solid var(--site-border);
  box-shadow: var(--site-shadow);
  padding: 2.5rem 2rem 2rem;
  width: 300px;
  max-width: 90vw;
  text-align: center;
  cursor: default;
  animation: stage-celebration-pop 0.5s cubic-bezier(.34,1.56,.64,1) backwards;
}

.stage-celebration-glow {
  position: absolute;
  top: 44%;
  left: 50%;
  width: 220px;
  height: 220px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, #ffe9a8 0%, rgba(255,233,168,0) 70%);
  animation: stage-celebration-glow-pulse 1.6s ease-in-out infinite;
  z-index: 0;
}

/* Evolve animation (celebrateStageUp() in welcome.html): the Gridpanion
   wobbles and glows, flickers between its old and new stage, a white
   flash, then the new stage pops in and the words fade up. */
.evo-stage { position: relative; z-index: 1; width: 190px; height: 190px; margin: 0 auto 0.6rem; }
.evo-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.evo-from { animation: evo-from 2.6s ease-in-out forwards; }
.evo-to   { opacity: 0; animation: evo-to 2.6s ease-in-out forwards; }
.evo-flash {
  position: absolute; inset: -20%; border-radius: 50%; opacity: 0; pointer-events: none;
  background: radial-gradient(circle, #fff 0%, rgba(255,255,255,0.85) 35%, rgba(255,255,255,0) 70%);
  animation: evo-flash 2.6s ease-out forwards;
}
.evo-words { position: relative; z-index: 1; animation: evo-words 0.5s ease 2.1s backwards; }
@keyframes evo-from {
  0%   { opacity: 1; transform: rotate(0) scale(1); filter: brightness(1); }
  8%   { transform: rotate(-6deg) scale(1.02); }
  16%  { transform: rotate(6deg) scale(1.04); }
  24%  { transform: rotate(-7deg) scale(1.05); }
  32%  { transform: rotate(7deg) scale(1.06); filter: brightness(1.4); }
  38%  { opacity: 1; transform: rotate(0) scale(1.06); filter: brightness(1.8); }
  42%  { opacity: 0; } 47% { opacity: 1; } 52% { opacity: 0; } 56% { opacity: 1; }
  60%  { opacity: 0; } 63% { opacity: 1; } 66% { opacity: 0; } 68% { opacity: 1; filter: brightness(2.6); }
  71%, 100% { opacity: 0; filter: brightness(3); }
}
@keyframes evo-to {
  0%, 38% { opacity: 0; transform: scale(0.9); filter: brightness(2.6); }
  42% { opacity: 1; } 47% { opacity: 0; } 52% { opacity: 1; } 56% { opacity: 0; }
  60% { opacity: 1; } 63% { opacity: 0; } 66% { opacity: 1; } 68% { opacity: 0; }
  71% { opacity: 1; transform: scale(0.85); filter: brightness(3); }
  82% { transform: scale(1.18); filter: brightness(1.2); }
  100% { opacity: 1; transform: scale(1); filter: brightness(1); }
}
@keyframes evo-flash { 0%, 66% { opacity: 0; } 72% { opacity: 1; } 100% { opacity: 0; } }
@keyframes evo-words { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.stage-celebration-title   { position: relative; z-index: 1; font-size: 1.1rem; font-weight: 800; margin-bottom: 0.2rem; }
.stage-celebration-stage   { position: relative; z-index: 1; font-size: 0.85rem; color: var(--site-muted); margin-bottom: 1.2rem; }
.stage-celebration-close   { position: relative; z-index: 1; }

@keyframes stage-celebration-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes stage-celebration-pop {
  from { transform: scale(0.7); opacity: 0; }
  to   { transform: scale(1);   opacity: 1; }
}
@keyframes stage-celebration-glow-pulse {
  0%, 100% { opacity: 0.55; transform: translate(-50%, -50%) scale(0.9); }
  50%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.08); }
}

/* Confetti burst overlay, populated by fireCelebrationConfetti() */
.confetti-wrap {
  position: fixed;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 1310;
}
.confetti-piece {
  position: absolute;
  top: -40px;
  line-height: 1;
  animation-name: confetti-fall;
  animation-timing-function: ease-in;
  animation-fill-mode: forwards;
  will-change: transform, opacity;
}
@keyframes confetti-fall {
  0%   { transform: translateY(0) rotate(0deg); opacity: 1; }
  100% { transform: translateY(105vh) rotate(360deg); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .stage-celebration-glow, .stage-celebration-card, .confetti-piece,
  .evo-from, .evo-to, .evo-flash, .evo-words {
    animation: none !important;
  }
  .evo-from { opacity: 0; }
  .evo-to   { opacity: 1; }
}

/* ── MOBILE (student hub — welcome.html's signed-in student panel) ──
   .stat-chip-row was a flat flex row of 6 chips (XP/Coins/Wins/Badges/
   Gridpanions/Games) that just kept shrinking on a narrow screen
   instead of wrapping; .badge-grid's 150px column minimum meant a
   phone-width card (~260-300px of usable width once the panel's own
   padding is subtracted) could only ever fit one column, so every
   theme's badge row rendered as one long vertical strip instead of a
   grid. Both switch to an explicit multi-column layout here instead of
   relying on space that isn't there. */
@media (max-width: 640px) {
  .student-panel { padding: 0 0.75rem; }
  .student-panel-inner { padding: 1.25rem 1.1rem; }

  .stat-chip-row {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.5rem;
  }
  .stat-chip { padding: 0.6rem 0.4rem; }
  .stat-chip-val { font-size: 1.25rem; }
  .stat-chip-lbl { font-size: 0.85rem; }

  .badge-grid { grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); }
  .badge-item { padding: 0.6rem 0.4rem; }

  /* Pet cards carry a growth illustration + progress bar + 3 food
     buttons, so they need more room than a badge icon — a single
     full-width column (the auto-fill default already collapses to
     this below ~500px) reads better here than forcing extra columns. */
  .pet-food-row { gap: 0.3rem; }
  .pet-food-btn { font-size: 0.85rem; padding: 0.35rem 0.45rem; }

  .egg-choice-grid { grid-template-columns: repeat(2, 1fr); }

  .stats-table { font-size: 0.95rem; }
}

@media (max-width: 380px) {
  .stat-chip-row { grid-template-columns: repeat(2, 1fr); }
}
