/* ─────────────────────────────────────────────────────────────────────────────
   ASSEMBLED INNER PAGES — the width and density half of src/lib/layout-grammar.ts.
   Global and static (served from /assembled.css), loaded by MainLayout ONLY for
   design="compose" clubs, so the flagship never downloads a byte of it.

   WHY A STATIC FILE AND NOT A SCOPED <style>. Three scoping traps have already
   shipped as bugs here: Astro's scoped CSS never reaches elements a script builds
   with innerHTML; on the PLATFORM build it does not reach JS-created elements at
   all; and a scoped selector with an ancestor (.public-body.theme-dark .x) dies on
   that build unless the ancestor is wrapped in :global(). A plain global
   stylesheet has none of those failure modes. It is also why the mode components
   must NOT copy these rules into their own <style> blocks.

   COLOURS. Not one is written here. Everything reads --public-* tokens, so
   palette, theme and accent still come from themes.css and a dark club is dark
   without this file knowing dark exists.

   THE SHAPE THE ASSEMBLER EMITS:

     <div class="asm asm-d-{density}">
       <section class="asm-region" data-region="pricing">   ← full width: a mode
         <div class="asm-w asm-w-{width}">                     may paint a band
           …the mode component's own markup…                    across this
         </div>
       </section>
       …
     </div>

   The section carries the vertical rhythm and spans the viewport; the inner .asm-w
   carries the measure. Splitting them is what lets one region run a full-bleed
   accent band while its text still lines up with the page.

   DENSITY CHANGES TYPE, NOT JUST MARGINS. Three pages whose only difference is
   whitespace still read as the same page. compact/standard/airy move the body
   size, the heading steps, the line-height and the measure together — that is
   what a visitor registers as a different document.
   ───────────────────────────────────────────────────────────────────────────── */

/* ── DENSITY SCALE ──────────────────────────────────────────────────────────── */

.asm {
  /* standard is the default so a wrapper that lost its density class still reads. */
  --asm-block: clamp(2.2rem, 5vw, 3.5rem);  /* region padding-block */
  --asm-gap: 1rem;                          /* stack gap inside a region */
  --asm-head-gap: 1.4rem;                   /* heading → body */
  --asm-text: 1rem;
  --asm-line: 1.7;
  --asm-h2: clamp(1.8rem, 3vw, 2.4rem);
  --asm-h3: 1.15rem;
  --asm-lead: 1.15rem;                      /* the one oversized paragraph */
  --asm-measure: 68ch;
}

.asm-d-compact {
  --asm-block: clamp(1.4rem, 3.2vw, 2.4rem);
  --asm-gap: 0.7rem;
  --asm-head-gap: 0.9rem;
  --asm-text: 0.95rem;
  --asm-line: 1.55;
  --asm-h2: clamp(1.5rem, 2.4vw, 1.9rem);
  --asm-h3: 1.02rem;
  --asm-lead: 1.05rem;
  --asm-measure: 64ch;
}

.asm-d-airy {
  --asm-block: clamp(3rem, 7vw, 5.5rem);
  --asm-gap: 1.5rem;
  --asm-head-gap: 2rem;
  --asm-text: 1.08rem;
  --asm-line: 1.85;
  --asm-h2: clamp(2.1rem, 3.8vw, 3rem);
  --asm-h3: 1.3rem;
  --asm-lead: 1.3rem;
  --asm-measure: 60ch;
}

/* ── REGION RHYTHM ──────────────────────────────────────────────────────────── */

.asm-region {
  padding-block: var(--asm-block);
}

/* :where() keeps these at zero specificity, so a mode component's own class
   selectors win without needing !important. */
.asm-region :where(h2) {
  font-size: var(--asm-h2);
  line-height: 1.12;
  letter-spacing: -0.02em;
  margin: 0 0 var(--asm-head-gap);
  text-wrap: balance;
}
.asm-region :where(h3) {
  font-size: var(--asm-h3);
  line-height: 1.25;
  margin: 0 0 var(--asm-gap);
}
.asm-region :where(p, li) {
  font-size: var(--asm-text);
  line-height: var(--asm-line);
}
.asm-region :where(p) {
  margin: 0 0 var(--asm-gap);
  max-width: var(--asm-measure);
}
.asm-region :where(p:last-child) {
  margin-bottom: 0;
}
/* The single oversized opening paragraph the editorial modes rely on. */
.asm-region :where(.asm-lead) {
  font-size: var(--asm-lead);
  line-height: calc(var(--asm-line) - 0.1);
}

/* ── WIDTH ──────────────────────────────────────────────────────────────────── */

.asm-w {
  margin-inline: auto;
}

/* A single reading column: one mode's text, nothing beside it. */
.asm-w-narrow {
  width: min(48rem, calc((100vw - var(--shell-inset, 0px)) - 32px));
}

/* The page's own measure — identical to .public-container, so an assembled
   region lines up with the header, the footer and any non-assembled section. */
.asm-w-contained {
  width: min(var(--public-max), calc((100vw - var(--shell-inset, 0px)) - 32px));
}

/* Wider than the site grid: for grids and tables that need the room. */
.asm-w-wide {
  width: min(90rem, calc((100vw - var(--shell-inset, 0px)) - 32px));
}

/* Edge to edge. The gutter is padding rather than width so a mode can paint a
   background on .asm-region and have it reach both edges. */
.asm-w-bleed {
  width: 100%;
  padding-inline: clamp(16px, 4vw, 48px);
}

/* Below the phone breakpoint the four widths converge — a "wide" grid and a
   "narrow" column are the same single column at 380px, and holding them apart
   only produces horizontal scroll. Density still separates the pages. */
@media (max-width: 720px) {
  .asm-w-narrow,
  .asm-w-contained,
  .asm-w-wide {
    width: calc((100vw - var(--shell-inset, 0px)) - 32px);
  }
  .asm-w-bleed {
    padding-inline: 16px;
  }
}

/* ── LEADERSHIP ROSTER — THE ONE JS-BUILT BLOCK IN THE ASSEMBLED SYSTEM ────────
   The board cards are written with innerHTML by src/lib/roster-cards.ts, from a
   fetch of /api/board-members. Astro's scoping attribute never reaches an element
   a script creates, and on the PLATFORM build — which does not run the obfuscate
   integration that globalises scoped CSS on the static build — a scoped <style>
   would leave the whole roster UNSTYLED while looking perfect locally. So these
   rules live here, in the global stylesheet this file exists to be, and the roster
   mode component carries no card CSS of its own.

   THE REGION HAS THREE MODES, AND NO TWO ARE THE SAME PAGE. It once had a
   different second mode that differed only by .asm-roster-lead — a president's card
   turned on its side, drawn in the browser and only for a club whose roster actually
   named a president, which almost none do. That one is gone and its rules with it.
   The two registered beside the mined card grid are `ruled-register` (a ruled list
   of rows) and `directory-table` (name and role on one tight line), both commissioned
   for the case the grid serves worst — a club that entered its board and uploaded no
   photographs — and sized for different boards: a register gives four officers room,
   a directory keeps twenty on one screen. Their rules are below, beside the card
   rules, because all three are built with innerHTML.

   Not one colour is written here — everything is a --public-* token, so palette,
   theme and accent still come from themes.css.
   ───────────────────────────────────────────────────────────────────────────── */

.asm-roster-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
  gap: clamp(1rem, 2.5vw, 1.5rem);
  align-items: stretch;
}

.asm-roster-state {
  text-align: center;
  padding: 3rem 1rem;
  font-size: 1.02rem;
  font-weight: 600;
  color: var(--public-muted);
}

.asm-roster-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.5rem;
  padding: 2rem 1.4rem 1.6rem;
  border: 1px solid var(--public-line-soft);
  border-radius: var(--public-radius, 16px);
  background: var(--public-bg-soft);
  box-shadow: var(--public-shadow);
}

.asm-roster-photo,
.asm-roster-initials {
  width: 104px;
  height: 104px;
  border-radius: 50%;
  display: block;
  margin-bottom: 0.35rem;
}
.asm-roster-photo {
  object-fit: cover;
  border: 3px solid var(--public-line-soft);
}
.asm-roster-initials {
  display: grid;
  place-items: center;
  font-size: 1.9rem;
  font-weight: 800;
  color: var(--public-accent);
  background: color-mix(in srgb, var(--public-accent) 12%, transparent);
  border: 3px solid color-mix(in srgb, var(--public-accent) 30%, transparent);
}

.asm-roster-role {
  display: inline-block;
  font-size: 0.64rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  padding: 0.3rem 0.8rem;
  border-radius: 999px;
  color: var(--public-accent);
  background: color-mix(in srgb, var(--public-accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--public-accent) 28%, transparent);
}

.asm-roster-name {
  margin: 0;
  font-family: var(--public-font-heading, inherit);
  font-size: 1.2rem;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--public-text);
}

.asm-roster-bio {
  margin: 0;
  max-width: 34ch;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--public-muted);
}

/* ── ROSTER · ruled-register ────────────────────────────────────────────────
   Built with innerHTML by roster-cards.ts, so these rules cannot live in the
   component's scoped <style>. Tokens only, like everything else in this file. */
.asm-roster-register {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--public-line);
}

.asm-roster-rrow {
  display: grid;
  /* Deliberately asymmetric: the role is a label, the name is the content. */
  grid-template-columns: minmax(8rem, 1fr) 2.6fr;
  gap: 0.35rem clamp(1rem, 3vw, 2.5rem);
  align-items: baseline;
  padding: clamp(0.8rem, 2vw, 1.15rem) 0;
  border-bottom: 1px solid var(--public-line);
}

.asm-roster-rrow-role {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--public-muted);
}

.asm-roster-rrow-name {
  font-family: var(--public-font-heading, inherit);
  font-size: clamp(1.05rem, 2vw, 1.4rem);
  font-weight: 800;
  line-height: 1.2;
  color: var(--public-text);
  overflow-wrap: anywhere;
}

.asm-roster-rrow-bio {
  grid-column: 2;
  margin: 0.25rem 0 0;
  max-width: 46ch;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--public-muted);
}

/* The role label sits ABOVE the name on a phone rather than being squeezed into a
   column too narrow to hold "Secretary/Treasurer". */
@media (max-width: 560px) {
  .asm-roster-rrow { grid-template-columns: 1fr; }
  .asm-roster-rrow-bio { grid-column: 1; }
}

/* ── ROSTER · directory-table ───────────────────────────────────────────────
   Also innerHTML-built. Two columns on a hairline grid, sized so a full slate of
   officers and directors stays on one screen. */
.asm-roster-directory {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
  gap: 0 clamp(1.5rem, 4vw, 3rem);
  border-top: 1px solid var(--public-line);
}

.asm-roster-drow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem 0;
  border-bottom: 1px solid var(--public-line-soft, var(--public-line));
}

.asm-roster-drow-name {
  font-weight: 700;
  font-size: 0.98rem;
  color: var(--public-text);
  overflow-wrap: anywhere;
}

.asm-roster-drow-role {
  flex: none;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--public-muted);
  text-align: right;
}
