/* ==================================================================
   CAMPAIGN CONTENT SPRINT — page-specific components

   Layered on top of style.css. Loaded only by
   /campaign-content-sprint/, so nothing here can affect the
   Production Partnership page.

   Everything is built from the tokens style.css already defines:
   --ink, --paper, --dim, --line, --accent, --accent-label, --radius,
   --font-mono, --font-disp, --mono-size, --gutter. No new colours.

   ------------------------------------------------------------------
   THE ONE TRAP TO KNOW ABOUT

   `:root .mono` in style.css has specificity (0,2,0) and paints every
   mono label with the accent. A single-class rule here cannot beat it.
   Any rule below that sets colour on a .mono element is therefore
   written with three classes on purpose. Do not "simplify" those
   selectors: the colour silently reverts to orange and the accent
   stops meaning anything.
   ================================================================== */

/* ---------------- 01 hero: copy left, film right ----------------
   style.css centres .hero__center as a column. This turns it into two
   columns for this page only, without touching the base rule, so the
   Production Partnership hero is unaffected. */

.hero__center--split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
  text-align: left;
  width: min(100%, 1320px);
  margin-inline: auto;
}
.hero__center--split .hero__copy { max-width: 34rem; }
/* the base rule centres .sub with auto margins; undo that here */
.hero__center--split .hero__copy .sub { margin-left: 0; margin-right: 0; }
.hero__center--split .hero__meta { align-items: flex-start; }

.hero__visual { margin: 0; display: grid; gap: 0.7rem; }
.hero__visual__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  width: 100%;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: color-mix(in srgb, var(--ink) 8%, transparent);
}
.hero__visual__shot {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  opacity: 0;
  /* Rest state is the UN-zoomed frame: every shot starts here and pushes in
     while it is on. The transform transition is 0ms but DELAYED past the
     fade, so an outgoing shot holds its zoom for the whole crossfade and only
     snaps back to 1 once it is already invisible. Without that delay the
     outgoing frame visibly rubber-bands outward as it leaves. */
  transform: scale(1);
  transition: opacity 1400ms ease, transform 0ms linear 1400ms;
}
/* the slow push-in is what makes stills read as footage rather than a slideshow.
   7600ms against a 5000ms hold is deliberate: the drift never reaches its end
   point before the cut, so it never appears to stop. */
.hero__visual__shot.is-on {
  opacity: 1;
  transform: scale(1.06);
  transition: opacity 1400ms ease, transform 7600ms linear;
}


@media (prefers-reduced-motion: reduce) {
  /* no drift, no crossfade: the first frame simply stands still */
  .hero__visual__shot, .hero__visual__shot.is-on { transition: none; transform: none; }
}
.hero__visual figcaption { color: var(--dim); }

@media (max-width: 900px) {
  .hero__center--split { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
  .hero__center--split .hero__copy { max-width: none; }
  /* .hero__chapter and .hero__scroll are absolutely positioned in the hero's
     bottom corners. Once the layout stacks, the visual grows down into them and
     mono type lands on top of the photograph. Reserve the strip they occupy
     rather than hiding them: they are part of the page's editorial furniture
     and appear on every other section. */
  .hero__center--split { padding-bottom: 4.5rem; }
}

/* ---------------- 02 trust strip ----------------
   The UNDP mark is the official vertical lockup: a solid blue block roughly
   1:2. style.css sizes strip logos by height (44px), which would render it
   about 22px wide, an illegible sliver next to the landscape marks. A UN
   emblem may not be redrawn or re-laid-out, so the accommodation is here in
   layout rather than in the asset. A horizontal UNDP lockup, if one can be
   sourced, would be better than this. */
.brands__cell--tall img { height: 78px; }
/* The Standard Bank shield is near-square and ships without its wordmark, so at
   the shared 44px height it renders ~38px wide against wordmarks at 108-148px.
   The strip's captions are hidden by design (style.css), so the mark carries
   recognition on its own and needs the room. */
.brands__cell--square img { height: 62px; }

/* ---- hover on knockout marks ----
   style.css hovers every logo to `filter: brightness(0)`. That works for a
   wordmark, but a knockout mark (UNDP's blue block with white letters, the
   Standard Bank shield's white swoosh, PluxNet's white counters) loses every
   internal detail and becomes a featureless black shape.

   grayscale + contrast keeps the structure instead: contrast() pivots on 128,
   and these marks measure 76-122 in their coloured areas against 255 in the
   white. brightness(0.7) first pushes the mid-tones clear of the pivot so the
   split is unambiguous. Colour goes to ink, white stays white. */
.brands__cell--knockout:hover img,
.brands__cell--knockout:focus-within img {
  filter: grayscale(1) brightness(0.7) contrast(6);
}
@media (max-width: 640px) {
  .brands__cell--tall img { height: 62px; }
  .brands__cell--square img { height: 50px; }
}

/* ---------------- 03 the problem ---------------- */

.probsplit {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
  margin-top: clamp(2rem, 4vw, 3.4rem);
}
.probsplit__lede p + p { margin-top: 1rem; color: var(--dim); }

.probgrid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.probgrid__item {
  background: var(--paper);
  padding: clamp(1.2rem, 2.4vw, 1.9rem);
}
.surface--white .probgrid__item { background: #fff; }
.probgrid__item h3 {
  margin: 0.55rem 0 0.5rem;
  font-size: calc(0.95rem + 2px);
  font-weight: 600;
  line-height: 1.3;
}
.probgrid__item p {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--dim);
}

/* ---------------- 04 one production, many outputs ----------------

   Editorial composition: a dominant production card left, a small hub in the
   middle, eight output cards right in four paired rows. The connector SVG sits
   behind everything at z-index 0 and is generated from measured anchors in
   sprint.js, so nothing here hardcodes a coordinate.

   Palette is the site's own tokens. The reference values in the brief map as:
     #E8E8E3 -> var(--paper)      #080807 -> var(--ink)
     #FD4700 -> var(--accent)     ~#65655F -> var(--dim)
   Borders and card ground are mixed from those rather than hardcoded, so the
   component follows the site if the tokens ever move.
   ---------------------------------------------------------------- */

.prodflow {
  --pf-border: color-mix(in srgb, var(--ink) 22%, transparent);
  --pf-card: color-mix(in srgb, var(--paper) 45%, #fff);
  --pf-shadow: 0 1px 2px rgba(8, 8, 7, 0.05), 0 1px 12px rgba(8, 8, 7, 0.03);

  position: relative;
  margin-top: clamp(2.2rem, 4.5vw, 3.6rem);
  display: grid;
  grid-template-columns: minmax(0, 1.06fr) clamp(132px, 12vw, 200px) minmax(0, 1.12fr);
  align-items: center;
  gap: 0;
  /* the dotted ground, drawn not raster */
  background-image: radial-gradient(color-mix(in srgb, var(--ink) 13%, transparent) 1px, transparent 1.5px);
  background-size: 4px 4px;
  padding: clamp(1.6rem, 3.6vw, 3.2rem) clamp(1.2rem, 3vw, 2.6rem);
  border-radius: var(--radius);
}

/* the generated connector layer, always behind the cards */
.prodflow__wires {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  overflow: visible;
}
.prodflow__wires path {
  fill: none;
  stroke: var(--accent);
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-dasharray: 1.5 5;
  stroke-opacity: 0.85;
  transition: stroke-opacity 220ms ease, stroke-width 220ms ease;
}

/* ---- the production card ---- */

.prodflow__card {
  position: relative;
  z-index: 1;
  border: 1px solid var(--pf-border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--pf-card);
  box-shadow: var(--pf-shadow);
  padding: clamp(1.5rem, 3.4vw, 2.9rem);
}
/* three classes: beats `:root .mono` — see the header note */
.prodflow .prodflow__card .prodflow__label {
  display: block;
  color: var(--accent-label);
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.prodflow__heading {
  margin: clamp(0.9rem, 2vw, 1.4rem) 0 0.6rem;
  /* style.css uppercases every h1-h6. The approved reference sets this line in
     sentence case, and at this size uppercase Alata reads as a section title
     rather than the editorial statement it is meant to be. Deliberate local
     departure from the house heading rule, scoped to this component. */
  text-transform: none;
  letter-spacing: -0.01em;
  font-family: var(--font-disp);
  font-weight: 400;
  font-synthesis-weight: none;
  /* sized so "One planned production" holds one line down to ~1300px, which is
     how the reference sets it. Below that it wraps, which is fine. */
  font-size: clamp(1.45rem, 2.9vw, 2.4rem);
  line-height: 1.1;
}
.prodflow__sub { margin: 0; color: var(--dim); font-size: clamp(0.98rem, 1.4vw, 1.1rem); }

.prodflow__stages {
  list-style: none;
  margin: clamp(2.2rem, 5vw, 3.6rem) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  position: relative;
}
/* the thin stage rule, sitting on the marker centres */
.prodflow__stages::before {
  content: "";
  position: absolute;
  left: 4px;
  right: 4px;
  top: 4px;
  height: 1px;
  background: var(--pf-border);
}
.prodflow__stage { display: grid; gap: 0.7rem; justify-items: start; }
.prodflow__stage:nth-child(2) { justify-items: center; }
.prodflow__stage:nth-child(3) { justify-items: end; }
.prodflow__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
  position: relative;
  z-index: 1;
}
/* three classes: beats `:root .mono` */
.prodflow .prodflow__stage span.mono {
  color: var(--accent-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ---- the hub ---- */

.prodflow__hub {
  position: relative;
  z-index: 1;
  display: grid;
  justify-items: center;
  gap: 1.15rem;
}
.prodflow__hubmark {
  position: relative;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
}
.prodflow__orbit, .prodflow__ring, .prodflow__core {
  position: absolute;
  border-radius: 50%;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
.prodflow__orbit {
  width: 34px; height: 34px;
  border: 1px dotted var(--accent);
  opacity: 0.75;
}
.prodflow__ring {
  width: 19px; height: 19px;
  border: 1px solid var(--accent);
  /* the only gradient in the component besides the ground texture */
  background: radial-gradient(circle, color-mix(in srgb, var(--accent) 16%, transparent) 0%, transparent 70%);
}
.prodflow__core { width: 7px; height: 7px; background: var(--accent); }
/* ---- the outputs ---- */

.prodflow__outputs { position: relative; z-index: 1; }
.prodflow__outhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 0.8rem;
  margin-bottom: clamp(0.8rem, 1.6vw, 1.15rem);
  border-bottom: 1px solid var(--pf-border);
}
/* three classes: beats `:root .mono` */
.prodflow .prodflow__outhead span.mono {
  color: var(--accent-label);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
/* span.mono in the rule above is (0,3,1); this needs to beat it, hence the
   element qualifier here too rather than three bare classes. */
.prodflow .prodflow__outhead span.prodflow__count { color: var(--dim); }

.prodflow__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(0.7rem, 1.4vw, 1.15rem);
}
.prodflow__out {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  border: 1px solid var(--pf-border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--pf-card);
  box-shadow: var(--pf-shadow);
  padding: clamp(1.05rem, 2.1vw, 1.5rem) clamp(1rem, 2vw, 1.5rem);
  transition: border-left-color 220ms ease, box-shadow 220ms ease;
}
.prodflow__outname { font-size: clamp(0.95rem, 1.3vw, 1.05rem); line-height: 1.3; }
/* three classes: beats `:root .mono` */
.prodflow .prodflow__out .prodflow__outnum { color: var(--dim); font-size: calc(0.66rem + 3px); }

/* ---- footer ---- */

.prodflow__footer {
  margin-top: clamp(1.6rem, 3.4vw, 2.6rem);
  padding-top: clamp(1rem, 2vw, 1.4rem);
  border-top: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.4rem;
  flex-wrap: wrap;
}
/* three classes: beats `:root .mono` */
.prodflow__footer > span.mono { color: var(--accent-label); letter-spacing: 0.14em; text-transform: uppercase; }
.prodflow__footer p { margin: 0; color: var(--dim); font-size: 0.97rem; }

/* ---------------- 05 how it works ---------------- */

.steps {
  list-style: none;
  margin: clamp(2rem, 4vw, 3.4rem) 0 0;
  padding: 0;
  display: grid;
  /* four steps across one row. At three columns the fourth orphaned onto a
     second row, costing the height the compression was meant to save. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1.4rem, 3vw, 2.6rem);
  counter-reset: none;
}
.steps__item {
  padding-top: 1.1rem;
  border-top: 1px solid var(--line);
}
/* three classes: beats `:root .mono` (0,2,0) — see the header note */
.steps .steps__item .steps__n {
  display: block;
  color: var(--accent-label);
  margin-bottom: 0.7rem;
}
.steps__item h3 {
  margin: 0 0 0.45rem;
  font-size: calc(1rem + 2px);
  font-weight: 600;
  line-height: 1.3;
}
.steps__item p { margin: 0; color: var(--dim); font-size: 0.98rem; line-height: 1.55; }

/* ---------------- 06 sprint options ---------------- */

.sprintgrid {
  margin-top: clamp(2rem, 4vw, 3.2rem);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: clamp(1rem, 1.8vw, 1.6rem);
  /* This gap does double duty: it separates the subgrid's internal rows AND,
     once the grid wraps to two or one columns, the gap between card rows. The
     flags sit half outside the card top, so wrapped rows need real clearance.
     The cards override row-gap below to keep their own internals tight. */
  row-gap: clamp(2.2rem, 3.4vw, 3rem);
  align-items: stretch;
  /* Five shared row tracks: top, price, includes, disclosure, CTA. Each card
     subgrids onto them, so the price row and the includes list start at the
     same height in every card no matter how long the description above runs. */
  grid-template-rows: auto auto auto auto auto;
}
.sprintcard {
  position: relative;
  grid-row: span 5;
  display: grid;
  grid-template-rows: subgrid;
  /* a subgrid may override the inherited gutters; internal rows stay tight */
  row-gap: 1rem;
  padding: clamp(1.3rem, 2.4vw, 1.9rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
}
.sprintcard--featured {
  border-color: var(--accent);
  /* a single-pixel ring and the faintest warm ground, so the recommended
     one-off reads as chosen without shouting next to the dark card */
  box-shadow: 0 0 0 1px var(--accent);
  background: color-mix(in srgb, var(--accent) 2%, var(--paper));
}
/* three classes: same specificity reason as above */
/* The custom-Marathon band under the pricing grid: copy left, action right,
   stacking on narrow screens so the button never gets squeezed. */
.customband {
  margin-top: clamp(1.4rem, 2.6vw, 2.2rem);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1rem, 2.4vw, 2rem);
  flex-wrap: wrap;
  padding: clamp(1.1rem, 2.2vw, 1.6rem);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--ink) 3%, var(--paper));
}
.customband__copy { flex: 1 1 26rem; min-width: 0; }
.customband__copy p { margin: 0.35rem 0 0; color: var(--dim); max-width: 58ch; }
/* three classes: beats `:root .mono` at (0,2,0) */
.customband .customband__copy .customband__label { color: var(--accent-label); }
.customband__cta { flex: 0 0 auto; }
@media (max-width: 620px) {
  .customband { align-items: stretch; }
  .customband__cta { justify-content: space-between; }
}

/* Production Partnership: the same card as Sprint 6 and Marathon 20, with the
   faintest neutral tint and the dark "Ongoing" tag doing the differentiating.
   No accent ring, so Sprint 10 keeps the only highlighted card on the row. */
.sprintcard--partner {
  background: color-mix(in srgb, var(--ink) 3%, var(--paper));
}
.sprintcard.sprintcard--partner .sprintcard__flag {
  position: absolute;
  top: 0;
  right: clamp(1.3rem, 2.4vw, 1.9rem);
  transform: translateY(-50%);
  background: var(--ink);
  color: var(--paper);
  padding: 0.28rem 0.6rem;
  border-radius: var(--radius);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* the "see what a month could look like" disclosure inside the card */
.popcard { margin-top: 0.9rem; }
.popcard > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
}
.popcard > summary::-webkit-details-marker { display: none; }
.popcard > summary:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.sprintcard .popcard .excard__body { margin-top: 0.8rem; }

.sprintcard.sprintcard--featured .sprintcard__flag {
  position: absolute;
  top: 0;
  right: clamp(1.3rem, 2.4vw, 1.9rem);
  transform: translateY(-50%);
  /* --accent-label, not --accent: this is small uppercase mono, so it needs
     small-text contrast. White on #FD4700 is 3.7:1 and fails; white on
     #B93400 is 6.6:1. The card border keeps the true accent. */
  background: var(--accent-label);
  color: #fff;
  padding: 0.28rem 0.6rem;
  border-radius: var(--radius);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.sprintcard__top h3 {
  margin: 0.6rem 0 0.5rem;
  font-family: var(--font-disp);
  font-weight: 400;
  font-synthesis-weight: none;
  font-size: clamp(1.35rem, 2.2vw, 1.8rem);
  line-height: 1.15;
}
/* three classes: beats `:root .mono` — see the header note */
.sprintcard .sprintcard__top .sprintcard__hours {
  display: block;
  color: var(--dim);
  margin-bottom: 0.55rem;
}
.sprintcard__fit { margin: 0; color: var(--dim); font-size: 0.95rem; line-height: 1.5; }
.sprintcard__price {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  align-items: flex-start;
  padding: 0.9rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.sprintcard__body { flex: 1; }
.sprintcard__body ul {
  list-style: none;
  margin: 0.6rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.42rem;
}
.sprintcard__body li {
  position: relative;
  padding-left: 1.05rem;
  font-size: 0.95rem;
  line-height: 1.45;
}
.sprintcard__body li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
}
.sprintcard__cta {
  grid-row: 5;
  /* The button is full width, so centring the label+arrow group together left
     the arrow badge stranded mid-button. Pushing them apart puts the label on
     the left and the badge hard right, which is what the .btn padding
     (0.95rem left, 0.45rem right) was built for. */
  justify-content: space-between;
  text-align: left;
  /* on top of the card's 1rem flex gap, so the button is not crowded by the
     last list item, especially where that item wraps to two lines */
  margin-top: clamp(0.7rem, 1.6vw, 1.3rem);
}

/* ---------------- 07 deliverable examples ---------------- */

.exnote {
  margin: clamp(1.6rem, 3vw, 2.4rem) 0 0;
  max-width: 62ch;
  color: var(--dim);
}
.exnote b { color: var(--ink); }

.exgrid {
  margin-top: clamp(1.6rem, 3vw, 2.4rem);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1rem, 1.8vw, 1.5rem);
  align-items: start;
}
.excard {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  overflow: hidden;
}
.surface--white .excard { background: #fff; }
.excard > summary {
  list-style: none;
  cursor: pointer;
  padding: clamp(1.15rem, 2.2vw, 1.7rem);
  display: grid;
  gap: 0.5rem;
}
.excard > summary::-webkit-details-marker { display: none; }
.excard > summary:focus-visible { outline: 2px solid var(--ink); outline-offset: -3px; }
.excard__title {
  font-family: var(--font-disp);
  font-weight: 400;
  font-synthesis-weight: none;
  font-size: clamp(1.25rem, 2.1vw, 1.6rem);
  line-height: 1.15;
}
/* which Sprint size this example usually needs. Two classes + an element
   beats `:root .mono` at (0,2,0). */
.excard > summary .excard__sprint {
  justify-self: start;
  color: var(--accent-label);
  border: 1px solid color-mix(in srgb, var(--accent-label) 38%, transparent);
  border-radius: var(--radius);
  padding: 0.16rem 0.5rem;
}
/* three classes: beats `:root .mono` */
.excard > summary .excard__more {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--accent-label);
}
.excard .chev { transition: transform 220ms ease; display: inline-block; }
.excard[open] .chev { transform: rotate(45deg); }
.excard__body {
  padding: 0 clamp(1.15rem, 2.2vw, 1.7rem) clamp(1.15rem, 2.2vw, 1.7rem);
  border-top: 1px solid var(--line);
  padding-top: 1.1rem;
}
.excard__body ul {
  list-style: none;
  margin: 0.6rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.42rem;
}
.excard__body li {
  position: relative;
  padding-left: 1.05rem;
  font-size: 0.98rem;
}
.excard__body li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent);
}

/* ---------------- 08 included production (dark) ---------------- */

.inclist {
  list-style: none;
  margin: clamp(2rem, 4vw, 3.2rem) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
}
.inclist li {
  padding: 0.9rem 0;
  border-bottom: 1px solid currentColor;
  border-color: color-mix(in srgb, currentColor 22%, transparent);
  font-size: 1rem;
}
.addserv {
  margin-top: clamp(2.2rem, 4vw, 3.4rem);
  padding-top: clamp(1.6rem, 3vw, 2.4rem);
  border-top: 1px solid color-mix(in srgb, currentColor 22%, transparent);
}
.addserv p { margin: 0.7rem 0 0; max-width: 56ch; opacity: 0.82; }
.addserv__list {
  list-style: none;
  margin: 1.1rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}
.addserv__list li {
  border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
  border-radius: var(--radius);
  padding: 0.45rem 0.8rem;
  font-size: 0.92rem;
}

/* ---------------- 09 case studies ---------------- */

.csgrid {
  margin-top: clamp(2rem, 4vw, 3.2rem);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1.2rem, 2.4vw, 2.2rem);
  align-items: start;
}
.cscard {
  border-top: 2px solid var(--accent);
  padding-top: 1.2rem;
}
.cscard__thumb {
  margin-bottom: 1.1rem;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}
.cscard__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cscard__thumb--empty {
  display: grid;
  place-items: center;
  border: 1px dashed var(--line);
  background:
    repeating-linear-gradient(135deg,
      transparent 0 10px,
      color-mix(in srgb, var(--ink) 4%, transparent) 10px 11px);
}
/* three classes: beats `:root .mono` */
.cscard .cscard__thumb--empty span.mono { color: var(--dim); }
.cscard__summary {
  margin: 0 0 1.1rem;
  font-size: 0.99rem;
  line-height: 1.55;
  color: var(--dim);
}
.cscard__open {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  cursor: pointer;
}
.cscard__more { margin: 1.4rem 0 0; }
/* three classes: beats `:root .mono` */
.csgrid .cscard .cscard__client {
  display: block;
  color: var(--accent-label);
  margin-bottom: 0.7rem;
}
.cscard h3 {
  margin: 0 0 1rem;
  font-family: var(--font-disp);
  font-weight: 400;
  font-synthesis-weight: none;
  font-size: clamp(1.2rem, 2vw, 1.55rem);
  line-height: 1.2;
}
/* NOT scoped under .cscard on purpose. The lightbox clones this <dl> out of
   the card, so any rule written as `.cscard .cscard__dl ...` silently stops
   applying the moment it is shown. `dt.mono` is (0,2,1), which still beats
   the `:root .mono` accent sweep at (0,2,0). */
.cscard__dl { margin: 0; }
.cscard__dl dt.mono {
  color: var(--dim);
  margin-top: 1.3rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.cscard__dl dt.mono:first-child { margin-top: 0; }
.cscard__dl dd {
  margin: 0.4rem 0 0;
  font-size: 0.99rem;
  line-height: 1.6;
}
.cscard .cs-link { margin-top: 1.3rem; display: inline-flex; }

/* ---------------- 10 portfolio ---------------- */

.portgrid {
  margin-top: clamp(2rem, 4vw, 3.2rem);
  display: grid;
  /* one column. The thumbnails are the point of these cards, and a full-width
     card gives four of them room to actually read. */
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(1.2rem, 2.2vw, 2rem);
  align-items: start;
}
.portcard--wide { grid-column: span 1; }
.portcard {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: clamp(1.2rem, 2.2vw, 1.8rem);
  background: var(--paper);
}
.surface--white .portcard { background: #fff; }
.portcard__head { display: flex; align-items: baseline; gap: 0.8rem; }
.portcard__head h3 {
  margin: 0;
  font-size: calc(1rem + 2px);
  font-weight: 600;
  line-height: 1.3;
}
.portcard p { margin: 0.6rem 0 0; color: var(--dim); font-size: 0.96rem; line-height: 1.55; }

.portstrip {
  list-style: none;
  margin: 1.3rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(0.6rem, 1.2vw, 1rem);
}
.portstrip--two { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* minmax(0,1fr), not the default auto: an auto column sizes to the intrinsic
   width of its contents, and an aspect-ratio box reports that as height x ratio.
   At 16:9 that came out wider than the cell and pushed the page sideways. */
.portstrip li { display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.5rem; }
.portstrip img {
  width: 100%;
  /* height:auto is load-bearing. The video thumbnails carry width/height
     attributes so the browser can reserve space and avoid layout shift, but
     that height acts as a presentational hint and beats aspect-ratio unless
     height is explicitly auto. Without this a 16:9 thumb renders 154x360. */
  height: auto;
  /* landscape by default: these are photographs and event stills. Only the
     campaign card holds vertical social films, and it opts in below. */
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius);
  display: block;
  background: var(--line);
}
.portstrip--film img { aspect-ratio: 9 / 16; }
/* video thumbnails are 16:9, because that is what the films are. Cropping a
   YouTube frame to 4:3 throws away the sides of somebody's composition. */
.portstrip--video img { aspect-ratio: 16 / 9; }
.portstrip--video .portshot__box { aspect-ratio: 16 / 9; }

/* a thumbnail that plays needs to look like one */
.portshot__play-btn {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  border-radius: var(--radius);
  overflow: hidden;
  line-height: 0;
}
.portshot__badge {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: clamp(34px, 3.4vw, 46px);
  aspect-ratio: 1;
  border-radius: 50%;
  display: grid;
  place-items: center;
  padding-left: 2px;
  color: #fff;
  font-size: 0.8rem;
  line-height: 1;
  background: color-mix(in srgb, var(--ink) 62%, transparent);
  border: 1px solid rgba(255,255,255,0.9);
  transition: background 200ms ease, transform 200ms ease;
}
.portshot__play-btn:hover .portshot__badge {
  background: var(--accent);
  border-color: var(--accent);
  transform: translate(-50%, -50%) scale(1.08);
}
.portshot__play-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .portshot__badge { transition: none; }
  .portshot__play-btn:hover .portshot__badge { transform: translate(-50%, -50%); }
}
.portstrip--two img { aspect-ratio: 4 / 3; }
/* four across inside every card */
.portcard .portstrip { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* a two-film card (Corporate video) sits two-up at half width, not four */
.portcard .portstrip--duo { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* five client tiles on the Photography card. Two classes, because the rule
   above is (0,2,0) and a bare .portstrip--five would lose to it. */
.portcard .portstrip--five { grid-template-columns: repeat(5, minmax(0, 1fr)); }
/* how many photographs are behind this tile, so the click is worth making */
/* The corner photo-count badge was removed on 2026-09-05 at Trevor's request:
   it read as a UI chip sitting on top of the photograph. The count is not
   lost, only unprinted, and it still reaches assistive tech through the
   .visually-hidden label inside each button ("... gallery, 34 photographs").
   Do not reintroduce a visible badge without also removing that label, or the
   count gets announced twice. */
/* the gallery data never loaded: no pop-up, so no affordance either */
.portshot__play-btn.is-inert { cursor: default; }
.portcard__gallery { margin-top: 1.1rem; }
/* the captions under each thumb are fine at rest but must not shout */
.portcard .portstrip li span.mono { color: var(--dim); font-size: calc(0.62rem + 3px); }

/* ---------------- 11 included / not included ---------------- */

.scopesplit {
  margin-top: clamp(2rem, 4vw, 3.2rem);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1.4rem, 3vw, 3rem);
  align-items: start;
}
.scopecol ul {
  list-style: none;
  margin: 1.1rem 0 0;
  padding: 0;
}
.scopecol li {
  position: relative;
  padding: 0.72rem 0 0.72rem 1.7rem;
  border-bottom: 1px solid var(--line);
  font-size: 1rem;
}
.scopecol--in li::before,
.scopecol--out li::before {
  position: absolute;
  left: 0;
  top: 0.72rem;
  font-family: var(--font-mono);
  font-size: var(--mono-size);
  line-height: 1.6;
}
.scopecol--in li::before  { content: "+"; color: var(--accent-label); }
.scopecol--out li::before { content: "\2013"; color: var(--dim); }
.scopecol--out li { color: var(--dim); }
.scopecol__note { margin: 1.1rem 0 0; font-size: 0.95rem; color: var(--dim); }

/* ---------------- 12 speed promise (dark) ---------------- */

.speedout {
  margin-top: clamp(2rem, 4vw, 3rem);
  padding-top: clamp(1.4rem, 2.6vw, 2rem);
  border-top: 1px solid color-mix(in srgb, currentColor 22%, transparent);
}
.speedout ul {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.55rem 1.6rem;
}
.speedout li {
  position: relative;
  padding-left: 1.05rem;
  font-size: 0.97rem;
  opacity: 0.82;
}
.speedout li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: currentColor;
}

/* ---------------- 14 the form ---------------- */

.replypromise {
  margin-top: 1rem;
  padding: 0.85rem 1rem;
  border-left: 2px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-radius: var(--radius);
  font-size: 0.97rem;
  line-height: 1.55;
}

.briefnote {
  margin: -0.4rem 0 1.4rem;
  padding: 0.9rem 1rem;
  border-left: 2px solid var(--accent);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  font-size: 0.95rem;
  line-height: 1.5;
  border-radius: var(--radius);
}

/* ==================================================================
   RESPONSIVE

   The spacious pacing is deliberate and is not compressed here. These
   rules only change how many columns a grid has.
   ================================================================== */

@media (max-width: 1080px) {
  .sprintgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .csgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 900px) {
  .probsplit { grid-template-columns: minmax(0, 1fr); }
  /* The rays connect two boxes that are no longer side by side, so they
     stop describing anything true. Hidden rather than rotated. */
}

@media (max-width: 720px) {
  .probgrid { grid-template-columns: minmax(0, 1fr); }
  .sprintgrid { grid-template-columns: minmax(0, 1fr); }
  .steps { grid-template-columns: minmax(0, 1fr); }
  .exgrid { grid-template-columns: minmax(0, 1fr); }
  .csgrid { grid-template-columns: minmax(0, 1fr); }
  .scopesplit { grid-template-columns: minmax(0, 1fr); }
  .inclist { grid-template-columns: minmax(0, 1fr); }
  .speedout ul { grid-template-columns: minmax(0, 1fr); }
  .portcard .portstrip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .portcard .portstrip--five { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- production flow: responsive ----
   Down to 900px the three regions stay side by side and only tighten. Below
   that the composition turns vertical and the connector system is replaced by
   a single spine with short branches, which sprint.js draws instead of the
   four routes. */
@media (max-width: 1080px) {
  .prodflow { grid-template-columns: minmax(0, 0.9fr) clamp(104px, 11vw, 150px) minmax(0, 1.28fr); }
  .prodflow__card { padding: clamp(1.2rem, 2.6vw, 2rem); }
}

@media (max-width: 900px) {
  .prodflow {
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(1.5rem, 4vw, 2.2rem);
    padding: 24px;
  }
  .prodflow__hub { justify-items: center; }
  /* one column, indented to leave the spine and its branches room */
  .prodflow__grid { grid-template-columns: minmax(0, 1fr); }
  .prodflow__outputs { padding-left: 38px; }
  /* the header used to be pulled back to the section edge, which ran it
     straight through the spine. Leave the spine column clear instead. */
  .prodflow__outhead { margin-left: 0; }
  .prodflow__stages { margin-top: 2rem; }
}

@media (max-width: 520px) {
  .prodflow__stages { grid-template-columns: minmax(0, 1fr); gap: 0.9rem; }
  .prodflow__stages::before { display: none; }
  .prodflow__stage,
  .prodflow__stage:nth-child(2),
  .prodflow__stage:nth-child(3) { justify-items: start; grid-auto-flow: column; align-items: center; justify-content: start; gap: 0.6rem; }
  .prodflow__footer { flex-direction: column; gap: 0.5rem; }
}

/* ---- production flow: motion ----
   Nothing moves until the section is reached, and everything is a one-shot
   except the hub's pulse. Reduced motion turns all of it off and leaves the
   finished state. */
.prodflow[data-pf-armed] .prodflow__card,
.prodflow[data-pf-armed] .prodflow__hub,
.prodflow[data-pf-armed] .prodflow__out { opacity: 0; transform: translateY(10px); }
.prodflow[data-pf-armed] .prodflow__wires path { opacity: 0; }

.prodflow[data-pf-on] .prodflow__card,
.prodflow[data-pf-on] .prodflow__hub,
.prodflow[data-pf-on] .prodflow__out {
  opacity: 1;
  transform: none;
  transition: opacity 620ms ease, transform 620ms ease;
}
.prodflow[data-pf-on] .prodflow__hub { transition-delay: 180ms; }
/* staggered by row, so the outputs arrive in production order */
.prodflow[data-pf-on] .prodflow__out[data-out-row="1"] { transition-delay: 420ms; }
.prodflow[data-pf-on] .prodflow__out[data-out-row="2"] { transition-delay: 520ms; }
.prodflow[data-pf-on] .prodflow__out[data-out-row="3"] { transition-delay: 620ms; }
.prodflow[data-pf-on] .prodflow__out[data-out-row="4"] { transition-delay: 720ms; }

.prodflow[data-pf-on] .prodflow__wires path {
  opacity: 1;
  animation: pf-draw 900ms ease forwards;
}
.prodflow[data-pf-on] .prodflow__wires path:nth-of-type(2) { animation-delay: 80ms; }
.prodflow[data-pf-on] .prodflow__wires path:nth-of-type(3) { animation-delay: 160ms; }
.prodflow[data-pf-on] .prodflow__wires path:nth-of-type(4) { animation-delay: 240ms; }
@keyframes pf-draw {
  from { stroke-dashoffset: 46; opacity: 0; }
  to   { stroke-dashoffset: 0;  opacity: 1; }
}

.prodflow[data-pf-on] .prodflow__orbit { animation: pf-pulse 4.2s ease-in-out infinite; }
@keyframes pf-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(1);    opacity: 0.75; }
  50%      { transform: translate(-50%, -50%) scale(1.12); opacity: 0.45; }
}

/* hover: strengthen the card's accent and its own route */
.prodflow__out:hover { border-left-color: var(--accent); box-shadow: 0 2px 14px rgba(253, 71, 0, 0.14); }
.prodflow[data-pf-hot="1"] .prodflow__wires path[data-route="1"],
.prodflow[data-pf-hot="2"] .prodflow__wires path[data-route="2"],
.prodflow[data-pf-hot="3"] .prodflow__wires path[data-route="3"],
.prodflow[data-pf-hot="4"] .prodflow__wires path[data-route="4"] { stroke-opacity: 1; stroke-width: 2; }

@media (prefers-reduced-motion: reduce) {
  /* These must out-specify the reveal rules above, which all carry
     [data-pf-on] or [data-pf-armed]. Matching that attribute here and relying
     on source order is what actually stops the animation: without it the
     pulse and the draw keep running for someone who asked for neither. */
  .prodflow[data-pf-armed] .prodflow__card,
  .prodflow[data-pf-armed] .prodflow__hub,
  .prodflow[data-pf-armed] .prodflow__out { opacity: 1; transform: none; }
  .prodflow[data-pf-armed] .prodflow__wires path { opacity: 1; }
  .prodflow[data-pf-on] .prodflow__wires path,
  .prodflow[data-pf-armed] .prodflow__wires path {
    animation: none;
    stroke-dashoffset: 0;
    transition: none;
  }
  .prodflow[data-pf-on] .prodflow__orbit,
  .prodflow[data-pf-armed] .prodflow__orbit { animation: none; }
  .prodflow[data-pf-on] .prodflow__card,
  .prodflow[data-pf-on] .prodflow__hub,
  .prodflow[data-pf-on] .prodflow__out { transition: none; }
  .excard .chev { transition: none; }
}

/* ---------------- the shared lightbox ---------------- */

body.sbx-open { overflow: hidden; }

.sbx {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
}
.sbx[hidden] { display: none; }
.sbx__scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ink) 78%, transparent);
}
.sbx__panel {
  position: relative;
  z-index: 1;
  width: min(100%, 860px);
  max-height: min(86vh, 900px);
  overflow-y: auto;
  background: var(--paper);
  border-radius: var(--radius);
  padding: clamp(1.4rem, 3vw, 2.4rem);
  /* the floating top bar fills this space instead */
  padding-top: 0;
}
/* eyebrow now sits at the top of the panel with the title */
.sbx .sbx__eyebrow.mono { margin: 0 0 0.3rem; color: var(--accent-label); }
/* Floating toolbar pinned to the TOP of the pop-up, with the same translucency
   mechanics as the page nav (paper at 88% + a blur). The eyebrow and title sit
   below it with a clear gap, so the toolbar never collides with the title. */
.sbx__bar {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  /* negative side margins span the panel width; top sits flush because the
     panel's top padding was removed. 1rem bottom margin keeps the title clear */
  margin: 0 calc(clamp(1.4rem, 3vw, 2.4rem) * -1) 1rem;
  padding: 0.75rem clamp(1.4rem, 3vw, 2.4rem);
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.sbx__bar__actions { display: flex; align-items: center; gap: 0.5rem; flex: 0 0 auto; }
/* project navigation, shown only for the photo galleries */
.sbx__nav {
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.4rem 0.7rem;
  font-family: var(--font-mono);
  font-size: var(--mono-size);
  cursor: pointer;
  color: var(--ink);
  white-space: nowrap;
}
.sbx__nav:hover { border-color: var(--ink); }
.sbx__nav:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.sbx__nav[hidden] { display: none; }
/* three classes: beats `:root .mono` */
.sbx .sbx__bar span.mono { color: var(--accent-label); }
.sbx__close {
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.4rem 0.75rem;
  font-family: var(--font-mono);
  font-size: var(--mono-size);
  cursor: pointer;
  color: var(--ink);
}
.sbx__close:hover { border-color: var(--ink); }
.sbx__close:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.sbx__title {
  margin: 0 0 1.2rem;
  font-family: var(--font-disp);
  font-weight: 400;
  font-synthesis-weight: none;
  font-size: clamp(1.4rem, 3vw, 2.1rem);
  line-height: 1.15;
}
/* the cloned node arrives with hidden set on it */
.sbx__body > [hidden] { display: block !important; }
.sbx__body .portstrip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.sbx__video { position: relative; aspect-ratio: 16 / 9; }
.sbx__video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; border-radius: var(--radius); }

/* ---------------- photography galleries ---------------- */

/* a 30-image gallery needs more room than a case-study note. Set by sprint.js
   when it opens one, and removed on close so every other lightbox is unchanged. */
.sbx--wide .sbx__panel {
  width: min(100%, 1180px);
  max-height: min(90vh, 1000px);
}

.gal__hero {
  position: relative;
  margin-bottom: 1.2rem;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--line);
}
.gal__hero img { display: block; width: 100%; height: auto; }
.gal__blurb {
  margin: 0 0 0.8rem;
  max-width: 62ch;
  color: var(--dim);
  font-size: 1rem;
  line-height: 1.6;
}
/* three classes: beats `:root .mono` at (0,2,0) */
.gal .gal__meta.mono { margin: 0 0 1.4rem; color: var(--accent-label); }

/* Columns rather than grid, on purpose. These sets mix portrait and landscape
   and a fixed-ratio grid would crop half of them; a column flow keeps every
   photograph at the shape it was taken. */
/* Row-major, fixed columns: images read left to right and wrap to a new row,
   so item 1 sits top-left, 2 to its right, 3 to the right of that, 4 begins the
   next row. A masonry column-fill was replaced here because it ordered top to
   bottom down each column instead. Uniform 4:3 cells keep the rows aligned; the
   full uncropped photograph opens on click. */
.gal__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.6rem, 1.2vw, 1rem);
}
.gal__item {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  border-radius: var(--radius);
  overflow: hidden;
  cursor: zoom-in;
  line-height: 0;
}
.gal__item img {
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  background: var(--line);
  transition: opacity 200ms ease;
}
.gal__item:hover img { opacity: 0.82; }
.gal__item:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

/* ---- the enlarged viewer ----
   z-index 210 puts it over the lightbox at 200. It is a sibling of .sbx on
   body, not a child of the panel, so a tall photograph is sized against the
   viewport instead of the panel's scroll box. */
.galv {
  position: fixed;
  inset: 0;
  z-index: 210;
  display: grid;
  place-items: center;
  padding: clamp(1rem, 4vw, 3rem);
}
.galv[hidden] { display: none; }
.galv__scrim {
  position: absolute;
  inset: 0;
  /* near-opaque on purpose: the gallery panel sits directly behind the viewer
     and a translucent scrim let its Close button and captions bleed through. */
  background: color-mix(in srgb, var(--ink) 97%, transparent);
}
.galv__fig {
  position: relative;
  z-index: 1;
  margin: 0;
  display: grid;
  justify-items: center;
  gap: 0.7rem;
  max-width: 100%;
  max-height: 100%;
}
.galv__fig img {
  display: block;
  /* max-width only, never a width: a 600px master renders at 600px rather
     than being stretched across a 1400px screen */
  max-width: 100%;
  max-height: calc(100vh - 9rem);
  width: auto;
  height: auto;
  border-radius: var(--radius);
}
.galv .galv__cap.mono { color: rgba(255, 255, 255, 0.72); }
.galv__btn {
  position: absolute;
  z-index: 2;
  background: color-mix(in srgb, var(--ink) 60%, transparent);
  border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: var(--radius);
  color: #fff;
  font-family: var(--font-mono);
  font-size: var(--mono-size);
  padding: 0.5rem 0.8rem;
  cursor: pointer;
  transition: background 200ms ease;
}
.galv__btn:hover { background: var(--accent); border-color: var(--accent); }
.galv__btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.galv__btn--close { top: clamp(0.8rem, 2vw, 1.6rem); right: clamp(0.8rem, 2vw, 1.6rem); }
.galv__btn--prev  { left: clamp(0.4rem, 1.6vw, 1.4rem); top: 50%; transform: translateY(-50%); }
.galv__btn--next  { right: clamp(0.4rem, 1.6vw, 1.4rem); top: 50%; transform: translateY(-50%); }

@media (prefers-reduced-motion: reduce) {
  .gal__item img,
  .galv__btn { transition: none; }
}

/* On a narrow screen the side arrows would sit on top of the photograph, so
   move both to the bottom. Placed AFTER the base .galv__btn rules on purpose:
   these override them and CSS resolves ties by source order, so an earlier
   media-query block would lose to the base rule regardless of width. */
@media (max-width: 720px) {
  .gal__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .galv__btn--prev { left: 50%; top: auto; bottom: clamp(0.8rem, 3vw, 1.4rem); transform: translateX(-135%); }
  .galv__btn--next { right: 50%; top: auto; bottom: clamp(0.8rem, 3vw, 1.4rem); transform: translateX(135%); }
  .galv__fig img { max-height: calc(100vh - 12rem); }
}

/* ---------------- portfolio: clickable ---------------- */

.portcard--interactive { cursor: pointer; transition: border-color 200ms ease, transform 200ms ease; }
.portcard--interactive:hover { border-color: var(--accent); transform: translateY(-2px); }
.portcard--interactive:has(.portcard__open:focus-visible) { border-color: var(--accent); }
.portcard__open {
  background: none;
  border: 0;
  padding: 0;
  margin-top: 1.1rem;
  font: inherit;
  cursor: pointer;
}
.portshot { display: grid; gap: 0.5rem; }
.portshot__box {
  display: grid;
  place-items: center;
  /* width must be the constraint. Without this the box derives its width from
     its height via aspect-ratio, which was harmless while the ratio was 9:16
     (narrow) but overflows the grid column at 16:9. */
  width: 100%;
  /* match the real thumbnails: 4:3 landscape. This was 9:16 back when the only
     portfolio content was vertical social film. */
  aspect-ratio: 4 / 3;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  background:
    repeating-linear-gradient(135deg,
      transparent 0 10px,
      color-mix(in srgb, var(--ink) 4%, transparent) 10px 11px);
}
.portshot__play { color: var(--accent-label); font-size: 0.85rem; }
/* three classes: beats `:root .mono` */
.portstrip .portshot span.mono { color: var(--dim); }

@media (prefers-reduced-motion: reduce) {
  .portcard--interactive { transition: none; }
  .portcard--interactive:hover { transform: none; }
}

@media (max-width: 720px) {
  .sbx__body .portstrip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ==================================================================
   THANK-YOU PAGE
   ================================================================== */

.ty-actions {
  margin-top: clamp(1.8rem, 3.4vw, 2.6rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem 1.4rem;
}
/* three classes: beats `:root .mono` — see the header note */
.thanks .ty-actions p.mono { margin: 0; color: var(--dim); }

/* ---------------- published contact address ----------------
   Added 2026-09-05. Both of these are quiet on purpose: the form is the path
   that captures selected_tier and the UTM set, so a visible email must read as
   a fallback rather than an alternative. Underline on hover only. */
/* The footer is the one place a visitor goes LOOKING for contact details, so
   here the address must read as a link, not a label. Inherit the colour its
   sibling mono lines already use rather than dimming it: a grey link sitting
   between orange ones reads as inert text. */
.footer__mid p.footer__contact a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid color-mix(in srgb, currentColor 35%, transparent);
}
.footer__mid p.footer__contact a:hover,
.footer__mid p.footer__contact a:focus-visible { border-bottom-color: currentColor; }

.finale__direct {
  margin-top: 0.9rem;
  color: var(--dim);
  font-size: 0.92rem;
  line-height: 1.55;
}
.finale__direct a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.finale__direct a:hover,
.finale__direct a:focus-visible { color: var(--accent-label); }

/* ---------------- thank-you: what the call actually is ----------------
   Added 2026-09-05. Sits between .ty-free and .ty-actions and exists to answer
   the four questions the bare button left open: how long, on what, with whom,
   and is this a pitch. Kept visually quiet - it explains the action, it is not
   a second call to action competing with the button below it. */
.ty-call {
  margin-top: clamp(1.6rem, 3.2vw, 2.4rem);
  padding-top: clamp(1.2rem, 2.4vw, 1.6rem);
  border-top: 1px solid var(--line);
}
/* three classes: beats `:root .mono` - see the header note */
.thanks .ty-call h3.ty-call__head {
  margin: 0 0 0.7rem;
  color: var(--accent-label);
  font-size: calc(0.62rem + 3px);
  letter-spacing: 0.08em;
}
/* Stacked, not an inline run with dot separators between items. Inline was
   tried first and breaks: the middle item is long enough to wrap on a phone,
   and a separator drawn with `li + li::before` then lands at the START of a
   wrapped line, where it reads as a broken bullet rather than a divider. A
   column has no wrap case to get wrong, and three short facts scan fine
   stacked on a page with this much vertical room. */
.ty-call__facts {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  justify-items: start;
  gap: 0.32rem;
  font-size: calc(0.62rem + 2px);
  /* The paragraphs above are centred on a 40rem measure. Left-aligning this
     list against the container edge reads as a mistake next to them, so centre
     the BLOCK and keep the items aligned to each other inside it. max-content
     is capped at 100% so a long item cannot push the page wide on a phone. */
  width: max-content;
  max-width: 100%;
  margin-inline: auto;
}
/* three classes: beats `:root .mono` - see the header note */
.thanks .ty-call ul.ty-call__facts li {
  color: var(--dim);
  position: relative;
  padding-left: 0.85rem;
}
.thanks .ty-call ul.ty-call__facts li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.55;
}
