/* ============================================================================
   NIMMERSATT x OTTOLINGER — Berlin Fashion Week
   Client layer on top of: tokens → pitch → deck → client-cc-tapis →
   client-sprs → client-otto. This file only holds what the Ottolinger deck
   needs on top of that stack:
     1. a centred hero (the cc-tapis layer splits it into two columns)
     2. the "What we delivered" ledger, read as full-width rows
     3. the project index tiles used by Fashion Week / Index / Recaps
     4. placeholder cards for reference stacks whose stills are still missing
   ========================================================================== */

body.ol-pitch {
  --bg: #fafafa;
  --fg: #0d0d0d;
  --ink: rgba(13, 13, 13, 0.72);
  --ink-strong: rgba(13, 13, 13, 0.9);
  --client: #0d0d0d;
  --client-brown: #1a1a1a;
  --client-line: rgba(13, 13, 13, 0.18);
  /* Plain ground. The cc-tapis layer paints three vertical hairlines at 25 /
     50 / 75 % of the page width via a background gradient on <body>; this deck
     runs without them. */
  background: var(--bg);
}

/* The intro footage was mastered on pure white. Multiply lets that white matte
   resolve to the deck's #fafafa ground without re-colouring the moving image. */
.ol-pitch .preloader {
  background: var(--bg);
  isolation: isolate;
}

.ol-pitch .preloader__media {
  mix-blend-mode: multiply;
}

/* The full navigation bar is gone. The remaining brand behaves like a small
   physical sticker: black artwork, off-white fill, crisp white contour. */
.ol-pitch .nav,
.ol-pitch .nav.is-stuck {
  inset: var(--space-4) auto auto var(--space-4);
  width: auto;
  padding: 0;
  background: transparent;
  border: 0;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  transform: none;
  pointer-events: none;
}

.ol-pitch .nav__brand {
  position: relative;
  display: inline-flex;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  color: var(--fg);
  background: var(--bg);
  border: var(--space-1) solid #fff;
  pointer-events: auto;
}

.ol-pitch .nav__mark {
  width: var(--space-5);
  color: var(--fg);
}

.ol-pitch .nav__name {
  color: var(--fg);
  font-family: var(--font-client-mono);
  font-size: var(--type-label);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
}

@media (max-width: 767px) {
  .ol-pitch .nav,
  .ol-pitch .nav.is-stuck {
    inset: var(--space-3) auto auto var(--space-3);
  }

  .ol-pitch .nav__brand {
    padding: var(--space-2);
  }

  .ol-pitch .nav__name {
    display: inline;
  }
}

/* The short dotted tick above the intro belonged to that same grid. With the
   grid gone it floats on its own, so it goes too. */
.ol-pitch .about .shell::after {
  content: none;
}

/* --- 1 / Hero: centred column, GIF between the tagline and the disciplines --
   The cc-tapis layer puts the wordmark left and the mark right. Otto asked for
   the centred sprs composition, so the grid placements are reset here. */
.ol-pitch .hero {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  gap: clamp(1.4rem, 3.2vw, 2.5rem);
}

.ol-pitch .hero__title,
.ol-pitch .hero__sub,
.ol-pitch .hero__mark,
.ol-pitch .hero__meta {
  grid-column: auto;
  grid-row: auto;
  justify-self: center;
}

.ol-pitch .hero__title {
  width: fit-content;
  max-width: 92vw;
  white-space: nowrap;
  margin-inline: auto;
  text-align: center;
  font-size: clamp(2rem, 9vw, 8rem);
  line-height: 0.9;
  letter-spacing: -0.035em;
}

/* Otto, 08.09.26: "the sub headline should always be one row, in mobile and
   desktop." A 46ch measure broke a 54-character line in two on every desktop
   width, so the cap goes and the line is pinned. The vw ceiling only bites in
   the narrow band just above the phone breakpoint, where --text-sub has already
   hit its 1.1rem floor and the shell gutters have not finished shrinking. */
.ol-pitch .hero__sub {
  max-width: none;
  margin-inline: auto;
  text-align: center;
  white-space: nowrap;
  font-size: min(var(--text-sub), 2.24vw);
}

.ol-pitch .hero__mark {
  width: clamp(150px, 26vw, 320px);
  max-width: min(80vw, 320px);
  margin-inline: auto;
  aspect-ratio: auto;
  justify-self: center;
}

/* The caterpillar is one baked loop: the intro silhouette masking Otto's runway
   gif, built by scripts/build-hero-raupe.py. It arrives on pure white like the
   intro footage does, so the same multiply that carries the preloader resolves
   it against the #fafafa ground. `isolation` keeps the blend inside this box
   instead of reaching for whatever paints behind the section, so the box has to
   carry the ground colour itself -- exactly what .preloader does. Without the
   background the multiply has nothing to resolve against and the loop's white
   matte sits as a 255 rectangle on the deck's 250. */
.ol-pitch .hero__mark.hero-raupe {
  position: relative;
  width: min(72vw, 760px);
  max-width: 760px;
  aspect-ratio: 1504 / 846;
  isolation: isolate;
  background: var(--bg);
}

.ol-pitch .hero-raupe__film {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  mix-blend-mode: multiply;
}

@media (max-width: 767px) {
  .ol-pitch .hero__title {
    max-width: calc(100vw - (2 * var(--space-4)));
    font-size: clamp(2.25rem, 10.5vw, 2.75rem);
  }

  .ol-pitch .hero__mark.hero-raupe {
    width: min(88vw, 520px);
  }
}

/* Reduced motion gets the first frame of the same loop, not a different mark.
   The box drops its isolation and does the multiply itself, so the poster's
   white matte resolves against the hero rather than against the box's own
   ground colour. */
@media (prefers-reduced-motion: reduce) {
  .ol-pitch .hero-raupe__film {
    display: none;
  }

  .ol-pitch .hero__mark.hero-raupe {
    isolation: auto;
    background-color: transparent;
    background-image: url("../assets/raupe-hero-poster.jpg?v=1");
    background-position: center;
    background-size: contain;
    background-repeat: no-repeat;
    mix-blend-mode: multiply;
  }
}

.ol-pitch .hero__meta {
  justify-content: center;
}

/* --- 2 / "What we delivered": one number per row, read across the page ------
   Otto: "make it sound bigger or list horizontally." Each line is a full-width
   row: oversized figure, the deliverable, and the detail that qualifies it. */
.ol-delivered {
  margin-top: clamp(2.5rem, 5vw, 4rem);
  border-top: 1px dotted var(--client-line);
}

.ol-delivered__row {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) minmax(0, 0.9fr);
  align-items: baseline;
  column-gap: clamp(1.5rem, 4vw, 4rem);
  row-gap: 0.35rem;
  padding: clamp(1.1rem, 2.4vw, 1.9rem) 0;
  border-bottom: 1px dotted var(--client-line);
}

.ol-delivered__num {
  font-family: var(--font-client-display);
  font-weight: 700;
  font-size: clamp(2.6rem, 7.5vw, 6.5rem);
  line-height: 0.82;
  letter-spacing: -0.05em;
  font-variant-numeric: tabular-nums;
}

.ol-delivered__name {
  font-family: var(--font-client-display);
  font-weight: 700;
  font-size: clamp(1.1rem, 2.4vw, 2rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
}

.ol-delivered__note {
  max-width: 40ch;
  color: var(--ink);
  font-size: clamp(0.72rem, 0.95vw, 0.86rem);
  font-weight: var(--w-read);
  line-height: 1.6;
}

@media (max-width: 860px) {
  .ol-delivered__row {
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  }
  .ol-delivered__note {
    grid-column: 2;
    max-width: none;
  }
}

/* --- 3 / Project index tiles ------------------------------------------------
   Used three times: the other Fashion Week work, the index® references, and the
   event recaps. One tile per project, image on top, metadata underneath. */
.ol-projects {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr));
  gap: 1px;
  margin-top: clamp(1.6rem, 3vw, 2.4rem);
  border: 1px dotted var(--client-line);
  background: var(--client-line);
}

.ol-project {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--bg);
}

.ol-project__media {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: #ededea;
}

.ol-project__media :is(img, video) {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-ui) var(--ease-spring);
}

.ol-project:hover .ol-project__media :is(img, video) {
  transform: scale(1.03);
}

/* Recap tiles that open a full-video lightbox signal it with the zoom cursor. */
.ol-project__media video[data-zoom] {
  cursor: zoom-in;
}

.ol-project__body {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: var(--client-row-pad);
}

.ol-project__num {
  font-family: var(--font-client-mono);
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

.ol-project__name {
  font-family: var(--font-client-display);
  font-size: clamp(1.05rem, 1.9vw, 1.5rem);
  font-weight: 700;
  line-height: 1.02;
  letter-spacing: -0.02em;
}

.ol-project__meta {
  font-family: var(--font-client-mono);
  font-size: 0.56rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --- 6 / Concept table --------------------------------------------------
   client-otto.css forces every .conceptbody into single full-width rows. The
   Case section wants exactly that, but Index and Recaps keep the cc-tapis
   five-column table, so it is restored per section. */
@media (min-width: 980px) {
  .ol-pitch .conceptbody--table {
    grid-template-columns: repeat(5, minmax(0, 1fr)) !important;
  }
}

/* --- 7 / Type: cc-tapis only, never the SPRS pitch's -----------------------
   client-otto.css (the layer behind nimmersatt-x-sprs.surge.sh) forces every
   heading to Poynter OS Display. That face does not belong here; headings run
   on the cc-tapis display face, Nimbus Sans Round.

   The HERO WORDMARK is the deliberate exception: it keeps Rock 3D, the
   hand-drawn Nimmersatt face, which is what Otto annotated. That comes from
   `.otto-pitch .hero__title`, so this file leaves the hero alone. */
body.ol-pitch h2,
body.ol-pitch h3,
body.ol-pitch .otto-cubebox__label strong {
  font-family: var(--font-client-display) !important;
}

/* The ASCII closing inherits its font from the h2 above, and any proportional
   face collapses the block. It needs a real monospace. */
.ol-pitch .closing__line--ascii,
.ol-pitch .closing__line--ascii .ascii-art {
  font-family: "Apercu Mono", "Martian Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace !important;
}

/* --- 9 / Offered services: two equal rows ----------------------------------
   Eight services, four per row on desktop, so the two rows are the same
   length. */
@media (min-width: 900px) {
  .ol-pitch .about__services {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    justify-content: initial;
  }
}

/* --- 10 / Case body: one column, sub-blocks stacked ------------------------
   client-otto.css splits each block into "tag | text | text" on wide screens,
   so Setup and Progression sit side by side. Otto wants them under each other. */
@media (min-width: 980px) {
  .ol-pitch .otto-main-film .conceptblock {
    grid-template-columns: minmax(150px, 0.32fr) minmax(0, 1fr);
  }
  .ol-pitch .otto-main-film .conceptblock p {
    grid-column: 2;
  }
}

/* --- 11 / Flipped season block: media right, copy left --------------------- */
.ol-pitch .otto-direction-head--flip .otto-cubebox {
  order: 2;
}
.ol-pitch .otto-direction-head--flip .concept__brief {
  order: 1;
}

/* --- 12 / Client / studio credits inside a season brief -------------------- */
.ol-creditblock {
  margin-top: clamp(1rem, 2vw, 1.5rem);
}
.ol-creditblock + .ol-creditblock {
  margin-top: clamp(0.9rem, 1.8vw, 1.3rem);
}
.ol-creditblock__label {
  display: block;
  margin-bottom: 0.45rem;
  font-family: var(--font-client-mono);
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}
.ol-creditblock__names {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.9rem;
  list-style: none;
}
.ol-creditblock__names li {
  font-family: var(--font-client-mono);
  font-size: clamp(0.62rem, 0.85vw, 0.74rem);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* --- 13 / Client wall: one uniform black silhouette ------------------------
   Several marks (lululemon, GmbH, index, assembly, ROVO, Jaded) ship as white
   on transparent and vanished on this white ground. brightness(0) flattens
   every logo to the same black, which is also how the live site does it. */
.ol-pitch .clientlist img {
  filter: brightness(0);
  opacity: 0.78;
}

/* Otto asked for the marks to sit bigger. Roughly +55 % over the cc-tapis
   default, with a taller cell so nothing collides. */
.ol-pitch .clientlist li {
  min-height: clamp(110px, 12vw, 165px);
}
.ol-pitch .clientlist img {
  max-width: min(84%, 240px);
  max-height: clamp(44px, 6.4vw, 82px);
}

/* Otto, 04.09.26: "6 logos side by side, 3 rows". The cc-tapis base is an
   auto-fit grid, which reflowed to 5 or 7 across depending on the viewport, so
   this pins the count instead of letting the track width decide it. 18 marks
   divide into 6 x 3 exactly. Six across a phone is 65px per cell, unreadable,
   so it steps to 3 then 2 — the row of six is the desktop instruction. */
.ol-pitch .clientlist {
  grid-template-columns: repeat(6, 1fr);
}

@media (max-width: 899px) {
  .ol-pitch .clientlist { grid-template-columns: repeat(3, 1fr); }
}

@media (max-width: 559px) {
  .ol-pitch .clientlist { grid-template-columns: repeat(2, 1fr); }
}

/* Clients we hold no mark for run as a plain name row under the wall. */
.ol-clientnames {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.4rem;
  margin-top: clamp(1.1rem, 2.2vw, 1.8rem);
  padding-top: clamp(1rem, 2vw, 1.5rem);
  border-top: 1px dotted var(--client-line);
  list-style: none;
}
.ol-clientnames li {
  font-family: var(--font-client-mono);
  font-size: clamp(0.62rem, 0.85vw, 0.74rem);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
}

/* --- 12b / The crown, inverted, with the crawl lane --------------------------
   The panel goes black with white type, and the bracket row is replaced by a
   column of Nimmersatt marks crawling across it.

   Colour is re-pointed rather than restyled, the same way §16 flips a section:
   --fg carries the meta row's two bold cells and the wordmark, --cc-blue is the
   upstream hover accent (a near-black blue, unreadable here). Only the three
   values cc-tapis hard-codes need overriding by hand. */
.ol-pitch .client-crown {
  --fg: #fafafa;
  --cc-blue: rgba(250, 250, 250, 0.62);
  background: #0d0d0d;
  border-color: rgba(250, 250, 250, 0.16);
  box-shadow: 0 18px 42px rgba(13, 13, 13, 0.42);
  width: min(320px, calc(100vw - (var(--client-edge-pad) * 2)));
}

.ol-pitch .client-crown__meta {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.5rem;
  padding: 0.45rem 0.65rem 0.35rem;
  border-bottom-color: rgba(250, 250, 250, 0.16);
  color: rgba(250, 250, 250, 0.45);
}

.ol-pitch .client-crown__choice {
  color: rgba(250, 250, 250, 0.58);
  font-weight: 600;
  text-align: center;
  transition: color var(--dur-ui) var(--ease-spring);
}

.ol-pitch .client-crown__choice:first-child {
  text-align: left;
}

.ol-pitch .client-crown__choice:last-child {
  text-align: right;
}

.ol-pitch .client-crown__choice:hover,
.ol-pitch .client-crown__choice:focus-visible {
  color: var(--fg);
  outline: none;
}

/* The brackets are gone from the markup; this keeps the rule from applying if a
   sibling deck's markup is ever pasted back in. */
.ol-pitch .client-crown__brackets {
  display: none;
}

/* The crawl lane takes over the row the brackets used to hold, so the panel does
   not grow and this works on the capped mobile crown too. */
.ol-pitch .client-crown__crawl {
  position: relative;
  height: 1.35rem;
  overflow: hidden;
  border-bottom: 1px solid rgba(250, 250, 250, 0.16);
}

@media (max-width: 640px) {
  body.ol-pitch {
    --client-crown-mobile-height: 4rem;
  }

  .ol-pitch .client-crown {
    right: max(1rem, env(safe-area-inset-right));
    left: max(1rem, env(safe-area-inset-left));
    width: auto;
  }

  .ol-pitch .client-crown__meta {
    gap: 0.5rem;
    padding: 0.5rem 0.75rem 0.4rem;
  }

  .ol-pitch .client-crown__crawl {
    height: 1.6rem;
  }
}

/* Each runner spans the lane, so `translateX(100%)` is exactly one lane width:
   travel is lane + mark, with no hard-coded panel width to keep in sync. The
   `from` value is the mark's own width, so it enters off-frame instead of
   popping in.

   ONE duration for all twelve, and the only difference is the start offset. That
   is what keeps the column honest: identical speed means the spacing set by the
   delays never changes, so no mark ever overtakes or overlaps another. Giving
   them individual speeds looks livelier for about five seconds and then they
   bunch into white clumps with empty bar between — the relative drift is roughly
   10px per second even for speeds only 2s apart. */
.ol-pitch .client-crown__runner {
  position: absolute;
  inset: 0;
  display: block;
  animation: olRaupeCrawl 12s linear infinite;
  will-change: transform;
}

.ol-pitch .client-crown__raupe {
  position: absolute;
  left: 0;
  top: 50%;
  height: 0.92rem;
  width: auto;
  transform: translateY(-50%);
}

@keyframes olRaupeCrawl {
  from { transform: translateX(-2.4rem); }   /* mark width */
  to   { transform: translateX(100%); }      /* one lane width */
}

/* Twelve marks, one second apart on a 12s cycle: an even 1/12 of the lane between
   each. Travel is lane + mark, so the pitch is 555/12 ≈ 46px against a 37px mark —
   a ~9px gap that stays constant, at every breakpoint (checked: the narrowest
   panel still leaves ~6px). Nothing here to keep in sync by hand: with the count
   and the duration matching, `-1s` per child is exactly one twelfth. */
.ol-pitch .client-crown__runner:nth-child(1)  { animation-delay:  0s; }
.ol-pitch .client-crown__runner:nth-child(2)  { animation-delay: -1s; }
.ol-pitch .client-crown__runner:nth-child(3)  { animation-delay: -2s; }
.ol-pitch .client-crown__runner:nth-child(4)  { animation-delay: -3s; }
.ol-pitch .client-crown__runner:nth-child(5)  { animation-delay: -4s; }
.ol-pitch .client-crown__runner:nth-child(6)  { animation-delay: -5s; }
.ol-pitch .client-crown__runner:nth-child(7)  { animation-delay: -6s; }
.ol-pitch .client-crown__runner:nth-child(8)  { animation-delay: -7s; }
.ol-pitch .client-crown__runner:nth-child(9)  { animation-delay: -8s; }
.ol-pitch .client-crown__runner:nth-child(10) { animation-delay: -9s; }
.ol-pitch .client-crown__runner:nth-child(11) { animation-delay: -10s; }
.ol-pitch .client-crown__runner:nth-child(12) { animation-delay: -11s; }

/* --- 12c / The panel on narrow screens --------------------------------------
   760px is where client-otto.css turns the crown into a full-width fixed bar, so
   the compact treatment starts there rather than at cc-tapis's 640px — otherwise
   the lane keeps its desktop height inside an already-cramped panel.

   The wordmark row is dropped here. It was being clipped anyway: the panel is
   capped at 4.45rem (71px) while meta + lane + bar need 80px, so the bottom of
   "nimmersatt" was cut off on every phone. It is also redundant — the header
   carries the mark and name, and .backtotop keeps the scroll-to-top affordance.
   With it gone the panel is two clean rows and the height below matches them. */
@media (max-width: 760px) {
  body.ol-pitch {
    --client-crown-mobile-height: 3.1rem;
    --client-crown-mobile-space: calc(
      var(--client-crown-mobile-height) + var(--client-crown-mobile-offset) + 0.5rem
    );
  }

  .ol-pitch .client-crown__bar { display: none; }

  .ol-pitch .client-crown__crawl {
    height: 1.65rem;
    border-bottom: 0;      /* it is the panel's last row now */
  }

  .ol-pitch .client-crown__raupe { height: 0.95rem; }
}

@media (max-width: 640px) and (max-height: 650px) {
  .ol-pitch .client-crown__crawl { height: 1.3rem; }
  .ol-pitch .client-crown__raupe { height: 0.8rem; }
}

/* An animated GIF cannot be paused, so the lane goes rather than sit there
   twitching for someone who asked for less motion. */
@media (prefers-reduced-motion: reduce) {
  .ol-pitch .client-crown__crawl { display: none; }
}

/* --- 13b / Partner studios: three across ------------------------------------
   client-otto.css forces this grid to two columns, which left the third studio
   (poly) orphaned on its own row. The cc-tapis default of three is right here. */
@media (min-width: 720px) {
  .ol-pitch .partners {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* --- 14 / Network note under the partner studios --------------------------- */
.ol-network {
  max-width: 56ch;
  margin-top: clamp(1.1rem, 2.2vw, 1.6rem);
  color: var(--ink);
  font-size: clamp(0.78rem, 1vw, 0.9rem);
  font-weight: var(--w-read);
  line-height: 1.6;
}

/* The Charli XCX block used to sit between the section lead and this label.
   With it gone the two collided, so the label carries its own top space. */
.ol-pitch #recaps .section__index {
  margin-top: clamp(2rem, 4vw, 3.25rem);
}

.ol-pitch #recaps {
  min-height: min(100svh, 960px);
  padding-block: clamp(8rem, 12vw, 12rem);
}

.ol-pitch .visual-frontload__music {
  margin-top: clamp(5rem, 10vw, 10rem);
  padding-top: clamp(4rem, 8vw, 8rem);
}

.ol-pitch .visual-frontload__music .manifesto__line {
  max-width: 12ch;
}

.ol-pitch .visual-frontload__fashion {
  margin-bottom: clamp(5rem, 10vw, 10rem);
}

/* --- 14b / Crew: one row, however many people ------------------------------
   client-otto.css pins #exchange to five columns, so the sixth person (Finnian
   joined as Director) wrapped onto a second row. Column auto-flow puts every
   entry in a single row regardless of headcount, so adding a seventh will not
   break it either. Phones still stack — six across 390px is not a row. */
@media (min-width: 761px) {
  .ol-pitch #exchange .projectteam__crew {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
  }
  /* Names have to survive the narrower cell. */
  .ol-pitch #exchange .artist__name {
    font-size: clamp(0.82rem, 1.25vw, 1.15rem);
    hyphens: none;
  }
  .ol-pitch #exchange .artist__role {
    font-size: clamp(0.5rem, 0.68vw, 0.62rem);
  }
  .ol-pitch #exchange .projectperson {
    padding-inline: clamp(0.7rem, 1.2vw, 1.35rem);
  }
}

/* Phones: client-otto.css puts the crew in two columns, which clips the longest
   label ("Photographer") at 390px. Five short rows stack instead. */
@media (max-width: 760px) {
  .ol-pitch #exchange .projectteam__crew {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- 15 / External reference links (Vimeo / YouTube / Instagram) ------------ */
.ol-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 1rem;
}
.ol-links a {
  padding: 0.4rem 0.75rem;
  border: 1px dotted var(--client-line);
  font-family: var(--font-client-mono);
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: color var(--dur-ui) var(--ease-spring), border-color var(--dur-ui) var(--ease-spring);
}
.ol-links a:hover,
.ol-links a:focus-visible {
  color: var(--bg);
  background: var(--fg);
  border-color: var(--fg);
}

/* A project tile that links out rather than playing inline. */
.ol-project__link {
  display: flex;
  flex-direction: column;
  height: 100%;
}

/* --- 16 / Stack cards that carry a film -------------------------------------
   Eight of the thirteen index® stills have a cut behind them. The badge is the
   only thing that separates them from a still-only card, so it has to be
   readable over any frame — hence the plate, not just white type. */
.ol-pitch .conceptstack__card.has-film {
  cursor: pointer;
}

/* The tick sits in the very first pixels of the card, so it stays visible even
   when the card is contracted to an 8px sliver — that is how you can tell which
   of the thirteen stills carry a cut before hovering anything. */
.ol-pitch .conceptstack__filmtick {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 2;
  width: 8px;
  height: 8px;
  background: #fafafa;
  pointer-events: none;
}

/* --- 16b / Cube tile that holds a linked film -------------------------------
   The cube is a 2x2 grid whose media are direct children, so client-otto.css
   targets them by nth-child. A YouTube film has no <video> to autoplay, it needs
   a still plus a link — which means a wrapper, and the wrapper has to reproduce
   what a video in that cell did: fill it, crop, and tilt away from the cube's
   centre. The per-slot values below mirror the nth-child rules in
   client-otto.css, so a film can sit in any of the three media cells. */
.ol-pitch .otto-cubebox__film {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background: var(--bg);
  cursor: pointer;
  transform: scale(1.001) rotateX(0) rotateY(0);
  transition: transform 480ms var(--ease-out-expo), filter 300ms var(--ease-spring);
  will-change: transform;
}

.ol-pitch .otto-cubebox__film img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ol-pitch .otto-cubebox--two-media .otto-cubebox__label {
  grid-column: 1 / -1;
}

.ol-pitch .otto-cubebox__film:nth-child(1) { transform-origin: 100% 100%; }
.ol-pitch .otto-cubebox__film:nth-child(2) { transform-origin: 0 100%; }
.ol-pitch .otto-cubebox__film:nth-child(4) { transform-origin: 0 0; }

.ol-pitch .otto-cubebox__film:hover,
.ol-pitch .otto-cubebox__film:focus-visible {
  z-index: 2;
  filter: contrast(1.06) saturate(1.04);
  outline: none;
}

.ol-pitch .otto-cubebox__film:nth-child(1):hover,
.ol-pitch .otto-cubebox__film:nth-child(1):focus-visible {
  transform: scale(1.08) rotateX(5deg) rotateY(-5deg);
}

.ol-pitch .otto-cubebox__film:nth-child(2):hover,
.ol-pitch .otto-cubebox__film:nth-child(2):focus-visible {
  transform: scale(1.08) rotateX(5deg) rotateY(5deg);
}

.ol-pitch .otto-cubebox__film:nth-child(4):hover,
.ol-pitch .otto-cubebox__film:nth-child(4):focus-visible {
  transform: scale(1.08) rotateX(-5deg) rotateY(5deg);
}

@media (prefers-reduced-motion: reduce) {
  .ol-pitch .otto-cubebox__film { transition: none; }
}

.ol-pitch .conceptstack__play,
.ol-pitch .otto-cubebox__play {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.42rem 0.7rem;
  background: #fafafa;
  color: #0d0d0d;
  font-family: var(--font-client-mono);
  font-size: 0.52rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* No fade: the plate has to be there the moment the card is open, or a quick
     click reads as "nothing happened". */
  opacity: 0;
  pointer-events: none;
}

/* A font-independent play triangle — no glyph that could fall back to a box. */
.ol-pitch .conceptstack__play::before,
.ol-pitch .otto-cubebox__play::before {
  content: "";
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 0.28rem 0 0.28rem 0.46rem;
  border-color: transparent transparent transparent #0d0d0d;
}

.ol-pitch .conceptstack__card.is-open .conceptstack__play { opacity: 1; }
.ol-pitch .conceptstack__card.is-open .conceptstack__filmtick { opacity: 0; }

/* --- 16c / Runway cards: a loop and a credit inside the deck ----------------
   The Fashion Week shows moved out of the #case block into the deck at the top
   (Otto, 04.09.26). client-cc-tapis.css only sizes `img` inside a card, so the
   <video> needs the identical box, or the loop renders at its intrinsic 560px
   inside a card the accordion has already sized. */
.ol-pitch .conceptstack__card video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: contrast(1.03);
  pointer-events: none;
}

/* Bottom right, because the Play plate owns the bottom left. Hidden in the
   sliver for the same reason the plate is: at 8px it would be a smear. */
.ol-pitch .conceptstack__cap {
  position: absolute;
  right: 0;
  bottom: 0;
  z-index: 3;
  display: grid;
  gap: 0.15rem;
  padding: 0.5rem 0.7rem;
  background: #fafafa;
  color: #0d0d0d;
  text-align: right;
  font-family: var(--font-client-mono);
  opacity: 0;
  pointer-events: none;
}

.ol-pitch .conceptstack__capname {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.ol-pitch .conceptstack__capmeta {
  font-size: 0.52rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #5a5a5a;
}

.ol-pitch .conceptstack__card.is-open .conceptstack__cap { opacity: 1; }

/* The cube tile is always fully visible, so its plate is always on — it is the
   only thing telling you this one tile behaves differently from its neighbours.
   It sits at the top edge, not the bottom: the fixed crown panel occupies the
   lower left of the viewport and swallowed it there. */
.ol-pitch .otto-cubebox__play {
  top: 0;
  bottom: auto;
  opacity: 1;
}

/* --- 17 / Film lightbox and the one player --------------------------------
   Shares the still lightbox's blurred backdrop; only the stage differs, because
   a player has no intrinsic size to fit. --film-ar is set per film (the Shorts
   are 9:16), so nothing is ever pillarboxed.

   Otto, 09.09.26: "make full resolution the standard setting for video
   players". YouTube picks its rendition from the size of the player, so the
   stage now takes what the viewport gives it instead of stopping at 1180px —
   at 1440x900 a 16:9 film is about 1370px wide, which is the 1080p bracket.
   --film-max still caps the six 560px runway cuts; upscaling those is worse
   than a smaller stage. */
.ol-pitch .cc-lightbox--film {
  --film-ar: 16 / 9;
  background: rgba(9, 9, 9, 0.72);
}

.ol-pitch .cc-lightbox__stage {
  width: min(92vw, calc((100svh - 8.5rem) * (var(--film-ar))));
  max-width: min(92vw, var(--film-max, 1600px));
  transform: scale(0.94);
  opacity: 0;
  transition: transform 0.42s var(--ease-out-expo), opacity 0.42s var(--ease-out-expo);
}

.ol-pitch .cc-lightbox--film.is-open .cc-lightbox__stage {
  transform: none;
  opacity: 1;
}

/* --- The player itself ------------------------------------------------------
   Otto, 09.09.26: "use the same graphic overlay for every player instead of
   showing some with the youtube overlay directly". One screen, one control bar,
   and behind them either a YouTube iframe running with controls=0 or a local
   <video> with its controls attribute off. Neither brings its own chrome, so a
   YouTube cut and a file on our server are the same object to look at. */
.ol-pitch .ccp {
  position: relative;
}

.ol-pitch .ccp__screen {
  position: relative;
  width: 100%;
  aspect-ratio: var(--film-ar);
  overflow: hidden;
  background: #0d0d0d;
  box-shadow: 0 40px 90px rgba(13, 13, 13, 0.45);
}

.ol-pitch .ccp__yt,
.ol-pitch .ccp__yt iframe,
.ol-pitch .ccp__frame,
.ol-pitch .ccp__native {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.ol-pitch .ccp__native {
  object-fit: contain;
  background: #0d0d0d;
}

/* display: block above outranks the UA rule for [hidden], so the source that is
   not in use has to be switched off explicitly or both would stack. */
.ol-pitch .ccp__yt[hidden],
.ol-pitch .ccp__native[hidden] {
  display: none;
}

/* The picture is a control. Over a cross-origin iframe this layer is the only
   way to read a click at all. */
.ol-pitch .ccp__hit {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

/* The fallback: the API did not load, so YouTube's own controls are showing and
   ours must get out of their way rather than sit on top of them. */
.ol-pitch .ccp.is-bare :is(.ccp__hit, .ccp__bar, .ccp__big) {
  display: none;
}

/* One triangle, drawn once, used by the card mark, the bar and the centre
   plate. That repetition is what makes it read as one piece of hardware. */
.ol-pitch .ccp__tri {
  box-sizing: border-box;
  display: block;
  width: 0;
  height: 0;
  margin-left: 2px;
  border-style: solid;
  border-width: 6px 0 6px 10px;
  border-color: transparent transparent transparent #fafafa;
  transition: border-color 0.22s var(--ease-spring);
}

/* Playing turns the same element into a pause mark, so the button never swaps
   nodes and never reflows the bar. */
.ol-pitch .ccp[data-ccp-state="playing"] .ccp__toggle .ccp__tri {
  width: 12px;
  height: 13px;
  margin-left: 0;
  border-width: 0 4px;
  border-color: #fafafa;
  background: none;
}

.ol-pitch .ccp__big {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: clamp(56px, 6.5vw, 78px);
  aspect-ratio: 1;
  border: 1px solid rgba(250, 250, 250, 0.55);
  background: rgba(9, 9, 9, 0.5);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.9);
  pointer-events: none;
  transition: opacity 0.26s var(--ease-out-expo), transform 0.26s var(--ease-out-expo);
}

.ol-pitch .ccp:is([data-ccp-state="idle"], [data-ccp-state="paused"]) .ccp__big {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.ol-pitch .ccp__big .ccp__tri {
  margin-left: 4px;
  border-width: 11px 0 11px 18px;
}

.ol-pitch .ccp__bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: clamp(0.55rem, 1.3vw, 0.95rem);
  padding: clamp(1.4rem, 3vw, 2.2rem) clamp(0.8rem, 1.8vw, 1.3rem) clamp(0.7rem, 1.4vw, 1rem);
  background: linear-gradient(to top, rgba(9, 9, 9, 0.94) 0%, rgba(9, 9, 9, 0.74) 42%,
                                     rgba(9, 9, 9, 0) 100%);
  font-family: var(--font-client-mono);
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--on-dark);
  transition: opacity 0.3s var(--ease-out-expo);
}

/* The bar steps back while a film runs and comes back on any pointer move. */
.ol-pitch .ccp.is-idle .ccp__bar {
  opacity: 0;
  pointer-events: none;
}

.ol-pitch .ccp__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-width: 36px;
  min-height: 34px;
  padding: 0 0.6rem;
  border: 1px solid rgba(250, 250, 250, 0.28);
  background: rgba(9, 9, 9, 0.5);
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
  transition: background 0.22s var(--ease-spring), color 0.22s, border-color 0.22s;
}

.ol-pitch .ccp__btn:hover,
.ol-pitch .ccp__btn:focus-visible {
  background: #fafafa;
  color: #0d0d0d;
  border-color: #fafafa;
  outline: none;
}

.ol-pitch .ccp__btn:hover .ccp__tri,
.ol-pitch .ccp__btn:focus-visible .ccp__tri {
  border-color: transparent transparent transparent #0d0d0d;
}

.ol-pitch .ccp[data-ccp-state="playing"] .ccp__toggle:is(:hover, :focus-visible) .ccp__tri {
  border-color: #0d0d0d;
}

.ol-pitch .ccp__mute.is-off {
  color: rgba(250, 250, 250, 0.55);
}

.ol-pitch .ccp__time {
  font-variant-numeric: tabular-nums;
  color: rgba(250, 250, 250, 0.72);
}

.ol-pitch .ccp__track {
  position: relative;
  flex: 1 1 auto;
  min-width: 40px;
  height: 22px;
  cursor: pointer;
  touch-action: none;
}

.ol-pitch .ccp__track::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  transform: translateY(-50%);
  background: rgba(250, 250, 250, 0.26);
}

.ol-pitch .ccp__track:focus-visible {
  outline: 1px solid rgba(250, 250, 250, 0.6);
  outline-offset: 2px;
}

.ol-pitch .ccp__fill {
  position: absolute;
  left: 0;
  top: 50%;
  width: 0;
  height: 2px;
  transform: translateY(-50%);
  background: #fafafa;
}

/* The handle rides the end of the fill, so there is one element to move. */
.ol-pitch .ccp__fill::after {
  content: "";
  position: absolute;
  right: -4px;
  top: 50%;
  width: 8px;
  height: 8px;
  transform: translateY(-50%);
  background: #fafafa;
}

.ol-pitch .cc-lightbox__cap {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin-top: 0.75rem;
  font-family: var(--font-client-mono);
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(250, 250, 250, 0.62);
}

.ol-pitch .cc-lightbox__out {
  color: rgba(250, 250, 250, 0.62);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  transition: color var(--dur-ui) var(--ease-spring);
}

.ol-pitch .cc-lightbox__out:hover,
.ol-pitch .cc-lightbox__out:focus-visible {
  color: var(--on-dark);
}

/* The close control sits on a dark backdrop here, not the blurred page. */
.ol-pitch .cc-lightbox--film .cc-lightbox__close {
  border-color: rgba(250, 250, 250, 0.28);
  background: rgba(9, 9, 9, 0.72);
  color: var(--on-dark);
}

.ol-pitch .cc-lightbox--film .cc-lightbox__close:hover,
.ol-pitch .cc-lightbox--film .cc-lightbox__close:focus-visible {
  background: var(--on-dark);
  color: #0d0d0d;
  border-color: var(--on-dark);
}

@media (max-width: 760px) {
  .ol-pitch .cc-lightbox__stage {
    width: min(94vw, calc((100svh - 7rem) * (var(--film-ar))));
  }
  .ol-pitch .ccp__bar {
    gap: 0.5rem;
    font-size: 0.5rem;
  }
  /* Two labels and two clocks do not fit next to a usable scrub track on a
     phone. The elapsed time and the seek bar are what a viewer actually needs. */
  .ol-pitch .ccp__time--total,
  .ol-pitch .ccp__full {
    display: none;
  }
}

/* --- 17b / The play mark on the cards ---------------------------------------
   Otto, 09.09.26: "generally, make a video preview boxes interactive". The
   cards played their loops but said nothing about being clickable — no badge,
   no cursor, nothing separating a card that opens a film from a picture on a
   wall. Same triangle as the player's own controls, injected by main.js onto
   every [data-video-embed] card, so all twenty read identically and one added
   later needs no markup. */
.ol-pitch .ol-project__link[data-video-embed] {
  cursor: pointer;
}

.ol-pitch .ol-project__link:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: -2px;
}

.ol-pitch .ol-play {
  position: absolute;
  left: 0;
  bottom: 0;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.5rem 0.7rem;
  border-top: 1px solid rgba(250, 250, 250, 0.34);
  border-right: 1px solid rgba(250, 250, 250, 0.34);
  background: rgba(9, 9, 9, 0.62);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  color: #fafafa;
  font-family: var(--font-client-mono);
  font-size: 0.5rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  pointer-events: none;
  transition: background 0.24s var(--ease-spring), color 0.24s var(--ease-spring),
              border-color 0.24s var(--ease-spring);
}

.ol-pitch .ol-play__tri {
  box-sizing: border-box;
  display: block;
  width: 0;
  height: 0;
  border-style: solid;
  border-width: 5px 0 5px 8px;
  border-color: transparent transparent transparent #fafafa;
  transition: border-color 0.24s var(--ease-spring);
}

.ol-pitch .ol-project__link:is(:hover, :focus-visible) .ol-play {
  border-color: #fafafa;
  background: #fafafa;
  color: #0d0d0d;
}

.ol-pitch .ol-project__link:is(:hover, :focus-visible) .ol-play__tri {
  border-left-color: #0d0d0d;
}

/* Three cards across on a phone leaves no room for the word. The triangle on
   its own still says the card plays. */
@media (max-width: 700px) {
  .ol-pitch .ol-play {
    padding: 0.4rem 0.45rem;
  }
  .ol-pitch .ol-play__label {
    display: none;
  }
}

/* --- 18 / Contact ledger -----------------------------------------------------
   Otto asked for the closing contacts in the register Radical PR use on their
   own site: three identical blocks, one neutral grotesk at one size, set large
   on black, with the local time next to the name. That face is the deliberate
   exception in this deck — it is a quotation, so it stays a plain grotesk and
   does not borrow the Nimbus display or the mono. */
.ol-pitch .ol-ledger {
  --font-ledger: "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif;
  margin-top: clamp(2.5rem, 5vw, 4rem);
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2.25rem, 4vw, 3.5rem);
  font-family: var(--font-ledger);
  font-size: clamp(1.05rem, 1.9vw, 1.65rem);
  font-weight: 400;
  letter-spacing: -0.012em;
  line-height: 1.22;
}

@media (min-width: 860px) {
  .ol-pitch .ol-ledger { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.ol-pitch .ol-ledger__head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5em;
}

/* Beats `body.ol-pitch h2, h3 { font-family: var(--font-client-display) }`
   further up — and the city is a span for the same reason. */
.ol-pitch .ol-ledger__city {
  text-transform: uppercase;
  color: var(--on-dark);
}

.ol-pitch .ol-ledger__meta {
  color: rgba(250, 250, 250, 0.42);
  font-variant-numeric: tabular-nums;
}

.ol-pitch .ol-ledger__lines {
  margin-top: 1.15em;
  color: var(--on-dark);
}

.ol-pitch .ol-ledger__coffee-copy {
  max-width: 26ch;
  margin-top: 1.15em;
  color: rgba(250, 250, 250, 0.62);
}

.ol-pitch .ol-ledger__link {
  display: inline-block;
  margin-top: 1.15em;
  color: var(--on-dark);
  text-decoration: underline;
  text-decoration-thickness: from-font;
  text-underline-offset: 0.16em;
  transition: color var(--dur-ui) var(--ease-spring);
}

.ol-pitch .ol-ledger__link:hover,
.ol-pitch .ol-ledger__link:focus-visible {
  color: rgba(250, 250, 250, 0.55);
}

.ol-pitch .ol-ledger__links {
  display: grid;
  justify-items: start;
  gap: 0.5rem;
  margin-top: 1.15em;
}

.ol-pitch .ol-ledger__links .ol-ledger__link {
  margin-top: 0;
}

/* A client mark standing in for a plate we hold no footage for. It deliberately
   does NOT carry .concept__rug-img: that class paints a white background, and
   brightness(0) blackens an element's own background too, which turned the
   plate into a solid black square. */
/* The breathing room around a plate is padding on the link, NOT subtracted from
   max-height. `calc(100% - 4rem)` went negative on a short plate (the mobile
   figure is ~110px tall) and clamped the mark to zero height — the Assembly logo
   loaded and then rendered 0x0. Same reason max-height is a plain 100%. */
.ol-pitch .ol-clientplate {
  max-width: min(70%, 240px);
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  filter: brightness(0);
  opacity: 0.78;
}

/* A photographic client mark (index®) — black on a white ground, so it must NOT
   be flattened with brightness(0) (that blackens its own white background into a
   solid square). It sits as a plate like Assembly, just without the filter. */
.ol-pitch .ol-clientplate--photo {
  filter: none;
  opacity: 1;
  max-width: min(74%, 256px);
  max-height: 100%;
}

/* ============================================================================
   16 / DARK GROUNDS — Ottolinger's own contrast
   Two treatments:
     .ol-dark      a whole section on black, type in white
     .ol-splitdark one panel inside a section on black, the rest stays paper
   Both work by re-pointing the design tokens rather than restyling components,
   so every hairline, label and card that already reads `var(--bg)` /
   `var(--client-line)` / `var(--ink)` follows automatically.
   ========================================================================== */
.ol-pitch .ol-dark,
.ol-pitch .ol-splitdark {
  --bg: #0d0d0d;
  --fg: #fafafa;
  --ink: rgba(250, 250, 250, 0.78);
  --ink-strong: rgba(250, 250, 250, 0.93);
  --muted: rgba(250, 250, 250, 0.56);
  --faint: rgba(250, 250, 250, 0.14);
  --panel: #0d0d0d;
  --client: #fafafa;
  --client-brown: #fafafa;
  --client-line: rgba(250, 250, 250, 0.24);
  background: #0d0d0d;
  color: #fafafa;
}

.ol-pitch .ol-dark {
  border-top-color: rgba(250, 250, 250, 0.24);
}

/* Headings, leads and body copy all sit on --fg / --ink, which the block above
   already flipped. These few carry hard-coded colours upstream. */
.ol-pitch .ol-dark :is(h2, h3, .manifesto__line, .concept__title, .ol-project__name,
                       .ol-delivered__num, .ol-delivered__name, .conceptblock strong),
.ol-pitch .ol-splitdark :is(h3, .concept__title) {
  color: var(--fg);
}

.ol-pitch .ol-dark :is(.section__lead, .conceptblock p, .ol-delivered__note,
                       .concept__premise, .ol-project__meta),
.ol-pitch .ol-splitdark :is(.concept__premise, .concept__style) {
  color: var(--ink);
}

/* Logo wall on black: the marks are flattened to one white silhouette instead
   of one black one. */
.ol-pitch .ol-dark .clientlist img {
  filter: brightness(0) invert(1);
  opacity: 0.86;
}

/* The cube number/name plate. */
.ol-pitch .ol-dark .otto-cubebox__label,
.ol-pitch .ol-splitdark .otto-cubebox__label {
  background: #0d0d0d;
  color: var(--fg);
}

/* External-link chips invert on hover so they stay legible on black. */
.ol-pitch .ol-dark .ol-links a:hover,
.ol-pitch .ol-dark .ol-links a:focus-visible,
.ol-pitch .ol-splitdark .ol-links a:hover,
.ol-pitch .ol-splitdark .ol-links a:focus-visible {
  color: #0d0d0d;
  background: var(--fg);
  border-color: var(--fg);
}

/* Split treatment: only the brief panel of a season block goes black, so each
   block reads as one half image, one half statement. */
.ol-pitch .otto-direction-head .concept__brief.ol-splitdark {
  padding: var(--client-box-pad);
}

@media print {
  .ol-pitch .ol-dark,
  .ol-pitch .ol-splitdark {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

@media print {
  .ol-project__media {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

/* ============================================================================
   §17 — Intro block: natural wrapping, one measure

   The lead was three hand-set <span> lines forced to break identically at
   every width (`display:block`, and `white-space:nowrap` under 768px). On
   desktop the 48ch override let the block run 1319px wide while each line
   held ~25 characters, so two fifths of the row sat empty. The spans are gone
   from the markup; these rules give the paragraph a measure it can actually
   fill and let the browser balance the rag.
   ========================================================================== */
.ol-pitch .about__lead {
  max-width: 20ch;
  text-wrap: balance;
  hyphens: none;
}

/* The pair of identical --full / --short bodies collapsed into one, so the
   breakpoint toggle that used to hide one of them is no longer needed. */
.ol-pitch .about__body {
  display: grid;
  max-width: 44ch;
}

@media (max-width: 767px) {
  .ol-pitch .about__lead {
    max-width: 16ch;
    font-size: clamp(1.35rem, 7vw, 1.95rem);
  }
}

/* ============================================================================
   19 / Reading flow

   The original typefaces, hierarchy and monochrome language stay intact.
   These final overrides only tune measure, leading and spacing so longer copy
   reads comfortably without changing the deck's visual identity.
   ========================================================================== */
body.ol-pitch {
  --maxw: 1280px;
  --measure: 62ch;
  --gap-section: clamp(4rem, 7vw, 7rem);
  --client-line: rgba(13, 13, 13, 0.12);
  font-weight: 500;
}

.ol-pitch .ol-dark,
.ol-pitch .ol-splitdark {
  --client-line: rgba(250, 250, 250, 0.16);
}

.ol-pitch :is(.manifesto__line, .section__lead, .concept__title, .closing__line) {
  text-wrap: balance;
}

.ol-pitch :is(
  .section__lead,
  .manifesto__body,
  .concept__premise,
  .conceptblock p,
  .ol-delivered__note,
  .ol-network,
  .roster__lead,
  .closing__lead
) {
  max-width: var(--measure);
  font-size: clamp(1rem, 1.05vw, 1.075rem);
  font-weight: var(--w-read);
  line-height: 1.65;
}

.ol-pitch .section__lead {
  margin-top: clamp(0.9rem, 1.8vw, 1.35rem);
}

.ol-pitch .concept__brief {
  align-content: start;
  row-gap: clamp(0.75rem, 1.5vw, 1.25rem);
}

.ol-pitch .ol-project__body {
  gap: 0.5rem;
}

.ol-pitch .ol-project__meta {
  line-height: 1.5;
}

.ol-pitch .pkg__scope,
.ol-pitch .single__scope {
  max-width: 58ch;
  font-size: 1rem;
  font-weight: var(--w-read);
  line-height: 1.55;
}

.ol-pitch .single {
  grid-template-columns: minmax(10rem, 15rem) minmax(0, 58ch) auto;
  column-gap: clamp(1.25rem, 3vw, 2.5rem);
  padding-block: 1.15rem;
}

@media (max-width: 640px) {
  body.ol-pitch {
    --gap-section: 4rem;
  }

  .ol-pitch :is(
    .section__lead,
    .manifesto__body,
    .concept__premise,
    .conceptblock p,
    .ol-delivered__note,
    .ol-network,
    .roster__lead,
    .closing__lead
  ) {
    line-height: 1.6;
  }

  .ol-pitch .single {
    grid-template-columns: minmax(0, 1fr) auto;
    padding-block: 1rem;
  }
}

/* ============================================================================
   20 / Distillation

   Lines no longer carry the hierarchy. Space, scale and the media do. This
   keeps the original monochrome Nimmersatt language while removing the dotted
   frames and repeated UI scaffolding that made the deck feel overbuilt.
   ========================================================================== */
body.ol-pitch {
  --client-line: transparent;
  --faint: transparent;
}

.ol-pitch .ol-dark,
.ol-pitch .ol-splitdark {
  --client-line: transparent;
  --faint: transparent;
}

.ol-pitch :is(
  .nav,
  .section,
  .casefeature,
  .about__servicesblock,
  .about__services li,
  .ol-delivered,
  .ol-delivered__row,
  .ol-projects,
  .ol-tilegrid,
  .otto-direction-head,
  .concept__head,
  .conceptbody,
  .conceptstack__deck,
  .conceptstack__bar,
  .projectteams,
  .projectteam,
  .projectteam__crew,
  .projectperson,
  .partners,
  .partner,
  .clientlist,
  .pricing__packages,
  .pkg__disc,
  .singles,
  .single,
  .ol-clientnames,
  .closing__foot,
  .client-crown,
  .client-crown__meta,
  .client-crown__crawl
) {
  border: 0;
}

.ol-pitch :is(.hero__meta, .section__index)::before,
.ol-pitch .about__services li::after {
  display: none;
}

.ol-pitch .client-crown {
  box-shadow: none;
}

.ol-pitch :is(.ol-tilegrid, .ol-projects) {
  gap: 1.5rem;
  background: transparent;
}

.ol-pitch :is(.pricing__packages, .projectteam__crew, .partners, .clientlist) {
  gap: 2rem;
  background: transparent;
}

.ol-pitch .projectteams {
  gap: 1.5rem;
  background: transparent;
}

.ol-pitch .conceptbody {
  gap: 1rem;
  margin-top: 1rem;
  background: transparent;
}

.ol-pitch .ol-delivered {
  display: grid;
  gap: 1.5rem;
}

.ol-pitch .ol-links a {
  border: 0;
  padding-inline: 0;
}

.ol-pitch :is(.cc-lightbox__close, .reel__back, .reel__nav) {
  border: 0;
}

@media (max-width: 700px) {
  .ol-pitch :is(.ol-tilegrid, .ol-projects, .pricing__packages, .projectteam__crew, .partners, .clientlist) {
    gap: 1rem;
  }
}

/* ============================================================================
   21 / Artist overview

   One quiet list and one shared preview stage replace the compressed row of
   equal tiles. The small local clips play only while a name is hovered or
   focused; clicking still opens the full reel.
   ========================================================================== */
.ol-pitch #exchange {
  padding-block: clamp(6rem, 10vw, 8rem);
}

/* Otto, 07.09.26: "use the same font for Artists as in WHAT WE DELIVER".
   That heading is .step__name (Apercu Mono, 500, uppercase), and nav.css only
   grants it the mono face through `.step .step__name`, which needs a .step
   ancestor this section does not have. #exchange is not a step chapter and
   should not become one just to inherit a font, so the same !important escape
   from `body.ol-pitch h2 { font-family: var(--font-client-display) }` is
   repeated here, scoped to this one heading. */
.ol-pitch #exchange .step__name {
  font-family: var(--font-client-mono, monospace) !important;
}

/* Otto, 07.09.26: "leave more space between the Assets and artist side" — the
   preview stage is the assets side, the name/role list the artist side. The gap
   was clamp(3rem, 6vw, 6rem), 86px in the 1440 viewport; it is now half again
   as wide. Only the gap grows, the 5/7 split stays, so the stage narrows by the
   same amount rather than the list reflowing. */
.ol-pitch .artist-preview-layout {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: clamp(4.5rem, 9vw, 9rem);
  align-items: start;
  margin-top: clamp(4rem, 8vw, 6rem);
}

.ol-pitch .artist-preview__list.projectteams {
  margin: 0;
}

.ol-pitch .artist-preview__list .projectteam {
  display: block;
  background: transparent;
}

.ol-pitch #exchange .artist-preview__list .projectteam__crew {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.ol-pitch #exchange .artist-preview__list .projectperson {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 1rem;
  min-height: 0;
  padding: 1.5rem 0;
  background: transparent;
  transition: opacity 320ms var(--ease-spring), transform 320ms var(--ease-spring);
}

.ol-pitch #exchange .artist-preview__list .projectperson:hover,
.ol-pitch #exchange .artist-preview__list .projectperson:focus-visible,
.ol-pitch #exchange .artist-preview__list .projectperson.is-previewing {
  background: transparent;
  transform: translateX(0.5rem);
  outline: none;
}

.ol-pitch #exchange .artist-preview__list .artist__name {
  font-size: clamp(1.5rem, 2.4vw, 2rem);
  line-height: 1.1;
}

.ol-pitch #exchange .artist-preview__list .artist__role {
  font-size: 0.875rem;
  line-height: 1.5;
}

.ol-pitch .artist-preview__stage {
  position: sticky;
  top: 6rem;
  margin: 0;
  opacity: 0;
  visibility: hidden;
  transform: translateY(var(--space-3));
  pointer-events: none;
  transition:
    opacity 320ms var(--ease-out-quart),
    transform 320ms var(--ease-out-quart),
    visibility 0s linear 320ms;
}

.ol-pitch .artist-preview-layout.is-preview-active .artist-preview__stage {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  transition-delay: 0s;
}

.ol-pitch .artist-preview__video {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--panel);
  filter: saturate(0.82) contrast(1.02);
}

.ol-pitch .artist-preview__caption {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1rem;
  font-family: var(--font-client-mono);
}

.ol-pitch .artist-preview__name {
  font-size: 1.25rem;
  font-weight: 700;
}

.ol-pitch .artist-preview__role {
  color: var(--muted);
  font-size: 0.875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.ol-pitch #exchange .roster__partners {
  margin-top: 8rem;
  padding-top: 0;
  border: 0;
}

.ol-pitch #exchange .roster__subhead {
  margin-bottom: 2rem;
}

.ol-pitch #exchange .partners {
  gap: 3rem;
}

.ol-pitch #exchange .partner {
  min-height: 0;
  padding: 0;
  background: transparent;
}

.ol-pitch #exchange .ol-network {
  margin-top: 3rem;
}

@media (max-width: 760px) {
  .ol-pitch #exchange {
    padding-block: 4rem;
  }

  .ol-pitch .artist-preview-layout {
    grid-template-columns: 1fr;
    gap: 2.75rem;
    margin-top: 4rem;
  }

  .ol-pitch .artist-preview__stage {
    display: none;
    position: static;
    width: 100%;
    margin: 0 0 var(--space-4);
    transform: translateY(var(--space-2));
  }

  .ol-pitch .artist-preview-layout.is-preview-active .artist-preview__stage {
    display: block;
  }

  .ol-pitch .artist-preview__caption {
    display: none;
  }

  .ol-pitch .artist-preview__video {
    aspect-ratio: 16 / 9;
  }

  .ol-pitch #exchange .artist-preview__list .projectperson {
    padding-block: 1.5rem;
  }

  .ol-pitch #exchange .roster__partners {
    margin-top: 6rem;
  }

  .ol-pitch #exchange .partners {
    gap: 2rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ol-pitch #exchange .artist-preview__list .projectperson {
    transition: none;
  }

  .ol-pitch .artist-preview__stage {
    transition: none;
  }
}

/* ============================================================================
   22 / Type, rhythm and restrained depth

   Two working faces stay in charge: Nimbus for statements, Apercu Mono for
   copy and labels. The hand-drawn hero wordmark remains a brand mark rather
   than becoming a third editorial register.
   ========================================================================== */
html:has(> body.ol-pitch) {
  overflow-x: clip;
}

body.ol-pitch {
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2rem;
  --space-6: 3rem;
  --space-7: 4rem;
  --space-8: 6rem;
  --space-9: 8rem;

  --type-display-xl: clamp(3rem, 9vw, 8rem);
  --type-display: clamp(2.25rem, 5vw, 4.75rem);
  --type-heading: clamp(1.5rem, 2.4vw, 2rem);
  --type-body: clamp(1rem, 1.05vw, 1.075rem);
  --type-label: 0.875rem;

  --leading-display: 0.98;
  --leading-heading: 1.1;
  --leading-body: 1.65;
  --tracking-display: -0.035em;
  --tracking-label: 0.12em;

  --gap-section: clamp(6rem, 8vw, 8rem);
  font-family: var(--font-client-mono);
  font-size: var(--type-body);
  line-height: var(--leading-body);
  overflow-x: clip;
}

.ol-pitch .hero__title {
  font-size: var(--type-display-xl);
  letter-spacing: var(--tracking-display);
}

.ol-pitch :is(.manifesto__line, .closing__line) {
  font-family: var(--font-client-display);
  font-size: var(--type-display);
  font-weight: 700;
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
}

.ol-pitch :is(
  .concept__title,
  .ol-project__name,
  .pkg__name,
  .roster__subhead,
  .partner__name,
  .artist__name
) {
  font-family: var(--font-client-display);
  font-size: var(--type-heading);
  line-height: var(--leading-heading);
  letter-spacing: -0.02em;
}

.ol-pitch :is(
  .section__lead,
  .manifesto__body,
  .concept__premise,
  .conceptblock p,
  .ol-delivered__note,
  .ol-network,
  .roster__lead,
  .closing__lead,
  .pkg__scope,
  .single__scope
) {
  font-family: var(--font-client-mono);
  font-size: var(--type-body);
  line-height: var(--leading-body);
}

.ol-pitch :is(
  .section__index,
  .conceptblock__tag,
  .conceptblock__sub,
  .ol-creditblock__label,
  .ol-project__num,
  .ol-project__meta,
  .artist__role,
  .partner__role,
  .about__emailrole,
  .about__serviceslabel,
  .single__price,
  .pkg__price
) {
  font-family: var(--font-client-mono);
  font-size: var(--type-label);
  line-height: 1.5;
  letter-spacing: var(--tracking-label);
}

/* The media is deliberately taller than its clip. GSAP only moves these five
   selected images and films, leaving the text hierarchy completely still. */
@media (min-width: 768px) {
  .motion-on .ol-pitch [data-parallax] {
    overflow: hidden;
  }

  .motion-on .ol-pitch [data-parallax-media] {
    display: block;
    width: 100%;
    height: 112%;
    margin-top: -6%;
    object-fit: cover;
    will-change: transform;
  }
}

@media (max-width: 767px) {
  body.ol-pitch {
    --gap-section: var(--space-7);
    --type-display: clamp(2rem, 10vw, 3rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  .ol-pitch [data-parallax-media] {
    transform: none !important;
    will-change: auto;
  }
}

/* ============================================================================
   23 / ASCII

   The hero used to render its fashion films as coloured ASCII behind a
   caterpillar-shaped clip. Otto replaced that mark on 08.09.26 (see §1
   .hero-raupe), so only the closing signal is still drawn in characters.
   ========================================================================== */
.ol-pitch .hero--masked-ascii {
  gap: var(--space-5);
}

.ol-pitch .closing__line.closing__line--ascii {
  width: fit-content;
  max-width: min(100%, 48rem);
  margin-right: auto;
  font-family: var(--font-client-mono) !important;
  font-size: clamp(0.34rem, 0.58vw, 0.6rem) !important;
  font-weight: 500;
  line-height: 0.96 !important;
  letter-spacing: 0.01em !important;
}

.ol-pitch .closing__line--ascii .ascii-art {
  max-width: 100%;
  overflow: visible;
  padding: 0;
  font: inherit;
}

@media (max-width: 767px) {
  .ol-pitch .hero--masked-ascii {
    gap: var(--space-4);
  }

  .ol-pitch .hero--masked-ascii .hero__title {
    font-size: clamp(2rem, 8.5vw, 2.5rem);
  }
}

/* ============================================================================
   §23 — Phone cut of the client wall and the recap / music cards
   Emilio, 04.09.26, two notes in one round.
   ========================================================================== */

/* --- 23a / Client wall on a phone -------------------------------------------
   "auf vier Zeilen anpassen, sodass wir mehrere Logos gleichzeitig sehen, aber
   bitte, dass da trotzdem Symmetrie drin ist. Es soll nicht unten links in die
   zwei sein."

   Four rows exactly, which is five marks across: 18 over five columns is
   5 + 5 + 5 + 3. Four across would have been five rows ending in the pair
   bottom left that he called out by name. The remainder of three is centred
   rather than left-aligned, so the block stays symmetric: the grid runs on ten
   half-tracks, every mark spans two, and the third-from-last is pinned to
   track 3, which lands the final three in the middle six tracks.
   The pin is written against a remainder of three. A wall whose count is a
   multiple of five needs the nth-last-child rule deleted with it. */
@media (max-width: 899px) {
  .ol-pitch .clientlist {
    grid-template-columns: repeat(10, 1fr);
  }

  .ol-pitch .clientlist li {
    grid-column: span 2;
  }

  /* Start AND end: `grid-column: span 2` above leaves the end line at auto, so
     overriding only the start would silently shrink this one cell to a single
     track. */
  .ol-pitch .clientlist li:nth-last-child(3) {
    grid-column: 3 / span 2;
  }
}

/* "auf Mobile sind die Logos viel zu groß." The cell was 110px tall for a 44px
   mark, which is why two of them filled a phone screen. Both come down, and the
   mark is allowed a wider share of the narrower cell so it does not also lose
   width. */
@media (max-width: 559px) {
  .ol-pitch .clientlist {
    gap: 0.75rem;
  }

  /* The cc-tapis base pads every cell with --client-row-pad, 15px a side. At a
     59px cell that left 28px for the mark itself, so the padding comes in with
     the cell. */
  .ol-pitch .clientlist li {
    min-height: 58px;
    padding: 0.3rem;
  }

  .ol-pitch .clientlist img {
    max-width: 92%;
    max-height: 26px;
  }
}

/* --- 23b / Recap and music cards on a phone ---------------------------------
   "die drei in einer Zeile ... das Wording drunter ... einfach in einer Zeile
   passt. Dafür kannst du auch gerne eine kleinere Fontgröße benutzen."

   Three across a 390px phone is a ~104px card, and the card body was still
   carrying desktop type: a 24px display name and a 14px label line with 0.16em
   tracking. Both are re-cut here, the row padding comes in so the text keeps
   its width, and the halves of the meta line that the section heading already
   states are dropped, which is what lets the rest sit on one line without
   truncating anything. Desktop is untouched. */
@media (max-width: 700px) {
  .ol-pitch .ol-projects {
    gap: 0.5rem;
  }

  .ol-pitch .ol-project__body {
    gap: 0.2rem;
    padding: 0.5rem 0.15rem 0;
  }

  .ol-pitch .ol-project__name {
    font-size: 0.8rem;
    line-height: 1.15;
    letter-spacing: -0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .ol-pitch .ol-project__meta {
    font-size: 0.5rem;
    letter-spacing: 0.06em;
    line-height: 1.4;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* The half already carried by "Event recaps." / "Music videos." above. */
  .ol-pitch .ol-project__meta-lead,
  .ol-pitch .ol-project__meta-tail {
    display: none;
  }
}

/* ============================================================================
   §24 — Otto, 07.09.26. One phone round plus two contact rows.

   Everything in this section is either scoped to a phone breakpoint or is a
   contact row that did not exist before, so the desktop composition he asked
   to keep is untouched.
   ========================================================================== */

/* --- 24a / The two named contacts under the hello mailbox --------------------
   "beides soll zusammen so gross sein wie die gesamte hello mail. spiel mit
   kursiver schriftart, um die berufszeichnung zu markieren."

   The hello row is one address at --text-sub over one tracked uppercase label.
   The pair below is that row split in two: each address is half the size, so
   the two of them stack into the same block of ink, and the role drops the
   uppercase label treatment for italic — a person reads differently from a
   mailbox. */
.ol-pitch .about__people {
  display: grid;
  gap: clamp(0.5rem, 1.2vw, 0.9rem);
  margin-top: clamp(0.7rem, 1.8vw, 1.3rem);
}

.ol-pitch .about__emailrow--person {
  margin-top: 0;
}

.ol-pitch .about__emailrow--person .about__email {
  font-size: max(calc(var(--text-sub) * 0.5), 0.75rem);
}

/* The role keeps the same ratio to its address that "General enquiries" keeps
   to the hello mail (0.71), so the pair reads as one system rather than as two
   different labels. Both carry a floor: half of --text-sub is 8.8px on a
   phone, and 6px italic underneath it would be decoration, not a job title. */
.ol-pitch .about__emailrole--title {
  margin-top: 0.2rem;
  font-size: max(calc(var(--text-sub) * 0.355), 0.62rem);
  font-style: italic;
  letter-spacing: 0.04em;
  text-transform: none;
}

/* --- 24b / Six client marks per row, on a phone too -------------------------
   "fuer mobile ebenfalls die anordnung der client logos ueberarbeiten, gleiche
   es aus, dass in jeder reihe 6 logos nebeneinander stehen. ich moechte keine
   zeilenbrueche."

   §23a cut the wall to five across below 900px, which is what produced the
   ragged 5 / 5 / 5 / 3 he is looking at. Eighteen marks over six columns is
   three full rows with no remainder at every width, so the ten-half-track grid
   and the nth-last-child pin that centred that remainder both come out. The
   cells are narrow at 390px, so the gap and the mark cap come down with them
   rather than the marks overflowing their cell. */
@media (max-width: 899px) {
  .ol-pitch .clientlist {
    grid-template-columns: repeat(6, 1fr);
    gap: 0.6rem;
  }

  .ol-pitch .clientlist li,
  .ol-pitch .clientlist li:nth-last-child(3) {
    grid-column: auto;
  }

  .ol-pitch .clientlist li {
    min-height: 54px;
    padding: 0.25rem;
  }

  .ol-pitch .clientlist img {
    max-width: 100%;
    max-height: 24px;
  }
}

@media (max-width: 559px) {
  .ol-pitch .clientlist {
    gap: 0.35rem;
  }

  .ol-pitch .clientlist li {
    min-height: 40px;
    padding: 0.15rem;
  }

  .ol-pitch .clientlist img {
    max-width: 100%;
    max-height: 18px;
  }
}

/* --- 24c / The hero line holds one line on a phone --------------------------
   "achte darauf, dass der satz 'Artist management and creative production from
   Berlin.' keinen zeilenbruch auf mobil hat."

   Fifty-four monospaced characters across a 390px screen only fit if the type
   scales with the viewport, so the size is a vw value capped at the size it
   already had. The hero also gives up its shell padding here, because those
   two gutters are worth about a character and a half each. */
@media (max-width: 767px) {
  .ol-pitch .hero--masked-ascii {
    padding-inline: 0.5rem;
  }

  .ol-pitch .hero__sub {
    max-width: none;
    white-space: nowrap;
    font-size: min(1.02rem, 2.32vw);
  }
}

/* --- 24d / A smaller phone type scale ---------------------------------------
   "allgemein kann der text insgesamt fuer mobile etwas kleiner gemacht
   werden." The tokens carry most of the page, so they come down together
   rather than every rule being re-cut one at a time. The hero wordmark and the
   ASCII closing are marks, not text, and keep their size. */
@media (max-width: 767px) {
  body.ol-pitch {
    --type-body: 0.9rem;
    --type-label: 0.78rem;
    --type-heading: clamp(1.2rem, 4.8vw, 1.5rem);
    --type-display: clamp(1.75rem, 8.4vw, 2.6rem);
  }

  .ol-pitch .about__lead {
    font-size: clamp(1.2rem, 6.2vw, 1.75rem);
  }

  .ol-pitch .closing__lead,
  .ol-pitch .section__lead {
    font-size: var(--type-body);
  }
}

/* --- 24e / The artist assets show themselves on a phone ---------------------
   "kannst du dir bitte einen weg ueberlegen wie fuer mobile zugang die assets
   bei den artists direkt angezeigt werden? fuer desktop alles behalten wie es
   ist."

   js/artist-preview.js inserts one .artist-inline per name below 760px and
   loads it on scroll. The shared hover stage has nothing left to do there, so
   it is taken out of the phone layout entirely instead of waiting for a tap
   that no longer comes. */
.ol-pitch .artist-inline {
  display: none;
  margin: 0 0 var(--space-4);
}

.ol-pitch .artist-inline__video {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--panel);
  filter: saturate(0.82) contrast(1.02);
}

@media (max-width: 760px) {
  .ol-pitch .artist-inline {
    display: block;
  }

  .ol-pitch .artist-preview__stage,
  .ol-pitch .artist-preview-layout.is-preview-active .artist-preview__stage {
    display: none;
  }

  .ol-pitch #exchange .artist-preview__list .projectperson {
    padding-block: 0.9rem 0.75rem;
  }
}

/* ============================================================================
   §25 — Otto, 07.09.26: "die client-logos sind gut so wie sie sind. vielleicht
   kann man die noch ein bisschen interaktiv einbauen."
   So the wall keeps everything it has — 6 x 3 at every width, the marks
   flattened to one silhouette — and only gains an answer to the pointer. The
   hovered mark comes up to full strength, lifts just far enough to clear room
   below it, and its name fades in there in the same mono label the rest of the
   pitch uses. Every <li> carries the name in data-client, so this needs no
   script and no second copy of the list.
   Pointer-only on purpose. A phone has no hover, and Otto said the wall is
   already right there; the names stay available as alt text.
   ========================================================================= */
@media (hover: hover) and (pointer: fine) {
  .ol-pitch .clientlist li {
    position: relative;
    overflow: hidden;
  }

  .ol-pitch .clientlist li img {
    transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.34s ease;
  }

  .ol-pitch .clientlist li::after {
    content: attr(data-client);
    position: absolute;
    left: 50%;
    bottom: clamp(0.6rem, 1.3vw, 1rem);
    max-width: 86%;
    transform: translate(-50%, 6px);
    font-family: var(--font-client-mono);
    font-size: clamp(0.5rem, 0.72vw, 0.63rem);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    opacity: 0;
    pointer-events: none;
    transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.34s ease;
  }

  .ol-pitch .clientlist li:hover img,
  .ol-pitch .clientlist li:focus-within img {
    opacity: 1;
    transform: translateY(-0.62rem) scale(1.04);
  }

  .ol-pitch .clientlist li:hover::after,
  .ol-pitch .clientlist li:focus-within::after {
    opacity: 0.7;
    transform: translate(-50%, 0);
  }
}

/* Someone who asked for less movement gets the strength shift and the name,
   never the travel. */
@media (prefers-reduced-motion: reduce) {
  .ol-pitch .clientlist li img,
  .ol-pitch .clientlist li::after {
    transition: opacity 0.2s ease;
  }
  .ol-pitch .clientlist li:hover img {
    transform: none;
  }
  .ol-pitch .clientlist li:hover::after {
    transform: translate(-50%, 0);
  }
}

/* ============================================================================
   §26 — Otto, 08.09.26, "Paris FW pitch (1).pdf": the page becomes the funnel
   in that deck. Everything here is new furniture for sections that did not
   exist before, plus three corrections that the reordering forced. No token,
   no face and no existing component was changed to make it fit.
   ========================================================================== */

/* --- 26a / Hero: the Paris window and the mail rows -------------------------
   The About page is deleted, so its contact block moves into the hero. It keeps
   .about__body, which is what makes the type identical to the page it came
   from; only the centring is new, because the About page was left-aligned and
   the hero is a centred column. */
.ol-pitch .hero__paris {
  margin: 0 auto;
  display: grid;
  justify-items: center;
  gap: 0.4rem;
  font-family: var(--font-client-mono);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--muted);
}

.ol-pitch .hero__paris-meta {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.ol-pitch .hero__mails {
  max-width: none;
  margin-inline: auto;
  justify-items: center;
  text-align: center;
  gap: 0;
}

/* The hero sub line is 39 characters now ("Creative production for fashion
   brands."), where the rule above it was cut for the 54-character sentence it
   replaced. Martian Mono's advance is exactly 0.7em, so the line needs
   39 * 0.7 * font-size px and the vw ceiling can rise from 2.24 to 3.4 without
   ever wrapping — at 320px that is 10.9px against the 11.1px the line has room
   for. Without this the sentence renders far smaller than it needs to. */
.ol-pitch .hero__sub {
  font-size: min(var(--text-sub), 3.4vw);
}

/* --- 26b / Fixed column counts for the two new work grids -------------------
   .ol-projects is auto-fit at 240px, which lands 8 cards as 4 + 4 in the shell
   but 9 as 4 + 4 + 1. The funnel's new sections declare their own count so none
   leaves a widow. Since Otto's round 2 the counts sit on the other sections:
   Editorial holds the 8 runway cuts over four columns, Fashion Week the 6
   placeholder stills and Fashion films the 9 index cuts over three. Below 900px
   the auto-fit takes over again and §23b's phone cuts apply unchanged. */
@media (min-width: 900px) {
  .ol-pitch .ol-projects--four  { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .ol-pitch .ol-projects--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* --- 26c / Editorial placeholders ------------------------------------------
   Otto has no editorial assets yet ("just duplicate the images from another
   section as place holder"), so these six are runway stills that already ship
   with the site. They are <figure>, not <a>: there is no film behind a still,
   and the hover lift on a project card promises one. */
.ol-pitch .ol-project--still .ol-project__media {
  cursor: default;
}

.ol-pitch .ol-project--still:hover .ol-project__media img {
  transform: none;
}

.ol-pitch .section__note {
  margin-top: clamp(1rem, 2vw, 1.6rem);
  font-family: var(--font-client-mono);
  font-size: var(--text-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --- 26d / The offer: three Paris blocks above the priced packages ----------
   PDF: "WE'LL BE IN PARIS. 1-11 October ... RUNWAY & BACKSTAGE / CAMPAIGNS &
   CONTENT / CREATIVE SUPPORT ... Small or big. Planned or spontaneous."
   The four packages under them are untouched (Otto: "keep pricing packes as
   currently displayed"). */
.ol-pitch .offer__dates {
  margin-top: clamp(1.2rem, 2.4vw, 2rem);
  font-family: var(--font-client-mono);
  font-size: var(--text-label);
  font-weight: 600;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.ol-pitch .offer__lead {
  max-width: 28ch;
  margin-top: clamp(0.7rem, 1.4vw, 1.1rem);
  font-family: var(--font-client-display);
  font-size: clamp(1.35rem, 3.2vw, 2.4rem);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.03em;
  color: var(--ink);
}

.ol-pitch .offer__blocks {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin-top: clamp(2rem, 4vw, 3.25rem);
  border-top: 1px dotted var(--client-line);
}

@media (min-width: 860px) {
  .ol-pitch .offer__blocks { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.ol-pitch .offer__block {
  padding: clamp(1.1rem, 2.2vw, 1.7rem) 0;
  border-bottom: 1px dotted var(--client-line);
}

@media (min-width: 860px) {
  .ol-pitch .offer__block + .offer__block {
    padding-left: clamp(1.2rem, 2.4vw, 2rem);
    border-left: 1px dotted var(--client-line);
  }
}

.ol-pitch .offer__block-name {
  font-family: var(--font-client-display);
  font-size: clamp(1rem, 1.7vw, 1.35rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
  text-transform: uppercase;
}

.ol-pitch .offer__block-meta {
  margin-top: 0.55rem;
  font-family: var(--font-client-mono);
  font-size: 0.56rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

.ol-pitch .offer__note {
  margin-top: clamp(1.4rem, 2.8vw, 2.2rem);
  font-family: var(--font-client-mono);
  font-size: var(--text-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
}

/* --- 26e / A third block in the closing ledger ------------------------------
   The PDF's closing page adds the Paris window between the studio and the
   coffee date. Two columns would leave it hanging alone on a second row, so
   the ledger takes a third track once there is width for three. */
@media (min-width: 1100px) {
  .ol-pitch .ol-ledger--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* --- 26f / The claim, last thing on the page --------------------------------
   Otto: "put 'you are what you eat' down below at the very end of the pitch."
   It was the About page's lead and the About page is gone, so this is now the
   only place the claim stands. Same display face and same treatment
   .about__lead gave it, centred and alone. */
.ol-pitch .eat {
  padding-block: clamp(6rem, 12vw, 11rem) clamp(3rem, 6vw, 5rem);
  text-align: center;
}

.ol-pitch .eat__line {
  max-width: 16ch;
  margin-inline: auto;
  font-family: var(--font-client-display);
  font-size: clamp(1.9rem, 7vw, 5.5rem);
  font-weight: 700;
  line-height: 0.98;
  letter-spacing: -0.04em;
  text-wrap: balance;
  hyphens: none;
  color: var(--fg);
}

/* The foot line was written for the black closing section, so its spans are
   --on-dark-muted. On this light ground that is white on white. */
.ol-pitch .eat .closing__foot {
  margin-top: clamp(4rem, 8vw, 7rem);
  justify-content: center;
  gap: clamp(1rem, 3vw, 2.5rem);
}

.ol-pitch .eat .closing__foot span {
  color: var(--muted);
}

/* --- 26g / Corrections the reordering forced --------------------------------
   1. #recaps used to open with a heading and carry the label further down, so
      §-earlier gave that label a top margin. The label is the first thing in
      the section now and that margin pushes it off the section's own padding.
   2. Music videos left #recaps for a section of their own, so the spacing that
      used to come from .visual-frontload__music has to come from the section.
   3. The deck at the foot needs the same air as the sections above it. */
.ol-pitch #recaps .section__index {
  margin-top: 0;
}

.ol-pitch #music-videos {
  min-height: 0;
  padding-block: clamp(6rem, 10vw, 10rem);
}

.ol-pitch #archive {
  padding-block: clamp(6rem, 10vw, 10rem);
}

/* --- 26h / The hero has to survive four more rows ---------------------------
   The About page's contact block and the PDF's Paris window add ~340px to a
   hero that was already a full viewport, which at 1440 x 900 pushed the
   wordmark off the top of the screen. Nothing is dropped; the composition is
   re-cut so it still lands in one screen on a laptop:
   the column gap comes in, the four mail rows sit closer together, and the
   caterpillar is bound by the viewport's HEIGHT as well as its width. The
   aspect is 1504 / 846, so 32vh of height is 1.778 x that in width — on a
   taller screen the mark grows back to its full 760px on its own. */
.ol-pitch .hero {
  gap: clamp(0.8rem, 1.8vw, 1.6rem);
  /* .hero__title runs at line-height 0.9 and this display face overshoots its
     line box, so a hero whose content exactly equals 100svh clips the tops of
     the letters. The padding is the slack that keeps the wordmark whole. */
  padding-block: clamp(1.4rem, 3vw, 2.25rem);
}

/* The two named contacts sit side by side from tablet up. Stacked they cost the
   hero another ~50px of height, which is the difference between the caterpillar
   at 528px and at 448px. */
@media (min-width: 600px) {
  .ol-pitch .hero__mails .about__people {
    grid-template-columns: repeat(2, auto);
    justify-content: center;
    align-items: start;
    column-gap: clamp(1.5rem, 4vw, 3.5rem);
  }
}

.ol-pitch .hero__mark.hero-raupe {
  width: min(72vw, 760px, calc(33vh * 1.778));
}

.ol-pitch .hero__mails .about__emailrow {
  margin-top: clamp(0.35rem, 0.9vw, 0.7rem);
}

.ol-pitch .hero__mails .about__emailrow:first-child,
/* §24 zeroes this margin on .about__emailrow--person; the rule above is one
   class more specific and would otherwise drop the second contact half a line
   below the first. */
.ol-pitch .hero__mails .about__emailrow--person {
  margin-top: 0;
}

.ol-pitch .hero__mails .about__people {
  margin-top: clamp(0.5rem, 1.2vw, 0.9rem);
  gap: clamp(0.35rem, 0.8vw, 0.6rem);
}

/* ============================================================================
   §27 — Otto, 08.09.26 round 2 (the five-point prompt in the channel).
   Four of his five points are markup moves and live in index.html; the three
   that need CSS are here. Nothing below introduces a token, a face or a size
   that the page did not already use.
   ========================================================================== */

/* --- 27a / The Paris window gets its own air -------------------------------
   "Vergroessere den Abstand zwischen der Haupt-Headline und den Reisedaten,
   sodass die Daten optisch klarer und separater hervorgehoben sind."
   §26a set `margin: 0 auto` on this line, so the only thing separating it from
   the sub line was the hero's own column gap, which §26h had already cut to
   make room for the contact rows. It gets that space back on its own account,
   which is affordable because 27b takes two rows out of the block below it. */
.ol-pitch .hero__paris {
  margin-top: clamp(0.7rem, 2.2vw, 1.8rem);
}

/* --- 27b / Contacts split to the two edges of the page ---------------------
   "Die beiden E-Mail-Adressen linksbuendig anordnen. Emiliano und Theresa
   rechtsbuendig auf der Seite platzieren."
   The block was a centred column of two mail rows with the two named contacts
   under them. It becomes two columns pinned to the shell's edges: the mailboxes
   left, the people right, spanning both rows. That is a row shorter than the
   stack it replaces, so the hero keeps its single screen.
   Below 720px there is not enough width for two addresses side by side, so the
   centred composition from §26h stands unchanged. */
@media (min-width: 720px) {
  .ol-pitch .hero__mails {
    width: 100%;
    grid-template-columns: auto auto;
    justify-content: space-between;
    align-items: start;
    column-gap: clamp(1.5rem, 5vw, 4rem);
    justify-items: start;
    text-align: left;
  }

  .ol-pitch .hero__mails > .about__emailrow {
    grid-column: 1;
  }

  /* §26h put the two people side by side to save the hero ~50px of height.
     Stacked in their own column they cost nothing: the left column is two mail
     rows tall and the right column is two person rows tall. */
  .ol-pitch .hero__mails .about__people {
    grid-column: 2;
    grid-row: 1 / span 2;
    grid-template-columns: 1fr;
    justify-items: end;
    text-align: right;
    margin-top: 0;
  }
}

/* --- 27c / The black space under the event recaps --------------------------
   "Verringere den Leerraum (Black Space) direkt unterhalb der Event Recaps
   deutlich, damit kein so grosser Abstand zum naechsten Abschnitt entsteht."
   Measured, not guessed: the space below the last card was the section's own
   `padding-block: clamp(8rem, 12vw, 12rem)`, the widest block padding on the
   page — 154 / 173 / 192px at 1280 / 1440 / 1920. It now runs
   clamp(2.75rem, 5vw, 4.5rem) at the foot and lands at 64 / 72 / 72px, and the
   top keeps the same air the other sections use.
   The `min-height: min(100svh, 960px)` goes with it. It is left over from when
   this section also carried the Charli XCX frontload and the music videos, both
   of which moved out; with three cards it no longer binds at any width I
   measured, so it is dead weight that would silently re-open the gap on a
   short viewport. */
.ol-pitch #recaps {
  min-height: 0;
  padding-block: clamp(6rem, 10vw, 10rem) clamp(2.75rem, 5vw, 4.5rem);
}

/* ============================================================================
   §28 — Otto, 09.09.26. Four screenshots plus the pricing prompt.

   Nothing below introduces a colour, a typeface or a motion idiom the page did
   not already use. The one new surface is the filled Specs control in
   pricing.css, and its fill is the same near-black as the type.
   ========================================================================== */

/* --- 28a / The hero: more air, and a composition instead of a stack ---------
   "leave more space between the written text info. also, make the layouting
   more playful instead of just plain."
   Two moves, no new furniture:
   1. The column gap goes back up. §26h had squeezed it to clamp(0.8, 1.8vw,
      1.6rem) to fit the contact rows into one screen; that squeeze is what made
      the block read as one undifferentiated stack.
   2. The Paris window becomes a marked strip, tilted. The black bar is the
      page's own language — the crown at the foot is #0d0d0d with #fafafa on it —
      and the tilt is the hand-drawn wordmark's energy, not a new effect. The
      second mailbox steps in behind the first so the left column reads as two
      steps rather than one flush block.
   Cost: the hero is taller than it was and no longer holds one screen on a
   short laptop. It did not hold one on a 720px-high window before this either
   (778px against 720 after round 2) — this widens that gap, deliberately,
   because "more space" and "one screen" cannot both be true here. */
.ol-pitch .hero {
  gap: clamp(1.25rem, 2.6vw, 2.3rem);
}

.ol-pitch .hero__paris {
  justify-self: center;
  margin-top: clamp(0.9rem, 2.6vw, 2.1rem);
  padding: 0.62rem 1.05rem 0.55rem;
  background: #0d0d0d;
  color: rgba(250, 250, 250, 0.62);
  transform: rotate(-1.2deg);
}

.ol-pitch .hero__paris-meta {
  color: #fafafa;
}

.ol-pitch .hero__mails {
  margin-top: clamp(0.6rem, 1.8vw, 1.5rem);
}

.ol-pitch .hero__mails .about__emailrow {
  margin-top: clamp(0.55rem, 1.3vw, 1rem);
}

.ol-pitch .hero__mails .about__people {
  margin-top: clamp(0.75rem, 1.8vw, 1.35rem);
  gap: clamp(0.55rem, 1.2vw, 0.95rem);
}

/* The air above has to be paid for out of the hero's own budget or the wordmark
   goes off the top of a laptop screen. §26h bound the caterpillar to 33vh of
   height; at 28vh it gives back the ~45px the new gaps cost. Measured, not
   assumed: hero == viewport at 1440x900, 1600x900 and 1920x1080, and the mark
   is 448px at 1440x900 where it was 528. On a window shorter than about 870px
   the hero runs past one screen — it did before this round too (778 against 720
   at 1440x720 after round 2, 840 now), and "more space" and "one screen" cannot
   both hold on a 720px-high laptop. */
.ol-pitch .hero__mark.hero-raupe {
  width: min(72vw, 760px, calc(26.3vh * 1.778));
}

/* The step. Only from 720px up, where §27b has already pinned the two mailboxes
   into a left column; below that the block is centred and an indent would just
   look like a mistake. */
@media (min-width: 720px) {
  .ol-pitch .hero__mails > .about__emailrow:nth-of-type(2) {
    padding-left: clamp(1.5rem, 3.5vw, 3rem);
  }
}

/* --- 28b / Services, lifted out of the offer -------------------------------
   "make the overall services a little bigger and text interactive."
   Bigger: the block names go from clamp(1, 1.7vw, 1.35rem) to the display size
   the section headings' younger sibling uses, and the meta line leaves its
   0.56rem hard-coded size for the page's own --type-label.
   Interactive: hovering a block decodes its name, the same text-scramble the
   artist roster runs, and the block lifts the way a project card does. */
.ol-pitch #services .offer__blocks {
  margin-top: clamp(2.25rem, 4.5vw, 3.5rem);
}

.ol-pitch .offer__block-name {
  font-size: clamp(1.3rem, 2.5vw, 2rem);
  transition: transform var(--dur-ui, 0.2s) var(--ease-spring, ease);
}

.ol-pitch .offer__block-meta {
  margin-top: 0.7rem;
  font-size: var(--type-label);
  letter-spacing: var(--tracking-label);
  line-height: 1.5;
}

.ol-pitch .offer__block {
  transition: color var(--dur-ui, 0.2s) var(--ease-spring, ease);
}

@media (hover: hover) and (pointer: fine) {
  .ol-pitch .offer__block:hover .offer__block-name {
    transform: translateY(-0.18rem);
  }
  .ol-pitch .offer__block:hover .offer__block-meta {
    color: var(--fg);
  }
}

/* --- 28c / One-line card headlines -----------------------------------------
   "kasia kucharska is breaking the lines, make all headlines in a size that its
   one liners" and, for the music cards, "make all music video titels in a font
   size so they dont break off like with maylie."
   The theme sets every .ol-project__name to --type-heading, which is up to 2rem.
   "Kasia Kucharska" at 2rem is wider than a quarter of the shell, so it wrapped
   and pushed its own meta line down, which is what makes that one card look a
   row taller than its neighbours. The two grids that hold long labels get their
   own ceiling and nowrap; every other grid keeps --type-heading untouched.
   nowrap without a ceiling would overflow instead of wrapping, so the two
   always travel together. Verified across widths, not estimated. */
.ol-pitch .ol-projects--four .ol-project__name,
.ol-pitch .ol-projects--music .ol-project__name {
  font-size: clamp(0.8rem, 1.5vw, 1.4rem);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The music meta line is the one Otto pointed at ("Tassère les passés · Music
   video" broke after MUSIC). It cannot be sized in vw: the shell stops growing
   at its max width while the viewport does not, so these cards are WIDER at
   1280 than at 1920 and a vw rule overshoots exactly where there is least room.
   It is sized against its own card instead. 30 characters at Martian Mono's
   0.7em advance plus 0.12em tracking need 24.6 x the font size, so 3.8% of the
   card width leaves the longest label a margin at every width measured.
   If a browser does not know cqi the whole declaration is dropped and the line
   falls back to --type-label, which is why the ellipsis stays as a floor.
   The editorial meta is deliberately NOT held to one line: "Berlin SS26 ·
   Funkturm-Palais" in a quarter-shell column would have to run at 8.6px to fit,
   which is below anything else on the page. It wraps, as it already did in the
   screenshot Otto sent, and he flagged the headline above it, not this. */
.ol-pitch .ol-projects--music .ol-project__body {
  container-type: inline-size;
}

/* Only from 900px, where the three music cards are at their full width. Below
   that the longest label cannot hold one line at any size anyone would read —
   measured, it would need 3.2px at 320 — so it wraps there, as it always has.
   Otto's screenshot is a 1512px window. */
@media (min-width: 900px) {
  .ol-pitch .ol-projects--music .ol-project__meta {
    font-size: clamp(0.5rem, 3.8cqi, 0.8rem);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* --- 28d / Pricing ---------------------------------------------------------
   "Der specs button muss etwas groesser und unmittelbar unter dem preis
   markiert sein. Ebenfalls bau ein interaktive reaction des Textes ein."
   The control itself is in pricing.css. What has to happen here is the price:
   the theme pins .pkg__price to --type-label (0.875rem), which is now SMALLER
   than the Specs control above it, and a card whose button outweighs its price
   is telling the client the wrong thing. The figure goes up to a size that
   clearly leads, and the whole card answers the pointer. */
.ol-pitch .pkg__price {
  margin-top: 0.5rem;
  font-size: clamp(1.35rem, 2.1vw, 1.75rem);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--fg);
}

.ol-pitch .pkg__price--request {
  font-size: var(--type-label);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}

.ol-pitch .pkg {
  gap: 0.45rem;
  transition: transform var(--dur-ui, 0.2s) var(--ease-spring, ease);
}

@media (hover: hover) and (pointer: fine) {
  .ol-pitch .pkg:hover { transform: translateY(-0.2rem); }
}

.ol-pitch .pkg__spec-label {
  font-family: var(--font-client-mono);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
}

.ol-pitch .pkg__spec-value {
  font-family: var(--font-client-mono);
  font-size: 0.88rem;
  line-height: 1.5;
}

/* ============================================================================
   §29 — Otto, 09.09.26 round 4 (six messages in the channel).
   Most of that round is markup: three sections deleted, one moved, the "NEXT —"
   labels dropped, nine stills replaced by loops. Four points need CSS and they
   are all in the hero.
   ========================================================================== */

/* --- 29a / The sub line makes room for Berlin ------------------------------
   "'Creative production for fashion brands.' > add berlin somewhere in there."
   The line is pinned to one row at every width (Otto, 08.09) and Martian Mono's
   advance is exactly 0.7em, so N characters need N * 0.7 * font-size px. It goes
   from 39 characters to 47, and the hero gives it the viewport minus its two
   0.5rem phone gutters: 304px at 320, 374px at 390.
     47 * 0.7 * f <= 304  =>  f <= 9.24px  =>  2.89vw at 320
   §26a's 3.4vw ceiling would render 10.88px there and push the last stop off
   the screen, so the ceiling comes down to 2.85vw — 9.12px at 320, 11.12px at
   390, both inside the box. Above ~617px --text-sub governs again and the
   desktop line is untouched. */
.ol-pitch .hero__sub {
  font-size: min(var(--text-sub), 2.85vw);
}

/* --- 29b / A bigger Paris window -------------------------------------------
   "make the 'IN PARIS FOR FASHION WEEK 1-11 OCTOBER · SS27' slightly bigger."
   The strip is the only black object in the hero and it was set at the page's
   smallest type token. One step up, with the padding following the type so the
   black keeps its proportions rather than tightening around a larger line. */
.ol-pitch .hero__paris {
  font-size: calc(var(--text-label) * 1.22);
  padding: 0.72rem 1.25rem 0.66rem;
}

/* --- 29c / The mail block reads as one masthead ----------------------------
   "work on the layouting of the mails again, it should feel more elegant and
   less random."
   The composition Otto asked for on 08.09 stays — mailboxes left, the two named
   contacts right (§27b). What made it look accidental was everything else:
     - §28a stepped production@ in by up to 3rem for no reason the eye can
       reconstruct, so the left column had no edge;
     - the right column started half a line below the left one and ended
       wherever its own two rows happened to fall, so nothing lined up across
       the gap;
     - the people sat at half the mailbox size, which reads as leftover rather
       than as a second rank;
     - the block floated with no edge of its own between the Paris strip above
       and the caterpillar below.
   Fixed in that order: one flush left edge, both columns stretched to the same
   two-row height with their rows pushed to the top and bottom of it (so
   emiliano@ tops out level with hello@ and theresa@ bottoms out level with
   production@), the people up from 0.5x to 0.62x, and a hairline over the whole
   block so the split reads as a masthead rule. */
@media (min-width: 720px) {
  .ol-pitch .hero__mails {
    /* §27b spread the two columns across the whole shell, which left ~500px of
       nothing between them and made the block read as two unrelated fragments
       rather than one object. Same left/right split, held to a measure. The
       four contacts are 634px of ink, so 50rem leaves a gap you read as a gap
       and not as a hole. */
    width: 100%;
    max-width: min(100%, 50rem);
    margin-inline: auto;
    padding-top: clamp(0.5rem, 1.25vw, 0.72rem);
    /* Not var(--faint): the .ol-pitch theme resolves it to transparent here. */
    border-top: 1px solid rgba(5, 5, 5, 0.14);
  }

  /* §28a's step. It was the loudest piece of the randomness. */
  .ol-pitch .hero__mails > .about__emailrow:nth-of-type(2) {
    padding-left: 0;
  }

  /* §28a pushed this column 21px below the left one. Zeroed, then stretched
     over both rows with its own two pushed to the top and the bottom of that
     height, so emiliano@ starts level with hello@ and theresa@ ends level with
     production@. */
  .ol-pitch .hero__mails .about__people {
    margin-top: 0;
    align-self: stretch;
    align-content: space-between;
    gap: 0;
  }
}

/* The second rank is a step down, not a whisper: 0.62 of the mailbox against
   0.5, and the italic role keeps the same 0.71 ratio to its own address that
   "General enquiries" keeps to hello@ (§24a). Floors unchanged in spirit —
   0.62 * 1.1rem is 10.9px on a phone, so the old 0.75rem floor only bites
   under about 340px now. */
.ol-pitch .about__emailrow--person .about__email {
  font-size: max(calc(var(--text-sub) * 0.62), 0.8rem);
}

.ol-pitch .about__emailrole--title {
  font-size: max(calc(var(--text-sub) * 0.44), 0.66rem);
}

/* ============================================================================
   §30 — Otto, 09.09.26 round 6: the asset rail replaces the client wall.

   "replace those assets within the carousell (in the references picture
   section) add no copy or overlay or graphic whatsoever. the goal is a
   completly interactive, floating asset carsousell with current [work]."

   Taken literally. The eighteen client marks and the "Client references" label
   are gone and this row stands in their place: one height, native aspect
   ratios, full bleed, black ground, nothing drawn on top. Every rule below
   exists to remove something rather than to add it.

   The row is full-bleed, so it deliberately escapes .shell — the section keeps
   its vertical rhythm and gives up its gutters.
   ========================================================================== */

.ol-pitch .clients--assets {
  padding-inline: 0;
  overflow: hidden;
}

.ol-pitch .assetrail {
  --rail-h: clamp(11rem, 25vh, 19rem);
  position: relative;
  width: 100%;
  overflow: hidden;
  cursor: grab;
  /* The browser must not claim horizontal panning, the pointer handler does. */
  touch-action: pan-y;
  -webkit-user-select: none;
  user-select: none;
}

.ol-pitch .assetrail.is-grabbing { cursor: grabbing; }

.ol-pitch .assetrail__track {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: max-content;
  will-change: transform;
}

.ol-pitch .assetrail__item {
  flex: 0 0 auto;
  height: var(--rail-h);
  /* --ar is written per item by asset-carousel.js from the delivered file, so
     the box is correct before the media has a single byte. */
  width: calc(var(--rail-h) * var(--ar, 1.777));
  overflow: hidden;
  background: rgba(245, 245, 240, 0.06);
}

.ol-pitch .assetrail__media {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;
  pointer-events: none; /* the drag belongs to the rail, not to a child */
}

/* Reduced motion: no drift. The row becomes an ordinary side-scroller so the
   material is still reachable, just never moving on its own. */
.ol-pitch .assetrail.is-static {
  overflow-x: auto;
  scrollbar-width: none;
}
.ol-pitch .assetrail.is-static::-webkit-scrollbar { display: none; }
.ol-pitch .assetrail.is-static .assetrail__track { transform: none !important; }

@media (max-width: 900px) {
  .ol-pitch .assetrail { --rail-h: clamp(8.5rem, 30vw, 13rem); }
  .ol-pitch .assetrail__track { gap: var(--space-3); }
}

/* ============================================================================
   §31 — Otto, 09.09.26 round 6: the sign-up panel.

   "please add a pop up window in the beginning after swiping away the initial
   animated gif. its a contact sign up ... make it within the webflow, not open
   up external tabs."

   It is the deck's own language, not a plugin's: the page ground, the mono
   face, hairline rules under the fields instead of boxes, no corner radius, no
   shadow, one black button. The only thing that reads as an overlay is the
   scrim behind it.

   Fields sit in a two-column grid because seven stacked rows would run past
   the bottom of a laptop screen and the panel would need its own scrollbar on
   first sight, which is exactly the impression a sign-up cannot afford.
   ========================================================================== */

.signup {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-preloader) - 1);
  display: grid;
  place-items: center;
  padding: var(--space-4);
}

.signup[hidden] { display: none; }

.signup__scrim {
  position: absolute;
  inset: 0;
  background: rgba(9, 9, 9, 0.62);
  opacity: 0;
  transition: opacity var(--dur-ui) var(--ease-spring);
}

.signup.is-open .signup__scrim { opacity: 1; }

.ol-pitch .signup__panel {
  position: relative;
  width: min(41rem, 100%);
  max-height: calc(100dvh - var(--space-6));
  overflow-y: auto;
  padding: clamp(1.75rem, 3.4vw, 2.75rem);
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-client-mono);
  opacity: 0;
  transform: translateY(1rem);
  transition: opacity 0.42s var(--ease-out-expo), transform 0.42s var(--ease-out-expo);
}

.signup.is-open .signup__panel {
  opacity: 1;
  transform: none;
}

.ol-pitch .signup__close {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  width: var(--space-5);
  height: var(--space-5);
  color: var(--fg);
  font-family: inherit;
  font-size: 1.4rem;
  line-height: 1;
  background: none;
  border: 0;
  cursor: pointer;
  opacity: 0.5;
  transition: opacity var(--dur-ui) var(--ease-spring);
}

.ol-pitch .signup__close:hover,
.ol-pitch .signup__close:focus-visible { opacity: 1; }

.ol-pitch .signup__title {
  font-family: var(--font-client-display);
  font-size: var(--type-heading);
  font-weight: 500;
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-display);
}

.ol-pitch .signup__lead {
  margin-top: var(--space-2);
  color: var(--ink);
  font-size: var(--type-label);
  line-height: 1.5;
}

.ol-pitch .signup__form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4) var(--space-4);
  margin-top: var(--space-5);
}

.ol-pitch .signup__field {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.ol-pitch .signup__field--wide,
.ol-pitch .signup__foot { grid-column: 1 / -1; }

.ol-pitch .signup__label {
  color: var(--ink);
  font-size: max(calc(var(--type-label) * 0.78), 0.66rem);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}

.ol-pitch .signup__input {
  width: 100%;
  padding: var(--space-2) 0;
  color: var(--fg);
  font-family: inherit;
  font-size: var(--type-label);
  background: none;
  border: 0;
  /* Not --client-line: a later rule resolves that token to transparent on
     this theme, and a form field with an invisible rule reads as a caption
     with nothing under it. */
  border-bottom: 1px solid rgba(13, 13, 13, 0.22);
  border-radius: 0;
  transition: border-color var(--dur-ui) var(--ease-spring);
}

.ol-pitch .signup__input::placeholder {
  color: rgba(13, 13, 13, 0.3);
}

.ol-pitch .signup__input:focus {
  outline: 0;
  border-bottom-color: var(--fg);
}

.ol-pitch .signup__input--area {
  resize: vertical;
  min-height: 4.5rem;
  line-height: 1.55;
}

/* Off-screen rather than display:none — a bot that respects display:none
   would skip it and the trap would catch nothing. */
.signup__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.ol-pitch .signup__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-1);
}

.ol-pitch .signup__note {
  flex: 1 1 auto;
  min-width: 0;
  color: var(--ink);
  font-size: max(calc(var(--type-label) * 0.82), 0.68rem);
  line-height: 1.45;
}

.ol-pitch .signup__note--bad { color: var(--fg); }

.ol-pitch .signup__mailto {
  color: var(--fg);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.ol-pitch .signup__submit {
  flex: 0 0 auto;
  padding: var(--space-2) var(--space-5);
  color: var(--bg);
  font-family: inherit;
  font-size: max(calc(var(--type-label) * 0.82), 0.68rem);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  background: var(--fg);
  border: 0;
  border-radius: 0;
  cursor: pointer;
  transition: opacity var(--dur-ui) var(--ease-spring);
}

.ol-pitch .signup__submit:hover:not(:disabled) { opacity: 0.78; }
.ol-pitch .signup__submit:disabled { opacity: 0.4; cursor: default; }

.ol-pitch .signup__done {
  grid-column: 1 / -1;
  color: var(--ink-strong);
  font-size: var(--type-label);
}

/* The page must not scroll behind the panel. */
.signup-locked, .signup-locked body { overflow: hidden; }

@media (max-width: 620px) {
  .ol-pitch .signup__form { grid-template-columns: 1fr; gap: var(--space-3); }
  .ol-pitch .signup__foot { flex-direction: column; align-items: stretch; }
  .ol-pitch .signup__submit { width: 100%; }
}
