/* ══════════════════════════════════════════════════════════════
   "Calm Grid" — shared by home.html, practice.html and
   package-lessons.html. A fixed light palette straight from the
   Claude Design mockup (project "English learning app home screen",
   file "English App Home.dc.html", variants 1a, 4a and 5b), not the
   app's usual dark/light theme tokens — these pages intentionally
   don't follow the theme toggle. One layout at every viewport width
   (no mobile/desktop breakpoint split), matching the mockup's single
   phone-content column, just centered on wider screens.
   ══════════════════════════════════════════════════════════════ */

:root {
  --cg-bg: #FBF7F0;
  --cg-ink: #2E2A26;
  --cg-ink-dim: rgba(46, 42, 38, 0.52);
  --cg-ink-faint: rgba(46, 42, 38, 0.5);
  --cg-border: rgba(46, 42, 38, 0.09);
  --cg-accent: #C2571F;
  --cg-teal: #2F8F8A;
  --cg-accent-2: #2F8F8A;
  --cg-accent-2-tint: #EAF4F2;
  --cg-surface: #F3ECE1;
  --cg-track: rgba(46, 42, 38, 0.09);
  --cg-font-heading: 'Nunito', system-ui, sans-serif;
  --cg-font-body: 'Plus Jakarta Sans', system-ui, sans-serif;
}

.cg-body {
  background: var(--cg-bg);
  color: var(--cg-ink);
  font-family: var(--cg-font-body);
}

.cg-container {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  padding: 16px 16px 8px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  box-sizing: border-box;
}

/* ─── LOADING SKELETON (Claude Design mockup #7a "Skeleton sweep") ──
   A full-viewport overlay shown from first paint and removed in one
   shot by landing.js once every part of the page's data has loaded —
   see initLandingPage(). Placeholder pieces reuse this page's real
   card classes for size/radius/shadow where the shapes match 1:1 (see
   .cg-skel-streak-card/.cg-skel-pkg-card below), so nothing shifts
   when the real content swaps in underneath. */
@keyframes om-sweep   { 0% { transform: translateX(-120%); } 100% { transform: translateX(320%); } }
@keyframes om-breathe { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
@keyframes om-pop     { 0%, 80%, 100% { transform: scale(.82); opacity: .5; } 40% { transform: scale(1); opacity: 1; } }
@keyframes om-fill    { 0% { width: 8%; } 100% { width: 86%; } }

.cg-skel-overlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: var(--cg-bg);
  overflow: hidden;
  display: flex;
  justify-content: center;
}

.cg-skel-sweep {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 120px;
  background: linear-gradient(100deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.7) 50%, rgba(255,255,255,0) 100%);
  animation: om-sweep 1.8s ease-in-out infinite;
  pointer-events: none;
}

.cg-skel-col {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  /* Bottom padding mirrors .cg-tabbar-spacer's height so skeleton
     content doesn't scroll in underneath the now-visible, higher
     z-index bottom tab bar (see .cg-tabbar). */
  padding: 16px 16px 78px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  box-sizing: border-box;
  overflow-y: auto;
}

.cg-skel-block {
  border-radius: 999px;
  background: var(--cg-track);
  flex-shrink: 0;
}
.cg-skel-block-accent {
  background: rgba(194, 87, 31, 0.16);
}

.cg-skel-brand {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 4px 0;
  animation: om-breathe 2.6s ease-in-out infinite;
}
.cg-skel-brand-dot {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  border-radius: 999px;
  background: var(--cg-accent);
  display: flex;
  align-items: center;
  justify-content: center;
}
.cg-skel-brand-dot span {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: #fff;
  transform: translateX(-1.5px);
}
.cg-skel-brand-word {
  font-family: var(--cg-font-heading);
  font-size: 19px;
  font-weight: 800;
  color: var(--cg-accent);
  letter-spacing: 0.01em;
}

.cg-skel-topbar {
  flex: none;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
}
.cg-skel-topbar-left {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cg-skel-topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
}

.cg-skel-streak-card {
  flex: none;
  background: #fff;
  border: 1px solid var(--cg-border);
  border-radius: 22px;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  box-shadow: 0 2px 10px rgba(46, 42, 38, 0.04);
}
.cg-skel-streak-top {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 8px;
}
.cg-skel-day-strip {
  display: flex;
  justify-content: space-between;
  gap: 6px;
}
.cg-skel-day-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
}
.cg-skel-day-dot {
  width: 30px;
  height: 30px;
  border-radius: 999px;
  background: var(--cg-track);
}
.cg-skel-day-dot.cg-skel-pop {
  animation: om-pop 1.5s ease-in-out infinite;
}
.cg-skel-day-today {
  background: transparent;
  border: 2px dashed var(--cg-border);
}
.cg-skel-hr {
  height: 1px;
  background: var(--cg-border);
}
.cg-skel-stat-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.cg-skel-stat-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}
.cg-skel-bar-bg {
  height: 6px;
  border-radius: 99px;
  background: var(--cg-track);
  overflow: hidden;
}
.cg-skel-bar-fill {
  height: 100%;
  width: 8%;
  border-radius: 99px;
  background: var(--cg-accent);
  animation: om-fill 1.9s ease-in-out infinite alternate;
}

.cg-skel-pkg-grid {
  flex: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}
.cg-skel-pkg-card {
  background: #fff;
  border: 1px solid var(--cg-border);
  border-radius: 20px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.cg-skel-pkg-img {
  aspect-ratio: 16 / 9;
  background: var(--cg-track);
}
.cg-skel-pkg-img.cg-skel-breathe {
  animation: om-breathe 1.8s ease-in-out infinite;
}
.cg-skel-pkg-body {
  padding: 11px 13px 13px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

/* ─── TOP BAR ──────────────────────────────────────────────── */
.cg-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.cg-topbar-greeting {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.cg-top-date {
  font-size: 13px;
  font-weight: 600;
  color: var(--cg-ink-dim);
}

.cg-top-hi {
  font-family: var(--cg-font-heading);
  font-size: 24px;
  font-weight: 800;
  line-height: 1.2;
  color: var(--cg-ink);
}

.cg-top-settings-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  background: #fff;
  border: 1px solid var(--cg-border);
  border-radius: 999px;
  color: var(--cg-ink-dim);
  cursor: pointer;
  padding: 0;
}
.cg-top-settings-btn:hover {
  background: var(--cg-surface);
  color: var(--cg-ink);
}

.cg-topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

/* ─── PRACTICE LEVEL PICKER ────────────────────────────────── */
.cg-level-picker {
  position: relative;
}

.cg-level-pill {
  display: flex;
  align-items: center;
  gap: 6px;
  background: rgba(194, 87, 31, 0.14);
  border: none;
  border-radius: 999px;
  padding: 8px 12px 8px 14px;
  cursor: pointer;
  font-family: var(--cg-font-body);
  font-size: 13px;
  font-weight: 800;
  color: var(--cg-accent);
  letter-spacing: 0.01em;
}
.cg-level-pill:hover {
  background: rgba(194, 87, 31, 0.2);
}
.cg-level-pill svg {
  flex-shrink: 0;
  color: var(--cg-accent);
}

.cg-level-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 200;
  width: 176px;
  background: #fff;
  border: 1px solid var(--cg-border);
  border-radius: 14px;
  padding: 8px;
  box-shadow: 0 10px 30px rgba(46, 42, 38, 0.16);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* The [hidden] attribute and the .cg-level-menu class have equal CSS
   specificity, and this rule's own display:flex loads after the UA
   stylesheet's [hidden]{display:none} — so without this override,
   toggling the `hidden` property wouldn't actually hide the menu. */
.cg-level-menu[hidden] {
  display: none;
}

.cg-level-menu-title {
  font-size: 11px;
  font-weight: 700;
  color: var(--cg-ink-faint);
  padding: 4px 10px 6px;
  display: block;
}

.cg-level-option {
  display: flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  border-radius: 999px;
  padding: 9px 12px;
  font-family: var(--cg-font-body);
  font-size: 13.5px;
  font-weight: 700;
  color: var(--cg-ink);
  cursor: pointer;
  text-align: left;
  width: 100%;
}
.cg-level-option:hover {
  background: rgba(194, 87, 31, 0.08);
}
.cg-level-option.active {
  background: rgba(194, 87, 31, 0.14);
  color: var(--cg-accent);
}
/* Hint-style label — the colored code chip is the primary identifier
   now, this is just a smaller, dimmer clarification next to it. */
.cg-level-option-name {
  flex: 1;
  min-width: 0;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--cg-ink-dim);
}
.cg-level-option.active .cg-level-option-name {
  color: var(--cg-accent);
}
.cg-level-option-code {
  flex-shrink: 0;
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--cg-track);
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.02em;
  color: var(--cg-ink-dim);
}
/* Same 4-color tonal ramp as .pl-level-pill.lvl-a1..b2 (package-lessons)
   and .prof-lesson-badge.lvl-a1..b2 (profile) — its own rule since this
   chip is sized differently from those. Left level-colored regardless
   of .active (it's a meaningful identity now, not a generic tint) —
   the row's own background + the name turning accent-colored above
   already signal selection. */
.cg-level-option-code.lvl-a1 { background: #f0fae1; color: #56633f; }
.cg-level-option-code.lvl-a2 { background: #e1eecc; color: #3d472b; }
.cg-level-option-code.lvl-b1 { background: #fff2eb; color: #8c491a; }
.cg-level-option-code.lvl-b2 { background: #ffe1d0; color: #643312; }

/* ─── STREAK CARD ──────────────────────────────────────────── */
.cg-streak-card {
  background: #fff;
  border: 1px solid var(--cg-border);
  border-radius: 22px;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  box-shadow: 0 2px 10px rgba(46, 42, 38, 0.04);
}

.cg-streak-top {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 8px;
}

.cg-streak-num-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.cg-streak-num {
  font-family: var(--cg-font-heading);
  font-size: 40px;
  font-weight: 900;
  line-height: 1;
  color: var(--cg-accent);
}

.cg-streak-num-label {
  font-size: 15px;
  font-weight: 700;
  color: var(--cg-ink);
}

.cg-streak-best {
  font-size: 12px;
  font-weight: 600;
  color: var(--cg-ink-faint);
}

/* Day strip: own markup (not the shared .streak-day-dot component —
   see renderHomeDayStrip in landing.js) so the weekday letter sits as
   a subtitle BELOW each dot instead of nested inside it, with clear
   gaps between the seven day columns. */
.cg-day-strip {
  display: flex;
  justify-content: space-between;
  gap: 6px;
}

.cg-day-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
}

.cg-day-dot {
  width: 30px;
  height: 30px;
  border-radius: 999px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.cg-day-label {
  font-family: var(--cg-font-body);
  font-size: 11px;
  font-weight: 700;
  color: var(--cg-ink-faint);
}

.cg-day-dot.completed {
  background: var(--cg-accent);
}

/* Bold solid fill (matching .completed's visual weight) instead of a
   pale near-background tan, which read as washed-out/barely-there
   next to the vivid solid-orange completed dots. The size rule
   targets the nested <svg> — GAMIFICATION_SVGS.shield hardcodes its
   own width/height attributes on the <svg> itself, so a rule on the
   wrapping .cg-day-shield-icon span alone never actually resizes it. */
.cg-day-dot.shielded {
  background: var(--cg-teal);
}
.cg-day-dot.shielded .cg-day-shield-icon {
  color: #fff;
}
.cg-day-dot.shielded .cg-day-shield-icon svg {
  width: 14px;
  height: 14px;
}

.cg-day-dot.missed {
  background: var(--cg-track);
}

.cg-day-dot.today-progress {
  background: transparent;
  border: 2px dashed rgba(46, 42, 38, 0.22);
}

.cg-streak-hr {
  height: 1px;
  background: var(--cg-border);
}

.cg-stat-block + .cg-stat-block {
  margin-top: -4px;
}

.cg-stat-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 6px;
}

.cg-stat-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--cg-ink);
}

.cg-stat-val {
  font-size: 12px;
  font-weight: 700;
  color: var(--cg-ink-dim);
}

.cg-stat-bar-bg {
  height: 6px;
  border-radius: 99px;
  background: var(--cg-track);
  overflow: hidden;
}

.cg-stat-bar-fill {
  height: 100%;
  border-radius: 99px;
  transition: width 0.5s ease;
}

.cg-level-bar-fill { background: #f0a43a; }
.cg-goal-bar-fill { background: var(--cg-teal); }

/* ─── PRACTICE PACKAGES ────────────────────────────────────── */
.cg-pkg-section {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.cg-pkg-section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

.cg-pkg-section-title {
  font-family: var(--cg-font-heading);
  font-size: 19px;
  font-weight: 800;
  color: var(--cg-ink);
}

.cg-pkg-see-all {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--cg-accent);
  text-decoration: none;
}
.cg-pkg-see-all:hover { text-decoration: underline; }

.cg-pkg-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}

.cg-pkg-card {
  background: #fff;
  border: 1px solid var(--cg-border);
  border-radius: 20px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  text-align: left;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.cg-pkg-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(46, 42, 38, 0.08);
}

/* Daily quest teaser card — same .cg-pkg-card box model (appended as the
   last item in #landing-packages-grid), tinted to match the popup it
   opens (.cg-quest-panel uses the same --cg-accent-2-tint) instead of
   a topic photo, so it visually announces itself as different. */
.cg-quest-teaser-card {
  background: var(--cg-accent-2-tint);
}
.cg-quest-teaser-card .cg-pkg-img-wrap {
  background: transparent;
}

/* Wide (16:9) box, white background; the square (1024x1024) source
   images use object-fit:contain instead of cover, so the full
   illustration always shows uncropped, letterboxed with white padding
   on the left/right rather than cropped top/bottom to fill the box. */
.cg-pkg-img-wrap {
  aspect-ratio: 16 / 9;
  background: rgb(235, 221, 197);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.cg-pkg-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.cg-pkg-body {
  padding: 11px 13px 13px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.cg-pkg-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--cg-ink);
}

.cg-pkg-meta {
  font-size: 11.5px;
  font-weight: 500;
  color: var(--cg-ink-faint);
}

.cg-pkg-bar-bg {
  height: 5px;
  border-radius: 99px;
  background: var(--cg-track);
  overflow: hidden;
  display: flex;
}

/* Two segments sit side by side inside the flex track, each sized to
   its own share of the total — done first, then working butted up
   against it, so together they read as one continuous fill with a
   color change partway through. */
.cg-pkg-bar-fill {
  height: 100%;
  flex: 0 0 auto;
  transition: width 0.5s ease;
}

.cg-pkg-bar-fill-done { background: var(--cg-teal); }
.cg-pkg-bar-fill-working { background: #f0a43a; }

/* ─── BOTTOM TAB BAR ───────────────────────────────────────── */
.cg-tabbar-spacer {
  height: 70px;
}

.cg-tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  /* Matches .cg-container's own 480px reading-column cap — without
     this the tabs (justify-content:space-around) spread edge-to-edge
     on a wide desktop viewport instead of staying under the narrow
     centered content above them. The auto side margins center it
     within left:0/right:0 even though max-width constrains its width. */
  max-width: 480px;
  margin: 0 auto;
  /* Above .cg-skel-overlay (z-index:500) so the bottom nav stays visible
     and usable through the loading skeleton instead of being covered by
     it — the overlay only needs to hide the page content above it. */
  z-index: 510;
  display: flex;
  justify-content: space-around;
  align-items: center;
  background: #fff;
  border-top: 1px solid var(--cg-border);
  padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
}

.cg-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--cg-ink-faint);
  text-decoration: none;
  font-family: var(--cg-font-body);
  font-size: 10.5px;
  font-weight: 600;
  padding: 4px 10px;
}

.cg-tab[aria-current="page"] {
  color: var(--cg-accent);
}

.cg-tab:hover {
  color: var(--cg-ink);
}
.cg-tab[aria-current="page"]:hover {
  color: var(--cg-accent);
}

/* .cg-tab is always an <a>, never a real <button>/<input> — :disabled
   never actually matches, so the guest-locked state (Practice/Starred,
   toggled in updateAuthUI()) uses a real .disabled class instead. Kept
   both selectors together since they're the same visual state. */
.cg-tab:disabled,
.cg-tab.disabled {
  color: var(--cg-ink-faint);
  opacity: 0.45;
  cursor: not-allowed;
}
.cg-tab:disabled:hover,
.cg-tab.disabled:hover {
  color: var(--cg-ink-faint);
}

/* Path tab "new feature" dot (header.js's updatePathTabHint(), cleared for
   good once a hop is opened) — same visual spec as path.css's .pt-mic-dot,
   just placed here since this stylesheet (unlike path.css) is shared by
   every page that has a tab bar, path.html included. */
.cg-tab-icon { position: relative; display: inline-flex; }
.cg-tab-dot {
  position: absolute;
  top: -1px;
  right: -1px;
  width: 10px;
  height: 10px;
  border-radius: 999px;
  background: #e5372f;
  box-shadow: 0 0 0 2px #fff; /* .cg-tabbar is always white, unlike path.css's themed surface */
  animation: cg-tab-dot-blink 1.1s ease-in-out infinite;
  pointer-events: none;
}
@keyframes cg-tab-dot-blink { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) {
  .cg-tab-dot { animation: none; }
}

/* ─── DAILY QUEST PANEL (home.html popup content; formerly practice.html's
   own page section before that page was removed and this content moved
   into a modal opened from home.html's teaser card) ─────────────── */
.cg-quest-panel {
  background: var(--cg-accent-2-tint);
  border-radius: 22px;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.cg-quest-panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  /* Clears .daily-quest-close, a 32px circle absolutely positioned
     14px from the panel's corner (home-calm.css, below) — without this
     the countdown text and the close button would overlap. */
  padding-right: 34px;
}

.cg-quest-title {
  font-family: var(--cg-font-heading);
  font-size: 26px;
  font-weight: 800;
  color: var(--cg-ink);
}

.cg-quest-countdown {
  font-size: 12px;
  font-weight: 700;
  color: var(--cg-accent-2);
  flex-shrink: 0;
}

.cg-quest-subtitle {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--cg-ink-dim);
  margin-top: -6px;
}

/* The tinted .cg-quest-panel IS the whole popup surface — .modal's own
   white background/padding/shadow are cleared here so the panel isn't
   nested inside a second, visibly different card, and the close button
   floats in the panel's own corner instead of a generic modal-header row. */
.daily-quest-modal {
  background: transparent;
  padding: 0;
  box-shadow: none;
}
.daily-quest-modal .cg-quest-panel {
  position: relative; /* anchors .daily-quest-close, its absolutely-positioned child */
  box-shadow: 0 12px 32px rgba(46, 42, 38, 0.22);
}
.daily-quest-close {
  position: absolute;
  top: 14px;
  right: 14px;
}

/* .cg-quest-card is an <a> — css/modals.css's generic `.modal-backdrop a`
   link style (accent color + underline) would otherwise bleed onto its
   title/level text now that this card lives inside a modal, since
   `.modal-backdrop a` (class+tag) outranks .cg-quest-card's own
   text-decoration:none (class only). Restated here at higher specificity
   so it wins regardless of source order. */
.modal-backdrop .cg-quest-card {
  text-decoration: none;
  color: inherit;
}

.cg-quest-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.cg-quest-card {
  background: #fff;
  border: 2px solid transparent;
  border-radius: 18px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(46, 42, 38, 0.05);
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease, filter 0.15s ease, opacity 0.15s ease;
}
.cg-quest-card:hover {
  transform: translateY(-2px);
  border-color: var(--cg-accent-2);
  box-shadow: 0 6px 16px rgba(46, 42, 38, 0.1);
}

/* Emphasize the card matching the user's selected practice level over
   the other 3 — transform/filter only, so .cg-quest-grid's own track
   sizing (and therefore .cg-quest-panel's height) never changes. The
   :hover variants combine both transforms explicitly, since `transform`
   isn't additive across rules and the plain .cg-quest-card:hover rule
   above would otherwise cancel out the scale on hover. */
.cg-quest-card.selected {
  transform: scale(1.06);
  z-index: 1;
  border-color: var(--cg-accent-2);
  box-shadow: 0 6px 18px rgba(46, 42, 38, 0.12);
}
.cg-quest-card.selected:hover {
  transform: scale(1.06) translateY(-2px);
}

.cg-quest-card.dimmed {
  filter: grayscale(1);
  transform: scale(0.9);
  opacity: 0.75;
}
.cg-quest-card.dimmed:hover {
  transform: scale(0.9) translateY(-2px);
}

/* Compact strip, matching the 4a mockup's proportions (a short band,
   not a near-square tile). Lesson illustrations are wide landscape
   art (not the square 1024x1024 topic images used on home.html's
   grid), so this uses object-fit:cover to fill the strip edge to
   edge — object-fit:contain would letterbox these with white bars
   above/below, since the box is much shorter than the source image. */
.cg-quest-img-wrap {
  position: relative;
  aspect-ratio: 2 / 1;
  background: var(--cg-surface);
  overflow: hidden;
}

.cg-quest-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.cg-quest-level-badge {
  position: absolute;
  top: 8px;
  left: 8px;
  background: #fff;
  color: var(--cg-accent-2);
  font-family: var(--cg-font-body);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: 0.03em;
  padding: 3px 8px;
  border-radius: 999px;
  box-shadow: 0 1px 4px rgba(46, 42, 38, 0.12);
}

.cg-quest-card-body {
  padding: 10px 12px 12px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.cg-quest-card-title {
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.25;
  color: var(--cg-ink);
}

.cg-quest-card-level {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--cg-ink-faint);
}

.cg-quest-guest {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
  padding: 10px 4px 4px;
}

.cg-quest-guest p {
  margin: 0;
  font-size: 13px;
  color: var(--cg-ink-dim);
  max-width: 280px;
}

/* ─── STARRED PAGE (mockup 12a) ──────────────────────────────
   Reuses .cg-quest-card/.cg-quest-img-wrap/.cg-quest-level-badge/
   .cg-quest-card-body as-is (practice.js's daily-quest card) — these
   .cg-star-* rules only add the star toggle and the fade/undo/
   countdown overlay on top, and are additive-only so practice.html's
   own use of .cg-quest-card is completely unaffected. */
.cg-star-card {
  position: relative;
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
/* Fade only the underlying image/title, not the whole card — opacity on
   .cg-star-card itself would compound onto .cg-star-pending-overlay and
   its Undo button too (descendant opacity multiplies with the parent's),
   washing the button out along with the content behind the scrim. */
.cg-star-card .cg-quest-img-wrap,
.cg-star-card .cg-quest-card-body {
  transition: opacity 0.38s ease;
}
.cg-star-toggle {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 30px;
  height: 30px;
  border: none;
  border-radius: 999px;
  background: #fff;
  box-shadow: 0 1px 4px rgba(46, 42, 38, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
  color: var(--cg-accent);
}
.cg-star-toggle:hover {
  background: rgba(194, 87, 31, 0.1);
}
.cg-star-card.pending {
  transform: scale(0.97);
}
.cg-star-card.pending .cg-quest-img-wrap,
.cg-star-card.pending .cg-quest-card-body {
  opacity: 0.45;
}
.cg-star-pending-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(249, 244, 237, 0.55);
}
/* This unconditional display:flex beats the browser's default
   [hidden]{display:none} UA rule (equal specificity, author style
   loads after) — without this override the overlay would still
   intercept clicks (e.g. on the star toggle) while "hidden". */
.cg-star-pending-overlay[hidden] {
  display: none;
}
.cg-star-undo-btn {
  padding: 11px 18px;
  border: none;
  border-radius: 999px;
  background: var(--cg-accent);
  box-shadow: 0 4px 12px rgba(46, 42, 38, 0.15);
  color: #fff;
  font-family: var(--cg-font-body);
  font-weight: 800;
  font-size: 12px;
  cursor: pointer;
}
.cg-star-undo-btn:hover {
  background: #9e441a;
}
.cg-star-empty {
  padding: 26px 16px;
  text-align: center;
  font-size: 12.5px;
  line-height: 1.5;
  font-weight: 500;
  color: var(--cg-ink-dim);
}

/* ─── PRACTICE PAGE: CONTINUE LIST ──────────────────────────── */
.cg-continue-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.cg-continue-row {
  display: flex;
  align-items: center;
  gap: 12px;
  background: #fff;
  border: 1px solid var(--cg-border);
  border-radius: 16px;
  padding: 10px 12px;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.cg-continue-row:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(46, 42, 38, 0.07);
}

.cg-continue-thumb-wrap {
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  border-radius: 12px;
  background: #fff;
  border: 1px solid var(--cg-border);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.cg-continue-thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.cg-continue-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.cg-continue-title {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--cg-ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.cg-continue-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

.cg-continue-chip {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--cg-ink-dim);
  background: var(--cg-track);
  padding: 3px 8px;
  border-radius: 999px;
}

.cg-continue-arrow {
  flex-shrink: 0;
  color: var(--cg-ink-faint);
}

/* ─── PACKAGE LESSONS PAGE (5b) ──────────────────────────────── */
.pl-hero {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  padding: 16px 16px 14px;
  box-sizing: border-box;
}

.pl-back-btn {
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 999px;
  background: #fff;
  border: 1px solid var(--cg-border);
  color: var(--cg-ink-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}
.pl-back-btn:hover {
  background: var(--cg-surface);
  color: var(--cg-ink);
}

.pl-thumb {
  flex-shrink: 0;
  width: 58px;
  height: 58px;
  border-radius: 14px;
  object-fit: cover;
  background: var(--cg-surface);
}

.pl-titles {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.pl-title {
  margin: 0;
  font-family: var(--cg-font-heading);
  font-size: 21px;
  font-weight: 800;
  line-height: 1.15;
  color: var(--cg-ink);
}

.pl-subtitle {
  margin: 0;
  font-size: 12px;
  font-weight: 500;
  color: var(--cg-ink-dim);
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.pl-search-outer {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  padding: 0 16px 10px;
  box-sizing: border-box;
}

.pl-search-wrap {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 42px;
  padding: 0 8px 0 16px;
  border-radius: 999px;
  background: #fff;
  border: 1px solid var(--cg-border);
  box-sizing: border-box;
}

.pl-search-icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  color: var(--cg-ink-faint);
}

.pl-search-input {
  flex: 1;
  min-width: 0;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--cg-font-body);
  font-size: 13px;
  font-weight: 600;
  color: var(--cg-ink);
}
.pl-search-input::placeholder {
  color: var(--cg-ink-faint);
  font-weight: 500;
}
/* Hide the native WebKit "clear" (x) control on type=search — .pl-search-clear replaces it. */
.pl-search-input::-webkit-search-cancel-button {
  display: none;
}

.pl-search-clear {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 999px;
  border: none;
  background: var(--cg-surface);
  color: var(--cg-ink-dim);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  align-items: center;
  justify-content: center;
}
/* Not `display:flex` unconditionally — that would beat the browser's
   default `[hidden] { display: none }` rule the same way a prior bug on
   this page did (.pl-lesson-list). Only the visible state opts into flex. */
.pl-search-clear:not([hidden]) {
  display: flex;
}
.pl-search-clear:hover {
  background: var(--cg-accent-2-tint);
  color: var(--cg-ink);
}

.pl-body {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  padding: 5px 16px 32px;
  display: flex;
  flex-direction: column;
  gap: 18px;
  box-sizing: border-box;
}

.pl-empty {
  margin: 24px 0 0;
  text-align: center;
  font-size: 13px;
  color: var(--cg-ink-faint);
}

.pl-level-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.pl-group-header {
  display: flex;
  align-items: center;
  gap: 9px;
  cursor: pointer;
  padding: 4px 4px 9px;
}

.pl-level-pill {
  padding: 4px 11px;
  border-radius: 999px;
  font-family: var(--cg-font-body);
  font-size: 11.5px;
  font-weight: 800;
  letter-spacing: 0.04em;
}
/* Two brand hues (sage for A-levels, terracotta for B), each in two
   weights — a lighter shade for the first sub-level, a richer one for
   the second — straight from the design mockup's own tonal ramp
   (organic.css --color-accent-2-100/200/700/800 and
   --color-accent-100/200/700/800), so all four level tags are visibly
   distinct colors rather than two repeated hues. Deliberately literal
   values here rather than the site's shared --cg-teal/--cg-accent
   variables, which stay untouched everywhere else (buttons, links,
   progress fills, the "done" status coin). */
.pl-level-pill.lvl-a1 {
  background: #f0fae1;
  color: #56633f;
}
.pl-level-pill.lvl-a2 {
  background: #e1eecc;
  color: #3d472b;
}
.pl-level-pill.lvl-b1 {
  background: #fff2eb;
  color: #8c491a;
}
.pl-level-pill.lvl-b2 {
  background: #ffe1d0;
  color: #643312;
}

.pl-group-count {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--cg-ink-dim);
}

.pl-group-divider {
  flex: 1;
  height: 1px;
  background: var(--cg-border);
}

/* Border-drawn chevron (matches the mockup) rather than an SVG polyline —
   points down when collapsed (rotate(45deg)), flips to point up when open
   via a further +180°, same relationship the old SVG's rotate(180deg)
   already encoded. */
.pl-group-caret {
  flex-shrink: 0;
  width: 9px;
  height: 9px;
  border-right: 2.75px solid var(--cg-ink-dim);
  border-bottom: 2.75px solid var(--cg-ink-dim);
  margin-bottom: 3px;
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}
.pl-group-caret.open {
  transform: rotate(225deg);
}

.pl-lesson-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* The unconditional `display: flex` above otherwise beats the browser's
   default `[hidden] { display: none }` UA rule (author styles win over UA
   styles regardless of matching specificity), so toggleGroup()'s
   `list.hidden = true` never actually collapsed anything visually. */
.pl-lesson-list[hidden] {
  display: none;
}

.pl-lesson-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 12px;
  border-radius: 999px;
  background: #fff;
  border: 1px solid var(--cg-border);
  cursor: pointer;
  transition: background 0.15s ease;
}
.pl-lesson-row:hover {
  background: var(--cg-surface);
}
/* Guest lock state (mockup 5b) — row stays tappable (opens the lock
   notice), just visually dimmed to read as unavailable. */
.pl-lesson-row.locked {
  opacity: 0.45;
}

.pl-lock-icon {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--cg-track);
  color: var(--cg-ink-dim);
}

.pl-status-coin {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border: 2px solid var(--cg-border);
  position: relative;
}
.pl-status-coin.working {
  background: var(--cg-surface);
  border-color: var(--cg-accent);
}
.pl-status-coin.done {
  background: var(--cg-teal);
  border-color: var(--cg-teal);
}
.pl-coin-check {
  color: #fff;
  font-size: 13px;
  font-weight: 800;
}
.pl-coin-dot {
  width: 11px;
  height: 11px;
  border-radius: 999px;
  background: var(--cg-accent);
}

.pl-lesson-title {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--cg-ink);
}

.pl-word-tag {
  flex-shrink: 0;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--cg-border);
  font-size: 10.5px;
  font-weight: 600;
  color: var(--cg-ink-dim);
}

.pl-lesson-duration {
  flex-shrink: 0;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--cg-ink-faint);
}

/* Status "mark as" popover — same structural shape as lesson-loader.js's
   .dot-reset-tip/.dot-option (viewport-edge repositioning in JS), own
   class names so it doesn't collide with styles.css's versions (this
   page loads styles.css too). */
.pl-status-tip {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  z-index: 20;
  width: 168px;
  background: #fff;
  border: 1px solid var(--cg-border);
  border-radius: 14px;
  padding: 6px;
  box-shadow: 0 10px 30px rgba(46, 42, 38, 0.16);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.pl-status-option {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  background: none;
  border: none;
  border-radius: 999px;
  padding: 8px 10px;
  font-family: var(--cg-font-body);
  font-size: 12.5px;
  font-weight: 700;
  color: var(--cg-ink);
  cursor: pointer;
  text-align: left;
}
.pl-status-option:hover {
  background: var(--cg-surface);
}
.pl-status-option.active {
  background: rgba(194, 87, 31, 0.1);
}

.pl-status-option-dot {
  flex-shrink: 0;
  width: 13px;
  height: 13px;
  border-radius: 999px;
  border: 2px solid var(--cg-border);
}
.pl-status-option-dot.working {
  background: var(--cg-surface);
  border-color: var(--cg-accent);
}
.pl-status-option-dot.done {
  background: var(--cg-teal);
  border-color: var(--cg-teal);
}
