/*
 * Shared components. The rules of this stylesheet are the design language (see docs/specs/DESIGN.md, and the controls
 * section of the owner's 2026-10 brief below):
 *   - Surfaces are separated by tone and spacing. Cards and controls are lit glass: a layered fill with an inset top
 *     highlight, an inset bottom shade and a soft outer shadow. No box outlines: a surface is never drawn with a border.
 *   - Halos are blurred glows in the hue that acts or confirms (cyan acts, green confirms, rose and red warn). Never a ring.
 *   - Dividers are hairlines between rows, nothing else.
 *   - Labels are plain text. No coloured left edges.
 *   - Chrome hues have one job each: cyan acts (buttons, links, focus), violet marks where you are and counts, blue informs,
 *     green confirms. Domain hues are data and always sit next to the domain's name.
 *   - Controls (owner, 2026-10): one shape (a rounded rectangle, --r-md; cards and tiles --r-lg), three kinds. .btn-primary is
 *     the lit hull (the one action of a screen), .btn is glass (a secondary action), .btn-quiet is a ghost (a text action).
 *     Sizes: .btn-sm 32px, .btn 40px, .btn-lg 52px. Round things are only portholes and glyphs larger than 14px.
 *     Every colour lives in tokens.css (--glass-*, --halo*, --lit-*, --stardust).
 */

/* ------------------------------------------------------------------ buttons */
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 40px;
  padding: 0 var(--sp-4);
  border: 0;
  border-radius: var(--r-md);
  background: linear-gradient(180deg, var(--glass-top), var(--glass-fill));
  box-shadow: inset 0 1px 0 var(--glass-hi), inset 0 -1px 0 var(--glass-shade), var(--glass-shadow-sm);
  color: var(--text);
  font: 600 .93rem/1 var(--font-ui);
  letter-spacing: .005em;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: transform .16s var(--ease), box-shadow .22s var(--ease), color .15s var(--ease), background-color .15s var(--ease);
}
.btn > svg { flex: none; }
/* Hover: the fill warms toward the acting hue and a tight halo lifts the control (close to its edge, short falloff). */
.btn:hover {
  background: linear-gradient(180deg, var(--glass-top), color-mix(in srgb, var(--glass-fill), var(--cyan) 12%));
  color: var(--text);
  text-decoration: none;
  box-shadow: inset 0 1px 0 var(--glass-hi), inset 0 -1px 0 var(--glass-shade), 0 0 12px -5px var(--halo-strong), var(--glass-shadow-sm);
}
/* Press: a small squeeze and the inner shade of a pressed key. Off under reduced motion (see the end of this file). */
.btn:active:not([disabled]):not([aria-disabled="true"]) { transform: translateY(1px) scale(.98); box-shadow: inset 0 2px 6px var(--glass-shade); }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
.btn:focus-visible { outline: 2px solid var(--bone); outline-offset: 3px; }

/* Lit hull: a cyan fill lit from above (the top is the lit colour, the bottom the accent), a shaded lower edge and a
   blurred halo. A diagonal glint crosses it once on hover. */
.btn-primary {
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent), var(--lit-hi) var(--lit-top)) 0%, var(--accent) 100%);
  color: var(--accent-ink);
  font-weight: 650;
  overflow: hidden;
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--lit-hi) 60%, transparent), inset 0 -2px 0 var(--lit-shade), 0 6px 14px -8px var(--halo-strong);
}
.btn-primary:hover {
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent), var(--lit-hi) var(--lit-top-hover)) 0%, color-mix(in srgb, var(--accent), var(--lit-hi) var(--lit-bottom-hover)) 100%);
  color: var(--accent-ink);
  box-shadow: inset 0 1px 0 var(--lit-hi), inset 0 -2px 0 var(--lit-shade), 0 0 12px -4px var(--halo-strong), 0 10px 18px -10px var(--halo-strong);
}
.btn-primary::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 38%, color-mix(in srgb, var(--lit-hi) 60%, transparent) 50%, transparent 62%);
  transform: translateX(-120%);
}
.btn-primary:hover::after { animation: btn-glint .8s var(--ease) both; }
@keyframes btn-glint { to { transform: translateX(120%); } }
.btn-primary:active:not([disabled]):not([aria-disabled="true"]) { box-shadow: inset 0 2px 7px var(--lit-shade), 0 4px 12px -6px var(--halo-strong); }
.btn-primary:focus-visible { box-shadow: inset 0 1px 0 var(--lit-hi), 0 0 12px -3px var(--halo-strong); }
.btn-primary .porthole { background: color-mix(in srgb, var(--on-hue) 14%, transparent); color: var(--on-hue); box-shadow: inset 0 1px 0 color-mix(in srgb, var(--on-hue) 22%, transparent); }

/* Ghost: text on the ground; a glass wash on hover. Nav, toggles and inline actions. */
.btn-quiet { background: transparent; box-shadow: none; color: var(--text-2); }
.btn-quiet:hover { background: color-mix(in srgb, var(--surface-2) 70%, transparent); color: var(--text); box-shadow: none; }
.btn-danger { background: transparent; box-shadow: none; color: var(--danger); }
.btn-danger:hover { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); box-shadow: none; }
.btn-violet { background: var(--violet); box-shadow: none; color: var(--on-hue); }
.btn-violet:hover { background: color-mix(in srgb, var(--violet) 84%, var(--text)); color: var(--on-hue); box-shadow: none; }

/* Sizes. .btn.btn-lg outranks the landing's own .btn-lg, which still sets an 8px radius and a 54px height (removed with the landing rebuild). */
.btn-sm { min-height: 32px; padding: 0 var(--sp-3); font-size: .84rem; gap: var(--sp-1); }
.btn.btn-lg { min-height: 52px; padding: 0 var(--sp-6); font-size: 1.02rem; border-radius: var(--r-md); }
/* Fingers need a bigger target than a pointer: 44px buttons (40px small) on touch screens. */
@media (pointer: coarse) { .btn { min-height: 44px; } .btn-sm { min-height: 40px; } }
/* An icon-only control is a round porthole. */
.btn-icon { width: 40px; min-width: 40px; padding: 0; border-radius: 50%; }
.btn-icon.btn-sm { width: 32px; min-width: 32px; }

/* Porthole: a small round window that holds an icon (a wrench, a tray, a chevron). Larger than 14px, so it is a glyph. */
.porthole {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 26%, color-mix(in srgb, var(--accent), var(--lit-hi) 40%) 0, var(--accent) 52%, color-mix(in srgb, var(--accent), var(--lit-shade) 45%) 100%);
  color: var(--on-hue);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--lit-hi) 50%, transparent);
}
.porthole > svg { width: 14px; height: 14px; }
/* Orb: a subject's planet, lit from the upper left in its domain hue (data-dom sets --tint). It always sits beside the
   subject's name: the footer links, the labs' group heads, the subject cards. */
.orb { flex: none; display: inline-block; width: 22px; height: 22px; border-radius: 50%; background: radial-gradient(circle at 34% 30%, color-mix(in srgb, var(--tint, var(--violet)), var(--lit-hi) 50%) 0, var(--tint, var(--violet)) 42%, color-mix(in srgb, var(--tint, var(--violet)), var(--bg) 72%) 100%); box-shadow: 0 0 14px -3px color-mix(in srgb, var(--tint, var(--violet)) 70%, transparent), inset -2px -3px 5px var(--glass-shade); }

/* ------------------------------------------------------------------ surfaces */
/* Glass: a translucent night panel, lit from the top, with a faint star-dust grain (dark theme only). Big panels get the
   backdrop blur (.glass--deep). The locked panel and the check result are glass too, listed here by their classes. */
.glass, .card, .lp-lock, .lp-check__out {
  border: 0;
  border-radius: var(--r-lg);
  background-image: var(--stardust), linear-gradient(180deg, var(--glass-top), var(--glass-bottom));
  background-size: 180px 180px, auto;
  background-repeat: repeat, no-repeat;
  box-shadow: inset 0 1px 0 var(--glass-hi), inset 0 -1px 0 var(--glass-shade), var(--glass-shadow);
}
.glass--deep { -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); }
.card { padding: var(--sp-5); }
.card h3 { margin-bottom: var(--sp-2); }
.panel { background: var(--bg-raised); border-radius: var(--r-lg); }
/* Tinted surface: the fill carries what the block is about (data-dom sets --tint to the domain hue; .tint-* to a chrome hue). */
.tint { background: color-mix(in srgb, var(--tint, var(--violet)) 12%, var(--surface)); border-radius: var(--r-lg); }
.tint-violet { --tint: var(--violet); } .tint-cyan { --tint: var(--cyan); } .tint-blue { --tint: var(--blue); } .tint-green { --tint: var(--green); }
/* Upsell: what the monthly plan opens, on a violet-tinted panel with the one action that gets it. */
.upsell { display: grid; gap: var(--sp-3); padding: var(--sp-5); border-radius: var(--r-lg); background: color-mix(in srgb, var(--violet) 14%, var(--surface)); }
.upsell h2, .upsell h3 { margin: 0; }
.upsell p { margin: 0; color: var(--text-2); max-width: 60ch; }
.divide > * + * { border-top: 1px solid var(--line); }

/* Forms: a filled field with an underline. The underline is the affordance; focus makes it bone. */
label { display: block; font-weight: 600; font-size: .9rem; margin-bottom: var(--sp-1); }
.field { margin-bottom: var(--sp-4); }
.input, select.input, textarea.input { width: 100%; min-height: 44px; padding: var(--sp-2) var(--sp-3); background: var(--surface-2); color: var(--text); border: 0; border-bottom: 2px solid var(--line-strong); border-radius: var(--r-sm) var(--r-sm) 0 0; font: inherit; transition: background .15s var(--ease), border-color .15s var(--ease); }
textarea.input { min-height: 96px; resize: vertical; }
.input:hover { background: var(--surface-3); }
.input:focus-visible { outline: none; background: var(--surface-3); border-bottom-color: var(--accent); }
.input::placeholder { color: var(--text-3); }
.hint { font-size: .85rem; color: var(--text-3); margin-top: var(--sp-1); }
.error { color: var(--danger); font-size: .9rem; margin-top: var(--sp-1); }
.notice { padding: var(--sp-3) var(--sp-4); border-radius: var(--r-md); background: color-mix(in srgb, var(--blue) 15%, var(--surface)); color: var(--text); }
.notice.is-ok { background: color-mix(in srgb, var(--ok) 16%, var(--surface)); }
.notice.is-warn { background: color-mix(in srgb, var(--violet) 18%, var(--surface)); }
.notice.is-bad { background: color-mix(in srgb, var(--danger) 17%, var(--surface)); }
input[type="range"] { accent-color: var(--accent); width: 100%; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }

/* Labels that carry data (status, domain): plain text, coloured only when the colour is the data. */
.tag { font: 600 .82rem/1.25 var(--font-ui); color: var(--tag, var(--text-2)); white-space: nowrap; }
.tag[data-dom="mth"] { --tag: var(--dom-mth); } .tag[data-dom="phy"] { --tag: var(--dom-phy); }
.tag[data-dom="aiml"] { --tag: var(--dom-aiml); } .tag[data-dom="ent"] { --tag: var(--dom-ent); }
.tag[data-state="unlocked"], .tag[data-state="ready"] { --tag: var(--text); }
.tag[data-state="nearly_ready"], .tag[data-state="nearly"] { --tag: var(--text-2); }
.tag[data-state="locked"] { --tag: var(--text-3); } .tag[data-state="mastered"] { --tag: var(--text); }
.tag[data-state="due"], .tag[data-state="due_today"], .tag[data-state="due_soon"] { --tag: var(--text); }
.tag[data-state="overdue"], .tag[data-state="critical"] { --tag: var(--danger); }
.tag[data-state="free"] { --tag: var(--green); } .tag[data-state="plan"] { --tag: var(--violet-text); }
.badge { font-size: .72rem; line-height: 1.5; background: var(--violet); color: var(--on-hue); border-radius: 3px; padding: 0 6px; font-weight: 700; }
/* Padlock: the plan lock (beyond the free lessons). Not the prerequisite lock, which has no glyph (a dim body, no ring). */
.lock { display: inline-block; flex: none; width: 1em; height: 1em; vertical-align: -0.12em; color: var(--violet-text); }

/* Mastery meter: light accumulating along a track */
.meter { --v: 0; position: relative; height: 6px; border-radius: 1px; background: var(--m0); overflow: hidden; }
.meter > i { position: absolute; inset: 0 auto 0 0; width: calc(var(--v) * 1%); background: var(--c, var(--m4)); }
.meter-lg { height: 10px; }
.meter-split { display: flex; height: 10px; border-radius: 1px; overflow: hidden; gap: 2px; }
.meter-split > span { flex: var(--w) 1 0; background: var(--c); min-width: 3px; }

/* Tables and lists */
.table { font-size: .95rem; }
.table th { text-align: left; font: 600 .82rem/1.2 var(--font-ui); color: var(--text-3); padding: var(--sp-3); border-bottom: 1px solid var(--line); }
.table td { padding: var(--sp-3); border-bottom: 1px solid var(--line); vertical-align: top; }
.table tr:hover td { background: color-mix(in srgb, var(--surface-2) 60%, transparent); }
.table-wrap { overflow-x: auto; border-radius: var(--r-md); background: var(--surface); }

/* Dialog + toast */
dialog { background: var(--surface); color: var(--text); border: 0; border-radius: var(--r-lg); padding: var(--sp-5); max-width: min(92vw, 560px); box-shadow: var(--shadow-1); }
dialog::backdrop { background: var(--scrim); backdrop-filter: blur(3px); }
.toast-host { position: fixed; inset: auto var(--sp-4) var(--sp-4) auto; display: grid; gap: var(--sp-2); z-index: 900; }
.toast { background: var(--surface-3); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); box-shadow: var(--shadow-1); max-width: 360px; animation: toast-in .2s var(--ease); }
@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } }

/* Disclosure (FAQ): rows divided by hairlines */
details.disclosure { border-top: 1px solid var(--line); padding: var(--sp-4) 0; }
details.disclosure:last-of-type { border-bottom: 1px solid var(--line); }
details.disclosure > summary { cursor: pointer; font-weight: 600; list-style: none; display: flex; justify-content: space-between; gap: var(--sp-4); }
details.disclosure > summary::-webkit-details-marker { display: none; }
details.disclosure > summary::after { content: "+"; font-family: var(--font-mono); color: var(--accent-text); }
details.disclosure[open] > summary::after { content: "\2013"; }
details.disclosure > div { margin-top: var(--sp-3); color: var(--text-2); max-width: var(--measure); }

/* Math */
.katex { font-size: 1.06em; }
.katex-display { overflow-x: auto; overflow-y: hidden; padding: var(--sp-2) 0; }

/* Key/value facts */
.kv { margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: var(--sp-4); }
.kv dt { font: 500 .85rem/1.2 var(--font-ui); color: var(--text-3); }
.kv dd { margin: var(--sp-1) 0 0; font-weight: 600; }

/* Brand lockup */
.brand { display: inline-flex; align-items: center; gap: 10px; font: 600 1.25rem/1 var(--font-display); color: var(--text); text-decoration: none; letter-spacing: .005em; }
.brand:hover { color: var(--text); text-decoration: none; }
.brand img, .brand svg { width: 30px; height: 30px; }

/* Account: sign in / sign up for visitors, a profile menu for everyone else (also in local mode, where the profile is the local learner). */
.auth-links { display: inline-flex; align-items: center; gap: var(--sp-2); }
.account { position: relative; }
.account > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center; gap: var(--sp-2); min-height: 36px; padding: 0 var(--sp-3) 0 var(--sp-2); border-radius: var(--r-md); color: var(--text); font-weight: 600; font-size: .9rem; transition: color .15s var(--ease); }
.account > summary::-webkit-details-marker { display: none; }
/* Hover and open: the name brightens and the avatar takes a tight violet halo (no wash behind the row). */
.account > summary:hover, .account[open] > summary { color: var(--text); }
.account > summary:hover .avatar, .account[open] > summary .avatar { box-shadow: 0 0 12px -3px color-mix(in srgb, var(--violet) 70%, transparent); }
/* The avatar is a lit planet (a round porthole). Its orbit is one ring, centred on the disc (a 38px box around the 30px disc,
   so the ring sits 2px outside it and clears the name's gap), with a brighter arc on its upper left (the lit side). */
.avatar { position: relative; display: inline-grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; background: radial-gradient(circle at 34% 28%, color-mix(in srgb, var(--violet), var(--lit-hi) 40%) 0, var(--violet) 55%, color-mix(in srgb, var(--violet), var(--bg) 45%) 100%); color: var(--on-hue); font: 700 .74rem/1 var(--font-ui); letter-spacing: .02em; transition: box-shadow .2s var(--ease); }
.avatar .orbit { position: absolute; left: 50%; top: 50%; width: 38px; height: 38px; margin: -19px 0 0 -19px; overflow: visible; color: var(--cyan); pointer-events: none; }
.account__menu { position: absolute; right: 0; top: calc(100% + 10px); z-index: 60; width: min(300px, 86vw); padding: var(--sp-2); border-radius: var(--r-lg); background: color-mix(in srgb, var(--surface) 94%, transparent); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); box-shadow: var(--glass-shadow); }
.account__who { display: flex; gap: var(--sp-3); align-items: center; padding: var(--sp-3); }
.account__who .avatar { width: 40px; height: 40px; font-size: .95rem; }
.account__who strong { display: block; line-height: 1.2; }
.account__who span:not(.avatar) { display: block; color: var(--text-3); font-size: .85rem; overflow-wrap: anywhere; }
.account__menu a, .account__menu button { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: 10px var(--sp-3); border: 0; border-radius: var(--r-sm); background: none; color: var(--text-2); font: 500 .95rem/1.2 var(--font-ui); text-align: left; text-decoration: none; cursor: pointer; }
.account__menu a:hover, .account__menu button:hover { background: var(--surface-2); color: var(--text); text-decoration: none; }
.account__rule { height: 1px; margin: var(--sp-2) var(--sp-3); background: var(--line); }
.account__note { margin: 0; padding: 0 var(--sp-3) var(--sp-2); color: var(--text-3); font-size: .82rem; }
@media (max-width: 560px) { .account__name { display: none; } }

/* The mark: a planet lit 64%, a ring (every prerequisite met) and a moon (a review is due), in the product's own sign language. */
.mark { flex: none; display: block; width: 34px; height: 34px; }

/* Plans (pricing page and billing): a free column and one violet-tinted block per plan. The price is set large in Jost;
   buttons align at the foot. Violet is the plan's colour everywhere (the "Monthly plan" tag, the padlock, the upsell). */
.plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--sp-3); margin-top: var(--sp-6); }
.plan { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-5); border-radius: var(--r-lg); background: color-mix(in srgb, var(--violet) 12%, var(--surface)); }
.plan--free { background: var(--surface); }
.plan__name { font: 500 1.2rem/1.2 var(--font-display); margin: 0; }
.plan__price { font: 500 clamp(2.2rem, 1.8rem + 1.2vw, 2.9rem)/1.05 var(--font-display); letter-spacing: -.02em; margin: var(--sp-4) 0 0; color: var(--text); }
.plan__price--text { font-size: 1.6rem; line-height: 1.2; }
.plan__unit { font: 500 .42em/1 var(--font-ui); color: var(--text-2); letter-spacing: 0; white-space: nowrap; }
.plan__body { flex: 1; margin: var(--sp-4) 0 var(--sp-5); color: var(--text-2); font-size: .95rem; }
.plan .btn { width: 100%; }
.plan__alt { margin: 0; text-align: center; font-size: .9rem; }
/* Unavailable checkout is still an explanation, so its label keeps full contrast (only the fill steps back). */
.plan .btn:disabled { opacity: 1; background: var(--surface-2); color: var(--text-2); }

/* ------------------------------------------------------------------ display type */
/* Display headings (.display, the landing's headings). Each word (.w) is filled with a vertical gradient from the text
   colour to a pale tint, so it reads as lit from above. The depth is a filter on the heading, not a text-shadow: a
   text-shadow paints over a transparent gradient fill and muddies it. The filter stacks a faint top edge, three one-pixel
   steps down-right in the night violet, and a soft glow beneath. */
.display {
  filter: drop-shadow(0 -1px 0 var(--display-edge)) drop-shadow(1px 1px 0 var(--display-shade)) drop-shadow(2px 2px 0 var(--display-shade)) drop-shadow(3px 3px 0 var(--display-shade)) drop-shadow(0 8px 20px var(--display-glow));
}
.display .w {
  display: inline-block;
  /* the word's box takes its ascenders and descenders, or the fill would stop at the line box and leave them unlit */
  padding: .08em .03em .16em;
  margin: -.08em -.03em -.16em;
  background: linear-gradient(180deg, var(--text) 0%, var(--display-low) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  transition: opacity .7s var(--ease), transform .7s var(--ease), filter .7s var(--ease);
  transition-delay: calc(var(--i, 0) * 70ms);
}
/* The first-view rise: display-text.js adds .is-in when the heading is seen. The hidden state needs js-display (set before
   first paint by an inline script), so without script every word is visible. */
.js-display .display:not(.is-in) .w { opacity: 0; transform: translateY(.35em); filter: blur(6px); }
@media (prefers-reduced-motion: reduce) {
  .display .w { transition: none; }
  .js-display .display:not(.is-in) .w { opacity: 1; transform: none; filter: none; }
}

/* Motion: the press, the glint and the hover lift are feedback, not decoration. Off for reduced motion. */
@media (prefers-reduced-motion: reduce) {
  .btn, .btn-primary::after { transition: none; }
  .btn:active { transform: none; }
  .btn-primary:hover::after, .btn-primary::after { animation: none; display: none; }
}
