/* ─────────────────────────────────────────────────────────────────────────
   cpeio.online — design tokens
   Source of truth: PRODUCT.md, DESIGN.md (project root)
   Loaded after Bulma; tokens override Bulma where surfaces are migrated.
   ───────────────────────────────────────────────────────────────────────── */

/* Self-hosted Geist (Vercel, OFL). Latin coverage; variable axis 100–900. */
@font-face {
  font-family: "Geist";
  src: url("/assets/fonts/geist/Geist-Variable.woff2") format("woff2-variations"),
       url("/assets/fonts/geist/Geist-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/geist/GeistMono-Variable.woff2") format("woff2-variations"),
       url("/assets/fonts/geist/GeistMono-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ─── Token primitives (light theme defaults) ────────────────────────────── */

:root {
  /* Brand hue primitive — anchors logo sky-blue. Every neutral and accent
     derives from this value. To re-key the palette, change this number. */
  --brand-hue: 245;

  /* Neutrals — Stripe-restraint mechanic, tinted toward brand hue.
     Pure black and pure white are forbidden (PRODUCT.md anti-references). */
  --color-paper-cream:    oklch(98%   0.005  var(--brand-hue));  /* primary surface */
  --color-paper-elevated: oklch(99.5% 0.004  var(--brand-hue));  /* card / elevated surface */
  --color-paper-band:     oklch(95.5% 0.008  var(--brand-hue));  /* navbar / footer band — slightly creamier than body */
  --color-graphite:       oklch(22%   0.012  var(--brand-hue));  /* primary text */
  --color-graphite-soft:  oklch(38%   0.012  var(--brand-hue));  /* secondary text, metadata */
  --color-hairline:       oklch(88%   0.006  var(--brand-hue));  /* border, divider */
  --color-hairline-strong:oklch(78%   0.008  var(--brand-hue));  /* border-emphasis, focus group */

  /* Sky-action accent — recognizably the logo blue, deepened for AA contrast.
     The accent appears on ≤10% of any view (DESIGN.md "The One Voice Rule"). */
  --color-sky-action:       oklch(60% 0.18 245);  /* primary buttons, focus rings, active state */
  --color-sky-action-hover: oklch(54% 0.20 245);  /* hover/active depth */
  --color-sky-action-deep:  oklch(48% 0.20 245);  /* inline accent text on cream (4.5:1 AA) */
  --color-sky-action-soft:  oklch(94% 0.04 245);  /* subtle wash, badge background */
  --color-sky-action-on:    oklch(99% 0.005 var(--brand-hue));  /* text/icon ON sky-action */

  /* Functional colors — tonal siblings, paired text/icon affordance required.
     No color-only signaling (DESIGN.md a11y). */
  --color-success-deep: oklch(46% 0.10 145);
  --color-success-soft: oklch(94% 0.04 145);
  --color-warning-deep: oklch(50% 0.12 75);
  --color-warning-soft: oklch(95% 0.05 75);
  --color-danger-deep:  oklch(48% 0.16 25);
  --color-danger-soft:  oklch(94% 0.05 25);
  --color-info-deep:    var(--color-sky-action-deep);
  --color-info-soft:    var(--color-sky-action-soft);

  /* Semantic surface roles — consume these in components, not the primitives.
     Lets dark-theme override flow through without restating component styles. */
  --color-surface:          var(--color-paper-cream);
  --color-surface-elevated: var(--color-paper-elevated);
  --color-surface-band:     var(--color-paper-band);
  --color-text:             var(--color-graphite);
  --color-text-soft:        var(--color-graphite-soft);
  --color-text-on-action:   var(--color-sky-action-on);
  --color-border:           var(--color-hairline);
  --color-border-strong:    var(--color-hairline-strong);

  /* Type families. System fallbacks load instantly; Geist swaps in on arrival. */
  --font-sans: "Geist", system-ui, -apple-system, "Segoe UI", "Helvetica Neue", sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  /* Type scale — 1.25 ratio (major third). Display fluid; UI/body fixed rem. */
  --type-display-size:    clamp(2.5rem, 1.5rem + 4vw, 4.25rem);
  --type-display-weight:  600;
  --type-display-line:    1.05;
  --type-display-track:   -0.022em;

  --type-headline-size:   clamp(1.75rem, 1.4rem + 1.6vw, 2.25rem);
  --type-headline-weight: 600;
  --type-headline-line:   1.15;
  --type-headline-track:  -0.014em;

  --type-title-size:      1.25rem;     /* 20px */
  --type-title-weight:    600;
  --type-title-line:      1.3;
  --type-title-track:     -0.008em;

  --type-body-size:       1rem;        /* 16px */
  --type-body-weight:     400;
  --type-body-line:       1.55;
  --type-body-track:      0;

  --type-label-size:      0.8125rem;   /* 13px */
  --type-label-weight:    500;
  --type-label-line:      1.4;
  --type-label-track:     0;

  --type-caption-size:    0.75rem;     /* 12px */
  --type-caption-weight:  500;
  --type-caption-line:    1.45;
  --type-caption-track:   0;

  --type-mono-size:       0.9375rem;   /* 15px — slightly under body for visual balance */
  --type-mono-weight:     400;
  --type-mono-line:       1.5;
  --type-mono-track:      0;

  /* Reading measure — body prose stays in the comfortable band. */
  --measure-prose: 70ch;

  /* Spacing scale — 4pt base, varied for rhythm (NOT uniform). */
  --space-3xs: 0.25rem;   /*  4px */
  --space-2xs: 0.5rem;    /*  8px */
  --space-xs:  0.75rem;   /* 12px */
  --space-sm:  1rem;      /* 16px */
  --space-md:  1.5rem;    /* 24px */
  --space-lg:  2rem;      /* 32px */
  --space-xl:  3rem;      /* 48px */
  --space-2xl: 4rem;      /* 64px */
  --space-3xl: 6rem;      /* 96px */

  /* Radii — small restrained set. */
  --radius-sm:   4px;
  --radius-md:   6px;
  --radius-lg:   8px;
  --radius-xl:   12px;
  --radius-pill: 9999px;  /* only for genuine pills (chips, badges) */

  /* Border presets. */
  --border-hairline:        1px solid var(--color-border);
  --border-hairline-strong: 1px solid var(--color-border-strong);

  /* Motion easings — exponential ease-out only. No bounce, no elastic.
     Duration values are defined per-use, not as tokens. */
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out-expo:  cubic-bezier(0.16, 1, 0.3, 1);

  /* Focus ring — functional, used only on :focus-visible. */
  --focus-ring: 0 0 0 3px oklch(60% 0.18 245 / 0.35);
}

/* ─── Dark theme — explicit toggle wins over system preference ──────────── */

:root[data-theme="dark"] {
  --color-paper-cream:    oklch(18% 0.012 var(--brand-hue));   /* slate */
  --color-paper-elevated: oklch(22% 0.014 var(--brand-hue));
  --color-paper-band:     oklch(14% 0.014 var(--brand-hue));   /* deeper band — receives content above */
  --color-graphite:       oklch(94% 0.005 var(--brand-hue));   /* bone */
  --color-graphite-soft:  oklch(76% 0.008 var(--brand-hue));
  --color-hairline:       oklch(28% 0.012 var(--brand-hue));
  --color-hairline-strong:oklch(38% 0.014 var(--brand-hue));

  --color-sky-action:       oklch(70% 0.16 245);
  --color-sky-action-hover: oklch(78% 0.16 245);
  --color-sky-action-deep:  oklch(76% 0.18 245);
  --color-sky-action-soft:  oklch(28% 0.06 245);
  --color-sky-action-on:    oklch(15% 0.012 var(--brand-hue));

  --color-success-deep: oklch(72% 0.14 145);
  --color-success-soft: oklch(28% 0.06 145);
  --color-warning-deep: oklch(76% 0.14 75);
  --color-warning-soft: oklch(28% 0.06 75);
  --color-danger-deep:  oklch(72% 0.16 25);
  --color-danger-soft:  oklch(28% 0.07 25);

  /* Light text on dark reads as heavier; reduce body weight one notch. */
  --type-body-weight: 380;

  --focus-ring: 0 0 0 3px oklch(70% 0.16 245 / 0.45);
}

/* ─── Base element styling ─────────────────────────────────────────────── */

html {
  background-color: var(--color-surface);
  color: var(--color-text);
}

body {
  font-family: var(--font-sans);
  font-feature-settings: "kern" 1;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Heading line-break quality — variable-font browsers will balance long titles. */
h1, h2, h3, h4, h5, h6,
.title, .subtitle {
  text-wrap: balance;
}

/* Prose paragraphs avoid orphans. */
p {
  text-wrap: pretty;
}

/* Mono utility for structured data — credit hours, course codes, certificate
   IDs, regulatory citations, ISO timestamps (DESIGN.md "Mono-for-Data Rule"). */
.is-mono,
.font-mono,
code,
kbd,
samp {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* Visually-hidden but accessible to assistive tech. */
.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;
}

/* ─── Bulma color-class redirection ─────────────────────────────────────── */
/* Existing markup uses `is-primary`, `is-link`, `is-info` etc. Redirect those
   to token-based values so legacy templates inherit the new palette without
   a class-name migration. Bulma's structural styling (flex, grid helpers,
   sizing) continues unchanged. */

.button.is-primary,
.button.is-link {
  background-color: var(--color-sky-action);
  border-color: var(--color-sky-action);
  color: var(--color-text-on-action);
  transition: background-color 0.18s var(--ease-out-quart),
              border-color   0.18s var(--ease-out-quart);
}
.button.is-primary:hover,
.button.is-link:hover {
  background-color: var(--color-sky-action-hover);
  border-color:     var(--color-sky-action-hover);
  color:            var(--color-text-on-action);
}
.button.is-primary:focus-visible,
.button.is-link:focus-visible,
.button.is-light:focus-visible,
.button:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.button.is-light {
  background-color: var(--color-surface-elevated);
  border-color:     var(--color-border);
  color:            var(--color-text);
}
.button.is-light:hover {
  background-color: var(--color-sky-action-soft);
  border-color:     var(--color-border-strong);
  color:            var(--color-text);
}

/* Navbar — Bulma's whitesmoke is too close to our cream surface. Use the
   band token so the nav reads as its own paper band, with a hairline below
   defining the edge (no shadow — flat-by-default). */
.navbar.is-light {
  background-color: var(--color-surface-band);
  color:            var(--color-text);
  border-bottom:    var(--border-hairline);
  box-shadow:       none;
}
.navbar.is-light .navbar-item,
.navbar.is-light .navbar-link {
  color: var(--color-text);
  background-color: transparent;
  transition: background-color 0.15s var(--ease-out-quart),
              color            0.15s var(--ease-out-quart);
}
.navbar.is-light .navbar-item:hover,
.navbar.is-light .navbar-link:hover,
.navbar.is-light .navbar-item.is-active,
.navbar.is-light .navbar-link.is-active {
  background-color: var(--color-sky-action-soft);
  color:            var(--color-sky-action-deep);
}
.navbar.is-light .navbar-burger {
  color: var(--color-text);
}
.navbar.is-light .navbar-burger:hover {
  background-color: var(--color-sky-action-soft);
}
/* Mobile: the dropdown menu surface should match the band, not Bulma white. */
@media (max-width: 1023px) {
  .navbar.is-light .navbar-menu {
    background-color: var(--color-surface-band);
    border-top: var(--border-hairline);
    box-shadow: none;
  }
}

/* Form fields — global Bulma redirects so every form across cpeio inherits.
   Surface-scoped overrides (e.g. .catalog-filters .input) still win on
   specificity. */
.input,
.textarea,
.select select {
  background-color: var(--color-surface);
  border: var(--border-hairline);
  border-radius: var(--radius-sm);
  color: var(--color-text);
  font-family: var(--font-sans);
  font-size: var(--type-body-size);
  line-height: var(--type-body-line);
  box-shadow: none;
  transition: border-color 0.15s var(--ease-out-quart);
}
.input::placeholder,
.textarea::placeholder {
  color: var(--color-text-soft);
}
.input:hover,
.textarea:hover,
.select select:hover {
  border-color: var(--color-border-strong);
}
.input:focus,
.input:focus-visible,
.textarea:focus,
.textarea:focus-visible,
.select select:focus,
.select select:focus-visible {
  outline: none;
  border-color: var(--color-sky-action);
  box-shadow: var(--focus-ring);
}
.input:disabled,
.textarea:disabled,
.select select:disabled {
  background-color: var(--color-surface-band);
  color: var(--color-text-soft);
  cursor: not-allowed;
}
.label {
  font-family: var(--font-sans);
  font-size: var(--type-label-size);
  font-weight: var(--type-label-weight);
  color: var(--color-text);
  margin-bottom: var(--space-3xs);
}
.help {
  font-family: var(--font-sans);
  font-size: var(--type-caption-size);
  color: var(--color-text-soft);
  margin-top: var(--space-3xs);
}
.checkbox,
.radio {
  font-family: var(--font-sans);
  font-size: var(--type-body-size);
  color: var(--color-text);
}
.checkbox input[type="checkbox"],
.radio input[type="radio"] {
  accent-color: var(--color-sky-action);
  margin-right: var(--space-3xs);
}

/* Default .tag (no color modifier) and .tag.is-light — neutral pill on the
   surface-band tone with hairline border. Placed BEFORE the colored variants
   so source-order precedence keeps .tag.is-info (etc) winning when both
   classes are applied (e.g. <span class="tag is-info is-light">). */
.tag,
.tag.is-light {
  background-color: var(--color-surface-band);
  color:            var(--color-text);
  border:           var(--border-hairline);
  font-family:      var(--font-sans);
}

.tag.is-info,
.tag.is-link {
  background-color: var(--color-sky-action-soft);
  color:            var(--color-sky-action-deep);
  border-color:     transparent;
}
.tag.is-success {
  background-color: var(--color-success-soft);
  color:            var(--color-success-deep);
  border-color:     transparent;
}
.tag.is-warning {
  background-color: var(--color-warning-soft);
  color:            var(--color-warning-deep);
  border-color:     transparent;
}
.tag.is-danger {
  background-color: var(--color-danger-soft);
  color:            var(--color-danger-deep);
  border-color:     transparent;
}
/* When .is-light is paired with a color modifier, fall through to the colored
   variant — keep the soft-tonal fill rather than the neutral surface-band. */
.tag.is-light.is-info,
.tag.is-light.is-link {
  background-color: var(--color-sky-action-soft);
  color:            var(--color-sky-action-deep);
  border-color:     transparent;
}
.tag.is-light.is-success {
  background-color: var(--color-success-soft);
  color:            var(--color-success-deep);
  border-color:     transparent;
}
.tag.is-light.is-warning {
  background-color: var(--color-warning-soft);
  color:            var(--color-warning-deep);
  border-color:     transparent;
}
.tag.is-light.is-danger {
  background-color: var(--color-danger-soft);
  color:            var(--color-danger-deep);
  border-color:     transparent;
}

/* Progress bars — Bulma .progress redirected to tokens. Sky-action fill on
   a hairline-strong track. No shadow, no border-radius beyond pill so the
   bar reads as a clean rule, not a card. */
progress.progress {
  height: 6px;
  background-color: var(--color-hairline-strong);
  border-radius: var(--radius-pill);
  border: none;
  box-shadow: none;
  overflow: hidden;
}
progress.progress::-webkit-progress-bar {
  background-color: var(--color-hairline-strong);
  border-radius: var(--radius-pill);
}
progress.progress::-webkit-progress-value {
  background-color: var(--color-sky-action);
  border-radius: var(--radius-pill);
  transition: width 0.3s var(--ease-out-quart);
}
progress.progress::-moz-progress-bar {
  background-color: var(--color-sky-action);
  border-radius: var(--radius-pill);
}

/* Ad unit card — overrides the .box chrome from the partials/ad_sidebar.twig
   wrapper so ads render as a clean hairline-bordered surface on every page
   that includes the partial. */
.ad-unit-card.box,
.ad-unit-card {
  background-color: var(--color-surface-elevated);
  border: var(--border-hairline);
  border-radius: var(--radius-md);
  box-shadow: none;
  padding: var(--space-2xs);
}
.ad-unit-card img {
  display: block;
  border-radius: var(--radius-sm);
}

/* Modal — Bulma .modal redirected to tokens. Used by base.twig
   confirmDeleteModal and any surface that opens a modal-card. */
.modal {
  align-items: center;
  display: none;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
  position: fixed;
  inset: 0;
  z-index: 40;
}
.modal.is-active {
  display: flex;
}
.modal-background {
  position: absolute;
  inset: 0;
  background-color: oklch(0% 0 0 / 0.55);
}
.modal-card {
  position: relative;
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 4rem);
  width: 640px;
  max-width: calc(100vw - 2rem);
  background-color: var(--color-surface-elevated);
  border: var(--border-hairline);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 16px 48px oklch(0% 0 0 / 0.18);
}
.modal-card-head,
.modal-card-foot {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  padding: var(--space-md) var(--space-lg);
  background-color: var(--color-surface-elevated);
  position: relative;
}
.modal-card-head {
  border-bottom: var(--border-hairline);
  justify-content: space-between;
}
.modal-card-foot {
  border-top: var(--border-hairline);
  gap: var(--space-2xs);
  justify-content: flex-end;
}
.modal-card-title {
  font-family: var(--font-sans);
  font-size: var(--type-title-size);
  font-weight: var(--type-title-weight);
  letter-spacing: var(--type-title-track);
  color: var(--color-text);
  margin: 0;
  flex: 1;
}
.modal-card-body {
  background-color: var(--color-surface-elevated);
  flex: 1;
  overflow: auto;
  padding: var(--space-md) var(--space-lg);
}
.modal .delete {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-pill);
  border: var(--border-hairline);
  background-color: transparent;
  cursor: pointer;
  position: relative;
  color: var(--color-text-soft);
  font-size: 0;
  transition: background-color 0.15s var(--ease-out-quart),
              color            0.15s var(--ease-out-quart);
}
.modal .delete::before,
.modal .delete::after {
  content: "";
  position: absolute;
  width: 0.875rem;
  height: 1px;
  background-color: currentColor;
  top: 50%;
  left: 50%;
}
.modal .delete::before { transform: translate(-50%, -50%) rotate(45deg); }
.modal .delete::after  { transform: translate(-50%, -50%) rotate(-45deg); }
.modal .delete:hover {
  color: var(--color-text);
  background-color: var(--color-sky-action-soft);
  border-color: var(--color-sky-action-deep);
}

/* Breadcrumb — Bulma .breadcrumb redirected to inline-text pattern with
   hairline below. Used by surfaces that haven't adopted a scoped breadcrumb
   class. Surface-specific patterns (.cd-breadcrumb, .lp-breadcrumb,
   .adm-breadcrumb, .in-breadcrumb, etc) win on specificity. */
.breadcrumb {
  font-family: var(--font-sans);
  font-size: var(--type-label-size);
  color: var(--color-text-soft);
  margin: 0 0 var(--space-md);
  padding-bottom: var(--space-2xs);
  border-bottom: var(--border-hairline);
}
.breadcrumb ul {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: 0;
}
.breadcrumb li {
  display: inline-flex;
  align-items: center;
}
.breadcrumb li + li::before {
  content: "→";
  margin: 0 var(--space-3xs);
  color: var(--color-border-strong);
  font-family: var(--font-sans);
}
.breadcrumb li a {
  color: var(--color-sky-action-deep);
  text-decoration: none;
  padding: 0;
  display: inline;
}
.breadcrumb li a:hover {
  color: var(--color-sky-action);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.breadcrumb li.is-active a {
  color: var(--color-text);
  pointer-events: none;
}

/* Tabs — Bulma .tabs redirected to underlined-active pattern. Hairline at the
   bottom of the tab bar; active tab gets a 2px sky-action bottom border + deep
   text. Drops the .is-boxed chrome (we deliberately don't read that modifier).
   Used by instructor course editor and inbox; cascades wherever .tabs renders. */
.tabs {
  border-bottom: var(--border-hairline);
  margin-bottom: var(--space-lg);
  font-family: var(--font-sans);
  overflow-x: auto;
}
.tabs ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  border-bottom: none;
}
.tabs li {
  margin-bottom: -1px;
  display: inline-flex;
}
.tabs li a {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2xs) var(--space-md);
  font-size: var(--type-label-size);
  font-weight: 500;
  color: var(--color-text-soft);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: color        0.15s var(--ease-out-quart),
              border-color 0.15s var(--ease-out-quart);
}
.tabs li a:hover {
  color: var(--color-text);
  background-color: transparent;
}
.tabs li.is-active a {
  color: var(--color-sky-action-deep);
  border-bottom-color: var(--color-sky-action);
}
.tabs li a:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
/* Bulma's .is-boxed modifier should yield to the underlined pattern. */
.tabs.is-boxed li a,
.tabs.is-boxed li.is-active a {
  background-color: transparent;
  border-radius: 0;
  border-left: none;
  border-right: none;
  border-top: none;
}

/* Pagination — Bulma's .pagination redirected to tokens. Numerals use mono
   with tabular figures so the pagination row doesn't reflow width across
   page changes. */
.pagination .pagination-link,
.pagination .pagination-previous,
.pagination .pagination-next {
  background-color: transparent;
  border: var(--border-hairline);
  color: var(--color-text);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  border-radius: var(--radius-sm);
  transition: background-color 0.15s var(--ease-out-quart),
              border-color    0.15s var(--ease-out-quart),
              color           0.15s var(--ease-out-quart);
}
.pagination .pagination-previous,
.pagination .pagination-next {
  font-family: var(--font-sans);
  font-variant-numeric: normal;
}
.pagination .pagination-link:hover,
.pagination .pagination-previous:hover,
.pagination .pagination-next:hover {
  background-color: var(--color-sky-action-soft);
  border-color:     var(--color-sky-action-deep);
  color:            var(--color-sky-action-deep);
}
.pagination .pagination-link.is-current {
  background-color: var(--color-sky-action);
  border-color:     var(--color-sky-action);
  color:            var(--color-text-on-action);
}
.pagination .pagination-link:focus-visible,
.pagination .pagination-previous:focus-visible,
.pagination .pagination-next:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}
.pagination .pagination-ellipsis {
  color: var(--color-text-soft);
}

/* Notifications — preserve Bulma's structure, swap colors to tonal siblings. */
.notification.is-success {
  background-color: var(--color-success-soft);
  color:            var(--color-success-deep);
}
.notification.is-warning {
  background-color: var(--color-warning-soft);
  color:            var(--color-warning-deep);
}
.notification.is-danger {
  background-color: var(--color-danger-soft);
  color:            var(--color-danger-deep);
}
.notification.is-info,
.notification.is-link,
.notification.is-primary {
  background-color: var(--color-sky-action-soft);
  color:            var(--color-sky-action-deep);
}

/* Inline accent links inherit the deep accent for AA contrast on cream. */
a {
  color: var(--color-sky-action-deep);
  text-decoration-color: color-mix(in oklch, var(--color-sky-action-deep) 35%, transparent);
  text-underline-offset: 2px;
  transition: color 0.15s var(--ease-out-quart);
}
a:hover {
  color: var(--color-sky-action);
  text-decoration-color: currentColor;
}

/* Hairline border default for cards (when surfaces choose tokens over Bulma). */
.has-token-border {
  border: var(--border-hairline);
  border-radius: var(--radius-md);
}

/* ─── Surface components ───────────────────────────────────────────────── */

/* Course-thumbnail fallback. Used in any card grid where a course may lack a
   thumbnail_url. Pairs with Bulma's <figure class="image is-16by9"> wrapper;
   `.has-ratio` inherits Bulma's absolute-fill positioning. The fallback is a
   tonal band with a hairline border (no gradient — anti-reference) and a
   single uppercased title-initial in display sans, restrained color. */
.course-thumbnail-fallback {
  background-color: var(--color-surface-band);
  border: var(--border-hairline);
  display: flex;
  align-items: center;
  justify-content: center;
}
.course-thumbnail-fallback__initial {
  font-family: var(--font-sans);
  font-size: clamp(2rem, 4.5vw, 3.25rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--color-text-soft);
  font-feature-settings: "kern" 1;
}

/* ─── Reduced motion — respect system preference everywhere ──────────────── */

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