/* ============================================================================
   Utilities - small, single-purpose helpers. Everything reads from tokens.
   Nothing here should carry a colour or size that is not already a token.
   ========================================================================= */

/* ---------------------------------------------------------------- spacing */

.mt-0 { margin-block-start: 0; }
.mt-2 { margin-block-start: var(--s-2); }
.mt-3 { margin-block-start: var(--s-3); }
.mt-4 { margin-block-start: var(--s-4); }
.mt-5 { margin-block-start: var(--s-5); }
.mt-6 { margin-block-start: var(--s-6); }
.mt-7 { margin-block-start: var(--s-7); }
.mt-8 { margin-block-start: var(--s-8); }
.mt-9 { margin-block-start: var(--s-9); }
.mt-10 { margin-block-start: var(--s-10); }

.mb-0 { margin-block-end: 0; }
.mb-2 { margin-block-end: var(--s-2); }
.mb-3 { margin-block-end: var(--s-3); }
.mb-4 { margin-block-end: var(--s-4); }
.mb-5 { margin-block-end: var(--s-5); }
.mb-6 { margin-block-end: var(--s-6); }
.mb-7 { margin-block-end: var(--s-7); }
.mb-8 { margin-block-end: var(--s-8); }
.mb-9 { margin-block-end: var(--s-9); }

.mx-auto { margin-inline: auto; }

/* ---------------------------------------------------------------- text */

.text-heading { color: var(--fg-heading); }
.text-body { color: var(--fg-body); }
.text-muted { color: var(--fg-muted); }
.text-meta { color: var(--fg-meta); }
.text-brand { color: var(--brand); }
.text-inverse { color: var(--fg-inverse); }

.text-center { text-align: center; }
.text-start { text-align: start; }
.text-end { text-align: end; }

.text-display { font-family: var(--font-display); }
.text-lead { font-size: var(--fs-lead); line-height: var(--lh-lead); }
.text-sm { font-size: var(--fs-sm); }
.text-xs { font-size: var(--fs-xs); }

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  color: var(--fg-meta);
  font-family: var(--font-display);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tr-2xs);
  text-transform: uppercase;
}

/* ---------------------------------------------------------------- layout */

.flex { display: flex; }
.grid-display { display: grid; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }
.justify-between { justify-content: space-between; }
.justify-center { justify-content: center; }
.justify-end { justify-content: flex-end; }
.flex-wrap { flex-wrap: wrap; }
.flex-1 { flex: 1; min-inline-size: 0; }

.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-5 { gap: var(--s-5); }
.gap-6 { gap: var(--s-6); }
.gap-7 { gap: var(--s-7); }

.relative { position: relative; }
.above { position: relative; z-index: 1; }

/* ---------------------------------------------------------------- measure */

.measure { max-inline-size: 72ch; }
.measure-narrow { max-inline-size: 52ch; }

/* ---------------------------------------------------------------- clamps */

/*
 * Line clamping. -webkit-line-clamp is still the only implementation with
 * universal support, and it needs all four declarations to work - dropping
 * `overflow: hidden` silently disables it in Firefox.
 */
.clamp-1,
.clamp-2,
.clamp-3,
.clamp-4 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.clamp-1 { -webkit-line-clamp: 1; line-clamp: 1; }
.clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }
.clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }
.clamp-4 { -webkit-line-clamp: 4; line-clamp: 4; }

/* ---------------------------------------------------------------- a11y */

.visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Visible again once focused - for a skip link or an in-card control. */
.visually-hidden-focusable:not(:focus):not(:focus-within) {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------- surfaces */

.surface { background: var(--bg-surface); }
.canvas { background: var(--bg-canvas); }

.panel {
  padding: var(--s-5);
  background: var(--bg-surface);
  border: var(--hairline) solid var(--line);
  border-radius: var(--r-lg);
}

.divider { block-size: var(--hairline); background: var(--line); }

/* ---------------------------------------------------------------- reveal */

/*
 * Scroll reveal.
 *
 * The hidden state is scoped to .js, so the content is visible whenever the
 * script has not taken charge of it - JS off, JS failed to parse, module
 * blocked. reveal.js adds .is-in, and also reveals everything unconditionally
 * a few seconds after load, so a card can never be left invisible because an
 * observer did not fire.
 *
 * The class name has to stay in step with reveal.js. It is .is-in.
 */
.js [data-reveal]:not(.is-in) {
  opacity: 0;
  transform: translateY(12px);
}

[data-reveal] {
  transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease);
  transition-delay: calc(var(--i, 0) * 60ms);
}

@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------- misc */

.hidden { display: none !important; }
.no-scroll { overflow: hidden; }
