/* ==========================================================================
   SECTIONS — per-section layout. design-rule.md §6.
   Substrate alternates light/dark; the switch is the divider, never a rule.
   ========================================================================== */

/* ------------------------------------------------------------------- HERO */

.hero { padding-top: calc(var(--nav-h) + var(--s12)); padding-bottom: var(--s12); }
.hero__inner { align-items: end; }

/* Headline spans cols 1–9; media sits 8–12 and overlaps its right edge (§5.3). */
.hero__type  { grid-column: 1 / 10; position: relative; z-index: 2; }
.hero__media { grid-column: 8 / 13; position: relative; z-index: 1; margin-left: calc(var(--s8) * -1); }

.hero__eyebrow { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s4); }
.hero__year { position: absolute; top: 0; right: 0; }
.hero__title { margin-bottom: var(--s4); }
.hero__foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); margin-top: var(--s6); }

/* Native aspect from the manifest — cropping a landscape showreel into a
   portrait box throws away most of the frame. */
.hero__reel {
  border: 1px solid var(--line);
  aspect-ratio: var(--ratio, 16 / 9);
  object-fit: cover;
  width: 100%;
  transform: rotate(1.2deg);
}

@media (max-width: 900px) {
  .hero__type  { grid-column: 1 / -1; }
  .hero__media { grid-column: 1 / -1; margin-left: 0; margin-top: var(--s6); }
  .hero__reel  { aspect-ratio: 16 / 9; }
  .hero__year  { position: static; margin-bottom: var(--s2); }
}

/* --------------------------------------------------------------- MARQUEE */

.sec-marquee { padding-block: var(--s4); border-block: 1px solid var(--line-invert); }
.marquee__item {
  font-family: var(--f-display);
  font-size: clamp(1.5rem, 3vw, 2.5rem);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  white-space: nowrap;
  display: flex;
  align-items: center;
  gap: var(--s6);
  color: var(--fg-invert);
}
.marquee__item .glyph { fill: var(--accent); width: 0.5em; height: 0.5em; }

/* ------------------------------------------------------------- MANIFESTO */

.manifesto__body { grid-column: 2 / 10; }
.manifesto__aside { grid-column: 10 / 13; align-self: end; }
.manifesto__line { margin-bottom: var(--s4); }
@media (max-width: 900px) {
  .manifesto__body, .manifesto__aside { grid-column: 1 / -1; }
  .manifesto__aside { margin-top: var(--s4); }
}

/* ----------------------------------------------------------- DISCIPLINES */

/* Open top-left rule rather than a closed box: with 5 cards in a 4-up grid the
   last row is short, and a full border would frame the empty cells as a hole.
   Cards supply their own right/bottom edges, so a short row just ends. */
.disc-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 0;
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
}
.disc {
  position: relative;
  padding: var(--s6) var(--s4);
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  transition: background var(--d-base) var(--ease-out);
}
.disc::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 6px;
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform var(--d-base) var(--ease-out);
}
.disc:hover::before { transform: scaleY(1); }
.disc__title {
  margin-block: var(--s2);
  transition: transform var(--d-base) var(--ease-out);
}
.disc:hover .disc__title { transform: translateX(8px); color: var(--accent); }
.disc__tools { margin-top: var(--s3); }

/* ------------------------------------------------------------------ WORK */

.work__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s4);
  margin-bottom: var(--s8);
}
.work__filters { display: flex; flex-wrap: wrap; gap: var(--s1); }

/* Deliberately unequal heights — an even grid is the enemy (§6.2). */
.work-grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: var(--s3);
  align-items: start;
}
.work-grid > .tile              { grid-column: span 4; }
.work-grid > .tile[data-span="wide"] { grid-column: span 6; }
.work-grid > .tile[data-span="tall"] { grid-column: span 4; }

/* Stagger the top edge so the row never reads as a table. */
.work-grid > .tile:nth-child(3n+2) { margin-top: var(--s6); }
.work-grid > .tile:nth-child(3n+3) { margin-top: var(--s2); }

/* …except for the illustration set. The stagger gives a mixed portfolio of
   posters and reels its rhythm (§6.2), but applied to 17 same-shaped cards it
   just reads as misalignment. The :nth-child(n) is there to out-specify the
   stagger rules above and their responsive counterparts below, so this holds at
   every breakpoint without !important. */
.work-grid > .tile[data-accent="illustration"]:nth-child(n) { margin-top: 0; }

@media (max-width: 1024px) {
  .work-grid > .tile,
  .work-grid > .tile[data-span="wide"],
  .work-grid > .tile[data-span="tall"] { grid-column: span 6; }
  .work-grid > .tile:nth-child(3n+2),
  .work-grid > .tile:nth-child(3n+3) { margin-top: 0; }
  .work-grid > .tile:nth-child(2n) { margin-top: var(--s4); }
}
@media (max-width: 640px) {
  .work-grid { grid-template-columns: 1fr; }
  .work-grid > .tile,
  .work-grid > .tile[data-span="wide"],
  .work-grid > .tile[data-span="tall"] { grid-column: 1 / -1; }
  .work-grid > .tile:nth-child(n) { margin-top: 0; }
}

/* ------------------------------------------------------- TREEMAP (WORK, §6.3)
   For a homogeneous discipline the pieces are an *index*, not hero cards, so
   they are laid out as a mosaic: one fixed-height box, subdivided recursively
   into unequal cells. The subdivision lives in data/treemap.php and reaches the
   DOM as nested .tm branches, each cell carrying its share as --w.

   Every cell is `flex: var(--w) 1 0`, so a cell's size is purely its share of
   its parent. Hovering a plate raises --w on that plate's cell *and on every
   ancestor branch*, and lowers it on everything else. Because the shares are
   proportional inside a box of fixed height, the hovered piece swells to
   roughly the largest cell's footprint while the rest close ranks — the mosaic
   never reflows the page, it redistributes inside itself.

   §7.3 EXCEPTION — this animates `flex-grow`, not transform. A transform can
   scale the hovered plate but cannot make its neighbours give up the space, and
   "others resize to fit" is the whole effect. The cost is a layout pass per
   frame over ~35 nodes inside a fixed-size, `contain: layout` root; measured
   smooth at 60fps. Do not generalise this to the main work grid, which has no
   height ceiling and would reflow the document.

   Placed after the base .work-grid rules so it overrides them by source order. */

.work-grid--tm {
  display: block;   /* the root treemap is the grid's only child */
}

.tm {
  display: flex;
  gap: var(--tm-gap);
  min-width: 0;
  min-height: 0;
}
.tm--row { flex-direction: row; }
.tm--col { flex-direction: column; }

.tm__cell {
  flex: var(--w) 1 0;
  min-width: 0;
  min-height: 0;
  position: relative;
  transition: flex-grow var(--d-slow) var(--ease-out);
}

.tm-root {
  --tm-gap: var(--s1);
  /* These compound up the ancestor chain, so they are far more powerful than
     they look: at depth four, 2.1 takes the smallest cell from ~1% of the
     mosaic to ~10%, i.e. roughly the width of the largest cell. Raising them
     further starts crushing the unhovered cells below ~30px, at which point the
     mosaic stops reading as a composition. Measured, not guessed. */
  --tm-swell: 2.1;
  --tm-shrink: 0.62;
  height: clamp(520px, 76vh, 840px);
  contain: layout;    /* confine the per-frame layout work to the mosaic */
}

/* The hovered cell floats above its neighbours so its offset lift is not
   clipped by the cell drawn after it. */
.tm__cell:has(.plate:hover),
.tm__cell:has(.plate:focus-visible) { z-index: 2; }

/* Keyboard parity: tabbing through the plates drives the same redistribution,
   on touch devices too. Kept out of the hover query for exactly that reason. */
.tm-root:has(.plate:focus-visible) .tm__cell:has(.plate:focus-visible) {
  flex-grow: calc(var(--w) * var(--tm-swell));
}
.tm-root:has(.plate:focus-visible) .tm__cell:not(:has(.plate:focus-visible)) {
  flex-grow: calc(var(--w) * var(--tm-shrink));
}

/* Guarded to fine pointers: on touch a tap would otherwise leave the mosaic
   stuck mid-redistribution with no way to undo it. */
@media (hover: hover) and (pointer: fine) {
  .tm-root:has(.plate:hover) .tm__cell:has(.plate:hover) {
    flex-grow: calc(var(--w) * var(--tm-swell));
  }
  .tm-root:has(.plate:hover) .tm__cell:not(:has(.plate:hover)) {
    flex-grow: calc(var(--w) * var(--tm-shrink));
  }
}

/* Below the tablet break the mosaic stops being readable — the smallest cells
   fall under a thumb's width. The branches flatten out with `display: contents`
   and every plate becomes an equal card in a two-up grid. */
@media (max-width: 900px) {
  .work-grid--tm .tm-root {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    height: auto;
    contain: none;
  }
  .work-grid--tm .tm__cell.tm { display: contents; }
  .work-grid--tm .tm__cell--leaf { aspect-ratio: 4 / 3; }
}
@media (max-width: 460px) {
  .work-grid--tm .tm-root { grid-template-columns: 1fr; }
}

/* Reduced motion gets the mosaic as a still composition. The selectors are
   spelled out in full rather than shortened to `.tm-root:has(…) .tm__cell`,
   which would lose on specificity to the redistribution rules above and leave
   the effect running. */
@media (prefers-reduced-motion: reduce) {
  .tm__cell { transition: none; }
  .tm-root:has(.plate:hover) .tm__cell:has(.plate:hover),
  .tm-root:has(.plate:hover) .tm__cell:not(:has(.plate:hover)),
  .tm-root:has(.plate:focus-visible) .tm__cell:has(.plate:focus-visible),
  .tm-root:has(.plate:focus-visible) .tm__cell:not(:has(.plate:focus-visible)) {
    flex-grow: var(--w);
  }
}

/* ---------------------------------------------------------- CASE SPOTLIGHT */

.case__media { grid-column: 1 / 8; }
.case__notes { grid-column: 8 / 13; position: sticky; top: calc(var(--nav-h) + var(--s4)); }
.case__step { margin-bottom: var(--s4); padding-bottom: var(--s4); border-bottom: 1px solid rgba(0,0,0,0.1); }
.case__step:last-child { border-bottom: 0; }
@media (max-width: 900px) {
  .case__media, .case__notes { grid-column: 1 / -1; }
  .case__notes { position: static; margin-top: var(--s6); }
}

/* ------------------------------------------------------------------ TEAM */

.team-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s6) var(--s4);
}
@media (max-width: 1024px) { .team-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 768px)  { .team-grid { grid-template-columns: repeat(2, 1fr); gap: var(--s4) var(--s3); } }

.member__name { margin-top: var(--s3); }
.member__tools { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--s2); }

/* --------------------------------------------------------------- TOOLKIT */

/* Fixed columns rather than auto-fit. 5 across divides the current 10 tools into
   two exact rows; the intermediate breakpoints may leave a short last row, which
   is harmless because separators are borders on the tiles. (With the older
   background-through-1px-gaps trick, a vacant cell rendered as a bare grey
   block — hence the change.) Revisit the 5 if the tool count changes. */
.tool-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
  border-top: 1px solid var(--line-invert);
  border-left: 1px solid var(--line-invert);
}
@media (max-width: 1100px) { .tool-grid { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 760px)  { .tool-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 480px)  { .tool-grid { grid-template-columns: repeat(2, 1fr); } }
.tool {
  background: var(--ink);
  border-right: 1px solid var(--line-invert);
  border-bottom: 1px solid var(--line-invert);
  padding: var(--s6) var(--s3);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s3);
  text-align: center;
  transition: background var(--d-base) var(--ease-out);
}

/* Gradient logo mark. Square, 0 radius — our geometry, not a vendor's (§8).
   design-rule §6.2 asks for greyscale at rest snapping to colour on hover; the
   gradient is held at partial saturation rather than fully grey so the colour
   is still legible in a static screenshot, then resolves fully on hover. */
.tool__logo {
  width: 64px;
  height: 64px;
  display: grid;
  place-items: center;
  background: linear-gradient(135deg, var(--g1, #333), var(--g2, #888));
  color: var(--gfg, #FFF);
  font-family: var(--f-display);
  font-size: 1.375rem;
  letter-spacing: -0.02em;
  line-height: 1;
  filter: saturate(0.35) brightness(0.85);
  transition:
    filter var(--d-base) var(--ease-out),
    transform var(--d-base) var(--ease-out);
}
.tool:hover .tool__logo {
  filter: none;
  transform: translateY(-4px);
}

/* A supplied vendor logo is never recoloured — only the rest/hover fade applies.
   It shares the generated mark's exact rest filter so a grid that mixes real
   logos with generated ones reads as one set rather than two. */
.tool__logo--file {
  background: none;
  object-fit: contain;
  filter: saturate(0.35) brightness(0.85);
}
.tool:hover .tool__logo--file { filter: none; }

.tool__name {
  font-family: var(--f-display);
  font-size: 1.1rem;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  color: var(--fg-mute-dark);
  transition: color var(--d-base) var(--ease-out);
}
.tool:hover { background: var(--ink-2); }
.tool:hover .tool__name { color: var(--fg-invert); }

@media (prefers-reduced-motion: reduce) {
  .tool:hover .tool__logo { transform: none; }
}

@media (max-width: 768px) {
  .tool { padding: var(--s4) var(--s2); gap: var(--s2); }
  .tool__logo { width: 52px; height: 52px; font-size: 1.125rem; }
  .tool__name { font-size: 0.9rem; }
}
.tool::after {
  content: '';
  position: absolute; left: var(--s3); right: var(--s3); bottom: var(--s3);
  height: 2px; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform var(--d-base) var(--ease-out);
}
.tool:hover::after { transform: scaleX(1); }

/* --------------------------------------------------------------- PROCESS */

.process-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s4);
}
.step { position: relative; padding-top: var(--s4); border-top: 2px solid var(--accent); }
.step__no { margin-bottom: var(--s2); }
.step__arrow {
  position: absolute;
  top: calc(var(--s4) * -1 - 10px);
  right: calc(var(--s4) * -0.5);
  width: 46px;
  stroke: var(--accent);
  fill: none;
  stroke-width: 2.5;
  stroke-linecap: round;
}
.step:last-child .step__arrow { display: none; }
@media (max-width: 900px) {
  .process-grid { grid-template-columns: repeat(2, 1fr); }
  .step__arrow { display: none; }
}
@media (max-width: 520px) { .process-grid { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------- CLIENTS */

.client-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--s4);
  align-items: center;
  margin-bottom: var(--s12);
}
.client {
  font-family: var(--f-display);
  font-size: 1.15rem;
  text-transform: uppercase;
  letter-spacing: -0.01em;
  color: var(--fg-mute-dark);
  text-align: center;
  transition: color var(--d-base) var(--ease-out);
}
.client:hover { color: var(--accent); }

.quote-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: var(--s4); }
.quote { border: 1px solid var(--line-invert); padding: var(--s4); }
.quote__text { margin-block: var(--s3); }

/* --------------------------------------------------------------- CONTACT */

.contact__type { grid-column: 1 / 8; }
.contact__form { grid-column: 8 / 13; }
@media (max-width: 900px) {
  .contact__type, .contact__form { grid-column: 1 / -1; }
  .contact__form { margin-top: var(--s8); }
}

/* ---------------------------------------------------------------- FOOTER */

.footer { padding-block: var(--s8) var(--s6); }
.footer__grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: var(--s4);
  margin-bottom: var(--s8);
}
.footer__col li { margin-bottom: var(--s1); }
.footer__bottom {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s2);
  padding-top: var(--s3); border-top: 1px solid var(--line-invert);
}
@media (max-width: 768px) { .footer__grid { grid-template-columns: 1fr 1fr; } }
