/* WHYZ LAYOUT PRIMITIVES - the composition system in docs/COMPOSITION.md, in CSS.
   No colour literals (standard 14). No z-index outside the stack in COMPOSITION section 8. */

/* ---------- the edge grid ----------
   One grid that holds BOTH the contained 12 columns and the two viewport edges, so a
   half-bleed is a grid span rather than a 100vw hack. 100vw includes the scrollbar and
   is the usual cause of a 1-17px horizontal scroll on desktop; this never computes it. */
/* --gutter lives in tokens.css and is the one place the page's side margin is decided, so
   .shell and .edge cannot drift apart - they had, and body paragraphs inside a grid column
   sat at 288px and 29 characters per line while the ones in a plain shell reached 300px
   and 31: one band, two answers, on the same page. */
.edge {
  display: grid;
  grid-template-columns:
    [full-start] minmax(var(--gutter), 1fr)
    repeat(12, [col-start] minmax(0, calc(90rem / 12)) [col-end])
    minmax(var(--gutter), 1fr) [full-end];
  column-gap: 0;
}
.edge > * { grid-column: col-start 1 / col-end 12; }        /* contained by default */
.edge > .to-right  { grid-column: col-start 7 / full-end; } /* half bleed, right edge */
.edge > .to-left   { grid-column: full-start / col-end 6; } /* half bleed, left edge */

/* Column spans. Integers only - COMPOSITION section 1. */
.c1-6  { grid-column: col-start 1 / col-end 6; }
.c1-7  { grid-column: col-start 1 / col-end 7; }
.c1-8  { grid-column: col-start 1 / col-end 8; }
.c8-12 { grid-column: col-start 8 / col-end 12; }
@media (max-width: 63.999rem) {
  /* Mobile is not a narrower desktop: every span collapses to the full content width
     and the DIFFERENT mobile composition is expressed by different components. */
  .c1-6, .c1-7, .c1-8, .c8-12,
  .edge > .to-right, .edge > .to-left { grid-column: col-start 1 / col-end 12; }
  .edge > .to-full-mobile { grid-column: full-start / full-end; }
}

/* ---------- vertical rhythm ---------- */
/* PHONE FIRST, and the phone is not a narrow laptop. Desktop breathes at --sp-6 (128px);
   the phone used to take exactly half of that, which satisfies MOBILE-100 item 24 on
   paper and looks wrong in practice - measured, the "next event" section was 317px tall
   carrying 128px of padding, 40% of itself, and the whole page ran 6303px at 390 wide.
   A quarter of the desktop value is the phone's own rhythm rather than a fraction of
   somebody else's. */
/* mobile100 item 28 (defect 511ca843b0): tightened again, from a quarter of the desktop
   value to an eighth (--sp-3, 16px), after the seam and hero levers alone did not close
   the gap to the 5-screen ceiling. Item 24 only sets a CEILING ("at most half the desktop
   value") - there is no floor - and item 22's own writeup names this exact number as
   "Ema's judgement about how the page should feel", left untouched last round in
   surveyability's favour. This round that judgement call is explicitly Ema's to make
   herself, for this defect - see the commit message for the measured before/after. */
.section  { padding-block: var(--sp-3); position: relative; }
@media (min-width: 48rem) { .section { padding-block: var(--sp-5); } }
@media (min-width: 64rem) { .section { padding-block: var(--sp-6); } }
.push-down  { margin-top: var(--sp-4); }
.push-down-l{ margin-top: var(--sp-5); }
.pull-up-l  { margin-top: var(--sp-4); }
@media (min-width: 64rem) {
  /* Above 64rem a pulled-up element crosses the boundary of the section above it, so
     the sticker reads as laid ON the page rather than set into a column. */
  .pull-up-l { margin-top: calc(var(--sp-6) * -1); }
}

/* ---------- plates: an image with a caption ---------- */
/* Grid items stretch to the row height by default, and the row is as tall as the text
   column beside it. Without align-self the plate's caption pins to the bottom of a box
   twice the image's height - measured: a 384px image inside a 725px figure. */
.plate { margin: 0; position: relative; z-index: 2; align-self: start; }
.plate img {
  width: 100%; display: block;
  object-fit: cover;
  object-position: 50% 50%;   /* overridden per image by focal.css - measured */
  filter: saturate(0.92) contrast(1.04);
}
.plate figcaption {
  font-size: var(--fs-1); letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--text-muted); margin-top: var(--sp-2); max-width: 34ch;   /* caption measure */
}
.invert .plate figcaption { color: var(--inv-muted); }

/* Fixed aspect boxes - the seven ratios, and only these (docs/COMPOSITION.md section 2) */
/* A 1:2 crop at half-bleed width is 1440px tall on a 1440px viewport, which turns the
   image into the page. Capped so it reads as a column of texture beside the type. */
.ar-tall      img { aspect-ratio: 1 / 2; }
@media (min-width: 64rem) { .ar-tall img { aspect-ratio: auto; height: 40rem; } }
.ar-portrait  img { aspect-ratio: 2 / 3; }
.ar-editorial img { aspect-ratio: 4 / 5; }
.ar-square    img { aspect-ratio: 1 / 1; }
.ar-landscape img { aspect-ratio: 3 / 2; }
.ar-wide      img { aspect-ratio: 16 / 9; }
.ar-letterbox img { aspect-ratio: 5 / 2; }
/* The two full-bleed seams are the same kind of element doing the same job at two points
   on the page, and they were 16/9 and 5/2 - measured 260px and 156px tall at 390 wide,
   which reads as one photograph and one strip rather than as a pair. Setting the ratio on
   the seam itself rather than per-class is what actually makes them equal: overriding one
   class left them 260 and 219, because the two seams are not the same width. */
.seam img { aspect-ratio: 3 / 2; }

/* ---------- the sticker ---------- */
.sticker {
  position: relative; z-index: 3; box-sizing: border-box; align-self: start;
  /* A rotated element's BOUNDING box is wider than its layout box. For the closed
     rotation set the worst case is 6deg, where bbox width is about 1.10x the layout
     width. 88% therefore always fits its column: 0.88 x 1.10 = 0.967 < 1.
     A fixed pixel inset does NOT work here, because the overshoot scales with the
     element - 32px per side was enough at 320px and 19px short at 768px (measured:
     bbox 774 inside a 736px column, scrollWidth 771 vs clientWidth 768). */
  width: 88%;
  margin-inline: auto;
  border: var(--sp-2) solid var(--inv-bg);
  box-shadow: 6px 6px 0 0 var(--whyz-ink);
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.sticker img { display: block; width: 100%; object-fit: cover; }
.sticker--b { transform: rotate(3deg);  }
.sticker--c { transform: rotate(-6deg); }
.sticker--d { transform: rotate(5deg);  }
.sticker:hover, .sticker:focus-within { transform: rotate(0deg); box-shadow: 3px 3px 0 0 var(--whyz-ink); }
.sticker figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: var(--field); color: var(--whyz-cream);   /* 10.29:1 - red as a FIELD */
  font-size: var(--fs-1); letter-spacing: var(--tracking-label); text-transform: uppercase;
  padding: var(--sp-1) var(--sp-2); margin: 0; max-width: none;
}
@media (prefers-reduced-motion: reduce) {
  .sticker, .sticker:hover, .sticker:focus-within { transition: none; }
}

/* ---------- seams: the strip between two sections ---------- */
.seam { position: relative; z-index: 1; }
.seam img { width: 100%; display: block; object-fit: cover; }
/* mobile100 item 28 (defect 511ca843b0): the collapsed homepage ran 1554px over its
   5-screen ceiling at 390 wide. The two seams cost 520px combined (260px each) purely as
   full-bleed decoration - the ratio above is deliberately unified across both classes (see
   its own comment) so they read as a matched pair, and that pairing is preserved here by
   tightening BOTH together on narrow viewports rather than one class alone, which is what
   the previous, no-op version of this rule did (.seam.ar-wide alone, already equal to the
   unconditional 3/2 above - a rule with no effect, PROMPT-V8 lesson #12). */
@media (max-width: 63.999rem) { .seam img { aspect-ratio: 5 / 2; } }  /* 260px -> 156px each at 390 wide */
/* mobile100 item 75 (defect 5c33a7e6d4): the 5/2 ratio above is set from WIDTH alone, so on
   a landscape phone - wide, not narrow - it grows instead of shrinking: measured on
   vol01.html at 844x390, a single seam came out 337.6px tall, 87% of the whole viewport,
   with nothing to tap for the length of it. .hero__media img already solves the same
   problem for the homepage hero (item 28) by switching to a viewport-height cap instead of
   an aspect-ratio; same fix here, same short-viewport threshold the landscape menu and
   nav.js's askbar already agree on (@media max-height: 30rem, components.css). */
@media (max-width: 63.999rem) and (max-height: 30rem) {
  .seam img { aspect-ratio: auto; height: 30vh; min-height: 8rem; }
}
.seam__note {
  font-size: var(--fs-1); letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--text-muted); margin: var(--sp-2) 0 0;
}

/* ---------- ONE element, two compositions ----------
   Below 64rem the Vol. 01 track is a horizontal scroll-snap rail. At and above it is a
   12-column asymmetric grid with unequal spans and offsets. Same DOM, same images,
   genuinely different composition (BRIEF section 20).
   It is one element on purpose: rendering both and hiding one duplicates the markup and
   makes browsers fetch images inside the display:none copy. */
.track {
  display: flex; gap: var(--sp-3);
  overflow-x: auto; scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  padding-block-end: var(--sp-3);
  scroll-padding-inline-start: var(--sp-3);
}
.track > * { flex: 0 0 72%; scroll-snap-align: start; }
/* ONE ratio inside the swipe track. The eight photographs of the room carried four
   different ratios between them - measured at 390px wide, all 222px across but 333, 277,
   222 and 148px tall - so in a horizontal row nothing lined up at the top or the bottom
   and the set read as a pile rather than a sequence. Ema: the ones showing the room are
   weirdly placed.
   4/5 is the editorial ratio and was already the most common of the four, three of eight,
   so this is the set agreeing with its own majority rather than a new ratio being
   invented - the closed set in docs/COMPOSITION.md section 2 is untouched. Every image in
   here is object-fit:cover with its own measured focal point, so a taller or shorter box
   re-crops around the subject rather than cutting it. */
.track .plate img { aspect-ratio: 4 / 5; }
/* The rail is a tab stop, so it needs a visible focus ring like any other. */
.track:focus-visible { outline: 3px solid var(--focus); outline-offset: 4px; }
.track__hint {
  font-size: var(--fs-1); letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--text-muted); margin: 0 0 var(--sp-2);
}
@media (min-width: 64rem) {
  .track__hint { display: none; }
  .track {
    display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--sp-4);
    align-items: start; overflow-x: visible; padding-block-end: 0;
  }
  .track > * { flex: none; }
  /* unequal spans and offsets from the space scale only - COMPOSITION section 5 */
  .track > *:nth-child(1) { grid-column: 1 / span 5; }
  .track > *:nth-child(2) { grid-column: 7 / span 4; margin-top: var(--sp-5); }
  .track > *:nth-child(3) { grid-column: 2 / span 3; margin-top: calc(var(--sp-4) * -1); }
  .track > *:nth-child(4) { grid-column: 6 / span 6; }
}

/* ---------- the list register (mobile) versus the column register (desktop) ---------- */
.ledger { display: grid; gap: 0; margin: 0; padding: 0; list-style: none; }
/* Below 64rem the number used to sit beside the paragraph in a `3rem 1fr` grid, which left
   the text column 236px wide at 320 - 24 characters/line, under the 30-45 phone band
   (mobile100#32, measured with Canvas measureText). Stacking the number above the text,
   the same treatment the >=64rem rule below already gives it, frees that 3rem-plus-gap back
   to the paragraph: 300px at 320, 30.6 characters/line. */
.ledger > li {
  display: block;
  padding-block: var(--sp-4); border-top: 1px solid var(--border);
}
.ledger > li:last-child { border-bottom: 1px solid var(--border); }
.invert .ledger > li { border-color: var(--inv-border); }
.ledger__n {
  font-family: var(--font-text); font-size: var(--fs-1); color: var(--accent);
  letter-spacing: var(--tracking-label);
  display: block; margin-bottom: var(--sp-3);
}
.invert .ledger__n { color: var(--inv-accent); }
.ledger h3 { font-size: var(--fs-5); color: var(--text-strong); margin-bottom: var(--sp-2); }
.invert .ledger h3 { color: var(--inv-text); }
.ledger p { margin: 0; }
@media (min-width: 64rem) {
  /* Measured: at --sp-5 the three ledger columns held 44 characters, one short of the 45
     minimum of the reading band. Halving the gap returns 64px across the row, about 21px
     and two characters to each column, which puts them inside it. */
  .ledger { grid-template-columns: repeat(3, 1fr); gap: var(--sp-4); }
  .ledger > li { border-top: 1px solid var(--border); border-bottom: 0; padding-block: var(--sp-4) 0; }
  .ledger > li:last-child { border-bottom: 0; }
}

/* ---------- cards (Get involved) ---------- */
/* min-width: 0 is load-bearing, the same reason .footer__grid > * carries it: a grid item
   defaults to min-width:auto, so a .btn inside a card that cannot wrap grows the whole
   track past the container - at 200% zoom that pushed all four "Four ways in" cards 383px
   wide inside a 390px viewport (mobile100#96). */
.cards { display: grid; gap: var(--sp-4); }
.cards > * { min-width: 0; }
@media (min-width: 48rem) { .cards { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .cards { grid-template-columns: repeat(3, 1fr); gap: var(--sp-5); } }
.card { border-top: 1px solid var(--border); padding-top: var(--sp-3); }
.card p { margin-bottom: var(--sp-3); }
.invert .card { border-color: var(--inv-border); }
.card p { margin-bottom: var(--sp-3); }
@media (min-width: 64rem) {
  /* two deliberate offsets, never three in one viewport - COMPOSITION section 5 */
  .cards > .card:nth-child(2) { margin-top: var(--sp-4); }
  .cards > .card:nth-child(6) { margin-top: var(--sp-4); }
}

/* ---------- the field list ----------
   Twelve occupations as a wrapping row of chips. The point is recognition: someone should
   find their own job here within a second of the page loading, which is why this section
   sits second rather than near the footer. */
.fields {
  list-style: none; margin: 0 0 var(--sp-4); padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--sp-2);
}
.fields li {
  font-family: var(--font-text); font-size: var(--fs-2);
  text-transform: uppercase; letter-spacing: var(--tracking-label);
  border: 1px solid var(--inv-border); color: var(--inv-text);
  padding: var(--sp-2) var(--sp-3);
}

/* ---------- dense track ----------
   The 5/4/3/6 asymmetric strip is right for four plates and wrong for eight: it leaves
   large empty wedges. Ema on the flashback and on /vol01: "put more photos next to each
   other and don't leave too many blank spaces". Eight plates, four columns, one offset. */
@media (min-width: 64rem) {
  .track--dense { grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); }
  .track--dense > * { grid-column: auto !important; margin-top: 0 !important; }
  .track--dense > *:nth-child(4n + 2) { margin-top: var(--sp-4) !important; }
}
@media (max-width: 63.999rem) {
  .track--dense > * { flex-basis: 62%; }
}
.cards--four { }
/* mobile100#29: four cards in a row shared one identical shape - transparent ground, 1px
   top border, number+heading+paragraph - one past the three-block ceiling. The fourth gets
   a filled ground instead, the smallest version of the "a change of ground marks a new
   subject" move the page already makes between .shell and .invert sections. */
.cards--four > .card:nth-child(4) {
  background: var(--surface); border-top-color: transparent; padding: var(--sp-4);
}
.invert .cards--four > .card:nth-child(4) { background: var(--whyz-white); }
@media (min-width: 64rem) {
  .cards--four { grid-template-columns: repeat(4, 1fr); gap: var(--sp-4); }
  /* Ema asked for equal heights in the three-column lists; the four cards follow suit. */
  .cards--four > .card { margin-top: 0 !important; }
}
