/* ==========================================================================
   pages.css — page-specific layout and the four hand-built SVG diagrams.
   Depends on tokens.css, base.css, components.css.
   ========================================================================== */

/* --------------------------------------------------------------------------
   HERO
   -------------------------------------------------------------------------- */
.hero { padding-block: clamp(36px, 5vw, 64px) clamp(32px, 4.5vw, 56px); }
/* One column since 16 Oct 2026. The hero photograph is the old site's own
   workshop shot, 2.53:1 — a band, not a column. Beside the copy it rendered
   492px wide and 194 tall, at which size none of the nine people in it is
   recognisable, which defeats the reason Marta wanted this photograph. Full
   width it runs 1100x434 and reads as the hero it is. The copy keeps a
   readable measure rather than stretching across the band. */
.hero__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-5);
  justify-items: start;
}
/* The copy keeps a readable measure; the photograph takes the whole band. */
.hero__grid > :not(.hero__media) { max-width: var(--maxw-read); }
/* No extra margin on the photograph: the grid gap already separates it, and
   the 24px it added pushed the primary button off a 900px-tall laptop. */
.hero__grid > .hero__media { justify-self: stretch; }
/* The hero grid gap owns the vertical rhythm now. */
.hero h1 { margin-bottom: 0; }

/* Hand-drawn orange underline under "Act On."
   Sits tight to the baseline: the display line-height is 1.12, so at the
   mobile H1 size there is only a few px of leading and a deeper offset
   collided with the line below when the heading wrapped. */
.underlined { position: relative; white-space: nowrap; }
.underlined::after {
  content: "";
  position: absolute; left: 0; right: -.04em; bottom: -.02em;
  height: .13em;
  background: url("../img/hero-underline.svg") no-repeat center / 100% 100%;
  pointer-events: none;
}

.hero__lede { margin-bottom: 0; }

.hero__media {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  /* LQIP sits behind the image while it loads; build_images.py emits it. */
  background: var(--purple-tint) center / cover no-repeat;
}
.hero__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

@media (max-width: 900px) {
  /* No reordering on small screens any more: the single column already
     reads label, headline, lede, photograph, buttons at every width. */
}

/* --------------------------------------------------------------------------
   SECTION PHOTO — full-width supporting image inside a band.
   -------------------------------------------------------------------------- */
.section-photo {
  margin-top: var(--s-6);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--purple-100);
}
.section-photo img { width: 100%; height: auto; display: block; }

/* --------------------------------------------------------------------------
   SPLIT — text column beside a supporting image (Home §4).

   This was written as an inline style on the element. Inline styles cannot be
   overridden by a media query without !important, so the two columns never
   collapsed on mobile: at 375px the text column became ~20px wide and every
   card inside it wrapped one character per line. Layout belongs in a
   stylesheet precisely so it can respond.
   -------------------------------------------------------------------------- */
.split {
  display: grid;
  grid-template-columns: 1.15fr .85fr;
  gap: clamp(24px, 4vw, 56px);
  align-items: center;
}
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   BENEFIT LIST — "What You Walk Away With", each with an orange spark check.
   -------------------------------------------------------------------------- */
.benefits {
  list-style: none; padding: 0; margin: 0;
  display: grid; gap: var(--s-4) var(--s-6);
  grid-template-columns: repeat(2, 1fr);
  max-width: none;
}
@media (max-width: 820px) { .benefits { grid-template-columns: 1fr; } }
.benefits li {
  display: grid; grid-template-columns: 28px 1fr; gap: var(--s-4);
  align-items: start; margin: 0;
}
.benefits .icon { width: 26px; height: 26px; margin-top: 2px; }
.benefits strong { display: block; margin-bottom: 2px; }
.benefits p { margin: 0; color: var(--ink-soft); }

/* --------------------------------------------------------------------------
   TESTIMONIAL ROTATOR
   Home carries 7 quotes in the copy. Three strongest stay inline; the rest
   move here so the page stops being a wall of text. Same copy, less height.
   Review slide 1: "there is too much info on the home page".
   -------------------------------------------------------------------------- */
.rotator { position: relative; }
/* Full width, so the rotating quotes sit on the same margins as the
   inline ones. Without this the grid sized itself to its content
   (the 70ch text measure) and the inline centring floated the box
   off the page's left edge. */
.rotator__track { display: grid; gap: var(--s-5); width: 100%; }

/* Progressive enhancement: without JS this is just a stacked list of quotes —
   all readable, nothing hidden. JS adds .rotator--live, which collapses them
   into one grid cell and reveals them one at a time. */
.rotator--live .rotator__track { gap: 0; }
.rotator--live .rotator__slide {
  grid-area: 1 / 1;
  opacity: 0; visibility: hidden;
  transition: opacity var(--dur) var(--ease);
}
.rotator--live .rotator__slide[aria-hidden="false"] {
  opacity: 1; visibility: visible;
}
.rotator__dots {
  display: flex; gap: var(--s-2); justify-content: center;
  margin-top: var(--s-5);
}
.rotator__dot {
  width: 34px; height: 4px; border-radius: 2px; border: 0; padding: 0;
  min-height: 0;
  background: var(--purple-line); cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.rotator__dot[aria-selected="true"] { background: var(--purple); }
/* Keep the 44px touch target without a 44px-tall bar. */
.rotator__dot::before {
  content: ""; position: absolute; inset: -20px -6px;
}
.rotator__dot { position: relative; }

/* ==========================================================================
   DIAGRAM 1 — THE MODEL: Human Insights → New Thinking → Strategy
   Answers review slide 2 ("delete this text and add the infographic here").
   Three nodes on a track; the track draws in, then the nodes pop in sequence.
   ========================================================================== */
.model { --node: 84px; }
.model__flow {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-5);
  position: relative;
}
/* The connecting track sits behind the nodes. */
.model__flow::before {
  content: "";
  position: absolute; top: calc(var(--node) / 2); left: 12%; right: 12%;
  height: 3px;
  background: var(--nu-orange);
  transform: scaleX(0); transform-origin: left;
  transition: transform 900ms var(--ease);
  border-radius: 2px;
}
.model.is-visible .model__flow::before { transform: scaleX(1); }

.model__node { text-align: center; position: relative; }
.model__disc {
  width: var(--node); height: var(--node);
  margin: 0 auto var(--s-3);
  border-radius: 50%;
  background: var(--white);
  border: 3px solid var(--purple-line);
  display: grid; place-items: center;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
.reveal-ready .model__disc { opacity: 0; transform: scale(.86); }
.model.is-visible .model__disc { opacity: 1; transform: none; }
.model.is-visible .model__node:nth-child(2) .model__disc { transition-delay: 180ms; }
.model.is-visible .model__node:nth-child(3) .model__disc { transition-delay: 360ms; }
.model__disc .icon { width: 36px; height: 36px; }
.model__node:hover .model__disc { border-color: var(--purple); }
.model__node h3 { margin-bottom: var(--s-2); }
.model__node p { color: var(--ink-soft); font-size: var(--small); margin: 0; }

/* The model's closing sentence, centred under the diagram. */
.model-close {
  margin: var(--s-7) auto 0;
  max-width: 680px;
  text-align: center;
  text-wrap: balance;
  font-size: var(--lede);
  color: var(--ink-soft);
}

@media (max-width: 760px) {
  /* Left-aligned on mobile, laid out exactly like the .stepper further down
     the same page: the track runs down the disc centres and the copy sits in
     its own column beside it. The centred full-height track this replaces ran
     straight through every heading and paragraph, because the nodes stayed
     centred while the line stayed at 50%. */
  .model { --node: 64px; }
  .model__flow { grid-template-columns: 1fr; gap: var(--s-6); }
  /* One continuous track would have to run the full height of the flow, which
     means past the last disc and down the side of its paragraph, ending in
     mid-air. A segment per node instead: disc bottom to next disc top. */
  .model__flow::before { content: none; }
  .model__node:not(:last-child)::after {
    content: "";
    position: absolute;
    left: calc(var(--node) / 2 - 1.5px);
    top: var(--node); bottom: calc(var(--s-6) * -1);
    width: 3px; border-radius: 2px;
    background: var(--nu-orange);
    transform: scaleY(0); transform-origin: top;
    transition: transform 520ms var(--ease);
  }
  .model.is-visible .model__node::after { transform: scaleY(1); }
  .model.is-visible .model__node:nth-child(2)::after { transition-delay: 180ms; }

  /* .model__node has THREE flat children (disc, h3, p). Place them explicitly
     — the same auto-placement trap that wrapped the stepper's paragraph into
     its 52px number column. */
  .model__node {
    text-align: left;
    display: grid;
    grid-template-columns: var(--node) 1fr;
    column-gap: var(--s-4);
    row-gap: var(--s-2);
    align-items: start;
  }
  .model__disc { grid-column: 1; grid-row: 1 / span 2; margin: 0; }
  .model__node h3 { grid-column: 2; grid-row: 1; margin: 0; }
  .model__node p  { grid-column: 2; grid-row: 2; }
  .model__disc .icon { width: 28px; height: 28px; }
}

/* ==========================================================================
   DIAGRAM 2 — THE PROCESS STEPPER (four steps)
   Answers review slide 5 ("a diagram that showcases a process, step by step")
   — replaces four stacked outline numerals with a real stepper.
   ========================================================================== */
.stepper { --progress: 0%; position: relative; }
.stepper__track {
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: var(--s-5); position: relative;
}
.stepper__track::before,
.stepper__track::after {
  content: ""; position: absolute; top: 26px; left: 11%; right: 11%; height: 3px;
  border-radius: 2px;
}
.stepper__track::before { background: var(--purple-line); }
.stepper__track::after {
  background: var(--nu-orange);
  width: calc((100% - 22%) * 1);
  transform: scaleX(0); transform-origin: left;
  transition: transform 1100ms var(--ease);
  left: 11%; right: auto;
}
.stepper.is-visible .stepper__track::after { transform: scaleX(1); }

.stepper__step { text-align: center; position: relative; }
.stepper__num {
  width: 52px; height: 52px; margin: 0 auto var(--s-4);
  border-radius: 50%;
  background: var(--white); border: 3px solid var(--purple);
  color: var(--purple-deep);
  font-family: var(--font-display); font-weight: 800; font-size: 19px;
  display: grid; place-items: center;
  position: relative; z-index: 1;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.reveal-ready .stepper__num { opacity: 0; transform: scale(.8); }
.stepper.is-visible .stepper__num { opacity: 1; transform: none; }
.stepper.is-visible .stepper__step:nth-child(2) .stepper__num { transition-delay: 140ms; }
.stepper.is-visible .stepper__step:nth-child(3) .stepper__num { transition-delay: 280ms; }
.stepper.is-visible .stepper__step:nth-child(4) .stepper__num { transition-delay: 420ms; }
.stepper__step h3 { font-size: 17px; margin-bottom: var(--s-2); }
.stepper__step p { font-size: var(--small); color: var(--ink-soft); margin: 0; }

@media (max-width: 820px) {
  /* Vertical on mobile, track running down the left. */
  .stepper__track { grid-template-columns: 1fr; gap: var(--s-6); }
  /* A segment per step rather than one track down the whole column: a single
     track has to run the full height and ends 79px past the last numeral, in
     mid-air beside its paragraph. Same treatment as the model diagram above. */
  .stepper__track::before, .stepper__track::after { content: none; }
  .stepper__step:not(:last-child)::after {
    content: "";
    position: absolute;
    left: 25px; top: 52px; bottom: calc(var(--s-6) * -1);
    width: 3px; border-radius: 2px;
    background: var(--nu-orange);
    transform: scaleY(0); transform-origin: top;
    transition: transform 460ms var(--ease);
  }
  .stepper.is-visible .stepper__step::after { transform: scaleY(1); }
  .stepper.is-visible .stepper__step:nth-child(2)::after { transition-delay: 140ms; }
  .stepper.is-visible .stepper__step:nth-child(3)::after { transition-delay: 280ms; }
  /* Explicit placement, not auto-placement. .stepper__step has THREE
     children (number, h3, p); a bare two-column grid put the number and the
     h3 on row 1 and then wrapped the paragraph into the 52px number column,
     one word per line. Same failure the .card--row cards had. */
  .stepper__step {
    text-align: left;
    display: grid;
    grid-template-columns: 52px 1fr;
    column-gap: var(--s-4);
    row-gap: var(--s-2);
    align-items: start;
  }
  .stepper__step > .stepper__num { grid-column: 1; grid-row: 1 / span 2; margin: 0; }
  .stepper__step > h3            { grid-column: 2; grid-row: 1; }
  .stepper__step > p             { grid-column: 2; grid-row: 2; }
}

/* ==========================================================================
   DIAGRAM 4 — OUR BOLD PROMISE (About)
   Review slide 15, MV: "Sounds good." The showcase animated infographic.
   Three pillars: icon path draws itself, pillar rises, text fades up, ~180ms
   apart. Fully disabled under prefers-reduced-motion (see base.css).
   ========================================================================== */
.promise { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-6); }
.promise__pillar {
  background: var(--white);
  border: 1px solid var(--purple-line);
  border-radius: var(--radius);
  padding: var(--s-6);
  text-align: center;
  transition: opacity var(--dur-slow) var(--ease),
              transform var(--dur-slow) var(--ease);
}
.reveal-ready .promise__pillar { opacity: 0; transform: translateY(22px); }
.promise.is-visible .promise__pillar { opacity: 1; transform: none; }
.promise.is-visible .promise__pillar:nth-child(2) { transition-delay: 180ms; }
.promise.is-visible .promise__pillar:nth-child(3) { transition-delay: 360ms; }
.promise__ring {
  width: 76px; height: 76px; margin: 0 auto var(--s-5);
  border-radius: 50%;
  background: var(--purple-tint);
  display: grid; place-items: center;
}
.promise__ring .icon { width: 34px; height: 34px; }
/* The icon strokes draw themselves along their path. */
.promise__ring .icon * { transition: stroke-dashoffset 900ms var(--ease); }
.reveal-ready .promise__ring .icon * {
  stroke-dasharray: 90; stroke-dashoffset: 90;
}
.promise.is-visible .promise__ring .icon * { stroke-dashoffset: 0; }
.promise__pillar p { color: var(--ink-soft); margin: 0; }
@media (max-width: 900px) { .promise { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------------------
   CASE TEASER — full-width purple card.
   Review slide 7: "I would add a purple box with the caption 'Read the case
   studies', something more appealing" — not a bare text link.
   -------------------------------------------------------------------------- */
.case-teaser { display: grid; grid-template-columns: 1.25fr .75fr; gap: clamp(24px,4vw,48px); align-items: center; }
.case-teaser__media { border-radius: var(--radius-sm); overflow: hidden; }
.case-teaser__media img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 860px) { .case-teaser { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------------------
   SECTION DIVIDER
   -------------------------------------------------------------------------- */
.divider {
  display: block; width: 160px; height: 12px;
  margin: var(--s-6) auto;
  background: url("../img/divider-flourish.svg") no-repeat center / contain;
}

/* ==========================================================================
   CASE STUDY BLOCK (case-studies.html)
   The copy doc gives each study a three-part structure — The Challenge /
   What We Did / Why It Worked — plus a method-tag row and one outcome figure.
   Rendered as three labelled columns with the figure pulled out beside them.
   ========================================================================== */
.case__head { margin-bottom: var(--s-6); }
.case__subtitle {
  font-size: var(--micro); font-weight: 700;
  letter-spacing: var(--ls-micro); text-transform: uppercase;
  color: var(--text-orange);      /* accessible orange: 5.7:1, not --nu-orange */
  margin: 0 0 var(--s-2);
}
.case__head h2 { margin-bottom: var(--s-4); }
.case__sep { color: var(--nu-orange); padding-inline: .25em; }
.case__client { color: var(--ink); }
.case__tags { margin-top: var(--s-4); }
.case__tags .chip { font-size: 13px; }

.case__body {
  display: grid;
  grid-template-columns: 1fr 220px;
  gap: clamp(24px, 4vw, 56px);
  align-items: start;
}
.case__steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-5); }
.case__step h3 {
  font-size: var(--micro); font-weight: 700;
  letter-spacing: var(--ls-micro); text-transform: uppercase;
  color: var(--nu-purple);
  margin-bottom: var(--s-3);
  padding-bottom: var(--s-2);
  border-bottom: 2px solid var(--line);
}
.case__step p { color: var(--ink-soft); font-size: var(--small); margin: 0; }

/* The one figure worth pulling out of each study. */
.case__stat {
  background: var(--purple-100);
  border-radius: var(--radius-md);
  padding: var(--s-5);
  text-align: center;
}
.case__stat-num {
  display: block;
  font-family: var(--font-display); font-weight: 800;
  font-size: clamp(30px, 3.2vw, 40px);
  line-height: 1.05;
  color: var(--nu-purple);
}
.case__stat-unit { font-size: .5em; margin-left: .08em; }
.case__stat-label {
  display: block; margin-top: var(--s-2);
  font-size: var(--small); color: var(--ink-muted);
}

@media (max-width: 1000px) {
  .case__body { grid-template-columns: 1fr; }
  .case__stat { justify-self: start; min-width: 200px; }
}
@media (max-width: 760px) {
  .case__steps { grid-template-columns: 1fr; gap: var(--s-6); }
}

/* ==========================================================================
   CONTACT (contact.html)
   ========================================================================== */
.contact-grid {
  display: grid;
  grid-template-columns: 1.15fr .85fr;
  gap: clamp(32px, 5vw, 64px);
  align-items: start;
}
@media (max-width: 900px) { .contact-grid { grid-template-columns: 1fr; } }

.timeline { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--s-5); }
.timeline li {
  display: grid; grid-template-columns: 44px 1fr; gap: var(--s-4);
  align-items: start; margin: 0;
}
.timeline__mark {
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--purple-100);
  display: grid; place-items: center;
}
.timeline__mark .icon { width: 22px; height: 22px; }
.timeline h3 { font-size: 17px; margin-bottom: 2px; }
.timeline p { margin: 0; color: var(--ink-soft); font-size: var(--small); }

/* Two low-commitment entry points — these matter commercially, so they get a
   distinct tinted, bordered treatment rather than plain cards. */
.entry { border: 2px solid var(--purple-line); background: var(--purple-100); }
.entry .card__icon { background: var(--surface); }

/* ==========================================================================
   404
   ========================================================================== */
.notfound {
  min-height: 60vh;
  display: grid; align-content: center;
  text-align: center;
}
.notfound__code {
  font-family: var(--font-display); font-weight: 900;
  font-size: clamp(72px, 14vw, 160px); line-height: 1;
  color: var(--purple-100);
  margin: 0;
}
.notfound h1 { margin-top: calc(var(--s-5) * -1); }

/* ==========================================================================
   HUB — the three services as ONE diagram (Home §5)

   Corrected deck, slide 4: "is there a way to make it look like one diagram?
   So that the information comes out of the research icon, and the same for
   the others." Previously this was three separate cards plus a standalone
   triangle below them, which read as two unrelated things.

   A shared engine sits at the top; each service hangs off its own node, so
   the text is visibly attached to its icon rather than floating near it.
   ========================================================================== */
.hub { display: grid; justify-items: center; }

.hub__core {
  display: inline-flex; align-items: center; gap: var(--s-3);
  background: var(--nu-purple); color: var(--on-purple);
  border-radius: var(--radius-pill);
  padding: var(--s-3) var(--s-5);
  font-family: var(--font-display); font-weight: 800; font-size: var(--small);
  text-align: center;
}
.hub__core .icon { color: var(--nu-orange); width: 22px; height: 22px; }

/* Connector band between the engine and the three nodes. */
.hub__links {
  width: 100%; height: 80px; display: block;
}
.hub__links path {
  fill: none; stroke: var(--purple-line); stroke-width: 2;
  stroke-dasharray: 1; stroke-dashoffset: 1;
  transition: stroke-dashoffset 900ms var(--ease);
}
.hub.is-visible .hub__links path { stroke-dashoffset: 0; }
.hub.is-visible .hub__links path:nth-child(2) { transition-delay: 120ms; }
.hub.is-visible .hub__links path:nth-child(3) { transition-delay: 240ms; }

.hub__spokes {
  width: 100%;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-5);
}
.hub__spoke { text-align: center; }
.hub__node {
  width: 60px; height: 60px; margin: 0 auto var(--s-4);
  border-radius: 50%;
  background: var(--surface);
  border: 3px solid var(--nu-purple);
  display: grid; place-items: center;
}
.hub__node .icon { width: 28px; height: 28px; }
.hub__spoke h3 { font-size: 17px; margin-bottom: var(--s-2); }
.hub__spoke p { color: var(--ink-soft); font-size: var(--small); margin: 0; }

@media (max-width: 820px) {
  /* The fanned connectors cannot survive a single column; a vertical rule
     down the left carries the same "one diagram" reading. */
  .hub__links { display: none; }
  .hub { justify-items: stretch; }
  .hub__core { justify-self: start; margin-bottom: var(--s-6); }
  .hub__spokes {
    grid-template-columns: 1fr; gap: var(--s-6);
    border-left: 2px solid var(--purple-line);
    padding-left: var(--s-5);
  }
  .hub__spoke { text-align: left; }
  .hub__node { margin: 0 0 var(--s-3); }
}

/* ==========================================================================
   APPROACH / SERVICES / ABOUT
   ========================================================================== */

/* The model heading carries literal arrows from the copy document. */
.arrow-sep { color: var(--nu-orange); padding-inline: .15em; }

/* Full-length process steps need more room than the condensed Home version. */
.stepper--full .stepper__step p { font-size: var(--body); }

.toolbox-stat {
  display: grid; grid-template-columns: 200px 1fr;
  gap: clamp(20px, 3vw, 40px); align-items: center;
  margin-top: var(--s-7);
}
.toolbox-stat p { color: var(--ink-soft); margin: 0; }
@media (max-width: 760px) { .toolbox-stat { grid-template-columns: 1fr; } }

/* The masterclass promo. Framed and captioned, because it is a reproduction
   of someone else's announcement rather than a photograph of the work. */
.masterclass { margin: var(--s-6) 0 0; }
.masterclass img {
  width: 100%; height: auto; display: block;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}
.masterclass figcaption {
  margin-top: var(--s-3);
  font-size: var(--micro); color: var(--ink-muted); text-align: center;
}

/* Method groups on Services. */
.methods { display: grid; gap: var(--s-6); }
.methods__group h3 {
  display: flex; align-items: center; gap: var(--s-3);
  font-size: 17px; margin-bottom: var(--s-3);
}
.methods__group p { color: var(--ink-soft); margin: 0; }

/* Industry disclosures — the lists are long enough to swamp the section. */
.disclosure {
  margin-top: var(--s-3);
  background: none; border: 0; padding: 0;
  font: inherit; font-size: var(--small); font-weight: 700;
  color: var(--nu-purple); cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.disclosure:hover { color: var(--purple-deep); }

/* About — the two portraits, centred as a pair beside the bio (deck slide 10). */
.portraits { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }
.portraits img {
  width: 100%; height: auto; display: block;
  border-radius: var(--radius-md);
}
@media (max-width: 480px) { .portraits { grid-template-columns: 1fr; } }

/* One portrait, not two (Jaime, 14 Oct 2026: keep only the black-background
   shot). Left in the two-column grid it would sit in the left half at half
   the size; centred and capped it keeps the deck's "these should be centered"
   intact with a single photograph. */
.portraits--single { grid-template-columns: 1fr; max-width: 360px; margin-inline: auto; }

/* Speaking grid. CSS columns before: they balance by content height, so with
   photographs this different in shape one column ran out and left a hole in
   the middle of the section. Every tile is cut to 4:3 in the pipeline now and
   this is an ordinary grid, which lines up by construction. Six photographs
   divide evenly by three and by two, so no breakpoint leaves an empty cell. */
.photo-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-4);
}
.photo-grid picture { display: block; }
.photo-grid img {
  /* Square since 16 Oct 2026: see the note in about.html. The tiles are cut
     to 1:1 in the pipeline, so object-fit has nothing left to do here — it
     stays as the guard against a future source arriving at another shape. */
  width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block;
  border-radius: var(--radius-md);
}
@media (max-width: 900px) { .photo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .photo-grid { grid-template-columns: 1fr; } }

/* Quotes shown side by side rather than one under another. */
.quote--stacked { margin-top: 0; }
.quote--stacked .quote__body { padding: var(--s-5); }
.quote--stacked .quote__body p { font-size: var(--body); max-width: none; }
.quote--stacked .quote__body::before { display: none; }

/* ==========================================================================
   LEGAL — the privacy policy. Long-form prose, so it reads at one column at
   --maxw-read rather than across the full wrap, and the headings carry the
   hierarchy on their own: no cards, no bands, nothing competing with the text.
   ========================================================================== */
.legal { max-width: var(--maxw-read); }
.legal h2 {
  font-size: var(--h3);
  margin-top: var(--s-7); margin-bottom: var(--s-3);
}
.legal p { margin: 0 0 var(--s-4); }
.legal ul {
  margin: 0 0 var(--s-4); padding-left: var(--s-5);
  display: grid; gap: var(--s-3);
}
.legal li { color: var(--ink-soft); }
.legal a { color: var(--purple); }
.legal__date {
  color: var(--ink-muted); font-size: var(--small); margin-top: var(--s-3);
}
.legal__contact { line-height: 1.8; }
.legal__revised {
  margin-top: var(--s-7); padding-top: var(--s-4);
  border-top: 1px solid var(--line);
  color: var(--ink-muted); font-size: var(--micro);
}
