/* ════════════════════════════════════════════════════════════
   TNAADO — /media-portfolio
   T-1110 (build), T-1149 (template rebuild), 2026-08-14.

   WHAT CHANGED IN T-1149
   ──────────────────────
   The page was the clearest example on the site of the pattern
   Ethan rejected: a dark --ink section carrying a short block of
   light text, followed by a photograph running border to border,
   repeated four times down the page. Every complaint he made
   tonight describes that loop.

   The rebuild inverts it.

   1  READING GROUNDS ARE LIGHT. Every .mpx section is --bone with
      --ink type. The shared .eyebrow/.sv-h2/.sv-lede classes are
      written for a black ground (they use --on-black-*, which is a
      near-white), so each one is re-coloured HERE, scoped inside
      .mpx / #mp-root. Nothing in site.css is touched — parallel
      agents are in that file.

   2  BLACK IS PUNCTUATION. It survives as the seams between light
      blocks (.mp-seam, hatched), as the angled cut at the top of
      each section (.mpx--cut-*), as photo plates, and as the two
      ink accent blocks (.mp-panel, .mp-hatch). It is no longer the
      page.

   3  IMAGES ARE CONTAINED. .mp-fig has a bounded frame with its own
      aspect ratio and a hard max-height; it sits on the 1140 rail
      with gutter around it. The old .mp-band, which spanned the
      viewport, is gone. Section BACKGROUNDS still run full width.

   4  SHAPES, SHARP AGAINST SOFT. The three soft radii are Ethan's
      own, lifted from unwrinkled-react/app/globals.css (.arch,
      .blob-a, .blob-b). The sharp ones are clip-path counterparts —
      chamfer, wedge, notch, blade — and the page alternates them so
      no two adjacent frames carry the same silhouette.

   TOKENS ONLY. --ink, --bone, --red, --font-mono, --band-* and the
   easing curve all come from site.css. The only new values here are
   the ink-on-bone text tints, which site.css has no token for
   because until tonight nothing on this site read dark-on-light.
   ════════════════════════════════════════════════════════════ */

/* ── LOCAL TINTS ─────────────────────────────────────────────
   Measured against --bone #faf8f3, not eyeballed:
     --mp-ink       #0a0a0a          → 18.7:1
     --mp-body      rgba(10,10,10,.78) →  9.9:1
     --mp-mute      rgba(10,10,10,.62) →  6.4:1
     --mp-cap       rgba(10,10,10,.58) →  5.8:1
     --red #c8102e                    →  5.6:1
   --red-text (#e8455a) is the ON-BLACK red and must never appear on
   bone; it measures 2.7:1 there. Every accent below uses --red. */
.mpx,
#mp-root {
  --mp-ink: #0a0a0a;
  --mp-body: rgba(10, 10, 10, .78);
  --mp-mute: rgba(10, 10, 10, .62);
  --mp-cap: rgba(10, 10, 10, .66);
  --mp-rule: rgba(10, 10, 10, .14);
  --mp-cut: clamp(16px, 2.2vw, 38px);
}

/* ── SECTION SHELL ───────────────────────────────────────────
   Padding is deliberately tighter than the old shell (was
   clamp(4rem,7vw,6rem)) because "sections are too long" was on the
   list. The angled top edge cuts the bone block against the ink
   body behind it, so the join between two light sections reads as a
   sharp black seam rather than a soft fade. */
.mpx {
  position: relative;
  background: var(--bone);
  color: var(--mp-ink);
  padding: clamp(2.9rem, 5vw, 4.2rem) 0 clamp(3.1rem, 5.4vw, 4.5rem);
}
.mpx--cut-a { clip-path: polygon(0 var(--mp-cut), 100% 0, 100% 100%, 0 100%); }
.mpx--cut-b { clip-path: polygon(0 0, 100% var(--mp-cut), 100% 100%, 0 100%); }
/* The cut eats into the top padding, so give it back on the side the
   triangle is deepest. */
.mpx--cut-a,
.mpx--cut-b { padding-top: calc(clamp(2.9rem, 5vw, 4.2rem) + var(--mp-cut)); }

.mp-rail {
  max-width: 1140px;
  margin: 0 auto;
  padding: 0 clamp(1.15rem, 4vw, 2.5rem);
}

/* ── TYPE ON A LIGHT GROUND ──────────────────────────────────
   The shared classes assume black behind them. Re-point every colour
   they set, scoped to this page's light sections only. */
.mpx .eyebrow,
#mp-root .eyebrow { color: var(--red); margin-bottom: 1rem; }
.mpx .eyebrow .eyebrow-num,
#mp-root .eyebrow .eyebrow-num { color: var(--mp-mute); }
.mpx .eyebrow-rule,
#mp-root .eyebrow-rule { background: var(--red); }

.mpx .sv-h2,
#mp-root .sv-h2 { color: var(--mp-ink); margin-bottom: .9rem; }
.mpx .sv-h2 em,
#mp-root .sv-h2 em { color: var(--red); }

.mpx .sv-lede,
#mp-root .sv-lede { color: var(--mp-body); max-width: 56ch; margin-bottom: 0; }
.mpx .sv-lede + .sv-lede { margin-top: .85rem; }
.mpx .sv-lede a,
#mp-root .sv-lede a { color: var(--red); border-bottom: 1px solid rgba(200, 16, 46, .4); }

.mpx__head { max-width: 58ch; }

/* ── SEAM ────────────────────────────────────────────────────
   The black band between two light sections. Crosshatched rather
   than flat — Ethan asked for patterns crisscrossing the site, and a
   seam is where a pattern can live without competing with text.
   Two gratings at opposing 45s, one bone and one red, at hairline
   width so it reads as texture at arm's length. */
.mp-seam {
  position: relative;
  height: clamp(46px, 5.6vw, 78px);
  background: var(--ink);
  overflow: hidden;
}
.mp-seam::before {
  content: "";
  position: absolute;
  inset: -30%;
  background:
    repeating-linear-gradient(45deg, rgba(245, 241, 232, .13) 0 1px, transparent 1px 11px),
    repeating-linear-gradient(-45deg, rgba(200, 16, 46, .34) 0 1px, transparent 1px 17px);
}
/* A second grating angle so the seams down the page are not identical. */
.mp-seam--tight::before {
  background:
    repeating-linear-gradient(-45deg, rgba(245, 241, 232, .16) 0 1px, transparent 1px 7px),
    repeating-linear-gradient(45deg, rgba(200, 16, 46, .26) 0 1px, transparent 1px 23px);
}

/* ── CONTAINED FIGURE ────────────────────────────────────────
   The replacement for the old viewport-wide .mp-band. The frame owns
   the crop; the image only fills it. max-height is belt and braces on
   top of the aspect ratio so a wide window cannot grow a picture into
   the screen — the hard rule is that nothing approaches viewport
   height, and --band-sm/--band-md are already min()-capped against
   svh in site.css, so they are the right ceilings to borrow. */
.mp-fig { margin: 0; }
.mp-fig__frame {
  position: relative;
  overflow: hidden;
  background: #0d0d0d;
  max-height: var(--band-md);
}
.mp-fig img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--fp, 50% 50%);
}
.mp-fig figcaption {
  margin-top: .8rem;
  font-family: var(--font-mono);
  font-size: .64rem;
  line-height: 1.7;
  letter-spacing: .04em;
  color: var(--mp-cap);
  max-width: 48ch;
}

/* Ratios — chosen per picture in the markup. */
.mp-fig--r43 .mp-fig__frame  { aspect-ratio: 4 / 3; }
.mp-fig--r32 .mp-fig__frame  { aspect-ratio: 3 / 2; }
.mp-fig--r11 .mp-fig__frame  { aspect-ratio: 1 / 1; }
.mp-fig--r169 .mp-fig__frame { aspect-ratio: 16 / 9; }
.mp-fig--wide .mp-fig__frame { aspect-ratio: 21 / 9; max-height: var(--band-sm); }

/* ── SHAPES ──────────────────────────────────────────────────
   SOFT — Ethan's three radii, unchanged, from his own globals.css.
   SHARP — clip-path counterparts. They alternate down the page so
   the eye never sees the same silhouette twice in a row. */
.sh-arch   { border-radius: 50% 50% 12% 12% / 34% 34% 8% 8%; }
.sh-blob-a { border-radius: 63% 37% 55% 45% / 56% 50% 50% 44%; }
.sh-blob-b { border-radius: 40% 60% 56% 44% / 55% 42% 58% 45%; }

.sh-chamfer { clip-path: polygon(0 0, calc(100% - 42px) 0, 100% 42px, 100% 100%, 42px 100%, 0 calc(100% - 42px)); }
/* 6% did not read at 512x288 — the slant measured 17px across a 512px
   edge and looked like a rendering artefact rather than a decision.
   11% is unmistakable and still clears every subject in the frame. */
.sh-wedge   { clip-path: polygon(0 11%, 100% 0, 100% 89%, 0 100%); }
.sh-notch   { clip-path: polygon(0 0, 100% 0, 100% 100%, 34% 100%, 0 76%); }
/* A shallower chamfer for blocks that carry TEXT rather than a
   picture — a 42px bite into a padded panel clips the first and last
   lines at the corners; 24px sits inside the panel's own padding. */
.sh-chamfer--sm { clip-path: polygon(0 0, calc(100% - 24px) 0, 100% 24px, 100% 100%, 24px 100%, 0 calc(100% - 24px)); }
.mp-panel.sh-chamfer--sm { padding: clamp(1.7rem, 2.9vw, 2.3rem) clamp(1.6rem, 2.7vw, 2.1rem); }
.sh-blade   { clip-path: polygon(14% 0, 100% 0, 100% 86%, 0 100%, 0 0); }

@media (max-width: 640px) {
  /* A 42px chamfer on a 320px box eats the corner rather than
     cutting it. Halve the bite. */
  .sh-chamfer { clip-path: polygon(0 0, calc(100% - 22px) 0, 100% 22px, 100% 100%, 22px 100%, 0 calc(100% - 22px)); }
}

/* ── LAYOUTS ─────────────────────────────────────────────────
   Head beside picture, not head above picture. That single change is
   what breaks the banned template: there is no longer a block of
   words alone on a ground with a photograph underneath it. */
.mp-split {
  display: grid;
  grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
  gap: clamp(1.6rem, 3.6vw, 3.2rem);
  align-items: center;
}
.mp-split--flip { grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr); }

.mp-pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.3rem, 2.8vw, 2.2rem);
  align-items: start;
  margin-top: clamp(1.5rem, 3vw, 2.4rem);
}
/* The trio on S.03: one tall soft frame carrying the pair beside it. */
.mp-trio {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: clamp(1.3rem, 2.8vw, 2.2rem);
  align-items: start;
  margin-top: clamp(1.5rem, 3vw, 2.4rem);
}
.mp-trio__stack {
  display: grid;
  gap: clamp(1.3rem, 2.8vw, 2.2rem);
}

@media (max-width: 860px) {
  .mp-split,
  .mp-split--flip,
  .mp-trio { grid-template-columns: minmax(0, 1fr); }
  .mp-split > :first-child { order: -1; }
}
@media (max-width: 600px) {
  .mp-pair { grid-template-columns: minmax(0, 1fr); }
}

/* ── INK ACCENT BLOCKS ───────────────────────────────────────
   Black as an object on the page rather than the page itself. Two
   forms: a panel that carries text (the shoot facts), and a hatch
   plate that carries none and exists to give the composition a hard
   dark mass where a fifth photograph would otherwise go. */
.mp-panel {
  position: relative;
  background: var(--ink);
  color: var(--on-black);
  padding: clamp(1.4rem, 2.6vw, 2.1rem) clamp(1.3rem, 2.4vw, 1.9rem);
}
/* NOT RED, AND THE REASON IS MEASURED. --red-text on --ink is nominally
   5.1:1, but at 9px the painted stroke core never reaches the full colour
   — the panel label came back at 3.97:1 and the fact labels at 3.22:1 on
   real pixels, all of it lost to antialiasing. Weight 500 did not recover
   it. So the 9px labels take bone, which paints at 8:1 and up, and the red
   moves to a RULE, where it is a graphic element and its contrast is not a
   legibility question. Same reasoning as the eyebrow rule in site.css. */
.mp-panel__label {
  display: flex;
  align-items: center;
  gap: .6rem;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: .58rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: rgba(245, 241, 232, .80);
  margin: 0 0 1rem;
}
.mp-panel__label::before {
  content: "";
  width: 22px;
  height: 1px;
  background: var(--red);
  flex-shrink: 0;
}

.mp-hatch {
  position: relative;
  overflow: hidden;
  background: var(--ink);
  min-height: clamp(150px, 20vw, 240px);
}
.mp-hatch::before {
  content: "";
  position: absolute;
  inset: -25%;
  background:
    repeating-linear-gradient(45deg, rgba(245, 241, 232, .15) 0 1px, transparent 1px 13px),
    repeating-linear-gradient(-45deg, rgba(200, 16, 46, .32) 0 1px, transparent 1px 21px);
}
/* A single red diagonal laid over the grating, so the plate has one
   deliberate line in it rather than reading as wallpaper. */
.mp-hatch::after {
  content: "";
  position: absolute;
  left: -10%; right: -10%; top: 50%;
  height: 1px;
  background: var(--red);
  transform: rotate(-9deg);
  opacity: .8;
}
.mp-hatch__cap {
  position: absolute;
  left: clamp(1rem, 2.2vw, 1.6rem);
  bottom: clamp(.9rem, 2vw, 1.4rem);
  margin: 0;
  font-family: var(--font-mono);
  font-size: .6rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: rgba(245, 241, 232, .72);
}

/* ── FACT LIST (now inside the ink panel) ────────────────────
   A ruled list, not a row of stat boxes. The rules are the panel's
   own hairlines and stop at the panel edge, which is the point of
   moving it off the full-bleed gutter it used to sit on. */
.mp-facts { margin: 0; border-top: 1px solid var(--rule-on-black); }
.mp-fact {
  display: grid;
  grid-template-columns: minmax(0, 9.5rem) minmax(0, 1fr);
  gap: .3rem 1.2rem;
  align-items: baseline;
  padding: .8rem 0;
  border-bottom: 1px solid var(--rule-on-black);
}
.mp-fact dt {
  font-family: var(--font-mono);
  font-weight: 500;   /* see the note on .mp-panel__label — red painted 3.22:1 here */
  font-size: .56rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: rgba(245, 241, 232, .74);
}
.mp-fact dd {
  margin: 0;
  font-size: .82rem;
  line-height: 1.6;
  color: var(--on-black-mute);
}
@media (max-width: 1040px) {
  .mp-fact { grid-template-columns: minmax(0, 1fr); }
}

/* ── MASTHEAD BLOCK (S.04) ───────────────────────────────────
   Where a fifth photograph would have gone. There are already two
   near-identical desk-with-a-laptop frames in the repo's media set
   and the standing rule is one workstation photo per page, so this
   section gets a built element instead of a borrowed picture. */
.mp-mast {
  position: relative;
  overflow: hidden;
  background: var(--ink);
  color: var(--on-black);
  padding: clamp(1.6rem, 3vw, 2.4rem);
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: clamp(180px, 22vw, 280px);
}
.mp-mast::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    repeating-linear-gradient(-45deg, rgba(245, 241, 232, .07) 0 1px, transparent 1px 9px),
    radial-gradient(120% 100% at 100% 0%, rgba(200, 16, 46, .28) 0%, transparent 62%);
  pointer-events: none;
}
.mp-mast > * { position: relative; z-index: 1; }
.mp-mast__name {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.4rem, 3vw, 2.1rem);
  line-height: 1.05;
  letter-spacing: -.02em;
  margin: 0 0 .55rem;
  color: var(--on-black);
}
.mp-mast__name em { font-style: italic; color: var(--red-text); }
.mp-mast__rule {
  width: 100%;
  height: 1px;
  background: linear-gradient(to right, var(--red) 0%, rgba(200, 16, 46, .12) 68%, transparent 100%);
  margin: 0 0 .9rem;
}
.mp-mast__note {
  font-size: .82rem;
  line-height: 1.65;
  color: var(--on-black-mute);
  margin: 0 0 1.2rem;
  max-width: 40ch;
}
.mp-mast__links { display: flex; flex-wrap: wrap; gap: .5rem 1.6rem; }
/* MEASURED AT 390: .sh-notch takes the bottom-left corner from 76% height
   across to 34% width. At phone width the two links wrap onto two lines and
   the second line fell inside that triangle — the screenshot read
   "W THE PRACTICE RUNS", the start of the link sliced off. The block keeps
   the notch, but a shallower one, and the padding below the links now clears
   it at every width rather than only on desktop. */
.mp-mast.sh-notch { clip-path: polygon(0 0, 100% 0, 100% 100%, 26% 100%, 0 90%); }
.mp-mast.sh-notch { padding-bottom: clamp(2.6rem, 5.5vw, 3.4rem); }
.mp-mast .mp-go { margin: 0; color: var(--red-text); }
.mp-mast .mp-go:hover { border-bottom-color: var(--red-text); }

/* ── OUTBOUND LINK ───────────────────────────────────────────
   Same mono arrow language as .csi__go. On bone it takes --red;
   inside an ink block it takes --red-text (see .mp-mast above). */
.mp-go {
  display: inline-flex;
  align-items: baseline;
  gap: .5rem;
  margin: clamp(1.3rem, 2.4vw, 1.9rem) 0 0;
  font-family: var(--font-mono);
  font-size: .7rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--red);
  border-bottom: 1px solid transparent;
  transition: border-color .22s var(--ease-out-expo);
}
.mp-go span:last-child { transition: transform .2s var(--ease-out-expo); }
@media (hover: hover) and (pointer: fine) {
  .mp-go:hover { border-bottom-color: var(--red); }
  .mp-go:hover span:last-child { transform: translateX(4px); }
}
.mp-go:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

/* ── ENGAGEMENTS GRID ────────────────────────────────────────
   #mp-root is a .sv-section, which is background: var(--ink) in
   site.css. The id beats that class, so the whole override lives
   here and site.css stays untouched — /case-studies keeps its dark
   grid, this page gets a light one. The card MEDIA plate stays
   #0d0d0d: the screenshots are dark-headed sites and a white plate
   behind them would halo. Black as the photo ground, exactly the
   role it is supposed to have now. */
#mp-root {
  background: var(--bone);
  color: var(--mp-ink);
  padding: clamp(3rem, 5.4vw, 4.4rem) 0 clamp(3.4rem, 6vw, 5rem);
}
#mp-root .cs-grid { margin-top: clamp(1.8rem, 3.4vw, 2.6rem); }
#mp-root .cs-card {
  background: #ffffff;
  border-color: rgba(10, 10, 10, .13);
  box-shadow: 0 14px 34px -30px rgba(10, 10, 10, .6);
}
#mp-root .cs-card:hover,
#mp-root .cs-card:focus-visible {
  border-color: rgba(200, 16, 46, .42);
  background: #ffffff;
}
#mp-root .cs-card__class { color: var(--red); }
#mp-root .cs-card__title { color: var(--mp-ink); }
#mp-root .cs-card__desc { color: var(--mp-mute); }
#mp-root .cs-card__arrow { color: var(--red); }
/* Every second card takes a chamfer, so the grid is not nine
   identical rectangles. Applied on the nth card rather than a class
   because the cards are rendered from Supabase by the script at the
   foot of the page and carry no per-card hook. */
#mp-root .cs-card:nth-child(3n + 2) {
  clip-path: polygon(0 0, calc(100% - 26px) 0, 100% 26px, 100% 100%, 26px 100%, 0 calc(100% - 26px));
}
@media (max-width: 620px) {
  #mp-root .cs-card:nth-child(3n + 2) { clip-path: none; }
}

@media (prefers-reduced-motion: reduce) {
  .mp-go, .mp-go span:last-child { transition: none; }
  .mp-go:hover span:last-child { transform: none; }
}


/* ════════════════════════════════════════════════════════════
   T-1120 · COMPOSITION FIXES, 2026-08-14
   Measured off real painted pixels at 390 / 768 / 1440.
   ════════════════════════════════════════════════════════════ */

/* ── CAPTIONS INSIDE THE TWO-UP ──────────────────────────────
   The pair used to share one caption written as "Left: … Right: …",
   which stopped being true the moment .mp-duo stacked at 720px.
   Each frame captions itself now (see media-portfolio.html), so the
   inset has to be the frame's own edge rather than the page gutter —
   the right-hand frame does not start at the page gutter, and a
   caption indented to the gutter would float away from its picture.
   The last line is the outer edges: the pair reads as one block, so
   only the outer captions take the full gutter. */
.mp-duo .mp-band figcaption { padding-inline: clamp(.75rem, 2vw, 1.1rem); }
@media (min-width: 721px) {
  .mp-duo .mp-band:first-child figcaption { padding-inline-start: clamp(1rem, 4vw, 2.5rem); }
  .mp-duo .mp-band:last-child  figcaption { padding-inline-end: clamp(1rem, 4vw, 2.5rem); }
}

/* ── THE FACT LIST GETS A MEASURE ────────────────────────────
   The hairlines run edge to edge on purpose and stay that way. The
   VALUE did too, and that is different: at 1440 the dd box measured
   1094px, which is around 163 characters on one line for any fact
   long enough to fill it. The rules keep the page's width; the words
   get a reading measure. */
.mp-fact dd { max-width: 62ch; }
