/**
 * Shared platform visual tokens (সহজশিক্ষা).
 * Copied per app under static/css/ — keep in sync when tokens change.
 */
:root {
  --ease-brand-gradient: linear-gradient(90deg, #4338ca 0%, #7c3aed 50%, #2563eb 100%);
  --ease-header-fg: #ffffff;
  --ease-footer-bg: #111827;
  --ease-footer-fg: #d1d5db;
  --ease-footer-accent: #818cf8;
  --ease-footer-muted: #9ca3af;
  --ease-radius-md: 0.375rem;
  --ease-space-page: 1rem;

  /* ── Typography tokens ──────────────────────────────────────────────────── */
  /* Font family — Bengali-first system stack, no webfont requests */
  --font-sans: "Anek Bangla", "Noto Sans Bengali", "Segoe UI", system-ui, "Nirmala UI",
    "Kohinoor Bangla", "Bangla Sangam MN", -apple-system, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
    "Liberation Mono", "Courier New", monospace;

  /* Type scale — fluid clamp() for responsive sizing */
  --font-size-xs: clamp(0.65rem, 0.6rem + 0.25vw, 0.75rem);
  --font-size-sm: clamp(0.75rem, 0.7rem + 0.3vw, 0.875rem);
  --font-size-base: clamp(0.875rem, 0.8rem + 0.35vw, 1rem);
  --font-size-lg: clamp(1rem, 0.9rem + 0.4vw, 1.125rem);
  --font-size-xl: clamp(1.1rem, 1rem + 0.45vw, 1.25rem);
  --font-size-2xl: clamp(1.25rem, 1.1rem + 0.55vw, 1.5rem);
  --font-size-3xl: clamp(1.5rem, 1.3rem + 0.7vw, 1.875rem);

  /* Weight scale */
  --font-weight-normal: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;
  --font-weight-extrabold: 800;

  /* Line heights */
  --leading-tight: 1.1;
  --leading-snug: 1.375;
  --leading-normal: 1.5;
  --leading-relaxed: 1.55;

  /* 3-Tier Typography Tokens (Dashboard & Quiz) */
  --text-title-size: clamp(1.5rem, 1.3rem + 0.7vw, 1.75rem);
  /* ~24-28px */
  --text-title-weight: 600;

  --text-section-size: clamp(1rem, 0.9rem + 0.4vw, 1.125rem);
  /* ~16-18px */
  --text-section-weight: 600;

  --text-body-size: 0.875rem;
  /* ~14px */
  --text-body-weight: 400;

  /* ── StudyPrep design-system color tokens ───────────────────────────── */
  --sp-brand: #3D52A0;
  /* muted indigo — titles, links, active states */
  --sp-brand-soft: #7886C7;
  /* lighter indigo — section labels */
  --sp-brand-bg: #EEF0FA;
  /* very light indigo — active item background */
  --sp-brand-border: #93A8D4;
  /* soft blue — left accent border */

  --sp-text-1: #111827;
  /* near-black — strong text */
  --sp-text-2: #374151;
  /* dark gray — body */
  --sp-text-3: #6B7280;
  /* medium gray — inactive nav/items */
  --sp-text-4: #9CA3AF;
  /* light gray — meta/dates */

  --sp-border: #E5E7EB;
  --sp-border-soft: #F3F4F6;

  --sp-logout-text: #B91C1C;
  /* red-700 — soft red */
  --sp-logout-border: #FECACA;
  /* red-200 */
  --sp-logout-bg: #FEF2F2;
  /* red-50 */
  --sp-logout-bg-hov: #FEE2E2;
  /* red-100 */

  /* Top-nav — light blue theme */
  --sp-nav: #3B82F6;
  /* blue-500 — nav link text */
  --sp-nav-strong: #2563EB;
  /* blue-600 — active nav text */
  --sp-nav-bg: #DBEAFE;
  /* blue-100 — active highlight */
  --sp-nav-bg-hov: #EFF6FF;
  /* blue-50  — hover */

  /* Sidebar items + perf rows — light green theme */
  --sp-item-text: #3D7A56;
  /* muted green — inactive items */
  --sp-item-strong: #166534;
  /* green-800 — active/hover items */
  --sp-item-bg: #DCFCE7;
  /* green-100 — active background */
  --sp-item-bg-hov: #F0FDF4;
  /* green-50  — hover background */

}

.ease-site-header {
  background: #1f2937;
  color: var(--ease-header-fg);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.ease-site-footer {
  background: var(--ease-footer-bg);
  color: var(--ease-footer-fg);
}

.ease-header-shell {
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ease-space-page);
}

@media (max-width: 640px) {
  .ease-header-shell {
    flex-direction: column;
    align-items: flex-start;
  }
}

.ease-header-brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  color: var(--ease-header-fg);
}

.ease-header-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.5rem;
}

.ease-nav-link {
  color: var(--ease-header-fg);
  text-decoration: none;
  font-weight: var(--font-weight-medium);
  padding: 0.25rem 0.5rem;
  border-radius: var(--ease-radius-md);
}

.ease-nav-link:hover {
  background: rgba(255, 255, 255, 0.15);
}

/* ============================================================================
   StudyPrep design system — reusable semantic component classes.
   Own TYPOGRAPHY + COLOR only (layout/spacing stays in Tailwind utilities).
   Reuse these on every page so fonts & colors stay consistent.
   ============================================================================ */

/* ===== Brand wordmark: relocated to
   elearn-web/standard-components/css/auth-shell.css (appended below via
   sync.sh) — do not re-add here. ===== */

/* ===== Top-nav links (light blue) ===== */
.sp-nav-link {
  font-size: var(--font-size-sm);
  color: var(--sp-nav);
  text-decoration: none;
}

.sp-nav-link:hover {
  color: var(--sp-nav-strong);
  background: var(--sp-nav-bg-hov);
}

.sp-nav-link.is-active {
  color: var(--sp-nav-strong);
  font-weight: var(--font-weight-medium);
  background: var(--sp-nav-bg);
}

/* ===== Logout (soft red) ===== */
.sp-logout {
  font-size: var(--font-size-sm);
  color: var(--sp-logout-text);
  border: 1px solid var(--sp-logout-border);
  background: var(--sp-logout-bg);
  text-decoration: none;
}

.sp-logout:hover {
  background: var(--sp-logout-bg-hov);
}

/* ===== Shared app bar (header + footer) — lighter dark ===== */
.sp-app-bar {
  background: #1f2937;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

/* ===== Dark-header nav links (white on #111827) ===== */
.sp-nav-dark {
  font-size: var(--font-size-sm);
  color: var(--ease-footer-fg);
  text-decoration: none;
}

.sp-nav-dark:hover {
  color: #fff;
  background: rgba(255, 255, 255, 0.1);
}

.sp-nav-dark.is-active {
  color: #fff;
  font-weight: var(--font-weight-medium);
  background: rgba(255, 255, 255, 0.15);
}

/* ===== Dark-header logout (muted on dark) ===== */
.sp-logout-dark {
  font-size: var(--font-size-sm);
  color: var(--ease-footer-fg);
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: rgba(255, 255, 255, 0.05);
  text-decoration: none;
}

.sp-logout-dark:hover {
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
}

/* ===== Headings (unified, lightly colored) ===== */
.sp-section-label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  color: var(--sp-brand);
}

.sp-card-title {
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-semibold);
  color: var(--sp-text-1);
  line-height: var(--leading-snug);
}

.sp-page-title {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  color: var(--sp-text-1);
  line-height: var(--leading-tight);
}

/* .sp-item*/
.sp-dash-sidebar/.sp-dash-main now come from the standard-components: dashboard-shell.css block below (single source). */

/* ===== Performance rows (light green) ===== */
.sp-perf-row:hover {
  background: var(--sp-item-bg-hov);
}

.sp-perf-name {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--sp-item-strong);
}

.sp-perf-meta {
  font-size: var(--font-size-xs);
  color: var(--sp-item-text);
}

/* ===== Reusable text roles (for next pages) ===== */
.sp-body {
  font-size: var(--font-size-sm);
  color: var(--sp-text-2);
}

.sp-meta {
  font-size: var(--font-size-xs);
  color: var(--sp-text-4);
}

.sp-link {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--sp-brand);
  text-decoration: none;
}

.sp-link:hover {
  text-decoration: underline;
}

/* 3-Tier Typography Utilities */
.text-title {
  font-size: var(--text-title-size);
  font-weight: var(--text-title-weight);
  line-height: var(--leading-tight);
}

.text-section {
  font-size: var(--text-section-size);
  font-weight: var(--text-section-weight);
  line-height: var(--leading-snug);
}

.text-body {
  font-size: var(--text-body-size);
  font-weight: var(--text-body-weight);
  color: var(--sp-text-4);
}

/* ===== Buttons ===== */
.sp-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-semibold);
  border-radius: 0.5rem;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s, transform 0.1s ease;
  cursor: pointer;
  border: none;
}

.sp-btn-primary {
  padding: 0.625rem 1.5rem;
  background: #7c3aed;
  color: #ffffff;
}

.sp-btn-primary:hover {
  background: #6d28d9;
}

.sp-btn-secondary {
  padding: 0.5625rem 1.4375rem;
  /* subtract 1px for border to keep height matched */
  background: transparent;
  color: #7c3aed;
  border: 1px solid #7c3aed;
}

.sp-btn-secondary:hover {
  background: rgba(124, 58, 237, 0.05);
}

.sp-btn-destructive {
  padding: 0.625rem 1.5rem;
  background: transparent;
  color: #dc2626;
}

.sp-btn-destructive:hover {
  background: rgba(220, 38, 38, 0.05);
  color: #b91c1c;
}

/* .sp-btn-success/-warning/-danger/-sm come from the standard-components:
   components.css block below (single source — a stale local duplicate of
   .sp-btn-danger/.sp-btn-sm used to conflict with it; .sp-btn-primary/
   -secondary are NOT part of that vendored block by design, so they stay
   defined here). */

/* Touch target: components.css's pointer:coarse pass can't reach these two
   since they're defined here, not in the vendored block — see
   standard-components/README.md's adoption step 4. */
@media (pointer: coarse) {

  .sp-btn-primary,
  .sp-btn-secondary {
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}

/* ===== Form inputs ===== */
.sp-input {
  width: 100%;
  padding: 0.5rem 0.75rem;
  border: 1px solid #d1d5db;
  border-radius: 0.5rem;
  background: #fff;
  font-size: var(--font-size-sm);
  transition: border-color 0.15s, box-shadow 0.15s;
}

.sp-input:focus {
  outline: none;
  border-color: #3b82f6;
  box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
}

.sp-input:disabled {
  background: #f9fafb;
  color: #9ca3af;
  cursor: not-allowed;
  border-color: #e5e7eb;
}

/* ===== Activity cards ===== */
.sp-activity-card {
  transition: border-color 0.15s, box-shadow 0.15s;
}

.sp-activity-card:hover {
  border-color: #93c5fd;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}

/* ===== Checkbox accent ===== */
.sp-checkbox {
  accent-color: #2563eb;
}

/* ===== Layout shell (viewport-locked) ===== */
html {
  overflow-x: hidden;
}

.sp-viewport {
  height: 100vh;
  overflow: hidden;
  font-family: var(--font-sans);
}

.sp-content-shell {
  flex: 1 1 0%;
  display: flex;
  overflow: hidden;
}


.sp-margin-x {
  padding-left: 5%;
  padding-right: 5%;
}

/* ===== Login split layout: relocated to
   elearn-web/standard-components/css/auth-shell.css (appended below via
   sync.sh) — do not re-add here. ===== */
@media (max-width: 768px) {
  .sp-viewport {
    height: auto;
    min-height: 100vh;
    overflow: auto;
  }

  .sp-content-shell {
    overflow: auto;
    flex-direction: column;
  }

  .sp-light-nav-blue {
    font-size: var(--font-size-sm);
  }

  .sp-margin-x {
    padding-left: 1%;
    padding-right: 1%;
  }
}

@media (max-width: 480px) {
  .sp-margin-x {
    padding-left: 1%;
    padding-right: 1%;
  }

  .sp-light-nav-blue {
    font-size: var(--font-size-xs);
  }

  .sp-pill {
    font-size: var(--font-size-xs);
    padding: 0.3rem 0.75rem;
  }

  /* AI Guide chat header: keep title + চ্যাট নোট + ফিল্টার on one row on
     narrow phones (tested at 402px) — trims button padding/gap and the
     title's font-size just enough to stop the row wrapping. */
  .ai-guide-title {
    font-size: 1.05rem;
  }

  .ai-guide-header-actions {
    gap: 0.375rem;
  }

  .ai-guide-header-actions .sp-btn-sm {
    padding: 0.375rem 0.625rem;
  }
}

.sp-overlay-z {
  z-index: 9999;
}

/* ===== Login showcase panel + feature cards + headline: relocated to
   elearn-web/standard-components/css/auth-shell.css (appended below via
   sync.sh) — do not re-add here. ===== */
.sp-photo-rect {
  width: 70%;
  aspect-ratio: 1 / 1;
  border-radius: 0.75rem;
  object-fit: cover;
  border: 3px solid rgba(255, 255, 255, 0.7);
  box-shadow: 0 4px 16px rgba(67, 56, 202, 0.12);
}

/* ===== Light header/footer (login page) ===== */
.sp-light-chrome {
  background: #ffffff;
}

.sp-light-nav {
  font-size: var(--font-size-sm);
  color: var(--sp-text-3);
  text-decoration: none;
}

.sp-light-nav:hover {
  color: var(--sp-brand);
}

.sp-light-nav-dark {
  font-size: var(--font-size-sm);
  color: var(--sp-text-1);
  text-decoration: none;
  white-space: nowrap;
}

.sp-light-nav-dark:hover {
  color: var(--sp-brand);
}

.sp-light-nav-blue {
  font-size: var(--font-size-base);
  color: #2563eb;
  text-decoration: none;
  white-space: nowrap;
  font-weight: var(--font-weight-medium);
}

.sp-light-nav-blue:hover {
  color: #1d4ed8;
}

.sp-light-nav-blue.is-active {
  color: #1d4ed8;
  font-weight: var(--font-weight-semibold);
  background: var(--sp-nav-bg);
}

.sp-light-nav-red {
  font-size: var(--font-size-base);
  color: var(--sp-logout-text, #ef4444);
  text-decoration: none;
  white-space: nowrap;
  font-weight: var(--font-weight-medium);
}

.sp-light-nav-red:hover {
  color: #991b1b;
}

.sp-light-nav-red.is-active {
  color: #991b1b;
  font-weight: var(--font-weight-semibold);
  background: var(--sp-nav-bg);
}

.sp-light-header-bar {
  min-height: 4.2rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  flex-wrap: wrap;
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

.sp-light-header-nav {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}

@media (max-width: 640px) {
  .sp-light-header-bar {
    justify-content: center;
  }

  .sp-light-header-nav {
    justify-content: center;
    gap: 0.5rem;
  }
}

.sp-pill {
  border-radius: 999px;
  font-size: var(--font-size-sm);
  padding: 0.4rem 1rem;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
}

.sp-pill-outline {
  color: var(--sp-nav-strong);
  border: 1px solid #C7D2FE;
}

.sp-pill-outline:hover {
  background: var(--sp-nav-bg-hov);
}

.sp-pill-solid {
  color: #fff;
  background: var(--sp-nav-strong);
}

.sp-pill-solid:hover {
  background: #1d4ed8;
}


/* ===== Login hero (gradient headline + floating pills) ===== */
.sp-hero-badge {
  display: inline-block;
  font-size: var(--font-size-xs);
  color: var(--sp-item-strong);
  background: var(--sp-item-bg);
  border-radius: 999px;
  padding: 0.25rem 0.75rem;
}

.sp-hero-title {
  font-size: var(--font-size-3xl);
  font-weight: var(--font-weight-bold);
  color: var(--sp-text-1);
  line-height: 1.25;
  letter-spacing: -0.01em;
}

.sp-hero-grad {
  background: var(--ease-brand-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.sp-login-card {
  background: #fff;
  border-radius: 14px;
  box-shadow: 0 8px 24px rgba(67, 56, 202, 0.10);
}

.sp-stat-num {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-bold);
  line-height: 1.1;
}

.sp-stat-label {
  font-size: var(--font-size-xs);
  color: var(--sp-text-3);
}

.sp-blob {
  position: absolute;
  pointer-events: none;
}

.sp-float-pill {
  position: absolute;
  background: #fff;
  border-radius: 999px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  padding: 0.35rem 0.75rem;
  font-size: var(--font-size-xs);
  display: flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}

/* === standard-components: assessment-card.css === */
/**
 * standard-components — assessment summary card
 * ============================================================================
 * SOURCE_VERSION: 2026-07-17
 *
 * Additive to each app's own static/css/brand-tokens.css. Deliberately
 * self-contained (own --sp-acard-* tokens, not borrowed from
 * question-card.css) so this file can be adopted independently — same
 * semantic palette (emerald/blue/red for full/pass/fail) but no hidden
 * cross-file dependency.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that — change the source here and re-sync instead.
 * ============================================================================
 */

:root {
  --sp-acard-full-text: #3B6D11;
  --sp-acard-pass-text: #185FA5;
  --sp-acard-fail-text: #854F0B;
  --sp-acard-badge-pending-bg: #FFFBEB;
  --sp-acard-badge-pending-text: #B45309;
  --sp-acard-badge-pending-border: #FDE68A;
  --sp-acard-badge-reviewed-bg: #ECFDF5;
  --sp-acard-badge-reviewed-text: #047857;
  --sp-acard-badge-reviewed-border: #6EE7B7;
}

.sp-acard {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  background: #fff;
  border: 1px solid var(--sp-border);
  border-radius: 0.5rem;
  padding: 0.75rem;
}

.sp-acard-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
}

.sp-acard-title {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--sp-text-1);
}

.sp-acard-grade {
  font-size: var(--font-size-xs);
  color: var(--sp-text-3);
}

.sp-acard-meta {
  font-size: var(--font-size-xs);
  color: var(--sp-text-2);
}

.sp-acard-bottom {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.75rem;
  margin-top: 0.25rem;
}

.sp-acard-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}

.sp-acard-score {
  font-size: var(--font-size-sm);
  font-weight: 700;
}

.sp-acard-score.is-full {
  color: var(--sp-acard-full-text);
}

.sp-acard-score.is-pass {
  color: var(--sp-acard-pass-text);
}

.sp-acard-score.is-fail {
  color: var(--sp-acard-fail-text);
}

.sp-acard-badge {
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  font-weight: 700;
  padding: 0.25rem 0.75rem;
  border-radius: 9999px;
  border: 1px solid transparent;
}

.sp-acard-badge.is-pending {
  background: var(--sp-acard-badge-pending-bg);
  color: var(--sp-acard-badge-pending-text);
  border-color: var(--sp-acard-badge-pending-border);
}

.sp-acard-badge.is-reviewed {
  background: var(--sp-acard-badge-reviewed-bg);
  color: var(--sp-acard-badge-reviewed-text);
  border-color: var(--sp-acard-badge-reviewed-border);
}

.sp-acard-badge.is-warning {
  background: var(--sp-acard-badge-pending-bg);
  color: var(--sp-acard-badge-pending-text);
  border-color: var(--sp-acard-badge-pending-border);
}

.sp-acard-badge.is-success {
  background: var(--sp-acard-badge-reviewed-bg);
  color: var(--sp-acard-badge-reviewed-text);
  border-color: var(--sp-acard-badge-reviewed-border);
}

/* === standard-components: auth-shell.css === */
/**
 * standard-components — auth-shell (login-page split layout + brand marks)
 * ============================================================================
 * SOURCE_VERSION: 2026-07-18
 *
 * Canonical home for .sp-login-*, .sp-brand-*, .sp-showcase*, .sp-feature-*
 * — previously hand-baked nearly-identically into teacher-desk's and
 * study-prep's own brand-tokens.css independently (confirmed byte-identical
 * at the time, but each app's login.html had already drifted to 6 different
 * independently-hand-picked feature-card accent colors). This is not a
 * byte-verbatim copy of either app's old rules — it bakes in a UX/
 * accessibility review's fixes (see standard-components/README.md):
 *   1. Caption/tagline/signature text uses --sp-text-3 (~4.6:1 contrast on
 *      white), not --sp-text-4 (~2.8:1 — fails WCAG AA).
 *   2. Feature-card accents reduced to 2 token-driven groups (.sp-feature-
 *      accent-a / -b) instead of 6 independently-hand-picked hex colors.
 *   3. .sp-promo-line replaces the old two-colorful-boxes "news/offers"
 *      pattern with one low-emphasis line.
 *   4. .sp-feature-grid adds a 1-column fallback under 400px width (the old
 *      grid was hardcoded 2-column Tailwind utility classes with no mobile
 *      override).
 *   5. .sp-feature-card/.sp-feature-num/.sp-feature-title get min-width: 0 +
 *      overflow-wrap so long Bengali labels wrap inside the card instead of
 *      overflowing past its border on narrow phones — grid/flex items
 *      default to min-width: auto, which lets unbreakable text push past
 *      the container's own width unless explicitly overridden.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that append — change the source here and re-sync instead.
 * ============================================================================
 */

/* ===== Brand marks ===== */
.sp-brand-name {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-bold);
  letter-spacing: -0.01em;
  line-height: 1.1;
  background: var(--ease-brand-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.sp-brand-tagline {
  font-size: var(--font-size-sm);
  color: var(--sp-text-3);
  line-height: 1.25;
  margin-top: 2px;
}

.sp-brand-tile {
  background: var(--ease-brand-gradient);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* ===== Login split layout ===== */
.sp-login-split {
  display: flex;
  width: 100%;
  height: 100%;
}

.sp-login-left {
  width: 50%;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  overflow-y: auto;
  padding-top: 2.5rem;
}

.sp-login-right {
  width: 50%;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  overflow-y: auto;
  padding-top: 1.5rem;
  padding-bottom: 1.5rem;
}

.sp-login-headline {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-bold);
  color: var(--sp-text-1);
  line-height: 1.35;
}

/* ===== Showcase panel (right side) ===== */
.sp-showcase {
  background: linear-gradient(135deg, #EEF0FA 0%, #E5E9FB 100%);
  position: relative;
  overflow: hidden;
}

.sp-showcase-blob {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}

.sp-showcase-title {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-semibold);
  color: var(--sp-brand);
}

/* ===== Feature cards — 2 accent groups instead of 6 independent hex colors ===== */
.sp-feature-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.75rem;
  margin-bottom: 1.5rem;
}

@media (max-width: 400px) {
  .sp-feature-grid {
    grid-template-columns: 1fr;
  }
}

.sp-feature-card {
  background: #fff;
  border-radius: 0.75rem;
  padding: 1rem;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
  min-width: 0;
}

.sp-feature-num {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-bold);
  line-height: 1.1;
  overflow-wrap: break-word;
}

.sp-feature-title {
  display: flex;
  align-items: center;
  gap: 5px;
  font-weight: 700;
  font-size: var(--font-size-sm);
  min-width: 0;
  overflow-wrap: break-word;
}

.sp-feature-label {
  font-size: var(--font-size-xs);
  color: var(--sp-text-3);
  margin-top: 2px;
}

.sp-feature-accent-a {
  border-left: 3px solid var(--sp-brand);
}

.sp-feature-accent-a .sp-feature-num,
.sp-feature-accent-a .sp-feature-title {
  color: var(--sp-brand);
}

.sp-feature-accent-b {
  border-left: 3px solid #0d9488;
}

.sp-feature-accent-b .sp-feature-num,
.sp-feature-accent-b .sp-feature-title {
  color: #0d9488;
}

/* ===== Promo line — replaces the old two-colorful-box "news/offers" pattern ===== */
.sp-promo-line {
  background: var(--sp-brand-bg);
  border-radius: 0.5rem;
  padding: 0.75rem 0.875rem;
  font-size: var(--font-size-sm);
  color: var(--sp-text-2);
  line-height: 1.5;
}

.sp-promo-line strong {
  color: var(--sp-brand);
}

/* ===== Responsive — kept alongside the base rules above on purpose: these
   MUST stay co-located with their base .sp-login-*/
.sp-feature-* rules in the same appended block,
or CSS source order breaks the override (a base rule appended later in the file would otherwise beat an earlier media- query rule at equal specificity, regardless of which file either came from). General shell classes (.sp-viewport, .sp-content-shell,
  .sp-light-nav-blue, .sp-pill, .sp-margin-x) are NOT part of this component — their responsive rules stay in each app's own
 brand-tokens.css,
untouched.=====*/ @media (max-width: 1024px) {
  .sp-login-left {
    width: 55%;
  }

  .sp-login-right {
    width: 45%;
  }

  .sp-feature-card {
    padding: 0.625rem;
  }

  .sp-feature-num {
    font-size: var(--font-size-lg);
  }

  .sp-showcase-title {
    font-size: var(--font-size-base);
  }
}

@media (max-width: 768px) {
  .sp-login-split {
    flex-direction: column;
    overflow-y: auto;
  }

  .sp-login-left {
    width: 100%;
    padding-top: 1.5rem;
    padding-bottom: 1.5rem;
    overflow-y: visible;
  }

  .sp-login-right {
    width: 100%;
    padding: 1.5rem 0;
    overflow-y: visible;
  }
}

@media (max-width: 480px) {
  .sp-login-left {
    padding-top: 1rem;
  }

  .sp-login-headline {
    font-size: var(--font-size-xl);
  }

  .sp-feature-card {
    padding: 0.5rem;
  }

  .sp-feature-num {
    font-size: var(--font-size-base);
  }
}

@media (max-width: 360px) {
  .sp-feature-num {
    font-size: var(--font-size-sm);
  }

  .sp-feature-title {
    font-size: var(--font-size-xs);
  }
}

/* === standard-components: components.css === */
/**
 * standard-components — button additions
 * ============================================================================
 * SOURCE_VERSION: 2026-07-27
 *
 * Additive to each app's own static/css/brand-tokens.css, which already
 * defines .sp-btn / .sp-btn-primary / .sp-btn-secondary and the --sp-* color
 * tokens these rules build on. This file only adds what brand-tokens.css is
 * missing (danger/success/warning/ghost/outline/pill variants, icon-only +
 * loading-spinner modifiers, a small size modifier) — it does not redefine
 * .sp-btn/-primary/-secondary.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that append — change the source here and re-sync instead.
 *
 * 2026-07-26: added a `pointer: coarse` touch-target pass. None of these
 * variants had a min-height/min-width before — height came purely from
 * padding + text line-height, landing well under the ~44px guideline (sm/
 * pill ≈24-26px, icon-only ≈20-24px square). Scoped to `pointer: coarse` so
 * mouse/desktop users keep the original compact sizing; only touch devices
 * get the larger targets. `.sp-btn`/`.sp-btn-primary`/`.sp-btn-secondary`
 * live in each app's own brand-tokens.css (not here — see note above), so
 * each app must add the matching rule to its own base button block; see
 * README.md's adoption steps.
 *
 * 2026-07-27: fixed contrast on danger/success/warning — the original
 * white-on-#ef4444/#16a34a/#f59e0b fills measured ~3.8:1 / ~3.4:1 / ~2.1:1,
 * all below WCAG AA's 4.5:1 for normal text (warning was the worst
 * offender). Darkened each fill to its own former hover shade (danger,
 * success) or one step further (warning, since even its hover shade
 * #d97706 only reaches ~2.9:1) so white text now measures ~4.8:1 / ~5.0:1 /
 * ~5.0:1 respectively — all pass AA. Hover shades moved one step darker
 * again so hover still reads as a distinct, darker state. Added
 * `.sp-btn:active` press feedback while here (was entirely absent before —
 * the only `active:scale` in either app was a one-off hand-rolled instance
 * outside this shared system).
 * ============================================================================
 */

/* Press feedback — relies on each app's base .sp-btn already declaring
   `transform` in its own `transition` property (see README.md's adoption
   note) so this animates instead of snapping instantly. */
.sp-btn:active {
  transform: scale(0.97);
}

.sp-btn-danger {
  padding: 0.625rem 1.5rem;
  background: #dc2626;
  color: #fff;
}

.sp-btn-danger:hover {
  background: #b91c1c;
}

.sp-btn-success {
  padding: 0.625rem 1.5rem;
  background: #15803d;
  color: #fff;
}

.sp-btn-success:hover {
  background: #166534;
}

.sp-btn-warning {
  padding: 0.625rem 1.5rem;
  background: #b45309;
  color: #fff;
}

.sp-btn-warning:hover {
  background: #92400e;
}

.sp-btn-ghost {
  padding: 0.625rem 1.5rem;
  background: transparent;
  color: var(--sp-text-2);
}

.sp-btn-ghost:hover {
  background: var(--sp-border-soft);
}

.sp-btn-outline-danger {
  padding: 0.625rem 1.5rem;
  background: transparent;
  color: #dc2626;
  border: 1px solid #fecaca;
}

.sp-btn-outline-danger:hover {
  background: #fef2f2;
  color: #b91c1c;
}

.sp-btn-outline-primary {
  padding: 0.625rem 1.5rem;
  background: #fff;
  color: var(--sp-brand);
  border: 1px solid var(--sp-brand-border);
}

.sp-btn-outline-primary:hover {
  background: var(--sp-brand-bg);
}

.sp-btn-sm {
  padding: 0.375rem 1rem;
  font-size: var(--font-size-xs);
}

.sp-btn:disabled,
.sp-btn[disabled] {
  opacity: 0.6;
  cursor: not-allowed;
}

/* ===== Pill / toggle filter chip — e.g. সব / শেয়ার্ড two-state filters ===== */
.sp-btn-pill {
  padding: 0.375rem 1rem;
  font-size: var(--font-size-xs);
  border-radius: 999px;
  background: transparent;
  color: var(--sp-text-3);
  border: 1px solid var(--sp-border);
}

.sp-btn-pill:hover {
  background: var(--sp-border-soft);
}

.sp-btn-pill.is-active {
  background: var(--sp-brand);
  border-color: var(--sp-brand);
  color: #fff;
}

.sp-btn-pill.is-active:hover {
  background: var(--sp-brand);
}

/* ===== Icon-only — square footprint, no visible label (aria-label carries
   the accessible name, see _button.html) ===== */
.sp-btn-icon {
  padding: 0.375rem;
  min-width: 0;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ===== Loading spinner — swapped in for the icon slot when loading=True ===== */
.sp-btn-spinner {
  width: 1em;
  height: 1em;
  animation: sp-btn-spin 0.75s linear infinite;
  vertical-align: -0.15em;
}

.sp-btn-spinner-track {
  opacity: 0.25;
}

.sp-btn-spinner-arc {
  opacity: 0.9;
}

@keyframes sp-btn-spin {
  to {
    transform: rotate(360deg);
  }
}

/* ===== Touch targets: bump to ~44px on touch devices only (mouse/desktop
   sizing above is untouched) — see 2026-07-17 header note ===== */
@media (pointer: coarse) {

  .sp-btn-danger,
  .sp-btn-success,
  .sp-btn-warning,
  .sp-btn-ghost,
  .sp-btn-outline-danger,
  .sp-btn-outline-primary {
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  .sp-btn-sm {
    min-height: 2.75rem;
    min-width: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  .sp-btn-pill {
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  .sp-btn-icon {
    min-height: 2.75rem;
    min-width: 2.75rem;
  }
}

/* === standard-components: dashboard-shell.css === */
/**
 * standard-components — dashboard shell (sidebar + bottom nav + content blocks)
 * ============================================================================
 * SOURCE_VERSION: 2026-07-21
 *
 * Relocates .sp-dash-sidebar/.sp-dash-main/.sp-item* (already token-driven,
 * confirmed byte-identical between teacher-desk and study-prep — pure
 * relocation, not a redesign) and adds what was missing per a UX review:
 *
 *   1. The sidebar fully disappears below 768px with NO replacement in both
 *      apps today (confirmed: teacher-desk has dead hamburger-toggle code
 *      with mismatched element IDs; study-prep has no mobile-nav attempt at
 *      all). .sp-bottom-nav* fixes this with a fixed bottom tab bar.
 *   2. .sp-stat-card* — a token-driven stat-card recipe, replacing
 *      teacher-desk's 4 independently-hand-picked hex colors per card.
 *   3. .sp-activity-card* — a token-driven activity-list card recipe,
 *      replacing study-prep's hardcoded Tailwind text-gray-* usage.
 *   4. Contrast fix: meta/timestamp text uses --sp-text-3 (~4.6:1), not
 *      --sp-text-4 (~2.8:1, fails WCAG AA) — same fix already applied in
 *      auth-shell.css, extended here to dashboard-scoped text.
 *
 * 2026-07-19: added .sp-dash-sidebar.is-open + .sp-sidebar-close/
 * .sp-sidebar-backdrop — the ☰ hamburger in app-header.css's mobile action
 * row dispatches a `toggle-sidebar` window event; each app's own
 * _sidebar.html toggles `sidebarOpen` and applies `.is-open` to
 * #left-sidebar. Previously neither app had any CSS for this at all, so
 * the hamburger dispatched the event but nothing visible happened.
 *
 * 2026-07-21: the mobile `.sp-dash-sidebar { display: none }` rule was
 * silently losing to Tailwind's plain `.flex{display:flex}` utility (also
 * present on the sidebar's own class list, unconditionally) — both are
 * single-class selectors of equal specificity, and each app's compiled
 * app.css loads AFTER this file, so app.css's `.flex` won the cascade tie
 * and the sidebar stayed visible on mobile regardless of this media query.
 * Fixed by adding the `#left-sidebar` id (already present in both apps'
 * _sidebar.html markup) alongside the class selector — an id beats a class
 * regardless of source order, so this can no longer lose to any
 * unconditional utility class.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that append — change the source here and re-sync instead.
 * ============================================================================
 */

/* ===== Sidebar shell (relocated, unchanged behavior) ===== */
.sp-dash-sidebar {
  width: 20%;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.sp-dash-main {
  width: 80%;
  overflow-y: auto;
  overflow-x: hidden;
}

/* ===== Sidebar nav items (relocated, unchanged behavior) ===== */
.sp-item {
  font-size: var(--font-size-sm);
  color: var(--sp-item-text);
}

.sp-item:hover {
  background: var(--sp-item-bg-hov);
  color: var(--sp-item-strong);
}

.sp-item.is-active {
  background: var(--sp-item-bg);
  color: var(--sp-item-strong);
  font-weight: var(--font-weight-medium);
}

.sp-item-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

/* ===== Bottom tab bar (NEW — mobile navigation fallback) ===== */
.sp-bottom-nav {
  display: none;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  background: #fff;
  border-top: 1px solid var(--sp-border);
  padding-bottom: env(safe-area-inset-bottom, 0);
}

.sp-bottom-nav-list {
  display: flex;
  align-items: stretch;
  justify-content: space-around;
}

.sp-bottom-nav-item {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 0.5rem 0.25rem;
  color: var(--sp-item-text);
  font-size: var(--font-size-xs);
  text-decoration: none;
  cursor: pointer;
  border: none;
  background: transparent;
}

.sp-bottom-nav-item .sp-item-icon {
  width: 20px;
  height: 20px;
}

.sp-bottom-nav-item.is-active {
  color: var(--sp-item-strong);
  font-weight: var(--font-weight-medium);
}

/* ===== Responsive: sidebar hidden below 768px, reachable instead as a
   ☰-triggered overlay drawer (see app-header.css's .sp-app-header-hamburger
   and each app's own base.html header). .sp-bottom-nav stays available for
   any app that hasn't adopted the app-header.css mobile pattern yet. ===== */
@media (max-width: 768px) {

  .sp-dash-sidebar,
  #left-sidebar {
    display: none;
  }

  .sp-dash-main {
    width: 100%;
    padding-bottom: 4rem;
  }

  .sp-bottom-nav {
    display: block;
  }

  .sp-dash-sidebar.is-open,
  #left-sidebar.is-open {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    left: 0;
    width: 85%;
    max-width: 20rem;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
    z-index: 50;
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15);
  }
}

/* ===== Sidebar mobile-drawer overlay controls — close button + backdrop.
   Hidden entirely on desktop; only meaningful once .sp-dash-sidebar.is-open
   (above) makes the drawer visible. ===== */
.sp-sidebar-close,
.sp-sidebar-backdrop {
  display: none;
}

@media (max-width: 767px) {
  .sp-sidebar-close {
    display: block;
    position: absolute;
    top: 0.5rem;
    right: 0.75rem;
    width: 2rem;
    height: 2rem;
    line-height: 2rem;
    text-align: center;
    border: none;
    background: transparent;
    color: var(--sp-item-strong);
    font-size: 1.1rem;
    cursor: pointer;
    z-index: 51;
  }

  .sp-sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 40;
  }
}

/* ===== Stat cards (NEW — token-driven, one recipe instead of N hex colors) ===== */
.sp-stat-card {
  background: #fff;
  border: 1px solid var(--sp-border);
  border-radius: 0.75rem;
  padding: 1rem;
}

.sp-stat-card .sp-stat-bar {
  height: 3px;
  border-radius: 999px;
  margin-bottom: 0.75rem;
  background: var(--sp-brand);
}

.sp-stat-num {
  font-size: var(--font-size-2xl);
  font-weight: var(--font-weight-bold);
  color: var(--sp-text-1);
  line-height: 1.1;
}

.sp-stat-label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  color: var(--sp-text-3);
}

.sp-stat-card.sp-stat-accent-b .sp-stat-bar {
  background: #0d9488;
}

/* ===== Activity cards (NEW — token-driven, replaces hardcoded text-gray-*) ===== */
.sp-activity-card {
  background: #fff;
  border: 1px solid var(--sp-border);
  border-radius: 0.75rem;
  overflow: hidden;
}

.sp-activity-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--sp-border-soft);
}

.sp-activity-row:last-child {
  border-bottom: none;
}

.sp-activity-title {
  font-size: var(--font-size-sm);
  color: var(--sp-text-2);
  font-weight: var(--font-weight-medium);
}

.sp-activity-meta {
  font-size: var(--font-size-xs);
  color: var(--sp-text-3);
}

/* === standard-components: creative-question-card.css === */
/**
 * standard-components — creative question (সৃজনশীল) card
 * ============================================================================
 * SOURCE_VERSION: 2026-07-19
 *
 * Additive to each app's own static/css/brand-tokens.css. Self-contained
 * --sp-crcard-* tokens (amber passage box, one consistent orange shade for
 * every part row regardless of source data shape — fixes the orange-300-vs-
 * orange-200 inconsistency found across teacher-desk's independent CR
 * implementations).
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that — change the source here and re-sync instead.
 * ============================================================================
 */

:root {
  --sp-crcard-passage-bg: #FFFBEB;
  /* amber-50 */
  --sp-crcard-passage-border: #FBBF24;
  /* amber-400 */
  --sp-crcard-passage-label: #B45309;
  /* amber-700 */

  --sp-crcard-part-bg: #FFF7ED;
  /* orange-50 */
  --sp-crcard-part-border: #FFEDD5;
  /* orange-100 */
  --sp-crcard-part-badge-bg: #FDBA74;
  /* orange-300 — one shade, always */
  --sp-crcard-part-badge-text: #7C2D12;
  /* orange-900 */

  --sp-crcard-answer-bg: #FFF7ED;
  /* orange-50, matches part rows */
  --sp-crcard-answer-border: #FFEDD5;
  /* orange-100 */
}

.sp-crcard {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  background: #fff;
  border: 1px solid var(--sp-border);
  border-radius: 0.75rem;
  padding: 1rem;
}

.sp-crcard-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.sp-crcard-number {
  flex-shrink: 0;
  width: auto;
  min-width: 1.75rem;
  height: 1.75rem;
  padding: 0 0.65rem;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-xs);
  font-weight: 700;
  background: var(--sp-brand);
  color: #fff;
  white-space: nowrap;
}

.sp-crcard-passage {
  background: var(--sp-crcard-passage-bg);
  border-radius: 0.5rem;
  padding: 0.75rem 1rem;
}

/* Note Book question card (_note_book_question_card.html only) — the
   passage box is already amber; tint the whole .sp-crcard the same shade
   so the full card reads as one seamless yellow block, matching the
   answer block's full-gradient look below it. Scoped to .nb-question-card
   so the shared _creative_question_card.html macro's white background
   stays unchanged everywhere else it's used (assessment pages, etc). */
.nb-question-card .sp-crcard {
  background: var(--sp-crcard-passage-bg);
}

.nb-question-card .sp-crcard-passage {
  background: transparent;
  padding: 0;
}

.sp-crcard-passage-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
}

.sp-crcard-passage-text {
  color: var(--sp-text-1);
  font-size: var(--font-size-sm);
  line-height: 1.6;
  text-align: justify;
}

.sp-crcard-source-badge {
  flex-shrink: 0;
  font-size: 11px;
  padding: 0.125rem 0.5rem;
  border-radius: 0.25rem;
  border: 1px solid var(--sp-nav-bg);
  background: var(--sp-nav-bg-hov);
  color: var(--sp-nav-strong);
  font-weight: var(--font-weight-medium);
}

.sp-crcard-parts {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.sp-crcard-part {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  background: var(--sp-crcard-part-bg);
  border: 1px solid var(--sp-crcard-part-border);
  border-radius: 0.75rem;
  padding: 0.5rem 0.75rem;
  font-size: var(--font-size-sm);
  color: var(--sp-text-2);
}

.sp-crcard-part-letter {
  flex-shrink: 0;
  font-weight: 700;
  color: var(--sp-crcard-part-badge-text);
  background: var(--sp-crcard-part-badge-bg);
  border-radius: 9999px;
  width: 1.75rem;
  height: 1.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-xs);
}

.sp-crcard-part-text {
  flex: 1;
  line-height: 1.5;
}

.sp-crcard-part-marks {
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  color: var(--sp-text-4);
}

.sp-crcard-answer {
  background: var(--sp-crcard-answer-bg);
  border: 1px solid var(--sp-crcard-answer-border);
  border-radius: 0.75rem;
  padding: 1rem;
}

.sp-crcard-answer-label {
  font-size: var(--font-size-xs);
  font-weight: 700;
  color: var(--sp-crcard-passage-label);
  margin-bottom: 0.5rem;
}

.sp-crcard-answer-image {
  max-width: 100%;
  max-height: 16rem;
  border-radius: 0.5rem;
  border: 1px solid var(--sp-border);
  object-fit: contain;
}

.sp-crcard-answer-empty {
  font-size: var(--font-size-sm);
  color: var(--sp-text-4);
  font-style: italic;
}

.sp-crcard-score {
  font-size: var(--font-size-sm);
  font-weight: 700;
  color: var(--sp-brand);
  margin-top: 0.5rem;
}

.sp-crcard-comment {
  font-size: var(--font-size-sm);
  color: var(--sp-crcard-passage-label);
  margin-top: 0.25rem;
}

.sp-crcard-file-input {
  font-size: var(--font-size-sm);
  color: var(--sp-text-2);
}

/* === standard-components: tab-bar.css === */
/**
 * standard-components — tab bar
 * ============================================================================
 * SOURCE_VERSION: 2026-07-19
 *
 * Additive to each app's own static/css/brand-tokens.css. Moved verbatim
 * from study-prep's own hand-written .sp-tab rules (values unchanged) so
 * every app now has one source of truth instead of a per-app duplicate.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that — change the source here and re-sync instead.
 *
 * 2026-07-19: active tabs previously only changed color+underline while
 * every tab (active and inactive) was already font-weight:semibold — the
 * "bolder when active" signal was fully diluted. Inactive tabs now sit at
 * `medium` weight and active tabs move up to `bold`, so weight is a real,
 * distinguishing signal again (per the accessibility guideline that active
 * state shouldn't rely on color alone). Also added a visible
 * `:focus-visible` ring, since the new keyboard arrow-key navigation
 * (tab_bar.js) needs a clear focus indicator to be usable. Active color
 * switched from a hardcoded #2563eb to `var(--sp-brand)` so it follows each
 * app's actual brand token instead of a one-off blue.
 *
 * 2026-07-19: added the `is-stacked` variant (icon above label, equal-width
 * flex-1 tabs) — this is teacher-desk's own hand-rolled "detail panel tabs"
 * pattern (👤 ব্যক্তিগত / 📅 উপস্থিতি / 💳 পেমেন্ট / 📊 একাডেমিক), previously
 * copy-pasted verbatim into 3 separate templates with no shared CSS at all.
 * Folded in here so all 3 call sites can move onto the shared macro instead
 * of maintaining 3 independent copies of the same markup.
 * ============================================================================
 */

.sp-tab-bar {
  display: flex;
  gap: 0.25rem;
}

.sp-tab-bar.is-segmented {
  background: #f3f4f6;
  border-radius: 0.75rem;
  padding: 0.25rem;
}

.sp-tab {
  padding: 0.625rem 1.25rem;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  border-bottom: 2px solid transparent;
  border-radius: 0.5rem;
  color: var(--sp-item-text);
  transition: color 0.15s, border-color 0.15s;
  cursor: pointer;
  background: none;
  border-top: none;
  border-left: none;
  border-right: none;
}

.sp-tab:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

.sp-tab.is-active {
  color: var(--sp-brand);
  font-weight: var(--font-weight-bold);
  border-bottom-color: var(--sp-brand);
}

/* ===== Stacked variant: icon above label, equal-width tabs — used for
   e.g. a detail-panel's own internal tab strip (teacher-desk's student
   detail: ব্যক্তিগত/উপস্থিতি/পেমেন্ট/একাডেমিক) ===== */
.sp-tab-bar.is-stacked {
  border-bottom: 1px solid var(--sp-border);
  background: #fff;
}

.sp-tab-bar.is-stacked .sp-tab {
  flex: 1 1 0;
  flex-direction: column;
  align-items: center;
  gap: 0.125rem;
  padding: 0.75rem 0.5rem;
  font-size: var(--font-size-xs);
  border-radius: 0;
}

.sp-tab-bar.is-stacked .sp-tab-icon {
  font-size: 1rem;
  line-height: 1;
}

/* === standard-components: question-card.css === */
/**
 * standard-components — MCQ question card
 * ============================================================================
 * SOURCE_VERSION: 2026-07-21
 *
 * Additive to each app's own static/css/brand-tokens.css. Defines new
 * semantic tokens for "correct answer" / "wrong answer" / "not yet revealed"
 * — brand-tokens.css has no such tokens today (--sp-item-* is scoped to nav
 * active-state, --sp-logout-* to the logout button; neither fits a question
 * card semantically), so this file adds dedicated ones rather than repurpose
 * either.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that — change the source here and re-sync instead.
 * ============================================================================
 */

:root {
  --sp-correct-bg: #ECFDF5;
  /* emerald-50 */
  --sp-correct-border: #6EE7B7;
  /* emerald-300 */
  --sp-correct-text: #047857;
  /* emerald-700 */

  --sp-wrong-bg: #FEF2F2;
  /* red-50 */
  --sp-wrong-border: #FCA5A5;
  /* red-300 */
  --sp-wrong-text: #B91C1C;
  /* red-700 — matches --sp-logout-text */

  --sp-neutral-bg: #F9FAFB;
  /* gray-50 */
  --sp-neutral-border: #F3F4F6;
  /* matches --sp-border-soft */
  --sp-neutral-text: #6B7280;
  /* matches --sp-text-3 */
}

.sp-qcard {
  background: #fff;
  border: 2px solid var(--sp-neutral-border);
  border-radius: 0.75rem;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
  padding: 1rem;
}

.sp-qcard-header {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.sp-qcard-number {
  flex-shrink: 0;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-xs);
  font-weight: 700;
  background: var(--sp-brand-bg, #EEF0FA);
  color: var(--sp-brand, #3D52A0);
}

.sp-qcard-number.is-correct {
  background: var(--sp-correct-bg);
  color: var(--sp-correct-text);
}

.sp-qcard-number.is-wrong {
  background: var(--sp-wrong-bg);
  color: var(--sp-wrong-text);
}

.sp-qcard-question-wrap {
  flex: 1;
  min-width: 0;
}

.sp-qcard-question {
  color: var(--sp-text-1);
  font-weight: var(--font-weight-medium);
  font-size: var(--font-size-sm);
  line-height: 1.6;
}

.sp-qcard-options {
  margin-left: 2.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.sp-qcard-option {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  font-size: var(--font-size-sm);
  border-radius: 0.5rem;
  padding: 0.5rem 0.75rem;
  background: var(--sp-neutral-bg);
  border: 1px solid transparent;
  color: var(--sp-text-2);
}

.sp-qcard-option.is-correct {
  background: var(--sp-correct-bg);
  border-color: var(--sp-correct-border);
  color: var(--sp-correct-text);
}

.sp-qcard-option.is-wrong {
  background: var(--sp-wrong-bg);
  border-color: var(--sp-wrong-border);
  color: var(--sp-wrong-text);
}

.sp-qcard-option-letter {
  font-weight: 700;
  flex-shrink: 0;
}

.sp-qcard-badge {
  margin-left: auto;
  flex-shrink: 0;
  font-size: var(--font-size-xs);
  font-weight: 700;
}

.sp-qcard-badge.is-correct {
  color: var(--sp-correct-text);
}

.sp-qcard-badge.is-wrong {
  color: var(--sp-wrong-text);
}

/* Neutral "picked this, not yet graded" state — used while an assessment is
   still pending teacher review, so the student sees what they answered
   without any correct/wrong coloring. */
.sp-qcard-option.is-selected {
  background: var(--sp-brand-bg);
  border-color: var(--sp-brand-border);
  color: var(--sp-brand);
}

.sp-qcard-badge.is-selected {
  color: var(--sp-brand);
}

.sp-qcard-source-badge {
  display: inline-block;
  font-size: 11px;
  padding: 0.125rem 0.5rem;
  margin-left: 0.25rem;
  border-radius: 0.25rem;
  border: 1px solid var(--sp-nav-bg);
  background: var(--sp-nav-bg-hov);
  color: var(--sp-nav-strong);
  font-weight: var(--font-weight-medium);
}

/* Live-input (taking) variant — same visual language, selectable option row */
.sp-qcard-option-input {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.5rem;
  border: 1px solid var(--sp-border);
  cursor: pointer;
  font-size: var(--font-size-sm);
  color: var(--sp-text-2);
}

.sp-qcard-option-input:hover {
  border-color: var(--sp-brand-border);
  background: var(--sp-brand-bg);
}

.sp-qcard-option-input:has(input:checked) {
  border-color: var(--sp-brand);
  background: var(--sp-brand-bg);
}

/* === standard-components: toast.css === */
/**
 * standard-components — error toast
 * ============================================================================
 * SOURCE_VERSION: 2026-07-17
 *
 * Additive to each app's own static/css/brand-tokens.css, which already
 * defines --sp-logout-text/-border/-bg (reused here as the error/danger
 * palette instead of inventing new colors) and, where the dashboard-shell
 * component has been adopted, --sp-bottom-nav-h (used so the toast clears
 * the mobile bottom tab bar).
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that append — change the source here and re-sync instead.
 * ============================================================================
 */

.sp-toast-container {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(1rem + var(--sp-bottom-nav-h, 0px));
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  max-width: calc(100vw - 2rem);
  pointer-events: none;
}

.sp-toast {
  pointer-events: auto;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1.25rem;
  border-radius: 0.75rem;
  background: var(--sp-logout-bg, #FEF2F2);
  color: var(--sp-logout-text, #B91C1C);
  border: 1px solid var(--sp-logout-border, #FECACA);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  font-size: var(--font-size-sm);
  line-height: var(--leading-snug, 1.375);
  max-width: 28rem;
  text-align: center;
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.sp-toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (max-width: 768px) {
  .sp-toast-container {
    bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
  }
}

/* === standard-components: modal.css === */
/**
 * standard-components — modal chrome (plain CSS, Tailwind-purge-safe)
 * ============================================================================
 * SOURCE_VERSION: 2026-07-17
 *
 * A handful of classes covering the modal backdrop/card/icon/button
 * treatments used by _confirm_modal.html (and study-prep's pre-existing
 * #custom-alert-modal). Added after discovering — by checking each class
 * against both apps' actually-compiled static/css/app.css, not just
 * assuming it was there — that several Tailwind utility classes these
 * modals relied on were never purged in:
 *   study-prep app.css missing: bg-black/60, backdrop-blur-[2px],
 *     shadow-2xl, scale-95, scale-100, text-amber-500, shadow-blue-200,
 *     active:scale-[0.98], w-14, h-14
 *   teacher-desk app.css missing: backdrop-blur-[2px], scale-95,
 *     scale-100, active:scale-[0.98]
 * (the two apps compile independently from their own templates, so the
 * missing set differs per app — see README.md's "why copy instead of
 * Tailwind" rationale for why vendored components use plain CSS instead
 * of relying on any one app's Tailwind purge output.) This means the
 * pre-existing #custom-alert-modal was already rendering without its
 * backdrop dim/blur and press/scale animation in production before this
 * fix, not just the newer confirm modal.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that append — change the source here and re-sync instead.
 * ============================================================================
 */

.sp-modal-backdrop {
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(2px);
}

.sp-modal-panel {
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
  transform: scale(0.95);
}

.sp-modal-panel.is-open {
  transform: scale(1);
}

.sp-modal-icon {
  width: 3.5rem;
  height: 3.5rem;
}

.sp-modal-icon-warning {
  color: #F59E0B;
  /* amber-500 */
}

.sp-modal-btn-shadow {
  box-shadow: 0 10px 15px -3px rgba(37, 99, 235, 0.2), 0 4px 6px -4px rgba(37, 99, 235, 0.2);
}

.sp-modal-press {
  transition: transform 0.1s ease;
}

.sp-modal-press:active {
  transform: scale(0.98);
}

/* === standard-components: step-indicator.css === */
/**
 * standard-components — step indicator
 * ============================================================================
 * SOURCE_VERSION: 2026-07-19
 *
 * Additive to each app's own static/css/brand-tokens.css. Added after a UI
 * review found teacher-desk's 5-stage assessment-creation wizard
 * (stage_1_filter.html -> ... -> stage_5_done.html) had no progress
 * indicator at all — a user had no way to know how many steps remained.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that append — change the source here and re-sync instead.
 * ============================================================================
 */

.sp-steps {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  margin-bottom: 1rem;
}

.sp-steps-track {
  display: flex;
  align-items: center;
  gap: 0.375rem;
}

.sp-steps-dot {
  flex: 1 1 0;
  height: 0.375rem;
  border-radius: 999px;
  background: var(--sp-border);
}

.sp-steps-dot.is-done {
  background: var(--sp-brand);
}

.sp-steps-dot.is-current {
  background: var(--sp-brand);
  box-shadow: 0 0 0 3px var(--sp-brand-bg);
}

.sp-steps-text {
  font-size: var(--font-size-xs);
  color: var(--sp-text-3);
  font-weight: var(--font-weight-medium);
}

/* === standard-components: inbox.css === */
/**
 * standard-components — unified Inbox list
 * ============================================================================
 * SOURCE_VERSION: 2026-07-19
 *
 * Additive to each app's own static/css/brand-tokens.css, which already
 * defines the --sp-* color tokens these rules build on. Backs
 * templates/_inbox_list.html — the shared "one line per item, বিস্তারিত to
 * expand" feed used by both teacher-desk's and study-prep's /inbox page.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that append — change the source here and re-sync instead.
 * ============================================================================
 */

.sp-inbox-list {
  display: flex;
  flex-direction: column;
}

.sp-inbox-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.875rem 0.25rem;
  border-bottom: 1px solid var(--sp-border);
}

.sp-inbox-row:last-child {
  border-bottom: none;
}

.sp-inbox-row-main {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  min-width: 0;
}

.sp-inbox-dot {
  flex-shrink: 0;
  width: 0.5rem;
  height: 0.5rem;
  margin-top: 0.4rem;
  border-radius: 50%;
  background: var(--sp-brand);
}

.sp-inbox-row:not(.is-unread) .sp-inbox-dot {
  visibility: hidden;
}

.sp-inbox-text {
  font-size: var(--font-size-sm);
  color: var(--sp-text-2);
  line-height: 1.5;
}

.sp-inbox-row.is-unread .sp-inbox-text {
  font-weight: var(--font-weight-semibold);
  color: var(--sp-text-1);
}

.sp-inbox-detail-btn {
  flex-shrink: 0;
  background: transparent;
  border: none;
  color: var(--sp-brand);
  font-size: var(--font-size-sm);
  font-weight: 600;
  cursor: pointer;
  padding: 0.25rem 0.5rem;
}

.sp-inbox-detail-btn:hover {
  text-decoration: underline;
}

.sp-inbox-detail {
  padding: 0 0.25rem;
}

.sp-inbox-detail:not(:empty) {
  padding-bottom: 0.875rem;
  margin-top: -0.5rem;
  font-size: var(--font-size-sm);
  color: var(--sp-text-3);
  line-height: 1.55;
  white-space: pre-wrap;
}

.sp-inbox-empty {
  padding: 2.5rem 1rem;
  text-align: center;
  font-size: var(--font-size-sm);
  color: var(--sp-text-4);
  border: 1px dashed var(--sp-border);
  border-radius: 0.75rem;
}

/* === standard-components: app-header.css === */
/**
 * standard-components — app header (responsive shell)
 * ============================================================================
 * SOURCE_VERSION: 2026-07-19
 *
 * Additive to each app's own static/css/brand-tokens.css. Moved verbatim
 * from study-prep's own hand-written .sp-app-header-* rules (values
 * unchanged) so every app now has one source of truth instead of a
 * per-app duplicate — teacher-desk adopted this for the first time
 * alongside study-prep's pre-existing usage.
 *
 * Adoption: appended into each app's own brand-tokens.css by
 * elearn-web/standard-components/sync.sh. Do not hand-edit the copy in an
 * app beyond that — change the source here and re-sync instead.
 *
 * Pairs with templates/_nav_icons.html (mobile primary-tab icons) and
 * each app's own base.html header markup (not vendored — brand name,
 * nav links, and primary-tab items differ per app; only the responsive
 * shell/behavior is shared). See the file's own header comment in each
 * app's base.html for the exact markup contract.
 * ============================================================================
 */

/* ===== Logged-in app header: desktop single row, mobile two-row
   (brand row + ☰/tabs/⋮ action row) — see base.html's header markup ===== */
.sp-app-header-row {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
}

.sp-app-header-topline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.sp-app-header-brand-row {
  display: flex;
  align-items: center;
  width: 20%;
  flex-shrink: 0;
}

.sp-app-header-brand-close {
  display: none;
}

@media (max-width: 768px) {
  .sp-app-header-topline {
    position: relative;
  }

  .sp-app-header-brand-row {
    width: auto;
  }

  .sp-app-header-brand-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 0;
    right: 0;
    width: 1.75rem;
    height: 1.75rem;
    border: none;
    background: transparent;
    color: var(--sp-text-3);
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    border-radius: 999px;
  }

  .sp-app-header-brand-close:hover {
    background: var(--sp-nav-bg);
  }

  /* Dismissed for this session — only takes effect at this same breakpoint,
     so resizing back above 768px always shows the brand row again. */
  .sp-brand-dismissed .sp-app-header-brand-row {
    display: none;
  }
}

.sp-app-header-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  flex-wrap: wrap;
  flex-grow: 1;
}

.sp-app-header-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
  width: 20%;
  flex-shrink: 0;
}

.sp-app-header-actionrow {
  display: none;
}

@media (max-width: 768px) {

  .sp-app-header-nav,
  .sp-app-header-right {
    display: none;
  }

  .sp-app-header-actionrow {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .sp-app-header-tabs {
    justify-content: center;
  }
}

.sp-app-header-hamburger {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
  flex-shrink: 0;
}

.sp-app-header-hamburger span {
  display: block;
  width: 1.25rem;
  height: 2px;
  background: var(--sp-text-2);
  border-radius: 1px;
}

.sp-app-header-tabs {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  overflow-x: auto;
  flex: 1 1 auto;
  min-width: 0;
}

.sp-app-header-tab {
  font-size: var(--font-size-sm);
  color: #2563eb;
  text-decoration: none;
  white-space: nowrap;
  padding: 0.35rem 0.6rem;
  border-radius: 0.375rem;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
}

.sp-app-header-tab.is-active {
  background: var(--sp-nav-bg);
  font-weight: var(--font-weight-semibold);
}

.sp-app-header-tab .sp-item-icon {
  width: 1.1rem;
  height: 1.1rem;
}

.sp-app-header-more {
  position: relative;
  flex-shrink: 0;
}

.sp-app-header-more-btn {
  width: 2rem;
  height: 2rem;
  border: none;
  background: transparent;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  color: var(--sp-text-2);
}

.sp-app-header-more-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.25rem);
  background: #fff;
  border: 1px solid var(--sp-border);
  border-radius: 0.5rem;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  min-width: 10rem;
  padding: 0.25rem;
  z-index: 50;
  display: flex;
  flex-direction: column;
}

.sp-app-header-more-panel .sp-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-radius: 0.375rem;
}

@media (min-width: 769px) {
  .sp-app-header-more {
    display: none;
  }
}
