/* ============================================================================
   CourseForge – reset, typography, form controls and a small utility layer.
   The utilities are deliberately few: anything that repeats as a *thing* gets a
   named component class in components.css instead.
   ========================================================================== */

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

* { margin: 0; }

html, body, #app { height: 100%; }

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-color: var(--border-strong) transparent;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--t-md);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow: hidden;                       /* the shell owns all scrolling */
}

h1, h2, h3, h4 { font-weight: 600; line-height: 1.25; letter-spacing: -0.01em; }
h1 { font-size: var(--t-2xl); }
h2 { font-size: var(--t-xl); }
h3 { font-size: var(--t-lg); }
h4 { font-size: var(--t-md); }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }

img { display: block; max-width: 100%; }

/* inline-block, not block: flex containers blockify their items anyway, while
   an icon dropped into a sentence would otherwise force a line break. */
svg { display: inline-block; vertical-align: -0.15em; flex: none; }

code, pre, .mono { font-family: var(--mono); font-variant-ligatures: none; }

hr { border: 0; border-top: 1px solid var(--border); }

/* -- scrollbars ------------------------------------------------------------ */
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border: 3px solid transparent;
  background-clip: content-box;
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-thumb:hover { background: var(--text-faint); background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* -- focus ----------------------------------------------------------------- */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

::selection { background: var(--accent-line); color: var(--text); }

/* -- form controls ----------------------------------------------------------
   Text fields are selected by a deny-list, never an allow-list.
   `input[type="text"]` does NOT match a plain `<input>`: omitting the attribute
   gives text *behaviour* but leaves nothing to select on, so an allow-list
   silently drops every field that does not spell its type out. The rule below
   therefore styles every input except the ones the browser draws itself, and a
   type added later is covered automatically.

   The same list has to guard :hover and :focus as well, otherwise a checkbox
   would inherit the text-field ring. */
input, select, textarea, button { font: inherit; color: inherit; }

.field,
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]),
select,
textarea {
  width: 100%;
  padding: 7px 10px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text);
  font-size: var(--t-sm);
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease), background var(--speed) var(--ease);
}

textarea { display: block; resize: vertical; line-height: 1.6; }

.field:hover,
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):hover,
select:hover,
textarea:hover { border-color: var(--border-strong); }

.field:focus,
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="color"]):focus,
select:focus,
textarea:focus {
  border-color: var(--accent);
  box-shadow: var(--ring);
  background: var(--surface);
}

input::placeholder, textarea::placeholder { color: var(--text-faint); }

input:disabled, select:disabled, textarea:disabled,
.field:disabled { opacity: 0.55; cursor: not-allowed; }

select {
  appearance: none;
  padding-right: 30px;
  background-image: linear-gradient(45deg, transparent 50%, currentcolor 50%),
                    linear-gradient(135deg, currentcolor 50%, transparent 50%);
  background-position: calc(100% - 15px) calc(50% + 1px), calc(100% - 10px) calc(50% + 1px);
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  cursor: pointer;
}
select option { background: var(--surface); color: var(--text); }

/* Checkboxes and radios are drawn from scratch. `accent-color` only tints the
   *checked* state, which leaves the far more common unchecked box rendered in
   the browser's own grey — visibly foreign next to everything else. */
input[type="checkbox"], input[type="radio"] {
  appearance: none;
  display: inline-grid;
  place-content: center;
  width: 16px;
  height: 16px;
  flex: none;
  margin: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-xs);
  background: var(--surface-2);
  cursor: pointer;
  transition: background var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
input[type="radio"] { border-radius: var(--r-pill); }

input[type="checkbox"]:hover:not(:disabled),
input[type="radio"]:hover:not(:disabled) { border-color: var(--accent); }

input[type="checkbox"]::before,
input[type="radio"]::before {
  content: "";
  width: 10px;
  height: 10px;
  background: var(--accent-ink);
  transform: scale(0);
  transition: transform 120ms var(--ease);
}
input[type="checkbox"]::before {
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
input[type="radio"]::before {
  width: 7px;
  height: 7px;
  border-radius: var(--r-pill);
}

input[type="checkbox"]:checked,
input[type="radio"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}
input[type="checkbox"]:checked::before,
input[type="radio"]:checked::before { transform: scale(1); }

input[type="checkbox"]:disabled, input[type="radio"]:disabled { cursor: not-allowed; }

/* The spinners cost width that a narrow value box cannot spare. */
input[type="number"] { font-variant-numeric: tabular-nums; appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { appearance: none; margin: 0; }

textarea.mono, input.mono { font-size: var(--t-sm); line-height: 1.65; }

label { display: block; }

/* -- layout utilities ------------------------------------------------------ */
.row     { display: flex; align-items: center; gap: var(--s-2); }
.row-top { display: flex; align-items: flex-start; gap: var(--s-2); }
.col     { display: flex; flex-direction: column; gap: var(--s-2); }
.wrap    { flex-wrap: wrap; }
.grow    { flex: 1 1 auto; min-width: 0; }
.none    { flex: none; }
.push    { margin-left: auto; }
.between { justify-content: space-between; }
.center  { justify-content: center; }
.end     { justify-content: flex-end; }
.items-end { align-items: flex-end; }

.gap-1 { gap: var(--s-1); }
.gap-2 { gap: var(--s-2); }
.gap-3 { gap: var(--s-3); }
.gap-4 { gap: var(--s-4); }
.gap-6 { gap: var(--s-6); }

/* minmax(0, 1fr) rather than the implicit `auto`: an auto track sizes to
   max-content, so a single-column grid full of long text would push its own
   container wider instead of wrapping inside it. */
.grid { display: grid; gap: var(--s-4); grid-template-columns: minmax(0, 1fr); }
.grid > * { min-width: 0; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-auto { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }

.mt-1 { margin-top: var(--s-1); }
.mt-2 { margin-top: var(--s-2); }
.mt-3 { margin-top: var(--s-3); }
.mt-4 { margin-top: var(--s-4); }
.mt-6 { margin-top: var(--s-6); }
.mb-2 { margin-bottom: var(--s-2); }
.mb-3 { margin-bottom: var(--s-3); }
.mb-4 { margin-bottom: var(--s-4); }
.mb-6 { margin-bottom: var(--s-6); }

.scroll-y { overflow-y: auto; overscroll-behavior: contain; }
.scroll-x { overflow-x: auto; }
.hidden { display: none !important; }

/* -- text utilities -------------------------------------------------------- */
.t-2xs { font-size: var(--t-2xs); }
.t-xs  { font-size: var(--t-xs); }
.t-sm  { font-size: var(--t-sm); }
.t-lg  { font-size: var(--t-lg); }
.t-xl  { font-size: var(--t-xl); }

.muted { color: var(--text-muted); }
.dim   { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.strong { font-weight: 600; }
.semi   { font-weight: 550; }
.nums   { font-variant-numeric: tabular-nums; }

.c-accent  { color: var(--accent); }
.c-success { color: var(--success); }
.c-warning { color: var(--warning); }
.c-danger  { color: var(--danger); }
.c-magic   { color: var(--magic); }

.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.clamp-2 {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.eyebrow {
  font-size: var(--t-2xs);
  font-weight: 650;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* -- boot splash ----------------------------------------------------------- */
.app-boot { display: grid; place-items: center; height: 100%; }
.boot-splash { display: grid; gap: var(--s-4); justify-items: center; color: var(--text-dim); font-size: var(--t-sm); }
.boot-splash__mark {
  width: 34px; height: 34px;
  border-radius: var(--r-md);
  border: 2px solid var(--border-strong);
  border-top-color: var(--accent);
  animation: cf-spin 0.8s linear infinite;
}

@keyframes cf-spin { to { transform: rotate(360deg); } }
@keyframes cf-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@keyframes cf-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
