/* ══ AIKYAM SPACE KIT STYLESHEET ══════════════════════════════════════════
   The block below is inlined verbatim inside every kit's <style>, so each kit
   stays a single self-contained file (the catsofkochi convention — a kit must
   be reviewable from one URL with nothing else fetched but the typeface).

   ⛔ Do not edit a kit's copy. Edit this file, then re-copy the whole block.
   Grep the fence markers to check a kit against this file.

   ⚠️ VERIFIED RENDERED, not read. 9 Aug 2026, in a real 390 x 844 frame and
   again at 1280: Figtree confirmed by fingerprint (1143.93px for
   "Water Metro closes 21:00" at 100px, against 1027.84 for a bogus family),
   zero page overflow, one family, every tap target >= 44px by hit area, text
   column 56 characters at 20px. Four defects were found by rendering it and
   are fixed here — see FIXED notes inline.
   ═══════════════════════════════════════════════════════════════════════ */

/* ══ SYSTEM TOKENS — copied verbatim from tokens/*.css ══ */
:root{color-scheme:light;
--aik-green:#2eac6d;--aik-green-ink:#217d4f;--aik-green-wash:#eaf7f0;
--aik-paper:#fff;--aik-paper-sunk:#fafafa;--aik-ink:#262626;--aik-ink-quiet:#595959;
--aik-rule:#ebebeb;--aik-edge:#8c8c8c;--aik-scrim:rgba(38,38,38,.76);--aik-focus:#0b60c4;
--surface-page:var(--aik-paper);--surface-quiet:var(--aik-paper-sunk);
--surface-way-in:var(--aik-green);--surface-wash:var(--aik-green-wash);
--text-primary:var(--aik-ink);--text-secondary:var(--aik-ink-quiet);
--text-on-way-in:var(--aik-ink);--text-green:var(--aik-green-ink);
--action-primary-bg:var(--aik-ink);--action-primary-text:var(--aik-paper);
--action-way-in-bg:var(--aik-green);--action-way-in-text:var(--aik-ink);
--action-secondary-bg:var(--aik-paper-sunk);--action-secondary-text:var(--aik-ink);
--border-hairline:var(--aik-rule);--border-control:var(--aik-edge);
--focus-ring:var(--aik-focus);--focus-ring-width:5px;--focus-ring-offset:3px;
--font-ui:'Figtree','Figtree Fallback: Roboto',Roboto,'Helvetica Neue',Arial,sans-serif;
--figures-aligned:tabular-nums;
--type-body:clamp(1.1875rem,1.156rem + 0.17vw,1.25rem);--leading-body:1.55;
--type-display:clamp(1.875rem,1.55rem + 1.65vw,2.5rem);
--type-heading:clamp(1.5625rem,1.47rem + 0.5vw,1.75rem);
--type-subheading:clamp(1.3125rem,1.26rem + 0.3vw,1.4375rem);
--leading-display:1.15;--leading-heading:1.2;
--type-ui:1.0625rem;--type-small:1rem;--type-micro:.875rem;
--leading-ui:1.3;--leading-tight:1.375;
--type-figure:clamp(2rem,1.8rem + 1vw,2.5rem);--paragraph-gap:.775em;
--weight-regular:400;--weight-medium:500;--weight-semibold:600;--weight-bold:700;
--tracking-display:-.01em;--tracking-micro:.04em;
--space-1:.25rem;--space-2:.5rem;--space-3:.75rem;--space-4:1rem;--space-5:1.25rem;
--space-6:1.5rem;--space-7:2rem;--space-8:2.5rem;--space-9:3rem;--space-10:4rem;
--gutter:clamp(1rem,.35rem + 2.667vw,1.5rem);
--pitch-rule-heading:2.0625rem;--pitch-row:1.25rem;
--measure:34rem;--measure-wide:62rem;--measure-small:22rem;
--radius-none:0;--radius-card:.5rem;--radius-control:.75rem;--radius-pill:999px;--radius-round:50%;
--portrait-inline:3rem;--portrait-list:4rem;--portrait-profile:6rem;
--rule-hairline:1px;--rule-band:8px;
--grid-gap-col:var(--space-6);--grid-gap-row:var(--space-8);--ratio-card:16 / 9;
--tap:44px;--tap-comfortable:52px;--button-height:3.25rem;}
/* ══ END SYSTEM TOKENS ══ */

/* ⭐ FAIL-LOUD GUARD. Each kit links this file with a RELATIVE path and carries a red
   banner in its own inline <style>. This rule hides the banner. So if ../_kit.css ever
   404s — a moved folder, a bad push, a case-sensitive server — the kit does not quietly
   render unstyled and get reviewed as if it were the design; it says so, in red, at the
   top. A silent failure here would mean a reviewer judging a page that is not the page. */
#css-guard{display:none}

*,*::before,*::after{box-sizing:border-box}
body{margin:0;background:var(--surface-page);color:var(--text-primary);
  font-family:var(--font-ui);font-size:var(--type-body);line-height:var(--leading-body);
  -webkit-font-smoothing:antialiased}
/* ⛔ FIXED by rendering: form controls do NOT inherit font-family from body, so the
   header's Search button rendered in Arial while everything else was Figtree. The
   "one family" check caught it; reading the CSS never would have. */
button,input,select,textarea{font:inherit;color:inherit}
h1,h2,h3{margin:0;font-weight:var(--weight-semibold);line-height:var(--leading-heading)}
h1{font-size:var(--type-display);line-height:var(--leading-display);letter-spacing:var(--tracking-display)}
h2{font-size:var(--type-heading)}
h3{font-size:var(--type-subheading)}
p{margin:0 0 var(--paragraph-gap)}
a{color:inherit}
:focus-visible{outline:var(--focus-ring-width) solid var(--focus-ring);outline-offset:var(--focus-ring-offset)}
.wrap{max-width:calc(var(--measure) + var(--gutter)*2);margin-inline:auto;padding-inline:var(--gutter)}
.wrap--wide{max-width:calc(var(--measure-wide) + var(--gutter)*2)}
/* ⛔ `--measure-wide` (62rem) is a LAYOUT width for grids and rails, never a text column.
   A paragraph placed directly in it ran 136–146 characters a line on the specimen page.
   Prose inside a wide wrapper carries its own measure; only grids get the full width.

   ⛔⛔ REWRITTEN 10 Aug 2026. This was three DEPTH-LIMITED selectors — `> p`, `> section > p`,
   `> section > div > p` — so a paragraph one level deeper than anyone anticipated lost its
   measure silently, and the page still passed every check because nothing measures a line
   that is merely too long on desktop. Depth-limited selectors encode the markup you happened
   to write, not the rule you meant. A blanket descendant cap is safe: grid items are already
   ≤24rem, so capping them at 34rem is a no-op. */
.wrap--wide :is(p,li,dd,blockquote,h1,h2,h3,h4){max-width:var(--measure)}
/* ⛔⛔ A MEASURE BELONGS TO A TYPE SIZE, and this system has said so since the footer fix
   without ever applying it here. Measured 10 Aug 2026: `.session__body` is 16px prose in a
   544px column — `--measure` is sized for 20px body — giving **88 characters a line** at
   6.18px/char. ⭐ It measured 88 BEFORE the wide-wrapper change too, because the cap was
   already 544px: widening the page did not cause this and does not worsen it.
   22rem at 16px is ~57 characters, inside the 45–75 band. */
.wrap--wide .session__body{max-width:var(--measure-small)}
/* ⛔ `h1` was missing from this list on the first attempt and measured **992px wide** in the
   browser. It looked fine only because "Clubs" is one short word — a long title would have
   run the full width with nothing to catch it. Found by measuring, not by reading. */
/* ⭐ …except the things that ARE the structure. A grid's own children must keep the full
   width, or the grid can never form a second column — see `.set` below. */
.wrap--wide :is(.cards,.orgs,.foot-cols,.week,.set,.sessions,.latest,.nav,.doors,.roll,.months,.feed,.sayslist)>li{max-width:none}
/* ⛔⛔ `.roll` was missing and assertion 13 caught it at 1280px — the quiet list's rows measured
   55% of their own container, which is precisely the "narrow item in a wide container" shape the
   assertion exists to find. SECOND time this list has failed by omission, and the second time
   the gate caught my own new component on its first run.
   ⭐ That is the gate working, and it is also the argument for retiring the list: a
   hand-maintained enumeration of structural classes cannot know about a class invented after it
   was written. The durable fix is the `.set` convention in RESPONSIVE.md — structural lists
   declare themselves — and every addition here is evidence for it rather than an alternative. */
/* ⛔ `.sessions` and `.latest` were missing on the first attempt, and capping their rows at
   544px inside a wider container made them 52% of their own list — which is exactly the
   "narrow item in a wide container" shape assertion 13 exists to catch. ⭐ My new assertion
   caught my new defect on its first run, which is the only real proof a gate works.
   ⚠️ This list is hand-maintained and therefore the same species as the depth-limited
   selectors above. It is enumerated from the kits and verified, not guessed — but the durable
   fix is the `.set` convention in RESPONSIVE.md, so structural lists declare themselves. */
/* ⛔⛔ TRIED AND REJECTED, 10 Aug 2026: centring the capped prose inside the wide wrapper
   (`margin-inline:auto`). The numbers were perfect — 583px of gutter each side, symmetric,
   zero overflow — and the RENDERED PAGE was incoherent: `h1` and the pill kept the left edge
   while paragraphs centred, so the page had TWO competing left edges. Caught by looking at a
   screenshot, invisible to every measurement taken. ⭐ The page keeps ONE left edge: prose
   capped and left-aligned, structure taking the extra width beneath it.
   ⚠️ Known and deliberate: the intro region still leaves dead space to the right on a very
   wide screen. Filling it means pairing the intro with the today-status — a design change,
   not part of making the page responsive, so it is proposed rather than done. */
/* ⛔ An unbreakable token — `autocomplete="email"` in a <code> — is 384px wide and cannot
   wrap, so it pushed the page 59px past the viewport at 200% text zoom even after the grid
   guards were fixed. A code span must be allowed to break. */
code,kbd,samp{overflow-wrap:break-word;word-break:break-word}

/* kit chrome — NOT part of the system, present so a reviewer knows what they hold */
.kitbar{background:#1d1d1d;color:#fff;font-size:14px;line-height:1.45;padding:10px 16px}
.kitbar b{color:#8fe0b6}.kitbar a{color:#8fe0b6}
/* ⭐ An annotation ABOUT the mockup, inside the mockup. It must be impossible to mistake
   for page copy, so it labels itself and wears a colour the system does not own. The
   check battery excludes .kitnote for the same reason it excludes .kitbar — neither is
   the design. ⛔ Never put real page copy in one to dodge a check. */
/* ⛔ `max-width` added 10 Aug 2026. Measured at 1280px: every line over 80 characters on every
   kit was a KITNOTE, never page copy — page copy measured 53–61 across all ten. The notes are
   `--type-small` (16px) in a wide wrapper, so they ran 80–87 characters.
   ⭐ Worth fixing precisely because they are not product copy: the owner reads these notes on a
   laptop more than anyone reads the mockup text, so the reviewer surface was the one failing
   our own line-length rule. ⚠️ 22rem at 16px ≈ 57 characters — a measure belongs to a type
   size, and `--measure` is sized for 20px body. */
.kitnote{border:1px dashed #b3261e;border-radius:var(--radius-card);padding:var(--space-4);
  margin-block:var(--space-5);font-size:var(--type-small);line-height:var(--leading-tight);
  color:var(--text-primary);background:var(--surface-page)}
/* ⛔⛔ SEPARATE RULE, and the selector is deliberate. `.kitnote` is a <p>, so the blanket cap
   `.wrap--wide :is(p,…)` — specificity (0,1,1) — BEATS a bare `.kitnote` at (0,1,0), and the
   cap silently resolved to `--measure` instead. Measured: still 85 characters after the fix
   "landed". ⭐ SECOND time this exact collision has happened in this file — `.foot-note` was
   the first, this morning — and both times the broad rule looked narrower than it was.
   `.wrap--wide .kitnote` is (0,2,0) and wins. ⚠️ Any future per-component measure override
   needs the `.wrap--wide` prefix for the same reason. */
.wrap--wide .kitnote{max-width:calc(var(--measure-small) + var(--space-4)*2)}

/* ══ ACTIVITIES ═══════════════════════════════════════════════════════════════════════════
   ⛔⛔ MOVED HERE FROM THE PAGE'S OWN <style> BLOCK, 10 Aug 2026, and the reason is the
   important part. While the component lived inside `activities/index.html` the page had opted
   out of the shared system: the responsive rules, the measure caps, the kitnote cap and the
   `li` exception list all live in this file and none of them reached it.
   ⭐⭐ Worse, and nobody spotted it for an hour: ASSERTION 13 COULD NEVER HAVE CAUGHT ANYTHING
   THERE, because the check knows classes and the page's layout was built from classes the
   system had never seen. **A page outside the system passes the system's checks by being
   invisible to them** — the "gate that cannot fail" pattern arriving through a new door.
   A clean measurement would have been meaningless and would have read as a pass. */

/* ⭐ MONTH SELECTOR, not a filter. Nobody browsing a record wants to filter it; they want to
   jump to a month. It is also what keeps a long feed navigable, and the months carry real
   search traffic — May pulls 282 six-month visitors. */
.months{display:flex;flex-wrap:wrap;gap:var(--space-2);list-style:none;margin:var(--space-5) 0 0;padding:0}
.months a{display:inline-flex;align-items:center;min-height:var(--tap-comfortable);
  padding-inline:var(--space-4);border:var(--rule-hairline) solid var(--border-hairline);
  border-radius:var(--radius-pill);text-decoration:none;font-size:var(--type-ui);
  font-weight:var(--weight-medium);font-variant-numeric:var(--figures-aligned)}
.months a[aria-current="page"]{background:var(--surface-way-in);color:var(--text-on-way-in);
  border-color:transparent;font-weight:var(--weight-semibold)}

/* ⭐ THE UNIT IS AN ACTIVITY, NOT A DAY — owner, 10 Aug 2026: several can share a date. So the
   date is a quiet GROUP LABEL and each activity keeps its own headline beneath it.
   ⛔ The date is never a headline: measured on Airbnb, a dated record puts the person at
   16/20/500 and the date at 14px grey. Here the headline is WHAT HAPPENED. ⭐ Multiple entries
   per date makes that load-bearing rather than merely better — "Saturday 8 August" cannot
   headline three different things. And a group label states the date ONCE instead of repeating
   it under every entry, which would be the count-in-prose problem in another costume. */
.dayband{font-size:var(--type-small);color:var(--text-secondary);font-weight:var(--weight-semibold);
  font-variant-numeric:var(--figures-aligned);margin:var(--space-7) 0 var(--space-3);
  padding-block-start:var(--space-5);border-block-start:var(--rule-hairline) solid var(--border-hairline)}
.dayband:first-of-type{margin-block-start:var(--space-5);padding-block-start:0;border-block-start:0}

/* ⭐ ONE FORM, not two — owner cut future entries entirely, so every activity is a record.
   ⛔ The photograph is still not guaranteed: measured in the real corpus, the 1 August rain
   closure has none and never can. So the rule stands and now covers the ONLY exception rather
   than one of two systematic states: an entry with a photograph is a card; an entry without
   one has no container at all. NO SLOT WITHOUT A GUARANTEED FILLER. */
/* ⛔ `.act__label` LIVED HERE FOR FOUR MINUTES AND IS DELETED, not commented out. A club
   kicker was added above every headline to answer "moved in where? which dance?", and the
   owner then ruled it out — categories will come later as Ghost tags, on the entry page only.
   ⭐ The completeness it was carrying moved into the headlines and stayed there, which is why
   removing it cost nothing: "The cats have moved INTO THE GARDEN", "THE STITCHING TEAM'S bags
   went on sale AT LOCAL SUSTAINABLE LIVING".
   ⚠️ Two headlines had quietly started depending on it and read as fragments once it was
   gone — that is the real cost of a decorative element, and the dependency only shows when it
   is taken away. ⛔ Dead CSS kept "in case" is how a system accumulates rules nobody can
   explain; it is in the history if anyone wants it back. */
/* ── THE HOMEPAGE LEAD AND GRID ───────────────────────────────────────────────────────────
   ⭐⭐ "Featured activities" is the owner's phrase and the design answer is that NOTHING IS
   CURATED. A homepage needing weekly curation decays the moment somebody is busy — the same
   failure as the quiet state, and this project has already solved it once by refusing to
   depend on an action.
   ⭐ So featuring is DERIVED: an activity with a photograph leads, an activity without one is
   a record line. Somebody already made that judgement when they decided the thing was worth
   photographing — 69 of 105 days had one — and it costs nobody a weekly decision. */
.lead__shot{width:100%;aspect-ratio:16/9;object-fit:cover;border-radius:var(--radius-card);
  display:block;margin:0 0 var(--space-4);background:var(--surface-quiet)}
.lead h2{font-size:var(--type-display);line-height:var(--leading-display);margin:0}
.acts{list-style:none;margin:var(--space-6) 0 0;padding:0;display:grid;
  gap:var(--grid-gap-row) var(--grid-gap-col);
  grid-template-columns:repeat(auto-fill,minmax(min(20.5rem,100%),24rem))}
/* ⛔⛔ EQUAL HEIGHTS, 10 Aug 2026 — owner: "the card size and layouts need to be fixed, right
   now they are all different sizes." He is right, and the cause was my own no-slot rule: a
   photo-less entry was simply a shorter card, which reads as deliberate in a note and as
   BROKEN in a grid. ⭐ Worse with real data, where photo-less entries arrive in RUNS — a block
   of short cards after a block of tall ones, not an occasional gap.

   ⭐⭐ The resolution, and it costs nothing the system refused: an entry WITHOUT a photograph
   uses the image area FOR ITS WORDS. Same box, headline set larger, filling the space the
   photograph would have occupied. ⛔ No invented image, no placeholder, no truncated body —
   and the card never rendered a body anyway, so body length was never the cause.
   ⭐ It also makes a run of photo-less entries read as deliberate variety rather than damage. */
/* ⛔⛔ THE ACTUAL CAUSE OF "the cards are all different sizes", and it was mine. Every photo
   was rendering at 1200px tall instead of 288px, making photo cards 1300px against 88px for a
   photo-less one — a 15× difference the owner correctly read as broken layout.
   ⭐ `aspect-ratio` was computing as `4 / 3` and being IGNORED, because the `height="1200"`
   attribute — which I added three pages ago to stop the feed reflowing as images arrive —
   becomes a presentational `height: 1200px`, and aspect-ratio only sizes the box when the
   other axis is `auto`.
   ⛔⛔ So the fix for one real defect silently created a worse one, and BOTH measurements
   looked right: `getComputedStyle` reported `aspect-ratio: 4 / 3` and `object-fit: cover`.
   ⭐ Only the rendered height showed it. Keep the attributes — they still prevent reflow — and
   set `height:auto` so the CSS wins. */
.acts__shot,.act__shot,.day__shot,.entry__shot,.lead__shot,.preview img{height:auto}
/* ⛔⛔ `grid-auto-rows:1fr` WAS TRIED AND REJECTED, and the reason is a measurement of mine
   that lied. It equalised every card — tallest 388, shortest 388 — and I computed "only 8px of
   slack", which made it look free. ⛔ The 8px was the difference between the CARD and its
   text ELEMENTS, and the h3 was itself `flex:1` — so I was measuring a box that had already
   absorbed the empty space, not the ink inside it.
   ⭐ The screenshot settled it in one look: the photo-less cards read as large empty boxes with
   a headline adrift in the middle. **Equal heights are geometrically achievable and look
   worse than the problem.**
   ⭐⭐ And the real conclusion is bigger: a photo-less entry in a MULTI-COLUMN grid always
   looks like a hole, because it has side-by-side neighbours to be unequal with. The owner's
   preferred `/activities/` layout is a SINGLE COLUMN, where the question cannot arise —
   adopting it IS the fix rather than a workaround. */
/* ⭐⭐ THE GRID IS BACK, and the reason it was dropped turns out to be a bug rather than a
   design finding. Sequence: he said "the cards are all different sizes" → I found
   `height="1200"` defeating `aspect-ratio`, photo cards 1300px against 88px → we concluded a
   photo-less entry reads as a hole beside its neighbours and went single-column → I fixed the
   bug. ⛔⛔ NOBODY RE-TESTED THE GRID AFTER THE FIX. Post-fix the spread is 3×, not 15×.
   ⚠️ And the single column cost 70% of a 2000px screen.
   ⭐ What makes the grid work now is the excerpt: a photo-less entry has a real paragraph where
   the photograph would have been, so it fills its cell because it IS full — the same finding
   that retired the typographic-headline hack.
   ⛔ Intrinsic columns, not breakpoints: one on a phone, three at desktop, from one rule. */
.acts{align-items:stretch;
  grid-template-columns:repeat(auto-fill,minmax(min(19rem,100%),1fr))}
.acts li{margin:0;display:flex;flex-direction:column}
.acts__shot{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--radius-card);
  display:block;margin:0 0 var(--space-3);background:var(--surface-quiet)}
.acts h3{font-size:var(--type-subheading);font-weight:var(--weight-semibold);margin:0}
.acts h3 a{text-decoration:underline;text-decoration-color:var(--border-hairline);
  text-decoration-thickness:1px;text-underline-offset:3px}
/* ⭐ The typographic card: no image, so the headline takes the space. `flex:1` makes it fill
   whatever the row's tallest card set, and `align-content:center` keeps short headlines from
   stranding at the top of a tall box. */
.acts li[data-no-photo] h3{font-size:var(--type-heading);line-height:var(--leading-tight);
  flex:1;display:flex;align-items:center;
  border-block-start:var(--rule-hairline) solid var(--border-hairline);
  padding-block-start:var(--space-4)}
/* ⛔ The date is pushed to the bottom of every card so the baselines agree across a row,
   whichever form the card takes. */
.acts__when{font-size:var(--type-small);color:var(--text-secondary);
  margin:var(--space-2) 0 0;font-variant-numeric:var(--figures-aligned)}
/* ⭐ The excerpt fills a photo-less cell, and the date pins to the bottom of every cell so the
   baselines agree across a row whichever form the cell takes. ⛔ No empty image well anywhere:
   an entry without a photograph simply starts with its headline. */
.acts__body{margin:var(--space-2) 0 0;font-size:var(--type-small);
  color:var(--text-secondary);line-height:var(--leading-tight)}
.acts li .acts__when{margin-block-start:auto;padding-block-start:var(--space-3)}
/* ⭐⭐ SAYS — a quotation from someone who was here. Airbnb's review treatment, re-measured live
   10 Aug on listing 23572181: border 0, radius 0, background transparent, NO shadow, PADDING
   ONLY. Text 16/400 #222222 at line-height 24 in a 450px column inside a 1120px section — i.e.
   they constrain the quotation's measure independently of its container, which is what
   `--measure-small` does here.
   ⛔ NO quotation marks drawn in CSS, no oversized curly glyph, no card. A quotation that needs
   decoration to read as a quotation is not being trusted to be one.
   ⛔⛔ THE ATTRIBUTION IS OPTIONAL BY DESIGN, and this is the load-bearing property. Several of
   the best lines here are unattributed in the source — "shared one of the students" — and
   consent to appear is NOT consent to be named. A card with an author slot would sit there
   empty and invite someone to fill it; this component simply has no slot when there is no name.
   ⛔ Never attach a photograph to an unattributed quotation. */
/* ⛔⛔ MEASURE CORRECTED 11 Aug 2026, AND IT WAS A PIXEL WIDTH COPIED ACROSS A TYPE-SIZE
   CHANGE. This capped at `--measure-small` (22rem) because Airbnb's review column measures
   450px. But Airbnb's review text is 16px and ours is 20px — ⭐ the invariant is the CHARACTER
   COUNT, not the pixel width. Theirs is ~56 characters; ours, COUNTED with a Range on the
   rendered page, was 36 on the entry and 38–41 on `/clubs/` — under the ~35 confetti floor this
   system already scarred itself on, on the two strongest sentences anybody has written about
   this room. At `--measure` the same three quotations count 53, 61 and 62. Zero overflow at
   390 and 1280, checked on both pages.
   ⚠️ `--measure-small` is this system's 16px column (~57 chars) and is correct where it is
   used for 16px — `.session__body`, `.foot-note`. Using it for 20px text was the mismatch.
   ⚠️ Consequence on `/clubs/`, looked at and accepted: the quotation (544px, 20px) is now wider
   than the club's own description (352px, 16px). That is the right way round — the description
   is secondary metadata and the quotation is evidence. See [[a-number-copied-from-another-project]]. */
.says{margin:var(--space-6) 0 0;max-width:var(--measure)}
.says p{margin:0;font-size:var(--type-body);line-height:var(--leading-body)}
.says cite{display:block;margin-block-start:var(--space-3);font-style:normal;
  font-size:var(--type-small);color:var(--text-secondary)}
.wrap--wide .says{max-width:var(--measure)}
/* ⛔⛔ A STACK OF QUOTATIONS RUNS TOGETHER, and only the rendered page showed it. Four in a
   column, each the same width with a grey name between them, and the name reads ambiguously —
   is it the end of the quote above or the start of the one below? Every measurement was clean.
   ⭐ The fix is a RATIO, not a bigger number: the gap BETWEEN quotations must be visibly larger
   than the gap between a quotation and its own name. Airbnb spaces their reviews 48px apart
   while the attribution sits tight under its text — measured on listing 23572181.
   ⭐⭐ And two columns, because that is what Airbnb actually does: their review section is a
   1120px band holding 450px text columns, i.e. two. Ours was one 352px column in the left third
   of a 1540px screen on a site that is 62% desktop. ⛔ Intrinsic, not a breakpoint — one column
   on a phone, two when there is room, from one rule. */
.sayslist{list-style:none;margin:var(--space-6) 0 0;padding:0;display:grid;
  gap:var(--space-7) var(--space-6);
  grid-template-columns:repeat(auto-fill,minmax(min(22rem,100%),1fr))}
.sayslist>li{margin:0}
.sayslist .says{margin:0;max-width:none}
/* ⭐ Beside a club, the quotation belongs to THAT club — so it sits inside the session, not in a
   strip of its own. A testimonial section is a page about praise; a line under the club is
   evidence about the club. */
.session .says{margin-block-start:var(--space-4);
  border-block-start:var(--rule-hairline) solid var(--border-hairline);
  padding-block-start:var(--space-4)}
/* ⭐ The open-today line is ONE SENTENCE, not a section. "Ignore other bits" was about
   emphasis, not deletion — a neighbour still comes for this, and /clubs/ is the second-busiest
   page on the site. It gets a line at the top and a link, not a panel. */
.todayline{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--space-2) var(--space-3);
  margin:var(--space-5) 0 0;font-size:var(--type-ui)}
.todayline b{font-weight:var(--weight-semibold)}
.act{margin:0 0 var(--space-6)}
.act__shot{width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--radius-card);
  display:block;margin:0 0 var(--space-4);background:var(--surface-quiet)}
.act__what{font-size:var(--type-subheading);font-weight:var(--weight-semibold);margin:0}
/* ⛔ NO "READ MORE", and the reason is a ruling he already made. The card is already a link, so
   "read more" is a SECOND affordance for the same action — and he objected to whole sections
   being clickable, which produced the "grow the link, do not extend it over things that are
   not the link" fix.
   ⭐ So: the PHOTOGRAPH and the HEADLINE are both links to the same entry, and nothing else is.
   That is Airbnb's own pattern — image and title clickable, body not — and it honours his
   objection exactly: the excerpt stays selectable, because nothing is laid over it.
   ⚠️ The cost, stated: a reader who wants the rest must aim at the headline or the picture
   rather than anywhere in the block. ⭐ With the headline padded to 44px that is a comfortable
   target, and the photograph is a large one. */
.act__what a{text-decoration:underline;text-decoration-color:var(--border-hairline);
  text-decoration-thickness:1px;text-underline-offset:3px;
  display:inline-block;min-height:var(--tap-min,44px);
  padding-block:calc((var(--tap-min,44px) - 1lh) / 2)}
/* ⛔⛔ THE PHOTO IS CAPPED AT `--measure`, NOT THE WRAPPER WIDTH — measured 10 Aug at 1280px.
   In a single column the image takes the full 992px container, which at 4:3 is **744px tall**:
   one entry fills a laptop screen, and the whole twelve-entry feed ran **7.5 screens**.
   ⭐ Capped at 544px the photo is 408px tall, the card 663px, and the feed 5.6 screens — a 25%
   reduction with nothing removed.
   ⭐⭐ And it is the same rule as the prose: an image column and a text column belong to the
   same reading measure. A photograph twice as wide as the sentence under it is not a lead, it
   is an interruption. ⚠️ The phone is unaffected — 358px there, viewport-bound. */
/* ⭐⭐ HORIZONTAL CARD — owner, 10 Aug: "the cards are too big! we should use a grid, 3 per
   row?" He is right about the size and wrong about the remedy, and both halves matter.
   ⛔ A 3-across grid reinstates exactly what the single column solved: a photo-less entry
   beside a photographed one reads as a hole, and they arrive in RUNS of four. He is reacting
   to SIZE, not to columns.
   ⭐⭐ So the card turns on its side instead: small image left, words right. Entry height drops
   from ~600px to ~140px — the quarter he actually asked for — AND the hole problem stays
   solved, because an entry with no photograph uses the full width for its text. There is no
   slot beside it to be empty. Same property as the single column, at a quarter the size.
   ⭐ The thumbnail is `clamp()`, not a breakpoint: ~100px on a phone, ~152px at desktop. The
   last media query in this file was removed deliberately and this does not bring one back. */
a.act__shotlink{display:block;border-radius:var(--radius-card);overflow:hidden;
  flex:none;width:clamp(96px, 28%, 168px)}
/* ⛔⛔ THE THUMBNAIL CLAMP LEAKED INTO THE GRID, and only a screenshot caught it. Every
   measurement was clean — 3 cards per row, identical heights within each row, zero overflow —
   and the photographs rendered at **96×72px postage stamps** in a 315px cell, because the
   horizontal card's `clamp(96px,28%,168px)` matched `.acts a.act__shotlink` too.
   ⭐ FOURTH time today that looking beat measuring, and the sharpest: the numbers were not
   merely silent, they were REASSURING. A grid of tiny images passes every geometric check
   ever written for it.
   ⛔ A component-scoped rule needs a component-scoped selector — `a.act__shotlink` names the
   element, not the context, so it applied wherever the element appeared. */
.acts a.act__shotlink{width:100%;max-width:none}
.act:has(a.act__shotlink){display:flex;gap:var(--space-4);align-items:flex-start}
.act:has(a.act__shotlink) .act__text{flex:1;min-width:0}
.act__shot{width:100%}
/* ⛔⛔ ONE LEFT EDGE — and this is a DECLARED media query, the only one in the file, added
   deliberately after the last one was removed deliberately.
   Measured at 1280 without it: TWO left edges, 144px for a photo-less entry and 328px for a
   photographed one, so the column zigzags. ⭐ That is the same defect found on `/about/` this
   morning, and the same fix: the page keeps one left edge.
   ⚠️ But indenting costs real width, and on a phone it costs too much — measured at 390 the
   feed grows 4,113 → 4,805px (4.6 → 5.3 screens) and the text column narrows to 242px.
   ⛔ So the indent applies only where there is room. A breakpoint is the honest mechanism here
   because the answer genuinely differs by available width rather than reflowing — the same
   test the week grid was held to, and the reason it failed it. */
@media (min-width: 48rem){
  .act:not(:has(a.act__shotlink)){
    padding-inline-start:calc(clamp(96px, 28%, 168px) + var(--space-4))}
}
.act__meta{font-size:var(--type-small);color:var(--text-secondary);margin:var(--space-2) 0 0;
  font-variant-numeric:var(--figures-aligned)}
/* ⭐⭐ THE EXCERPT IS WHAT SOLVES THE EMPTY CARD, and the real numbers made it free. Measured
   on the live 195: shortest body 82 characters, MEDIAN 227, longest 531. Cut at 300 and
   **83% of entries show in full** — the card simply ends. Only the longest sixth is trimmed,
   which is exactly when "read more" is informative rather than decorative.
   ⛔ So the trade I flagged — "equal heights cost you the excerpt" — DOES NOT EXIST at these
   lengths. My figure of "60 to 3,400 characters" came from the day-level extraction and died
   with it; per-activity bodies are a fifth of that.
   ⭐ And it retires the typographic-headline hack: a photo-less entry now has a real paragraph
   where the photograph would have been, so it looks deliberate because it IS full. */
/* ⛔⛔ THE CARD READ FLAT BECAUSE OF THIS ONE LINE. Measured 10 Aug: `.act__body` was 20px /
   400 / #262626 — **the same colour as the headline and only 3px smaller**, so the excerpt
   competed with the headline instead of receding.
   ⭐ Measured on the page the owner linked, Airbnb's card carries exactly THREE treatments:
   title 15/500 #222222, secondary 15/400 #6C6C6C, price 15/500 #222222. ⭐⭐ **One type size.
   Two weights. Two colours. The hierarchy is entirely weight and colour — they do not vary
   size at all**, and three consecutive lines share one treatment.
   ⛔ So the answer to "is it flat because the two are too similar, or because the grey is not
   grey enough" is BOTH, in one element. The excerpt now takes the secondary treatment and
   shares it with the meta line — which is the pattern, not a compromise. */
.act__body{margin:var(--space-3) 0 0;max-width:var(--measure-small);
  font-size:var(--type-small);color:var(--text-secondary);line-height:var(--leading-tight)}
.feed{list-style:none;margin:0;padding:0}

/* ── THE SINGLE ENTRY ─────────────────────────────────────────────────────────────────────
   ⭐⭐ This is the ARRIVAL case and it is bigger than the feed: direct traffic is 60.8% of this
   site, and the owner's words are "activity entries are very shareable". Most readers reach an
   entry from a message, not by browsing. So it is designed for the moment a link lands in a
   WhatsApp thread — which makes the preview card the first impression and the page the second. */
/* ⛔⛔ THE ENTRY PHOTOGRAPH IS BOUNDED BY HEIGHT, NOT CROPPED TO A RATIO — and the screenshot is
   the only thing that found this. Measured 10 Aug on the served page: `aspect-ratio:4/3` +
   `object-fit:cover` at container width rendered **992×744**, so the ENTIRE first screen of a
   1280×871 laptop was one photograph — h1 at y=947, below the fold — and the 960×1280 PORTRAIT
   source was cropped to a strip and upscaled past its own pixel width. **The arrival page for
   60.8%-direct traffic opened on a blurry cat and no words.**
   ⭐⭐ Airbnb, measured live the same hour: their whole hero gallery is **483px tall in a 1120px
   column** and the **title sits ABOVE it** (TITLE top=96, HERO top=150). They never let one image
   be 4:3 at full column width. **The defect was never the photo's WIDTH — 992/544 is a 1.8×
   ratio and Airbnb runs 1120/653, the same. It was its HEIGHT.**
   ⛔ So no forced ratio here: a record's photograph is EVIDENCE, and cropping a portrait phone
   snapshot to a landscape strip removes the thing it is evidence of. Cap the height, let the
   image keep its own shape, and never scale it past its intrinsic width. */
.entry__shot{max-width:100%;width:auto;max-height:32rem;
  border-radius:var(--radius-card);
  display:block;margin:var(--space-5) 0;background:var(--surface-quiet)}
.entry__meta{font-size:var(--type-small);color:var(--text-secondary);margin:var(--space-3) 0 0;
  font-variant-numeric:var(--figures-aligned)}
.entry__body{margin:var(--space-5) 0 0;font-size:var(--type-body)}
/* ⛔⛔ `.entry__host` (the inline "With the Olimalar Foundation" line) IS DELETED, not commented
   out — owner, 11 Aug 2026: the credit takes the host-CARD treatment in a side column, "not the
   inline sentence". Keeping both would say the same thing twice on one page, and dead CSS kept
   "in case" is how this system accumulates rules nobody can explain. It is in the history.

   ══ THE ENTRY'S SIDE COLUMN ═══════════════════════════════════════════════════════════════
   ⛔⛔ MEASURED ON A REAL AIRBNB LISTING FIRST (rooms/23572181, 1280px, 11 Aug 2026) AND THE
   BRIEF'S PREMISE DOES NOT HOLD. Every `[data-section-id]` box, in page order:
     content column x=80 w=653 · right rail x=827 w=372
     the rail holds `BOOK_IT_SIDEBAR` (h=272) and a `REPORT_TO_AIRBNB` link. NOTHING ELSE.
     `HOST_OVERVIEW_DEFAULT`  x=80  w=653  y=763   — inline in the BODY column, NO avatar at all
     `REVIEWS_DEFAULT`        x=80  w=1120 y=3352  — FULL WIDTH
     `MEET_YOUR_HOST`         x=80  w=1120 y=5694  — FULL WIDTH, 2,342px BELOW the reviews
   ⭐ So Airbnb has TWO host blocks and neither is in a rail: a text-only "Hosted by Gigi ·
   Superhost · 13 years hosting" row inline in the body, and a full-width "Meet your host"
   section at the very bottom holding a 395x265 card. **The right rail is the booking form.**
   ⭐⭐ WE BUILD THE SIDE COLUMN ANYWAY, and the reason is that the divergence is in our favour:
   Airbnb's rail is occupied because they have something to sell. **We have no reserve form, so
   our rail is free** — the owner's layout is available to us precisely because we do not have
   Airbnb's constraint. ⛔ It is NOT "Airbnb-faithful" and must not be described as such.

   ⛔ INTRINSIC, NOT A BREAKPOINT. `flex-wrap` + a `min(…,100%)` basis: at 992px the two sit side
   by side (main 640 / side 320); when 34rem + 18rem + the gap no longer fit, the side wraps
   under the body and grows to the full line. No `@media`, so the file still carries exactly one. */
/* ⛔⛔ `padding-block-start` ADDED 11 Aug — owner: "no padding between the nav menu and the
   content". Measured against the nav's bottom edge: this page's h1 ink began at **-1px** and the
   host card's top BORDER at **exactly 0**, drawing a line hard against the nav's own border.
   ⭐ PRE-EXISTING, not caused by the side column — every other page opens with `.lede`, whose
   `padding-block: var(--space-8)` puts its first ink at **69px**; this page has no `.lede` and
   never had the gap. The side column only made it visible, because a bordered box in the gap
   reads as broken where bare text read as merely tight.
   ⚠️ The durable fix is a page-level rule rather than one on this component — the other four
   pages get their 40px by accident of carrying a `.lede`, which is spacing riding on a semantic
   class. Scoped here because widening it would re-space four reviewed pages. */
/* ⭐⭐ THE HEADLINE AND DATE MOVED OUT OF THE COLUMN — owner 11 Aug: the host card's top edge
   should line up with the PHOTOGRAPH's top edge, not the page's.
   ⛔ NOT done with a magic margin. Pushing the card down by a fixed number would be wrong the
   moment the headline wraps to one line or three — and it wraps to two at 1280 and three at 390
   on this very entry. ⭐ Instead the headline and date sit ABOVE the two columns, so the grid's
   first row is photo-left / card-right and they start together BY CONSTRUCTION, at any headline
   length and any width.
   ⭐ It is also what Airbnb does: measured, `TITLE_DEFAULT` is x=80 w=1120 — full width above the
   gallery — and the reading measure still caps the headline at 34rem, so it does not run wild.
   ⚠️ The nav-gap fix moved WITH it. `--space-8` now sits on `.entryhead`, which is the new first
   thing on the page; `.entrygrid` keeps a smaller `--space-5` as the gap between the date line
   and the photo. Checked: first ink still lands 40px below the nav. */
.entryhead{padding-block-start:var(--space-8)}
.entrygrid{display:flex;flex-wrap:wrap;gap:var(--space-8) var(--space-7);align-items:flex-start;
  padding-block-start:var(--space-5)}
/* ⛔ The photograph's own top margin is zeroed INSIDE the grid, because it is now the first thing
   in its column. Left in place it would push the photo 20px below the card and defeat the whole
   alignment. ⚠️ Scoped to `.entrygrid` so the no-photo exemplar further down the page, which
   still uses `.entry__shot` in normal flow, keeps its spacing. */
.entrygrid .entry__shot{margin-block-start:0}
.entrygrid__main{flex:3 1 min(34rem,100%);min-width:0}
/* ⛔ 20rem, not 18. At an 18rem basis the side rendered 320px and the quotation inside it
   counted 32 CHARACTERS A LINE — under the ~35 confetti floor, the same defect I had just
   fixed on the wide column, reintroduced by putting the component somewhere narrower.
   ⭐ At 20rem the side is 344 and the card's own text 302: 41 and 42 characters. Narrow, and
   above the floor. A secondary column cannot reach 45–75 without taking width from the story. */
.entrygrid__side{flex:1 1 min(20rem,100%);min-width:0}
/* ⛔⛔ AND THE QUOTATION STEPS DOWN TO 16px IN THIS COLUMN, which is not a demotion. At
   `--type-body` (20px) in a 344px column it counted 32; at 16px it counts 42. ⭐ The side column
   is a 16px zone — the host card's own description is 16px — so the quotation matches its
   NEIGHBOURS rather than shrinking below them. ⚠️ Watch this if the column ever widens: the
   payload must never end up set as the footnote. */
.entrygrid__side .says p{font-size:var(--type-small)}

/* ⭐ THE HOST CARD. Airbnb's own is 395x265, `radius:24px`, white, BOX-SHADOW, no border — and
   the shadow is not imported. This system has no shadow anywhere, and `.orgs li` already solves
   "an organisation in a box" with a hairline + `--radius-card`. ⭐ Reuse beats a new primitive.
   ⛔ NO STAT ROW, and that is the ruling plus the data agreeing for once. Airbnb stacks
   `315 / Reviews`, `4.87 / Rating`, `13 / Years hosting` at 22px/600 over 10px/500 — measured,
   and ⚠️ NOT in caps, `text-transform:none`, which the brief had wrong. We have no verified
   counts for any partner, and "keep the page, drop the count" already forbids a session tally.
   An empty stat row is the no-slot-without-a-filler rule broken in public. */
/* ⛔⛔ OWNER OVERRIDE 11 Aug — THE WHOLE CARD CENTRES AS ONE COLUMN, and this deliberately goes
   past the reference. Five changes marked on the card: avatar centred · name centred AND stepped
   down to the link's size · "Ran this session" struck out entirely · description centred · link
   centred.
   ⚠️ THE DIVERGENCE, NAMED: on Airbnb the host's NAME is a prominent heading, and every piece of
   prose is LEFT-aligned and lives OUTSIDE the card (measured below — that finding stands as a
   fact about their page). Ours is now a quiet, fully centred widget instead.
   ⭐ It is a legitimate call rather than a mistake: their card is a booking-adjacent block with a
   STATS COLUMN, and ours has no stats column since the count was dropped this morning — so a
   single centred stack is the honest shape for what is actually left in the box. A two-column
   rule copied onto a one-column card would be cargo.
   ⚠️ THE COST, STATED ONCE AND NOT RELITIGATED: centred prose is harder to read than
   left-aligned — both edges rag, so the eye loses the line start on every return sweep. The
   description is three to four lines at 41–49 characters. Raised before the ruling; his call. */
.hostcard{border:var(--rule-hairline) solid var(--border-hairline);
  border-radius:var(--radius-card);padding:var(--space-5);text-align:center}
/* ⭐ Circular, and it IS Airbnb-faithful: their radius ladder is role-based — a full-bleed photo
   0, a photo inside a card takes the card's corner, an avatar 50%. Theirs is 104px; 88 matches
   `.person__face` on `/team/` so one face size serves the whole system.
   ⛔ A HAIRLINE ON THE DISC, because this asset is a LOGO on a white ground, not a portrait —
   without an edge the circle is invisible against paper and the mark appears to float. */
.hostcard__face{width:88px;height:88px;border-radius:var(--radius-round);object-fit:cover;
  display:block;margin:0 auto var(--space-4);background:var(--surface-page);
  border:var(--rule-hairline) solid var(--border-hairline)}
/* ⭐⭐ THE IDENTITY UNIT CENTRES AND THE PROSE DOES NOT — measured 11 Aug on the real card, and
   it is sharper than "the card is centred". Owner said the avatar and name are centred and ours
   were left-aligned; he is right, but centring the whole card would have been wrong.
   Airbnb's 395x265 card (`padding:32px 24px`) is TWO COLUMNS:
     • left column **227x201, `display:flex`, `align-items:center`, `justify-content:center`** —
       avatar 104x104 sits at x=86 with 62px of column to its left and 61px to its right, so it
       is **centred in ITS COLUMN, not in the card** (206px of card sits to its right).
       The "Superhost" badge under it computes **`text-align:center`**.
     • right column at x=275 — the stats, and they compute **`text-align:start`. LEFT, not
       centred.**
   ⛔⛔ AND EVERY PIECE OF PROSE IS LEFT-ALIGNED AND LIVES OUTSIDE THE CARD ENTIRELY —
   "Speaks English and Hindi", "My work: …", "Response rate: 100%", "Responds within an hour",
   all `text-align:start`, all outside the 395x265 box.
   ⭐ So the rule that transfers is: **the identity unit (face, name, role) centres; anything you
   READ stays left.** Centring our three-line description would be a legibility loss and would
   contradict their own card, which mixes alignment inside one box on purpose.
   ⚠️ We keep the description INSIDE the card where Airbnb puts it outside — a 344px sidebar has
   nowhere else for it — so the alignment rule is honoured and the placement is not. */
/* ⛔ `.hostcard__id` (the centred-identity wrapper) and `.hostcard__role` (the "Ran this session"
   line) are BOTH DELETED, not disabled. The card centres as a whole now, so a wrapper whose only
   job was to centre part of it has nothing left to do; and the role line is struck from the
   design, so the rule that styled it has nothing to style. Dead CSS kept "in case" is how this
   system accumulates rules nobody can explain — both are in the history.
   ⚠️ Losing `.hostcard__role` also retires the specificity fix that had just been made to it
   (bare `.hostcard__role` at (0,1,0) was losing to `.hostcard p` at (0,1,1), rendering 12px where
   it asked for 8px). ⭐ The bug was real and is now moot — worth recording rather than quietly
   dropping, because the same collision will bite the next single-class rule added to this card. */
/* ⛔ NAME STEPPED DOWN to `--type-small`, matching `.hostcard__more` — owner, marked with a
   ruler. It keeps `--weight-semibold` from the base `h1,h2,h3` rule, so at one size the weight is
   the only thing separating the card's title from its link. ⚠️ That is the whole hierarchy of
   this card now: one size, two weights, two colours — which is the Airbnb card grammar even
   though the alignment is not. */
.hostcard h2{font-size:var(--type-small);margin:0}
.hostcard p{margin:var(--space-3) 0 0;font-size:var(--type-small);
  color:var(--text-secondary);line-height:var(--leading-tight)}
/* ⛔ INK, and it was found by looking. The link sits inside `.hostcard p`, so `a{color:inherit}`
   handed it `--text-secondary` and the one actionable line in the card rendered the same grey as
   the description around it. ⭐ The card's darkest text should be the thing you can do. */
/* ⛔ `.hostcard__more` IS DELETED — owner 11 Aug: drop "More about them", the NAME becomes the
   link. ⭐ One less line for the same destination, and it is the better shape: the card's title
   and its only action were two separate things saying the same thing, which is the "read more"
   objection from `/activities/` arriving in a different costume.
   ⚠️ THE TAP TARGET IS THE THING TO WATCH, because the name is now the SMALLEST text on the card
   (stepped to `--type-small` last round). At 16px with `--leading-heading` 1.2 the line box is
   ~19px, so the padding does the rest: (44 − 1lh) / 2 each side reaches 44. ⭐ The negative
   inline margin is symmetric, so it survives the card's centring without shifting off-axis.
   ⛔ Keeps `--weight-semibold` from the base `h1,h2,h3` rule, so the name still reads as the
   card's title and not as a stray link. */
.hostcard h2 a{display:inline-block;color:var(--text-primary);text-decoration:underline;
  text-decoration-color:var(--border-hairline);text-decoration-thickness:1px;
  text-underline-offset:3px;min-height:var(--tap-min,44px);
  padding-block:calc((var(--tap-min,44px) - 1lh) / 2);
  padding-inline:var(--space-2);margin-inline:calc(var(--space-2) * -1)}

/* ⭐⭐ IDENTITY BEFORE THE TEXT — and this was VERIFIED, not accepted. Airbnb's review card,
   measured: avatar 48px 50% → name 16/20/500 → subline 14/18/400 → stars+date → body 16/24/400.
   The name is ABOVE the words. This file's own `.says` note quoted their body treatment and
   silently inverted their ORDER.
   ⛔⛔ IT APPLIES ONLY WHERE THERE IS AN IDENTITY. `/clubs/`'s quotations are unattributed by
   design ("one of the students") — consent to appear is not consent to be named — so there is
   nothing to lead with and the quotation simply starts. That is the same rule with an absent
   field, not an inconsistency, and it is why this is a MODIFIER rather than a change to `.says`.
   ⛔ `.says.says--named` is (0,2,0) to beat `.says cite` at (0,1,1) on the margin flip. */
/* ⭐ INK AND MEDIUM, not grey — Airbnb's reviewer NAME is 16/20/500 in ink; the grey 14/400 line
   under it is the tenure subline, a different field. Our `.says cite` is grey because on
   `/clubs/` it carries a DESCRIPTION ("one of the students"), not a name. ⭐ So the modifier
   flips the colour with the order: a name is ink, a description stays grey. */
.says.says--named cite{margin-block:0 var(--space-3);
  color:var(--text-primary);font-weight:var(--weight-medium)}
/* ⭐ A STACK IN A NARROW COLUMN. The gap BETWEEN quotations must stay visibly larger than the gap
   inside one, or a column of them runs together — the defect `/who-we-are/` was built on and only
   a screenshot found. 32px between, 12px from a name to its own words. */
/* ⛔⛔ "SHOULD WE CARD THEM?" — MEASURED ON THE REAL PAGE, AND THE ANSWER IS NO. Owner asked
   11 Aug. Airbnb listing 23572181, all four visible review roots: **457×72, `radius:0`,
   `border:0`, `background:transparent`, `padding:0`, NO shadow.** Not one of them is a box.
   ⭐⭐ AND THE SEPARATION IS PURE WHITESPACE — row pitch **226px** for a 72px clamped body, with
   no rule between reviews at all. The only 1px hairline in the section is a **1120px `#DDDDDD`
   SECTION divider**, not a between-reviews line.
   ⛔ So a bordered box here would reverse the finding that produced this component, and the same
   finding that got Ghost's bookmark-card embed rejected. **The box is what a review is NOT.**
   ⭐⭐ THE HIERARCHY HE IS ASKING FOR IS REAL AND IS ALREADY MOSTLY HERE — it comes from three
   things, none of them a container: an IDENTITY BLOCK above the words, LOOSER LEADING than the
   text around it (theirs 16/24 against a 16/20 page; ours 16×1.55=24.8 against the host card's
   16×1.375=22 — the same relationship), and AIR. ⭐ Air was the one we were short of, so the
   stack now clears the host card by 40px instead of 24. */
.saysstack{list-style:none;margin:var(--space-8) 0 0;padding:0;display:grid;gap:var(--space-7)}
.saysstack>li{margin:0}
.saysstack .says{margin:0;max-width:none}
/* ⭐ LENGTH — owner: "since testimonials are posts, if they are long, they can have the show
   more". Measured on Airbnb: the clamp is **`-webkit-line-clamp:3`**, clamped height **72px**
   against a **144px** scrollHeight, and the control reads `Show more` at **16px/500, underlined**.
   ⭐ His framing is the load-bearing part and is worth keeping: a testimonial is a real Ghost
   POST, so its length is known at build time and can carry a `custom_excerpt`. A plain text blob
   in a card could not be cut this cleanly.
   ⛔⛔ ONE THING NOT TO COPY: Airbnb's `Show more` is a SIBLING of the clamp box and renders
   UNCONDITIONALLY — a review reading only "Great stay." still shows it, because nothing compares
   scrollHeight to clientHeight. **The control is emitted only when the source actually exceeds
   three lines**, which the build knows and the browser would have to be asked. */
.says--clamped p{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;
  overflow:hidden}
/* ⭐ `.says--face` / `.says__face` ARE DELETED — owner ruled "no pic needed" 11 Aug, so the
   specimen that existed to settle the question has done its job and gone with it. They were
   marked SPECIMEN-ONLY with "delete if the ruling is no avatar" written on them, and this is
   that deletion. ⛔ A comparison of a settled question is clutter, and a kit that keeps every
   rejected option becomes a museum. The reasoning survives as a reviewer note; the rendered
   alternatives do not. */
.says__more{display:inline-block;margin-block-start:var(--space-2);
  font-size:var(--type-small);font-weight:var(--weight-medium);color:var(--text-primary);
  text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;
  min-height:var(--tap-min,44px);padding-block:calc((var(--tap-min,44px) - 1lh) / 2);
  padding-inline:var(--space-2);margin-inline:calc(var(--space-2) * -1)}
/* ⛔ The no-photo entry (the rain closure) must not look broken, and a lone entry has no feed
   around it to carry the rhythm. It gets no image well at all — same rule as the feed — and the
   headline simply starts the page. NO SLOT WITHOUT A GUARANTEED FILLER. */

/* ⭐ A SPECIMEN of the link preview, so the thing that is actually the first impression can be
   reviewed instead of imagined. ⚠️ Not page furniture — it renders in WhatsApp, not here. */
.preview{max-width:22rem;border:var(--rule-hairline) solid var(--border-hairline);
  border-radius:var(--radius-card);overflow:hidden;margin:var(--space-4) 0 0;
  background:var(--surface-page)}
.preview img{width:100%;aspect-ratio:1.91/1;object-fit:cover;display:block;background:var(--surface-quiet)}
.preview__t{margin:0;padding:var(--space-3) var(--space-4) 0;font-size:var(--type-ui);
  font-weight:var(--weight-semibold)}
.preview__d{margin:var(--space-1) 0 0;padding:0 var(--space-4);font-size:var(--type-small);
  color:var(--text-secondary)}
.preview__u{margin:var(--space-2) 0 0;padding:0 var(--space-4) var(--space-3);
  font-size:var(--type-micro);letter-spacing:var(--tracking-micro);
  text-transform:uppercase;color:var(--text-secondary)}
.kitnote::before{content:'REVIEWER NOTE — not page copy';display:block;
  font-size:var(--type-micro);font-weight:var(--weight-semibold);
  letter-spacing:var(--tracking-micro);color:#b3261e;margin-block-end:var(--space-2)}

.site-head{position:sticky;top:0;z-index:20;background:var(--surface-page);border-block-end:var(--rule-hairline) solid var(--border-hairline)}
/* ⛔ `flex-wrap` ADDED — at 200% text zoom this row did not wrap and pushed Subscribe 37px
   past the viewport edge on all five kits. WCAG 1.4.4. */
.site-head__in{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-3);max-width:calc(var(--measure-wide) + var(--gutter)*2);margin-inline:auto;padding:var(--space-2) var(--gutter);min-height:64px}
.brand{font-weight:var(--weight-bold);font-size:var(--type-ui);letter-spacing:-.01em;display:inline-flex;align-items:center;min-height:var(--tap-comfortable);text-decoration:none;margin-inline-end:auto}
.brand span{font-weight:var(--weight-regular);color:var(--text-secondary)}
.iconbtn{font:inherit;min-width:var(--tap-comfortable);min-height:var(--tap-comfortable);display:inline-flex;align-items:center;justify-content:center;border:0;background:none;border-radius:var(--radius-control);cursor:pointer;font-size:var(--type-ui);color:var(--text-primary)}
.nav{border-block-end:var(--rule-hairline) solid var(--border-hairline);background:var(--surface-page)}
.nav ul{display:flex;flex-wrap:wrap;gap:0 var(--space-5);list-style:none;margin:0;max-width:calc(var(--measure-wide) + var(--gutter)*2);margin-inline:auto;padding:0 var(--gutter)}
.nav a{display:inline-flex;align-items:center;justify-content:center;min-height:var(--tap-comfortable);min-inline-size:var(--tap-comfortable);font-size:var(--type-ui);font-weight:var(--weight-medium);text-decoration:none;white-space:nowrap}
.nav a[aria-current]{box-shadow:inset 0 -3px 0 var(--aik-ink)}

.lede{padding-block:var(--space-8) var(--space-6)}
.kicker{font-size:var(--type-micro);font-weight:var(--weight-medium);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--text-secondary);margin:0 0 var(--space-2)}
.today{margin-block:var(--space-5) var(--space-7)}
.today__pill{display:inline-flex;align-items:center;gap:.5ch;background:var(--surface-way-in);color:var(--text-on-way-in);border-radius:var(--radius-pill);padding:var(--space-2) var(--space-5);font-size:var(--type-ui);font-weight:var(--weight-semibold);font-variant-numeric:var(--figures-aligned);min-height:var(--tap-comfortable)}
.today__note{margin:var(--space-3) 0 0;color:var(--text-secondary);font-size:var(--type-small)}

.week{list-style:none;margin:0 0 var(--space-7);padding:0;border:var(--rule-hairline) solid var(--border-hairline);border-radius:var(--radius-card)}
/* `minmax(0,5.5rem)` not `5.5rem` — a fixed rem track cannot shrink, and at 200% text zoom
   5.5rem is 176px of a 360px viewport. The floor of 0 lets it give way instead of overflowing. */
.week li{display:grid;grid-template-columns:minmax(0,5.5rem) 1fr;gap:var(--space-3);padding:var(--space-4);border-block-end:var(--rule-hairline) solid var(--border-hairline);align-items:baseline}
.week li:last-child{border-block-end:0}
.week__day{font-size:var(--type-ui);font-weight:var(--weight-semibold);font-variant-numeric:var(--figures-aligned)}
.week__what{font-size:var(--type-small);color:var(--text-secondary);line-height:var(--leading-tight)}
.week li[data-shut] .week__what{color:var(--text-primary)}
.week li[data-today]{background:var(--surface-wash)}

/* ⛔⛔ THE 7-COLUMN WEEK IS REVERTED. Owner, 10 Aug 2026: "can we keep this vertical instead
   of horizontal?" — and with it goes the one media query in this stylesheet, so the file is
   back to zero breakpoints and responds entirely through intrinsic layout.

   ⭐ My argument for horizontal was "a week is a horizontal structure, and stacking it is the
   one case where narrow destroys the meaning." I still think that is true of a week in the
   abstract. It was overruled, and the ruling costs less than it would have this morning:
   the argument rested on the rows being SIX IDENTICAL LINES, which made vertical feel like
   wasted space. After the 10 August rewrite the rows genuinely differ — Mon–Fri carry three
   clubs, Saturday four, Sunday only Dance — and seven differing rows read perfectly well
   stacked. ⭐ The reason the vertical version was weak has gone away on its own.

   ⚠️ He also reported the 7-column table clipping its Sunday column. I could NOT reproduce it:
   measured at 780, 900, 1024, 1280 and 1440px, the week's box equals its container at every
   width, `scrollWidth - clientWidth` is 0, and the last column overhangs by -1px. His
   screenshot is equally consistent with a cropped region capture, which is what shottr
   produces. ⛔ It is not fixed here because it was not found — reverting to vertical makes the
   question moot rather than answering it. If it recurs, it recurs with evidence. */

.sessions{margin-block:var(--space-8);padding:0;list-style:none}
.session{position:relative;padding:var(--space-5) 0;border-block-start:var(--rule-hairline) solid var(--border-hairline)}
.session:last-child{border-block-end:var(--rule-hairline) solid var(--border-hairline)}
.session__when{font-size:var(--type-micro);font-weight:var(--weight-medium);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--text-secondary);font-variant-numeric:var(--figures-aligned);margin:0 0 var(--space-2)}
.session__name{font-size:var(--type-subheading);font-weight:var(--weight-semibold);margin:0}
/* ⛔⛔ THE ROW OVERLAY IS GONE. Owner, 10 Aug 2026: "Why are whole sections clickable — only
   the relevant anchors should?" — and he is right, but so was the overlay: it existed because
   three links measured 34px against the 44px minimum, which is a measured accessibility
   defect, not a preference. ⛔ Two legitimate concerns in direct conflict.

   ⭐ THE THIRD OPTION, and it is better than either side: GROW THE LINK, do not extend it over
   things that are not the link. Padding the anchor to 44px gives the same tap target, keeps
   text selection working on the rest of the row, and makes what is clickable obvious — the
   overlay's real cost was never the size, it was that the link's boundary was invisible.

   What it costs: a little vertical space per row, and the padding must not swallow the gap to
   the next element — hence the negative inline margin, so the box grows without shifting the
   text off the left edge. ⛔ Do NOT silently drop this and return to a bare inline link; that
   reintroduces a 34px target that was measured, not guessed. */
.session__name a{display:inline-block;text-decoration:underline;text-decoration-color:var(--border-hairline);text-decoration-thickness:1px;text-underline-offset:3px;
  min-height:var(--tap-min,44px);padding-block:calc((var(--tap-min,44px) - 1lh) / 2);
  padding-inline:var(--space-2);margin-inline:calc(var(--space-2) * -1)}
/* ⛔⛔ `max-width` ADDED after v1 shipped. The line-length assertion did not exist during the
   v1 pass, and when it was added for v2 it immediately caught this: .session__body measured
   77 characters a line at 1280px on pages that had already been reviewed as clean. A gate
   that does not exist cannot fail, and "it passed" meant "nothing asked". */
.session__body{margin:var(--space-2) 0 0;max-width:27rem;font-size:var(--type-small);color:var(--text-secondary);line-height:var(--leading-tight)}
.session__facts{margin:var(--space-3) 0 0;display:flex;flex-wrap:wrap;gap:var(--space-2) var(--space-5);font-size:var(--type-micro);font-weight:var(--weight-medium);font-variant-numeric:var(--figures-aligned);color:var(--text-secondary);list-style:none;padding:0}
.session__facts b{color:var(--text-primary);font-weight:var(--weight-semibold)}
.session__action{margin:var(--space-4) 0 0;font-size:var(--type-micro);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--text-primary)}

/* ⛔ FIXED by rendering: at padding --space-6 this card rendered a 308px column =
   31 characters per line, under the ~35 floor where a paragraph reads as confetti.
   The copy was shortened FIRST (the system's own rule: reach for the words before
   the token), then the padding stepped down. Now 34. */
.empty{border:var(--rule-hairline) solid var(--border-hairline);border-radius:var(--radius-card);padding:var(--space-5);margin-block:var(--space-7)}
/* ⛔ A measure cap, because `.empty` inherits its container. Inside `.wrap--wide` on the
   specimen page it rendered a 950px column at 20px = **107.8 characters a line**. A component
   that can sit in two containers must carry its own measure. */
.empty h3,.empty p{max-width:var(--measure)}
.empty h3{margin:0 0 var(--space-2)}
/* ⛔ FIXED by rendering: this was --type-small. It is an invitation somebody reads,
   not a caption, so it is body size. */
.empty p{margin:0;color:var(--text-secondary);font-size:var(--type-body)}

.band{border:0;border-block-start:var(--rule-band) solid var(--border-hairline);margin-block:var(--space-9) 0}
/* ⛔ Capped track, same reason as .orgs — `1fr` stretched cards at 1280 and the 16px
   excerpt inside ran 67 characters a line. Caught by the same v2 assertion. */
.cards{display:grid;gap:var(--grid-gap-row) var(--grid-gap-col);grid-template-columns:repeat(auto-fill,minmax(min(20.5rem,100%),24rem));list-style:none;margin:var(--space-6) 0 0;padding:0}
.card__img{aspect-ratio:var(--ratio-card);width:100%;border-radius:var(--radius-none);background:var(--surface-quiet) center/cover no-repeat;display:block;border:var(--rule-hairline) solid var(--border-hairline)}
.card__kicker{font-size:var(--type-micro);font-weight:var(--weight-medium);letter-spacing:var(--tracking-micro);text-transform:uppercase;color:var(--text-secondary);margin:var(--space-3) 0 var(--space-1)}
.card h3{font-size:var(--type-subheading);margin:0}
/* ⛔⛔ THE ROW OVERLAY REACHED `.session` AND NOT `.card`, AND A GREEN GATE HID IT.
   `.card h3 a` was a 238x26 box with no overlay and a `position:static` parent, so its real
   hit height was 34px on three story cards — while the kit index claimed "0 under, measured
   by hit area". The assertion probed only rows it could see in the viewport and, for the
   rest, fell back to trusting the ANCESTOR'S SIZE. A card <li> is 384x376, so it passed on
   size alone. ⛔ Never infer a tap target from a big ancestor: the ancestor being large is
   exactly what an overlay is for, not evidence that one exists. */
/* ⭐ THE CARD OVERLAY STAYS, and the line is worth stating because the owner's objection was
   to "whole SECTIONS" being clickable. A card is a single object — image, kicker, headline and
   excerpt are one thing, and the image is part of what a reader expects to click. A section is
   not one object; it is several unrelated things sharing a box, and covering them was the
   defect. ⛔ So: overlay on a CARD, padded link in a ROW. ⚠️ If he wants it gone from cards
   too, that is his call — but it costs the image as a target, which is the part most people
   actually aim at. */
.cards li{position:relative}
.card h3 a{text-decoration:none}
.card h3 a::after{content:'';position:absolute;inset:0}
.card p{margin:var(--space-2) 0 0;max-width:27rem;font-size:var(--type-small);color:var(--text-secondary);line-height:var(--leading-tight)}
.card__meta{margin:var(--space-3) 0 0;font-size:var(--type-micro);font-weight:var(--weight-medium);color:var(--text-secondary);font-variant-numeric:var(--figures-aligned)}

.btn{display:inline-flex;align-items:center;justify-content:center;min-height:var(--button-height);min-inline-size:var(--tap-comfortable);padding:0 var(--space-6);font:inherit;font-size:var(--type-ui);font-weight:var(--weight-medium);text-decoration:none;border:0;cursor:pointer}
.btn--primary{background:var(--action-primary-bg);color:var(--action-primary-text);border-radius:var(--radius-pill)}
/* ⛔ BORDER ADDED. #2eac6d on white is 2.90:1 — 0.10 short of the 3:1 WCAG 1.4.11 asks of a
   control's boundary. The ink border is 15.13:1 against paper, so the control has an edge
   even though its fill does not. (In the neighbour-facing pages green only ever appears on
   `.today__pill`, which is a <p> and not a control, so this bites only the specimen — but a
   component the system sanctions has to be usable where it is sanctioned.) */
.btn--wayin{background:var(--action-way-in-bg);color:var(--action-way-in-text);
  border:1px solid var(--action-way-in-text);border-radius:var(--radius-pill)}
/* ⛔⛔ BORDER ADDED, AND IT OVERTURNS A RULE THIS SYSTEM INHERITED FROM AIRBNB.
   theme.css said "no border on a secondary button — the fill is the affordance, and an
   outline on top of a fill is two signals for one fact." Measured: #fafafa on #fff is
   **1.04:1**. The fill is not a weak signal, it is NO signal — the button has no visible
   boundary at all, on 7 instances across the kits. So the border is not a second signal, it
   is the only one. Airbnb's own secondary (#F2F2F2 on white) measures 1.12:1 and fails the
   same way; this is the third place their answer has been refused for our reader, after
   14px body and their tap targets. */
.btn--secondary{background:var(--action-secondary-bg);color:var(--action-secondary-text);
  border:1px solid var(--border-control);border-radius:var(--radius-control)}

.field{display:block;margin-block:var(--space-5)}
.field span{display:block;font-size:var(--type-ui);font-weight:var(--weight-medium);margin-block-end:var(--space-2)}
.field input,.field textarea{width:100%;min-height:var(--button-height);padding:var(--space-3) var(--space-4);
  border:var(--rule-hairline) solid var(--border-control);border-radius:var(--radius-control);
  background:var(--surface-page);font-size:var(--type-ui)}

.site-foot{margin-block-start:var(--space-10);border-block-start:var(--rule-hairline) solid var(--border-hairline);background:var(--surface-quiet);padding-block:var(--space-8) var(--space-9)}
.site-foot h2{font-size:var(--type-subheading);margin:0 0 var(--space-3)}
/* ⛔⛔ THE DOMINANT 200%-ZOOM OVERFLOW — 122px on all five kits, and the guard was already
   in this file 21 lines above, in `.cards`. A bare `minmax(15rem,1fr)` cannot go below 15rem,
   which is 480px at a 32px root — wider than the viewport. `min(15rem,100%)` is the whole fix.
   ⭐ Having the right pattern elsewhere in the same file is not the same as applying it. */
.foot-cols{display:grid;gap:var(--space-7);grid-template-columns:repeat(auto-fit,minmax(min(15rem,100%),1fr))}
.hours{list-style:none;margin:0;padding:0;font-variant-numeric:var(--figures-aligned)}
.hours li{display:flex;justify-content:space-between;gap:var(--space-4);padding-block:var(--space-2);font-size:var(--type-small);border-block-end:var(--rule-hairline) solid var(--border-hairline)}
.hours li:last-child{border-block-end:0}
.hours b{font-weight:var(--weight-semibold)}
.foot-links{list-style:none;margin:0;padding:0}
/* ⛔ `min-inline-size` ADDED. This had the height floor and not the width one, so "Team"
   rendered 38x52 — under 44 on the axis nobody measures. tokens/spacing.css carries this
   as a named scar ("a target has two axes") and this file still only applied one of them.
   ⚠️ `justify-content` stays default so short labels sit left in the column, not centred. */
.foot-links a{display:inline-flex;align-items:center;min-height:var(--tap-comfortable);
  min-inline-size:var(--tap-comfortable);font-size:var(--type-small);text-decoration:underline;
  text-decoration-color:var(--border-hairline);text-underline-offset:3px}
/* Footer prose sits in `.wrap--wide` too — the kit index's closing paragraph ran 142.5
   characters a line without this. Same defect as `.empty`, same fix.
   ⚠️ DECLARED BEFORE `.foot-note` ON PURPOSE. See the specificity note below. */
.site-foot p{max-width:var(--measure)}
/* ⛔⛔ A FIX THAT NEVER APPLIED, AND THE CAUSE WAS SPECIFICITY INSIDE THIS FILE.
   14px micro text needs ~22rem for ~58 characters — `--measure` is sized for 20px body,
   and capping this at 544px still rendered 96 characters a line. The 22rem was written,
   and then did nothing: `.foot-note` is (0,1,0) while `.site-foot p` above is (0,1,1),
   one class plus one element, so it won on both specificity AND source order. Computed
   `max-width` stayed 544px and the line only fell 96 → 87.6. `.site-foot .foot-note` is
   (0,2,0) and actually binds.
   ⭐ The lesson is not "watch specificity" — it is that a declaration you can read in the
   file is not a declaration the element HAS. Verify `getComputedStyle(el).maxWidth`, not
   the stylesheet.
   ⚠️ AND THE SAMPLING TRAP UNDER IT: there are TWO `.foot-note` elements. The one inside
   a `.foot-cols` column renders 309px and was always fine; only the full-width one at the
   foot was broken. A check that samples the FIRST match passes while the defect stands. */
.site-foot .foot-note{max-width:22rem}
.foot-note{margin-block-start:var(--space-7);font-size:var(--type-micro);color:var(--text-secondary)}

/* ══ v2: the two new content types ══════════════════════════════════════
   The site has FOUR content types, not two: partner organisations who work from
   the space · clubs those partners run · daily activity entries · one-off dated
   events. v1 surfaced only clubs and stories. These two components carry the
   other two, and each has to survive going stale — see the notes on each. */

/* LATEST FROM THE CLUBS — a dated strip, sourced from entries already written
   daily in the monthly post. ⭐ It costs no new writing discipline; it surfaces
   work that is currently buried in a 12,854px scroll.
   ⚠️ HOW IT DEGRADES: every row carries its own date, so the strip AGES VISIBLY
   instead of claiming freshness it does not have. If the newest entry passes ~14
   days the heading becomes "Last written up on 7 August" — the date is promoted
   to the headline and the word "latest" is dropped, so a quiet fortnight reads as
   a fact rather than as a broken feed. ⛔ Never pad it to three rows. Two real
   rows beat three with filler, and one is fine. */
.latest{list-style:none;margin:var(--space-5) 0 0;padding:0;
  border-block-start:var(--rule-hairline) solid var(--border-hairline)}
.latest li{padding:var(--space-4) 0;border-block-end:var(--rule-hairline) solid var(--border-hairline)}
.latest__meta{display:flex;flex-wrap:wrap;gap:var(--space-1) var(--space-3);align-items:baseline;
  font-size:var(--type-micro);font-weight:var(--weight-medium);
  letter-spacing:var(--tracking-micro);text-transform:uppercase;
  font-variant-numeric:var(--figures-aligned);color:var(--text-secondary);margin:0}
.latest__club{color:var(--text-primary);font-weight:var(--weight-semibold)}
/* ⛔ `max-width: 27rem` is NOT decoration. 16px text inheriting the 544px body column
   renders 72-77 characters a line. tokens/spacing.css carries the measured answer:
   16px needs ~27rem for ~58 characters. Every 16px block needs its own cap. */
.latest p:last-child{margin:var(--space-2) 0 0;max-width:27rem;font-size:var(--type-small);
  line-height:var(--leading-tight);color:var(--text-secondary)}

/* WHO WORKS HERE — organisations and people who work FROM the space.
   ⛔ This is NOT the club-facilitator list. Measured 9 Aug 2026, the overlap
   between the two sets is exactly ONE (Olimalar Foundation). Merging them to
   make the section easier would misrepresent both. */
/* ⛔ The track has a MAX, not `1fr`. With `1fr` the grid stretched two cards to 544px each
   at 1280 and the 16px text inside ran 67-72 characters a line. auto-fill + a capped track
   removes the breakpoint ladder AND the overrun in one declaration. */
/* A face is 88px, not the live page's 64px. This page's job is "who you will meet", and 64px
   is a thumbnail — too small to recognise somebody by. ⛔ `width`/`height` attributes are on
   the element as well, so the row does not reflow as thirteen images arrive. */
.person__face{width:88px;height:88px;border-radius:var(--radius-card);object-fit:cover;
  display:block;margin:0 0 var(--space-3);background:var(--surface-quiet)}
.orgs{list-style:none;margin:var(--space-5) 0 0;padding:0;display:grid;
  gap:var(--space-4);grid-template-columns:repeat(auto-fill,minmax(min(17rem,100%),22rem))}
.orgs li{border:var(--rule-hairline) solid var(--border-hairline);
  border-radius:var(--radius-card);padding:var(--space-4)}
.orgs h3{font-size:var(--type-ui);font-weight:var(--weight-semibold);margin:0}
.orgs p{margin:var(--space-2) 0 0;font-size:var(--type-small);
  line-height:var(--leading-tight);color:var(--text-secondary)}
/* ⛔ INK, not green. A cross-reference is not a way in, and the accent has one job.
   The walked-down --text-green would have passed the checker (it gates the brand
   green specifically) and still been wrong — a gate is not the rule. */
.orgs__also{display:block;margin-block-start:var(--space-2);font-size:var(--type-micro);
  font-weight:var(--weight-semibold);letter-spacing:var(--tracking-micro);
  text-transform:uppercase;color:var(--text-primary)}

/* system-kit only */
/* ⛔ FIXED by rendering: the type specimen used a real <h1> to show the display step, so
   the specimen page emitted TWO document h1s. A specimen must not damage the structure of
   the page it is printed on — the sample carries the display TOKENS on a <p>. */
.spec-display{font-size:var(--type-display);line-height:var(--leading-display);
  font-weight:var(--weight-semibold);letter-spacing:var(--tracking-display);margin:0;max-width:34rem}
.spec{border-collapse:collapse;width:100%;font-size:var(--type-small);font-variant-numeric:var(--figures-aligned)}
.spec th,.spec td{text-align:start;padding:var(--space-3) var(--space-3) var(--space-3) 0;border-block-end:var(--rule-hairline) solid var(--border-hairline);vertical-align:top}
.spec th{font-weight:var(--weight-semibold)}
.swatch{display:inline-block;width:var(--tap-comfortable);height:var(--tap-comfortable);border-radius:var(--radius-control);border:var(--rule-hairline) solid var(--border-hairline);vertical-align:middle}
.scroller{overflow-x:auto}
