/* KVCoders 2.0 -- Component Primitives
 *
 * One recipe per component (Master Plan Part 7), each harvested from the
 * existing implementation it's built from rather than invented fresh.
 * Requires design-tokens.css to be loaded first.
 *
 * Naming note: the student shell (examiner/includes/student_chrome_header.php)
 * already has a working, live three-tier button system named
 * .cta-primary/-secondary/-subtle, referenced by three already-migrated
 * pages (mcq/universal_test.php, output_test/universal_output.php, SQL
 * Arena's submit button). Renaming those classes now would break three
 * live pages for zero benefit, so the shell keeps its own .cta-* names
 * (now pointed at the canonical --brand-* tokens via the shell's own
 * :root, not this file) and this file's .btn-* is the same recipe under
 * the forward-looking canonical name, for pages built from this point
 * on -- starting with examiner/exams/index.php. See
 * KVCODERS_UI_UX_2_0_PHASE1_FOUNDATION.md for the full rationale. The
 * two are not meant to silently drift apart: any future change to one
 * recipe should be mirrored in the other until the three legacy .cta-*
 * consumers are migrated and .cta-* can be retired.
 */

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  border-radius: 999px;
  font-weight: 700;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  text-decoration: none;
  cursor: pointer;
  border: none;
  transition: transform var(--duration-micro) var(--ease),
              box-shadow var(--duration-micro) var(--ease),
              background var(--duration-micro) var(--ease),
              opacity var(--duration-micro) var(--ease);
}
.btn:disabled, .btn[aria-disabled="true"] {
  cursor: not-allowed;
  opacity: 0.5;
  transform: none !important;
}

.btn-primary {
  background: var(--brand-600);
  color: #fff;
  padding: 14px 28px;
  box-shadow: 0 8px 20px rgba(91,79,232,0.22);
}
.btn-primary:hover:not(:disabled) {
  background: var(--brand-700);
  transform: translateY(-2px);
  box-shadow: 0 12px 26px rgba(91,79,232,0.3);
}

.btn-secondary {
  background: transparent;
  color: var(--brand-600);
  border: 2px solid var(--brand-600);
  padding: 12px 26px;
}
.btn-secondary:hover:not(:disabled) {
  background: var(--brand-100);
  transform: translateY(-2px);
}
/* --brand-600 on a transparent (page-canvas) background measures 3.58:1
   against --surface-canvas's dark value (#020617) -- below AA. Never
   caught before because no page using .btn-secondary had also opted
   into painting the dark canvas background (see exams/index.php's fix,
   same audit pass); a lighter indigo restores real contrast (13.5:1)
   without changing the light-mode recipe at all. */
:root[data-theme="dark"] .btn-secondary {
  color: #C7D2FE;
  border-color: #C7D2FE;
}

.btn-subtle {
  background: var(--brand-100);
  color: var(--brand-600);
  padding: 8px 16px;
  font-size: var(--text-xs);
}
.btn-subtle:hover:not(:disabled) {
  background: #E0DCFC;
}

/* ---------- Cards ---------- */
/* Base recipe generalized from examiner/question-browser/includes/
   qb_shared_head.php's .qb-block-card -- one hue, decreasing opacity for
   authority signal, not a rainbow of borders per card type. */

.card {
  background: var(--surface-card);
  border: 1px solid var(--border-canvas);
  border-left: 3px solid var(--border-canvas);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  padding: var(--space-6);
  transition: transform var(--duration-standard) var(--ease),
              box-shadow var(--duration-standard) var(--ease);
}
.card:hover {
  box-shadow: var(--shadow-md);
}

/* Subject-identity accent -- the left border only. Reserved for content
   genuinely scoped to one subject; never decorative. */
.card-cs { border-left-color: var(--cs-500); }
.card-ip { border-left-color: var(--ip-500); }

/* Content-authority accent -- mirrors qb-block-card's board/sample/
   preboard/practice modifiers exactly (one hue, varying opacity). */
.card-board    { border-left-color: rgba(91,79,232,0.85); }
.card-sample   { border-left-color: rgba(91,79,232,0.5); }
.card-preboard { border-left-color: rgba(91,79,232,0.28); }
.card-practice { border-left-color: var(--border-canvas); }

/* ---------- Badges ---------- */
/* Generalized from qb-badge-easy/-medium/-hard. */

.badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 20px;
  font-size: var(--text-xs);
  font-weight: 700;
  font-family: var(--font-body);
}
.badge-easy   { background: rgba(16,185,129,0.12); color: var(--success-500); }
.badge-medium { background: rgba(249,115,22,0.12); color: var(--warning-500); }
.badge-hard   { background: rgba(239,68,68,0.12);  color: var(--danger-500); }
/* --cs-700/--ip-700, not --cs-500/--ip-500 -- axe-core measured the
   500-shades at 3.48:1/2.31:1 against these exact tint backgrounds,
   below WCAG AA's 4.5:1 for text. Found and fixed via the Phase 2
   public homepage rebuild; see design-tokens.css's own comment on
   --cs-700/--ip-700 for the full contrast math. */
.badge-cs { background: var(--cs-100); color: var(--cs-700); }
.badge-ip { background: var(--ip-100); color: var(--ip-700); }

/* ---------- Forms ---------- */
/* Harvested from examiner/auth/includes/auth_head.php's .field recipe --
   the most internally consistent, accessible subsystem in the codebase.
   auth_head.php itself is NOT modified in this phase (see migration
   doc); this is a parallel recipe for new pages to consume directly. */

.field { margin-bottom: 18px; }
.field label {
  display: block;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--text-sm);
  color: var(--text-canvas-muted);
  margin-bottom: 7px;
}
.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 13px 14px;
  border-radius: var(--radius-sm);
  border: 1.5px solid var(--border-canvas);
  background: var(--surface-alt);
  color: var(--text-canvas);
  font-family: var(--font-body);
  font-size: var(--text-base);
  box-shadow: inset 0 1px 2px rgba(15,23,42,0.05);
  transition: background-color var(--duration-micro) var(--ease),
              border-color var(--duration-micro) var(--ease),
              box-shadow var(--duration-micro) var(--ease);
}
.field input::placeholder,
.field textarea::placeholder { color: var(--text-canvas-muted); opacity: 0.7; }
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  background: var(--surface-card);
  border-color: var(--brand-600);
  box-shadow: 0 0 0 4px var(--brand-100);
}

/* ---------- Tables ---------- */
/* One recipe replaces ~4 different table treatments found in the audit.
   Zebra-free by design -- a bottom border per row is enough separation. */

.table { width: 100%; border-collapse: collapse; font-family: var(--font-body); }
.table th {
  text-align: left;
  text-transform: uppercase;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.4px;
  /* --slate-500 measured 4.13:1 on --surface-alt at this weight/size --
     below WCAG AA's 4.5:1 (found via axe on the Student Experience 3.0
     pilot once .table started appearing inside a .panel for the first
     time). --text-canvas-muted clears AA with real margin on every
     canvas surface (--surface-card/-alt/-canvas alike, 6.6-7.6:1), so
     it replaces --slate-500 here rather than patching just this one
     background. */
  color: var(--text-canvas-muted);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-canvas);
}
.table td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-canvas);
  font-size: var(--text-sm);
  color: var(--text-canvas);
}
.table td.numeric, .table th.numeric { font-variant-numeric: tabular-nums; text-align: right; }

/* ---------- Alerts ---------- */
/* Generalized from auth_head.php's .auth-alert-*. */

.alert {
  padding: 13px 16px;
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-weight: 500;
  line-height: 1.5;
  font-family: var(--font-body);
}
.alert-error   { background: rgba(239,68,68,0.1);  color: #B91C1C; border-left: 3px solid var(--danger-500); }
.alert-success { background: rgba(16,185,129,0.1); color: #065F46; border-left: 3px solid var(--success-500); }
.alert-warning { background: rgba(249,115,22,0.1); color: #9A3412; border-left: 3px solid var(--warning-500); }

/* ---------- Progress indicators ---------- */
/* One shape language: pill for inline stats, ring for a single hero
   score -- currently student.php's .stat-pill and SQL Arena's
   .score-circle are unrelated; this is the convergence point. */

.stat-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: 999px;
  background: var(--surface-alt);
  border: 1px solid var(--border-canvas);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-canvas);
}
.stat-pill i { color: var(--brand-600); }

.score-circle {
  --score-size: 120px;
  --score-ring: 10px;
  position: relative;
  width: var(--score-size);
  height: var(--score-size);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: conic-gradient(var(--brand-600) calc(var(--score-percent, 0) * 1%), var(--border-canvas) 0);
}
.score-circle::before {
  content: '';
  position: absolute;
  inset: var(--score-ring);
  border-radius: 50%;
  background: var(--surface-card);
}
.score-circle span {
  position: relative;
  z-index: 1;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--text-canvas);
}

/* ---------- Accessibility utility ---------- */
/* Standard visually-hidden-but-announced pattern. Genuinely missing --
   several tables/icon-only controls across the app have an empty <th>
   or unlabeled button rather than this, which is exactly what axe's
   empty-table-header/button-name rules catch. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---------- Empty / loading / error states ---------- */
/* Genuinely new -- Part 7 flags this as a real gap, not a consolidation
   target. One pattern for "no data yet," reused everywhere a list/card
   grid can legitimately be empty. */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-12) var(--space-6);
  color: var(--text-canvas-muted);
}
.empty-state i { font-size: 2rem; color: var(--slate-400); }
.empty-state p { margin: 0; font-family: var(--font-body); font-size: var(--text-sm); max-width: 40ch; }

.skeleton {
  background: linear-gradient(90deg, var(--surface-alt) 25%, var(--border-canvas) 37%, var(--surface-alt) 63%);
  background-size: 400% 100%;
  animation: kv-skeleton-pulse 1.4s ease infinite;
  border-radius: var(--radius-sm);
}
@keyframes kv-skeleton-pulse {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
}

/* ---------- Contained dark stage (KVCoders 3.0) ---------- */
/* A rounded, elevated --ink-950 panel meant to sit INSIDE a light
   section -- a "window into the real product" (a live code console, the
   Learn/Practice/Test/Analyse pipeline), not a full section-background
   swap. This is the one sanctioned way dark appears outside chrome:
   contained, elevated, and always because something genuinely technical
   (code, live data) is being shown -- never as a decorative section
   alternation. Text inside uses --slate-400 for muted content (verified
   7.51:1 against --ink-950 in Phase 1) since --text-canvas-muted is
   calibrated for light backgrounds only. */
.stage-dark {
  background: var(--ink-950);
  color: #fff;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  position: relative;
}
.stage-dark .stage-muted { color: var(--slate-400); }
.stage-dark .stage-border { border-color: rgba(255,255,255,0.09); }

/* ---------- Panels (Student Experience 3.0) ---------- */
/* Generalizes the ad hoc section-grouping wrappers found independently
   invented across student pages during the 3.0 audit (dashboard.php's
   .workspace-block/.panel-box/.course-hero-card, each with its own local
   :root tokens) -- one section-level surface, distinct from .card:
   .card is an individual item inside a list/grid; .panel is the section
   that groups those items and gives it a heading. Consolidation only --
   dashboard.php itself is not modified to adopt this in this phase. */

.panel {
  background: var(--surface-alt);
  border: 1px solid var(--border-canvas);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}
.panel + .panel { margin-top: var(--space-6); }
.panel-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.panel-title {
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--text-canvas);
  margin: 0;
}
.panel-subtitle {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-canvas-muted);
  margin: 2px 0 0;
}
@media (max-width: 767px) {
  .panel { padding: var(--space-4); border-radius: var(--radius-md); }
}

/* ---------- Status pills (Student Experience 3.0) ---------- */
/* A second, distinct shape from .badge on purpose: .badge marks a
   property of content (difficulty, subject, resource type -- static,
   descriptive). .status-pill marks the CURRENT STATE of a stateful
   thing a student is tracking (a Live Challenge event, a Coding
   Challenge attempt) -- it always carries a dot, because the blueprint's
   Tests architecture (KVCODERS_STUDENT_EXPERIENCE_3_0.md S18-19) commits
   the two Test products to permanent nav presence with only their STATE
   changing, so state needs a consistent, scannable visual regardless of
   which of the two lifecycles (Scheduled/Live/Ended vs Open/Awaiting/
   Graded) it belongs to. Semantic color only -- never the brand hue
   decoratively, never subject color (--cs-*/--ip-*) borrowed for state. */

.status-pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.2px;
  background: var(--surface-card);
  border: 1px solid var(--border-canvas);
  color: var(--text-canvas-muted);
}
.status-pill::before {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
/* "Live"/"Open" -- something is happening right now and actionable.
   The one state that pulses; every other state is static on purpose, so
   the pulse stays a meaningful signal instead of decorative motion. */
.status-pill--live, .status-pill--open {
  background: var(--brand-100);
  color: var(--brand-700);
  border-color: transparent;
}
.status-pill--live::before, .status-pill--open::before { animation: kv-status-pulse 1.8s var(--ease) infinite; }
.status-pill--scheduled {
  color: var(--slate-500);
}
.status-pill--awaiting {
  background: rgba(249,115,22,0.1);
  color: #9A3412;
  border-color: transparent;
}
.status-pill--completed, .status-pill--graded {
  background: rgba(16,185,129,0.1);
  color: #065F46;
  border-color: transparent;
}
.status-pill--ended {
  color: var(--slate-500);
}
@keyframes kv-status-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(91,79,232,0.4); }
  50% { opacity: 0.7; box-shadow: 0 0 0 4px rgba(91,79,232,0); }
}
@media (prefers-reduced-motion: reduce) {
  .status-pill--live::before, .status-pill--open::before { animation: none; }
}
/* Dark-canvas overrides -- found necessary (not assumed) via axe against
   the Student Experience 3.0 pilot: the light-mode tint+dark-text pairs
   above invert to low-contrast dark-on-dark once --surface-card switches
   to #1E293B, so state color needs its own light-on-dark pair here
   rather than relying on the light-mode value surviving the theme flip.
   Every value below independently re-measured against #1E293B (the real
   --surface-card dark value), not reused from an unrelated dark-panel
   context. This is the first time any component in this file has been
   verified against data-theme="dark" -- .alert-*/.badge-easy-medium-hard
   above predate this check and are NOT yet confirmed theme-safe; flagged
   in KVCODERS_STUDENT_DESIGN_SYSTEM_3_0.md as a follow-up, not silently
   assumed fine. */
:root[data-theme="dark"] .status-pill--live,
:root[data-theme="dark"] .status-pill--open {
  background: rgba(91,79,232,0.22);
  color: #C7D2FE;
}
:root[data-theme="dark"] .status-pill--awaiting {
  background: rgba(249,115,22,0.16);
  color: #FDBA74;
}
:root[data-theme="dark"] .status-pill--completed,
:root[data-theme="dark"] .status-pill--graded {
  background: rgba(16,185,129,0.16);
  color: #6EE7B7;
}
:root[data-theme="dark"] .status-pill--scheduled,
:root[data-theme="dark"] .status-pill--ended {
  color: var(--slate-400);
}

/* ---------- Question preview card (Student Experience 3.0) ---------- */
/* Sub-parts for .card/.card-board/.card-sample/.card-preboard (already
   the correct authority-accent recipe, see those rules above) when the
   card's content is a PYQ Search result specifically -- a metadata row
   and a clamped excerpt, the two pieces that recipe didn't yet need
   until PYQ Search results became a named target (blueprint S15/S28). */

.qp-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-canvas-muted);
}
.qp-meta span { display: inline-flex; align-items: center; gap: 4px; }
.qp-excerpt {
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--text-canvas);
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ---------- Mode tabs (Student Experience 3.0) ---------- */
/* For a practice tool with genuinely different modes shown up front
   (MCQ/Output's Chapter-wise vs Full Syllabus, blueprint S14) -- a
   two/three-way switch, never a dropdown, so both choices are visible
   without a click. Not a generic tab component for arbitrary content. */

.mode-tabs {
  display: inline-flex;
  padding: 4px;
  background: var(--surface-alt);
  border: 1px solid var(--border-canvas);
  border-radius: 999px;
  gap: 4px;
}
.mode-tab {
  border: none;
  background: transparent;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--text-sm);
  color: var(--text-canvas-muted);
  padding: 9px 20px;
  border-radius: 999px;
  cursor: pointer;
  transition: background var(--duration-micro) var(--ease), color var(--duration-micro) var(--ease);
}
.mode-tab.active {
  background: var(--surface-card);
  color: var(--brand-600);
  box-shadow: var(--shadow-sm);
}

/* ---------- Content-type badge rule (Student Experience 3.0) ---------- */
/* Deliberate restraint, not a gap: a resource's TYPE (Notes/Papers/
   Marking Scheme/Project/Syllabus, blueprint S16) uses .badge in its
   plain neutral form (no new color per type) plus an icon + label --
   color already carries three other meanings in this system (subject
   identity, content authority, semantic state); a fourth axis of color
   for content-type would make all four harder to read at a glance, and
   the blueprint's own anti-pattern list names "a rainbow of borders per
   card type" as exactly what to avoid. One additional neutral variant
   is enough: */
.badge-neutral { background: var(--surface-alt); color: var(--text-canvas-muted); border: 1px solid var(--border-canvas); }

/* ---------- Code / data surface ---------- */
/* A contained, elevated mono-type surface for real code or tabular
   machine output (SQL Practice results, Python stdout, a frozen
   question's code block) -- distinct from .stage-dark, which is a
   product-window/pipeline visualization, not a text-content surface.
   Selectable, wrapping, monospace; never used for prose. */

.code-surface {
  background: var(--ink-950);
  color: #E2E8F0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.6;
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-6);
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ---------- Responsive table wrapper ---------- */
/* .table is unopinionated about width -- correct for short tables, but
   a table with more/wider columns than fit a small viewport needs a
   scroll boundary of its own rather than forcing the page body to
   scroll sideways. Wrap any .table expected to run wide on mobile in
   <div class="table-scroll">; short tables (e.g. a 3-column score list)
   don't need it and shouldn't be wrapped by default. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* ---------- Pipeline diagram (KVCoders 3.0) ---------- */
/* The Learn -> Practice -> Test -> Analyse loop, rendered as connected
   nodes -- built once, used at two information densities: .pipeline
   (compact, icon + label, for a dark stage's header/aside) and
   .pipeline-detailed (icon + title + description, one cell per stage,
   for a dedicated section). Both consume the same node data/order;
   kept as two recipes rather than one collapsing-component because the
   detailed version carries real explanatory copy the compact one has
   no room for -- not a duplicate, a deliberate density variant. */
.pipeline { display: flex; align-items: center; }
.pipeline-node { flex: 1; text-align: center; }
.pipeline-node .node-icon {
  width: 34px; height: 34px; margin: 0 auto var(--space-2);
  border-radius: var(--radius-sm);
  border: 1px solid rgba(255,255,255,0.14);
  background: var(--ink-900);
  display: grid; place-items: center;
  color: var(--slate-400);
}
.pipeline-node.active .node-icon { border-color: var(--brand-600); color: #9B90FF; }
.pipeline-node .node-label { font-family: var(--font-mono); font-size: 9px; color: var(--slate-400); letter-spacing: 0.03em; }
.pipeline-connector { flex: 0 0 auto; width: 26px; height: 1px; background: rgba(255,255,255,0.14); margin-top: -17px; }

.pipeline-detailed {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1px;
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.pipeline-detailed .stage-cell { background: var(--ink-900); padding: var(--space-6) var(--space-4); }
.pipeline-detailed .stage-num { font-family: var(--font-mono); font-size: 10px; color: var(--slate-400); margin-bottom: var(--space-3); }
.pipeline-detailed .stage-icon {
  width: 34px; height: 34px; border-radius: var(--radius-sm);
  background: var(--ink-800); border: 1px solid rgba(255,255,255,0.12);
  display: grid; place-items: center; margin-bottom: var(--space-4); color: #9B90FF;
}
.pipeline-detailed .stage-title { font-family: var(--font-display); font-weight: 700; font-size: var(--text-lg); color: #fff; margin-bottom: var(--space-2); }
.pipeline-detailed .stage-desc { font-size: var(--text-sm); color: var(--slate-400); line-height: 1.55; }
@media (max-width: 767px) {
  .pipeline-detailed { grid-template-columns: 1fr 1fr; }
}
