/* vitalite-ui.css — shared design-system layer. LOAD THIS LAST in <head>. No
   markup; delete the <link> and the site reverts. Separate file because pages
   carry 50-134KB inline <style> (guide.html is 1.1MB): this wins by source
   order, so it is additive and reversible. Fixed (all measured, full data in
   DESIGN-NOTES.md): depth (home had 2 bg tones, 1 border, one 5%-black shadow
   invisible on navy); button heights 21-44px with three under the 40px tap
   floor; form fields 32/37/40px with no single rule; no ::selection /
   focus-visible / scrollbar anywhere. Left alone on purpose: font stacks (Optima
   vs Georgia is a small-caps choice); --accent and the blue/gold split (shell
   blue, IB gold); .lin-search-input (40px floor would grow the fixed 48px bar);
   input[type=range] (infirmary #vxPain is 8px because it is a slider). */

/* ═══ 1 · Tokens ═══════════════════════════════════════════════════════════
   Added alongside the existing palette rather than replacing it: every token
   below reads FROM the live theme, so this layer follows whatever skin is active
   instead of hard-coding a second palette that could drift. */
:root {
  /* vertical rhythm. 56/24 is the file's stated target; the page-level values are
   untouched, these are here for anything new to use. */
  --space-section: 56px;
  --space-block: 24px;
  --space-tight: 12px;

  /* ELEVATION — rationale: elevation scale (DESIGN-NOTES 5e) */
  --elev-0: var(--bg);            /* canvas */
  --elev-1: var(--surface);       /* cards, panels */
  --elev-2: var(--surface3);      /* raised: hover, popovers, sticky chrome */
  --elev-sunken: var(--surface2); /* wells: inputs, code, stripes */

  /* The theme's --shadow is a 4px hard offset (print-poster look) and reads as a
   smudge on navy, so elevation gets soft blue-tinted shadows. */
  --elev-shadow-1: 0 1px 2px rgba(4, 10, 20, .38);
  --elev-shadow-2: 0 4px 16px rgba(4, 10, 20, .42), 0 1px 3px rgba(4, 10, 20, .34);
  --elev-shadow-3: 0 14px 38px rgba(4, 10, 20, .50), 0 3px 10px rgba(4, 10, 20, .36);

/* ── Apply the scale ──────────────────────────────────────────────────── Card
   classes actually in use (measured). One step of lift + a matching edge so a
   card reads as an object on the canvas, not a rectangle painted on it. */
.cr,
.stat-card,
.dash-card,
.feature-card,
.fork-card,
.qo-card,
.res-card,
.acc-item,
.plan-card,
.quiz-card,
.vt-plan,
.kn-card,
.hmx-card,
.ch-plan,
.g10-card {
  background: var(--elev-1);
  border: 1px solid var(--elev-border, var(--border));
  box-shadow: var(--elev-shadow-1);
}
/* Hover lifts one step. Costs a transition the button group already declares. */
.cr:hover,
.stat-card:hover,
.dash-card:hover,
.feature-card:hover,
.fork-card:hover,
.res-card:hover,
.hmx-card:hover,
.kn-card:hover {
  background: var(--elev-2);
  border-color: var(--elev-border-strong, var(--border2));
  box-shadow: var(--elev-shadow-2);
}
/* Floating chrome takes the top step. */
.lin-topbar,
.sb-mark,
.lin-search,
.log-menu,
.qna-widget .qna-panel {
  background: var(--elev-2);
  box-shadow: var(--elev-shadow-2);
}
/* Wells go DOWN, not up. */
input:not([type]),
select,
textarea {
  background: var(--elev-sunken);
}

  /* one motion vocabulary, matching the rail's existing easing */
  --motion-fast: .14s;
  --motion-base: .18s;
  --motion-slow: .5s;
  --motion-ease: cubic-bezier(.16, 1, .3, 1);

  /* the single tap-target floor the project already uses */
  --tap: 40px;

  /* one focus ring, used by every :focus-visible below */
  --focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent);
}

/* Steps widened for dark, where elevation has to do the work. In light mode the
   palette already separates and a heavier shadow would only muddy it. */
body.dark {
  --elev-1: #13253f;
  --elev-2: #1b3050;
  --elev-sunken: #101d33;
  --elev-border: rgba(141, 182, 226, .16);
  --elev-border-strong: rgba(141, 182, 226, .26);
}

/* ═══ 2 · Selection, focus, scrollbar ════════════════════════════════════ Zero
   visual cost until used, so this is the cheapest quality win on the whole site. */
::selection {
  background: var(--accent);
  color: var(--surface);
}
/* Only for keyboard users: a mouse click on a <div> must not draw a ring. */
:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}
/* Firefox */
* { scrollbar-color: var(--border2) transparent; scrollbar-width: thin; }
/* WebKit. Thin, rounded, and it follows the theme's border colour. */
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--border2);
  border-radius: 99px;
  border: 3px solid transparent;
  background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background-color: var(--text3); background-clip: content-box; }

/* ═══ 3 · Tap targets ═════════════════════════════════════════════════════ Two
   techniques. Compact controls keep their painted size and get a 40px ::before
   overlay (AGENTS.md §3) because growing them overflows the container. Isolated
   controls (.btn-icon 34px, .qo-btn 36px) are simply raised to 40px.
   .sidebar-link is left alone on purpose: 32px desktop / 40px phone (the mobile
   pass covers it) and WCAG 2.5.8 exempts non-intersecting centres - 40px rail
   rows would trade working density for a rule that does not apply to a mouse. */
.tut-replay,
.vx-kb-ask,
.for-cat {
  position: relative;
}
.tut-replay::before,
.vx-kb-ask::before,
.for-cat::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: var(--tap);
  transform: translateY(-50%);
  /* the overlay must not eat clicks meant for the control underneath */
  pointer-events: none;
}
body .btn-icon { min-width: 40px; min-height: 40px; }
body .qo-btn { min-height: 40px; }

/* ═══ 4 · Form fields ═════════════════════════════════════════════════════ One
   height, one radius, one focus ring. The :not() list is load-bearing.
   input[type=range] is 8px tall on infirmary.html because that is what a range
   looks like; checkbox / radio / color / file must never be given a 40px box
   either. */
input[type="text"],
input[type="search"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="tel"],
input[type="url"],
input:not([type]),
select {
  min-height: var(--tap);
  border-radius: var(--radius-sm);
  /* vitalite-skin already sets a border; this only makes sure one exists */
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: .95rem;
  padding-inline: .7rem;
  transition: border-color var(--motion-fast) var(--motion-ease),
              box-shadow var(--motion-fast) var(--motion-ease);
}
/* the top-bar field is deliberately shorter — see the header note */
.lin-search-input {
  min-height: 0;
  border: 0;
  background: transparent;
  padding-inline: 0;
  border-radius: 0;
  font-size: .88rem;
}
input[type="text"]:focus,
input[type="search"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="number"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
input:not([type]):focus,
select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
textarea {
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: .95rem;
  padding: .7rem;
  transition: border-color var(--motion-fast) var(--motion-ease),
              box-shadow var(--motion-fast) var(--motion-ease);
}
textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}
::placeholder { color: var(--text3); opacity: 1; }

/* Button rhythm — rationale: button rhythm (DESIGN-NOTES) */
button,
.qo-btn,
.btn,
.btn-login,
.btn-icon,
.lin-study-btn,
.adm-btn,
.aq-btn,
.tab-btn,
.reveal-btn,
.chat-sug-btn,
.habit-chip {
  transition:
    transform var(--motion-base) var(--motion-ease),
    box-shadow var(--motion-base) var(--motion-ease),
    background-color var(--motion-fast) var(--motion-ease),
    border-color var(--motion-fast) var(--motion-ease),
    color var(--motion-fast) var(--motion-ease);
}
/* A 2px lift on the things that read as cards or primary actions. Deliberately
   NOT applied to .btn-icon or anything inside the top bar: the bar is 48px and a
   lift there reads as the whole bar jumping. */
.qo-btn:hover,
.btn:hover,
.adm-btn:hover,
.chat-sug-btn:hover {
  transform: translateY(-2px);
}
.qo-btn:active,
.btn:hover:active,
.adm-btn:active {
  transform: translateY(0);
}

/* ═══ 6 · Card surface ════════════════════════════════════════════════════ One
   radius and one border for anything that is visually a card. The list is the
   measured set of card classes, not a guess. */
.vt-plan,
.stat-card,
.feature-card,
.fork-card,
.ch-card,
.qo-card,
.res-card,
.dash-card,
.for-topic,
.acc-item,
.chat-window,
.plan-card,
.quiz-card,
.vx-card,
.kn-card {
  border-radius: var(--radius);
  border: 1px solid var(--border);
}

/* Pricing tiers — rationale: pricing row bug (DESIGN-NOTES) */
@media (min-width: 900px) {
  .vt-plans {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    /* stretch, not start: three tiers of different copy length must not end at three
   different heights. Measured before: 504 / 476 / 496px. */
    align-items: stretch;
  }
  .vt-plan { display: flex; flex-direction: column; }

  /* The Free card carries a 21px "Your current plan" badge and the other two carry
   an EMPTY .vt-current that measures 0px. That 21px pushed every line on card 1
   down by 21px, so the three price lines — the single most important thing on a
   pricing page — did not sit on one baseline. Reserving the row on all three is
   what aligns them; nothing moves on the card that actually has the badge. */
  .vt-current { min-height: 21px; }

  /* push each CTA to the bottom of its (now equal-height) card */
  .vt-plan ul { flex: 1 1 auto; }
  .vt-plan .qo-btn { margin-top: auto; }
}

/* Long lists — rationale: taste-skill §4.9 audit */
@media (min-width: 720px) {
  #vtFreeList {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .6rem 2.2rem;
    max-width: none;
    list-style: none;
    padding-left: 0;
  }
  #vtFreeList li {
    position: relative;
    padding-left: 1.15rem;
    line-height: 1.6;
  }
  #vtFreeList li::before {
    content: "";
    position: absolute;
    left: 0;
    top: .58em;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--accent);
  }
}

/* Footer — rationale: footer (DESIGN-NOTES) */
body > footer:not([class]),
footer:not([class]) {
  border-top: 1px solid var(--border);
  padding-top: var(--space-block);
  color: var(--text3);
  font-size: .85rem;
  line-height: 1.6;
}
footer a { color: var(--text2); text-decoration: none; }
footer a:hover { color: var(--accent); }

/* Reduced motion — rationale: why these 13 classes (DESIGN-NOTES) */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after,
  .for-cat,
  .for-tag-chip,
  .fc-chip,
  .guide-chip,
  .guide-tab,
  .btn-login,
  .tut-replay,
  .lin-study-btn,
  .fc-nav button,
  .capture-btn,
  .acc-btn,
  .plan-btn,
  .pl-btn,
  .qo-btn,
  .log-menu {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
  .qo-btn:hover,
  .btn:hover,
  .adm-btn:hover,
  .chat-sug-btn:hover {
    transform: none !important;
  }
}

/* TYPE LADDER — rationale: type ladder, measured on home/exam/cn-cert (DESIGN-NOTES 5e) */
body h2 { font-weight: 700; letter-spacing: -.012em; }
body h3 { font-weight: 700; }
body h4 { font-weight: 700; }
/* big headings track tighter; 12-14px ones need looser or they clump */
body h1 { letter-spacing: -.018em; }
body h4, body h5, body h6 { letter-spacing: .004em; }

