/* ==========================================================================
   LEECOI — Utilities
   ==========================================================================

   Loaded LAST of the shared stylesheets, so a utility always beats the
   component rule it is correcting. That ordering is the only reason none of
   these need `!important`.

   SCOPE: single-purpose, single-class, no variants of variants. If a set of
   utilities is being repeated on the same element every time it appears, that
   is a component asking to exist — promote it to components.css instead of
   growing this file.

   Page-scoped layout does NOT belong here. It belongs in
   `assets/css/pages/<page>.css`.
   ========================================================================== */

/* ==========================================================================
   1. LAYOUT PRIMITIVES
   ========================================================================== */

/* Vertical stack with one consistent gap. The most-used layout on the site. */
.stack {
  display: flex;
  flex-direction: column;
  gap: var(--stack-gap, var(--space-md));
}

.stack-xs { --stack-gap: var(--space-xs); }
.stack-sm { --stack-gap: var(--space-sm); }
.stack-lg { --stack-gap: var(--space-lg); }
.stack-xl { --stack-gap: var(--space-xl); }
.stack-2xl { --stack-gap: var(--space-2xl); }

/* Horizontal row that wraps rather than overflowing. Wrapping is the default
   because a row that cannot wrap is the single most common cause of
   horizontal scroll on a phone. */
.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--row-gap, var(--space-md));
}

.row-xs { --row-gap: var(--space-xs); }
.row-sm { --row-gap: var(--space-sm); }
.row-lg { --row-gap: var(--space-lg); }

.row-nowrap { flex-wrap: nowrap; }
.row-between { justify-content: space-between; }
.row-center { justify-content: center; }
.row-end { justify-content: flex-end; }
.row-top { align-items: flex-start; }
.row-baseline { align-items: baseline; }

/* Pushes everything after it to the far end of a flex container. */
.push { margin-inline-start: auto; }

.relative { position: relative; }
.isolate { isolation: isolate; }

/* ==========================================================================
   2. SPACING
   --------------------------------------------------------------------------
   Margin only, and only in the block direction. Components own their internal
   padding; parents own the space between children. Inline margins are
   deliberately absent, since `gap` handles that case without collapsing.
   ========================================================================== */

.mt-0 { margin-block-start: 0; }
.mt-xs { margin-block-start: var(--space-xs); }
.mt-sm { margin-block-start: var(--space-sm); }
.mt-md { margin-block-start: var(--space-md); }
.mt-lg { margin-block-start: var(--space-lg); }
.mt-xl { margin-block-start: var(--space-xl); }
.mt-2xl { margin-block-start: var(--space-2xl); }
.mt-auto { margin-block-start: auto; }

.mb-0 { margin-block-end: 0; }
.mb-xs { margin-block-end: var(--space-xs); }
.mb-sm { margin-block-end: var(--space-sm); }
.mb-md { margin-block-end: var(--space-md); }
.mb-lg { margin-block-end: var(--space-lg); }
.mb-xl { margin-block-end: var(--space-xl); }
.mb-2xl { margin-block-end: var(--space-2xl); }

/* ==========================================================================
   3. TYPE
   --------------------------------------------------------------------------
   The named steps from the scale. A font-size that is not one of these is a
   font-size that will drift out of the system.
   ========================================================================== */

.t-d1 {
  font-size: var(--step-d1);
  line-height: var(--step-d1-lh);
  letter-spacing: var(--step-d1-ls);
  font-weight: var(--weight-bold);
}

.t-d2 {
  font-size: var(--step-d2);
  line-height: var(--step-d2-lh);
  letter-spacing: var(--step-d2-ls);
  font-weight: var(--weight-bold);
}

.t-d3 {
  font-size: var(--step-d3);
  line-height: var(--step-d3-lh);
  letter-spacing: var(--step-d3-ls);
  font-weight: var(--weight-semi);
}

.t-lead {
  font-size: var(--step-lead);
  line-height: var(--step-lead-lh);
  letter-spacing: var(--step-lead-ls);
}

.t-body {
  font-size: var(--step-body);
  line-height: var(--step-body-lh);
}

.t-small {
  font-size: var(--step-small);
  line-height: var(--step-small-lh);
}

.t-micro {
  font-family: var(--font-mono);
  font-size: var(--step-micro);
  line-height: var(--step-micro-lh);
  letter-spacing: var(--step-micro-ls);
  text-transform: uppercase;
}

.t-mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.t-normal { font-weight: var(--weight-normal); }
.t-medium { font-weight: var(--weight-medium); }
.t-semi { font-weight: var(--weight-semi); }
.t-bold { font-weight: var(--weight-bold); }

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

.t-balance { text-wrap: balance; }
.t-pretty { text-wrap: pretty; }
.t-nowrap { white-space: nowrap; }

/* ---- Colour roles ------------------------------------------------------- */
/* Names describe the ROLE, never the hue, so a utility stays correct when the
   surface underneath it changes from dark to light. */
.t-ink { color: var(--ink); }
.t-muted { color: var(--muted); }
.t-accent { color: var(--accent-strong); }
.t-link { color: var(--link); }
.t-positive { color: var(--positive); }
.t-critical { color: var(--critical); }
.t-inherit { color: inherit; }

/* Underline only on interaction: inline links inside prose. */
.t-underline-hover:hover { text-decoration: underline; }

/* Clamps a description to a fixed number of lines so a grid of cards keeps
   its rhythm when one entry runs long. */
.clamp-2,
.clamp-3 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

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

/* ==========================================================================
   4. MEASURE
   ========================================================================== */

.measure { max-width: var(--width-prose); }
.measure-narrow { max-width: 32rem; }
.measure-wide { max-width: 52rem; }
.measure-none { max-width: none; }

.w-full { inline-size: 100%; }

/* Centres a constrained block, including one that also carries `.measure`. */
.center-block { margin-inline: auto; }

/* ==========================================================================
   5. LISTS
   --------------------------------------------------------------------------
   globals.css strips list markers, because most lists on this site are
   structural. These put them back where a list is genuinely prose.
   ========================================================================== */

.list-bullet,
.list-number {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding-inline-start: var(--space-lg);
  color: var(--muted);
}

.list-bullet { list-style: disc outside; }
.list-number { list-style: decimal outside; }

.list-bullet > li,
.list-number > li {
  padding-inline-start: var(--space-2xs);
}

/* Marker in the accent, for deliverable and output lists. Uses `::marker`
   rather than a pseudo-element so the bullet stays a real list marker to
   assistive technology. */
.list-marked > li::marker {
  color: var(--accent-strong);
}

/* ==========================================================================
   6. VISIBILITY
   ========================================================================== */

.hidden { display: none; }

/* Hide below / above the layout breakpoint used by the header. */
@media (max-width: 61.999rem) {
  .lg-only { display: none; }
}

@media (min-width: 62rem) {
  .sm-only { display: none; }
}

/* Decorative graphics: hidden from assistive tech in markup via aria-hidden,
   and excluded from print here since they carry no information. */
@media print {
  .decorative { display: none; }
}

/* ==========================================================================
   7. SCROLL LOCK
   --------------------------------------------------------------------------
   Applied to <html> while the mobile drawer is open.

   `position: fixed` on the body is the usual approach and it loses scroll
   position on iOS. `overflow: hidden` plus a scrollbar-width compensation
   keeps the position and stops the layout jumping sideways as the scrollbar
   disappears on desktop.
   ========================================================================== */

.scroll-locked {
  overflow: hidden;
  /* Written by navbar.js from `window.innerWidth - documentElement.clientWidth`
     before the lock is applied. */
  padding-inline-end: var(--scrollbar-w, 0px);
}

/* ==========================================================================
   8. ASPECT RATIOS
   --------------------------------------------------------------------------
   Reserving the box before an image or an SVG paints is what stops the page
   reflowing as media arrives.
   ========================================================================== */

.ratio-16-9 { aspect-ratio: 16 / 9; }
.ratio-4-3 { aspect-ratio: 4 / 3; }
.ratio-3-2 { aspect-ratio: 3 / 2; }
.ratio-square { aspect-ratio: 1; }

.object-cover {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* ==========================================================================
   9. BORDERS AND FILLS
   ========================================================================== */

.bordered {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}

.border-top { border-block-start: 1px solid var(--line); }
.border-bottom { border-block-end: 1px solid var(--line); }

.fill-panel { background-color: var(--panel); }
.fill-sunk { background-color: var(--bg-sunk); }

.radius-md { border-radius: var(--radius-md); }
.radius-lg { border-radius: var(--radius-lg); }
.radius-xl { border-radius: var(--radius-xl); }

/* Padding for ad-hoc boxes that are not full components. */
.pad-md { padding: var(--space-md); }
.pad-lg { padding: var(--space-lg); }
.pad-xl { padding: var(--space-xl); }
