/* ============================================================
   STELLARIA DESIGN SYSTEM — UTILITIES
   Layout helpers, accessibility, common one-offs.

   Composability rules:
     · Every utility is single-purpose. No utility carries an
       irreversible side effect.
     · All spacing is sourced from --sp-* tokens or clamp() rooted
       in tokens. No raw px values appear in this file outside of
       breakpoint media queries (the literal values for those are
       the canon documented in SPECIFICATIONS.md §6.3).
     · Asymmetric grids over symmetric. Mobile is one column,
       always. Use gap on parents over margin on children.
     · Breakpoints (literals) — sm 480, md 768, lg 980, xl 1280,
       2xl 1600. Responsive helpers escape the colon (.md\:foo)
       so they read as `md:foo` in HTML.
   ============================================================ */


/* === CONTAINER ============================================== */

/* Container — centered, with fluid gutters. Default institutional
   shell at 1440px. Variants below cover prose, dashboards, and
   full-bleed contexts. */
.container {
  width: 100%;
  max-width: var(--max-w);                /* 1440px */
  margin-inline: auto;
  padding-inline: var(--gutter);          /* clamp(24px, 5vw, 80px) */
}

/* Long-form reading column. ~720px / ~66ch. Use for editorial
   prose, blog posts, terms-of-service pages. */
.container--prose {
  max-width: var(--max-w-prose);
}

/* Forms, dashboards, dense data screens. Tighter than the shell
   so readers don't track across 1440px of mixed UI. */
.container--narrow {
  max-width: var(--max-w-narrow);
}

/* Full-bleed. Removes the max-width entirely; gutters remain so
   content still has air against the viewport edge. */
.container--full {
  max-width: none;
}


/* === SPLIT (legacy two-up) ================================== */

/* Asymmetric two-up: copy left, visual right. Preserved for
   back-compat with existing pages. New work should prefer the
   .grid + .grid--asym-* utilities below. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: clamp(32px, 6vw, 96px);
  align-items: end;
}
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; gap: var(--sp-3); }
}


/* === STACK & CLUSTER ======================================== */

/* Stack — vertical flow with sensible default gap. The --gap
   custom property lets a single instance override its rhythm
   without a modifier class:
     <div class="stack" style="--gap: var(--sp-6)">…</div>
   The modifier classes below cover the common cases. */
.stack {
  --gap: var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: var(--gap);
}
.stack--tight { --gap: var(--sp-1); }      /* 8px  — chip lists */
.stack--loose { --gap: var(--sp-4); }      /* 32px — section internals */
.stack--xl    { --gap: var(--sp-8); }      /* 64px — between section blocks */

/* Cluster — horizontal flow that wraps. Ideal for tag rows,
   button groups, breadcrumb-like meta. */
.cluster {
  --gap: var(--sp-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--gap);
  align-items: center;
}

/* Row — horizontal flow that does not wrap. Inline meta strips
   that must remain on one line; let the parent decide overflow. */
.row {
  --gap: var(--sp-2);
  display: flex;
  flex-wrap: nowrap;
  gap: var(--gap);
  align-items: center;
}


/* === GRID =================================================== */

/* The 12-column grid. Default columns and gap can be overridden
   via the --cols and --gap custom properties on any instance:
     <div class="grid" style="--cols: 8; --gap: var(--sp-4)">…</div>
   For the common ratios use the modifiers below. */
.grid {
  --cols: 12;
  --gap: var(--sp-3);
  display: grid;
  grid-template-columns: repeat(var(--cols), minmax(0, 1fr));
  gap: var(--gap);
}

/* Equal-column shortcuts. */
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid--6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }

/* Asymmetric featured-content patterns. The 7+5 split is the
   institutional default for "hero on one side, copy on the
   other". The 8+4 split is wider and reads more editorially. */
.grid--asym-7-5 { grid-template-columns: 7fr 5fr; }
.grid--asym-5-7 { grid-template-columns: 5fr 7fr; }
.grid--asym-8-4 { grid-template-columns: 8fr 4fr; }
.grid--asym-4-8 { grid-template-columns: 4fr 8fr; }

/* Column span utilities — for use inside .grid (12-col default).
   Generated 1 through 12. */
.col-span-1  { grid-column: span 1; }
.col-span-2  { grid-column: span 2; }
.col-span-3  { grid-column: span 3; }
.col-span-4  { grid-column: span 4; }
.col-span-5  { grid-column: span 5; }
.col-span-6  { grid-column: span 6; }
.col-span-7  { grid-column: span 7; }
.col-span-8  { grid-column: span 8; }
.col-span-9  { grid-column: span 9; }
.col-span-10 { grid-column: span 10; }
.col-span-11 { grid-column: span 11; }
.col-span-12 { grid-column: span 12; }

/* Row spans — used for bento heroes that occupy two visual rows
   while neighbours fill one. */
.row-span-2 { grid-row: span 2; }
.row-span-3 { grid-row: span 3; }

/* Responsive column-span helpers. The colon is escaped so the
   class name reads `md:col-span-6` in markup. Mobile is always
   one column — these only ever apply at the breakpoint and up. */
@media (min-width: 480px) {
  .sm\:col-span-1  { grid-column: span 1; }
  .sm\:col-span-2  { grid-column: span 2; }
  .sm\:col-span-3  { grid-column: span 3; }
  .sm\:col-span-4  { grid-column: span 4; }
  .sm\:col-span-5  { grid-column: span 5; }
  .sm\:col-span-6  { grid-column: span 6; }
  .sm\:col-span-7  { grid-column: span 7; }
  .sm\:col-span-8  { grid-column: span 8; }
  .sm\:col-span-9  { grid-column: span 9; }
  .sm\:col-span-10 { grid-column: span 10; }
  .sm\:col-span-11 { grid-column: span 11; }
  .sm\:col-span-12 { grid-column: span 12; }
}
@media (min-width: 768px) {
  .md\:col-span-1  { grid-column: span 1; }
  .md\:col-span-2  { grid-column: span 2; }
  .md\:col-span-3  { grid-column: span 3; }
  .md\:col-span-4  { grid-column: span 4; }
  .md\:col-span-5  { grid-column: span 5; }
  .md\:col-span-6  { grid-column: span 6; }
  .md\:col-span-7  { grid-column: span 7; }
  .md\:col-span-8  { grid-column: span 8; }
  .md\:col-span-9  { grid-column: span 9; }
  .md\:col-span-10 { grid-column: span 10; }
  .md\:col-span-11 { grid-column: span 11; }
  .md\:col-span-12 { grid-column: span 12; }
}
@media (min-width: 980px) {
  .lg\:col-span-1  { grid-column: span 1; }
  .lg\:col-span-2  { grid-column: span 2; }
  .lg\:col-span-3  { grid-column: span 3; }
  .lg\:col-span-4  { grid-column: span 4; }
  .lg\:col-span-5  { grid-column: span 5; }
  .lg\:col-span-6  { grid-column: span 6; }
  .lg\:col-span-7  { grid-column: span 7; }
  .lg\:col-span-8  { grid-column: span 8; }
  .lg\:col-span-9  { grid-column: span 9; }
  .lg\:col-span-10 { grid-column: span 10; }
  .lg\:col-span-11 { grid-column: span 11; }
  .lg\:col-span-12 { grid-column: span 12; }
}
@media (min-width: 1280px) {
  .xl\:col-span-1  { grid-column: span 1; }
  .xl\:col-span-2  { grid-column: span 2; }
  .xl\:col-span-3  { grid-column: span 3; }
  .xl\:col-span-4  { grid-column: span 4; }
  .xl\:col-span-5  { grid-column: span 5; }
  .xl\:col-span-6  { grid-column: span 6; }
  .xl\:col-span-7  { grid-column: span 7; }
  .xl\:col-span-8  { grid-column: span 8; }
  .xl\:col-span-9  { grid-column: span 9; }
  .xl\:col-span-10 { grid-column: span 10; }
  .xl\:col-span-11 { grid-column: span 11; }
  .xl\:col-span-12 { grid-column: span 12; }
}
@media (min-width: 1600px) {
  .\32 xl\:col-span-1  { grid-column: span 1; }
  .\32 xl\:col-span-2  { grid-column: span 2; }
  .\32 xl\:col-span-3  { grid-column: span 3; }
  .\32 xl\:col-span-4  { grid-column: span 4; }
  .\32 xl\:col-span-5  { grid-column: span 5; }
  .\32 xl\:col-span-6  { grid-column: span 6; }
  .\32 xl\:col-span-7  { grid-column: span 7; }
  .\32 xl\:col-span-8  { grid-column: span 8; }
  .\32 xl\:col-span-9  { grid-column: span 9; }
  .\32 xl\:col-span-10 { grid-column: span 10; }
  .\32 xl\:col-span-11 { grid-column: span 11; }
  .\32 xl\:col-span-12 { grid-column: span 12; }
}


/* === BENTO ================================================== */

/* Bento — a 12-col grid sized for 3–6 distinct surfaces with
   visible internal composition. Use for the "domains" and
   "deployment" patterns from DESIGN.md §5. Tiles default to
   full width on mobile, then take their assigned span at md+. */
.bento {
  --gap: var(--sp-3);
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-rows: minmax(0, auto);
  gap: var(--gap);
}

/* Default tile — full row on mobile. Override with the span
   modifiers at md and above. */
.bento__tile {
  grid-column: span 12;
}

@media (min-width: 768px) {
  .bento__tile--span-5  { grid-column: span 5;  }
  .bento__tile--span-6  { grid-column: span 6;  }
  .bento__tile--span-7  { grid-column: span 7;  }
  .bento__tile--span-12 { grid-column: span 12; }
}

/* Featured tile — adds the canonical 3px accent rail on the
   left. Use sparingly: a bento with two featured tiles has no
   featured tile. */
.bento__tile--featured {
  border-left: var(--border-3) solid var(--c-accent);
}


/* === SECTION RHYTHM ========================================= */

/* Section — the basic editorial container for a page block.
   Hairline border on top so adjacent sections separate without
   a heavy divider. The .section--first override removes the top
   border under a hero/nav. */
.section {
  padding-block: var(--section-tight);
  border-top: var(--border-hairline);
}

.section--loose { padding-block: var(--section-loose); }
.section--first { border-top: 0; }

/* Section header — the eyebrow + headline + lede block at the
   top of a section. The bottom margin is the only place we use
   margin instead of gap; the section body is a sibling element,
   not a child of a stack. */
.section-header {
  margin-bottom: var(--sp-8);
}


/* === STICKY ================================================= */

/* Sticky — position: sticky with a sensible default offset.
   Override the offset with the --top custom property:
     <aside class="sticky" style="--top: 96px">…</aside>
   The --nav modifier sets a default offset large enough to
   clear the standard 60px nav plus a hairline of breathing room. */
.sticky {
  --top: var(--sp-3);                     /* 24px */
  position: sticky;
  top: var(--top);
}

.sticky--nav {
  --top: 60px;
}


/* === ASPECT RATIO =========================================== */

/* Aspect ratio utilities for content that needs a fixed shape:
   image slots, video frames, hero canvases. Pair with
   object-fit: cover on inner media. */
.ratio-1x1   { aspect-ratio: 1 / 1; }
.ratio-4x3   { aspect-ratio: 4 / 3; }
.ratio-16x9  { aspect-ratio: 16 / 9; }
.ratio-21x9  { aspect-ratio: 21 / 9; }
.ratio-4x5   { aspect-ratio: 4 / 5; }


/* === VISIBILITY ============================================= */

/* Visually hidden — present to assistive tech, removed from
   visual flow. Use for skip links and label text that the
   sighted user reads from icon context. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
  padding: 0;
  margin: -1px;
}

/* Hide-by-breakpoint helpers. The naming is "hide-below-md"
   meaning "hidden when the viewport is below the md breakpoint
   (768px)". The inverse "hide-above-md" hides at md and up. */
.hide-below-md { display: none; }
@media (min-width: 768px) { .hide-below-md { display: revert; } }

.hide-above-md { display: revert; }
@media (min-width: 768px) { .hide-above-md { display: none; } }

.hide-below-lg { display: none; }
@media (min-width: 980px) { .hide-below-lg { display: revert; } }

.hide-above-lg { display: revert; }
@media (min-width: 980px) { .hide-above-lg { display: none; } }


/* === TYPOGRAPHY HELPERS ===================================== */

.text-center { text-align: center; }
.text-right  { text-align: right; }
.text-left   { text-align: left; }

/* Wrap helpers. text-balance is for headlines (avoid the
   short last line). text-pretty is for body prose (prevents
   single-word orphans without changing line counts). */
.text-balance { text-wrap: balance; }
.text-pretty  { text-wrap: pretty; }

/* Reading-measure helpers. Caps line length to the published
   measure tokens; pair with .container--prose for full prose
   surfaces. */
.measure-body { max-width: var(--measure-body); }
.measure-lead { max-width: var(--measure-lead); }


/* === COLOR HELPERS ========================================== */

.text-dim    { color: var(--c-fg-dim); }
.text-muted  { color: var(--c-fg-muted); }
.text-accent { color: var(--c-accent); }

/* Surface tints — match the surface tokens from tokens.css.
   Use surface-2 for nested panels above a surface ground. */
.surface   { background: var(--c-surface); }
.surface-2 { background: var(--c-surface-2); }


/* === BORDER HELPERS ========================================= */

/* Hairline — the canonical 1px border in the brand neutral.
   Side-specific variants for partial enclosures (e.g. a top-
   bordered section without a frame). */
.hairline   { border:        var(--border-hairline); }
.hairline-t { border-top:    var(--border-hairline); }
.hairline-b { border-bottom: var(--border-hairline); }
.hairline-l { border-left:   var(--border-hairline); }
.hairline-r { border-right:  var(--border-hairline); }
.hairline-x { border-inline: var(--border-hairline); }
.hairline-y { border-block:  var(--border-hairline); }


/* === DIVIDER ================================================ */

/* Standalone <hr> divider. Matches the hairline composite. */
.divider {
  border: 0;
  border-top: var(--border-hairline);
  margin: 0;
}


/* === SPACING (margin-between siblings) ====================== */

/* These mimic .stack but as a one-off margin-based helper for
   contexts where flex isn't available (e.g. flow inside <main>
   children that must stay block-level for anchor targeting).
   Prefer .stack with gap. Reach for these only when you can't.
   The doc explains why; see LAYOUT.md §1. */
.space-y-1 > * + * { margin-block-start: var(--sp-1); }
.space-y-2 > * + * { margin-block-start: var(--sp-2); }
.space-y-3 > * + * { margin-block-start: var(--sp-3); }
.space-y-4 > * + * { margin-block-start: var(--sp-4); }
.space-y-5 > * + * { margin-block-start: var(--sp-5); }
.space-y-6 > * + * { margin-block-start: var(--sp-6); }
.space-y-7 > * + * { margin-block-start: var(--sp-7); }
.space-y-8 > * + * { margin-block-start: var(--sp-8); }


/* === LEGACY SECTION ALIASES ================================= */

/* Preserved aliases. The new canon is .section / .section--loose
   above; these remain so existing markup keeps working. */
.section--tight { padding: var(--section-tight) 0; }


/* === REDUCED MOTION ========================================= */

/* Disable transitions on every utility-bearing element when the
   reader has reduced-motion preferred. Component-level animations
   still need their own guards (see tokens.css for the rationale). */
@media (prefers-reduced-motion: reduce) {
  .container,
  .container--prose,
  .container--narrow,
  .container--full,
  .stack, .cluster, .row,
  .grid, .bento, .bento__tile,
  .section, .section-header,
  .sticky,
  .surface, .surface-2 {
    transition: none !important;
  }
}
