/* Yacoda — computational engineering studio
   Single stylesheet. Sections are identified by id; colour comes from one
   block-* class. Type is authored in normal case and uppercased here. */

/* ---------------------------------------------------------------- tokens */
:root {
  --paper: #eee9df;
  --ink: #17151a;
  --purple: #5a1658;
  --lime: #d9ff43;
  --grey: #c9c7c0;
  --fluo: #d4ff00; /* placeholder marker — nothing wearing this is real yet */
  --sans: Syne, sans-serif;
  --mono: "DM Mono", monospace;
  --rule: 2px solid;
}

* {
  box-sizing: border-box;
}
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
}
a {
  color: inherit;
  text-decoration: none;
}
:focus-visible {
  outline: 3px solid currentColor;
  outline-offset: 3px;
}

/* ----------------------------------------------------------- the blocks */
/* A block sets ground + ink. Emphasis takes the block's counter-colour, so
   <em> never has to know which section it is in. */
.block-white {
  background: var(--paper);
  color: var(--ink);
}
.block-yellow {
  background: var(--lime);
  color: var(--ink);
}
.block-purple {
  background: var(--purple);
  color: var(--paper);
}
.block-black {
  background: var(--ink);
  color: var(--paper);
}

em {
  font-style: normal;
}
.block-white em,
.block-yellow em {
  color: var(--purple);
}
.block-purple em,
.block-black em {
  color: var(--lime);
}

/* --------------------------------------------------------------- type */
h1,
h2,
h3 {
  margin: 0;
  text-transform: uppercase;
}

h1 {
  font-size: clamp(48px, 9.5vw, 145px);
  line-height: 0.87;
  letter-spacing: -0.07em;
  text-transform: none;
  padding-bottom: 0.12em;
}

/* One eyebrow treatment everywhere: 11px mono, uppercase, no exceptions. */
.eyebrow,
.portrait figcaption {
  font: 11px var(--mono);
  text-transform: uppercase;
}

.lead {
  font-size: clamp(22px, 2vw, 30px);
  line-height: 1.2;
  margin: 0;
}

/* ------------------------------------------------- outbound link arrow */
/* The href decides. An internal #anchor gets none; a link that goes
   nowhere (href="#") gets none either, which is a useful tell. */
a[href^="http"]::after,
a[href^="mailto"]::after {
  content: "\00a0↗";
}
/* .video is a flex column, so an ::after on the anchor would land on its own
   line — put the arrow on the title instead. */
.video::after {
  content: none;
}
/* Inline citation in prose: underlined, and it keeps the ↗ like any other
   outbound link. */
.ref {
  border-bottom: 1px solid currentColor;
}
/* An arrow trailing an image link is meaningless. */
.casadi-logo a::after {
  content: none;
}
.casadi-logo a {
  display: block;
  line-height: 0;
}
.video h3::after {
  content: "\00a0↗";
}

/* ------------------------------------------------------------- header */
header {
  min-height: 92px;
  border-bottom: var(--rule);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  padding: 12px 2.5vw;
}
nav {
  display: flex;
  gap: 32px;
  font: 12px var(--mono);
  text-transform: uppercase;
}
nav a {
  border-bottom: 1px solid transparent;
}
nav a:hover,
nav a:focus-visible {
  border-bottom-color: currentColor;
}
.mail {
  text-align: right;
  font: 12px var(--mono);
  text-transform: uppercase;
}
.kit-logo {
  display: block;
  height: auto;
  max-width: 100%;
}
.kit-logo--footer {
  width: 170px;
}
.kit-logo--mark {
  width: 46px;
  height: 46px;
}
.kit-logo-link {
  display: flex;
  align-items: center;
  line-height: 0;
  flex-shrink: 0;
}
footer .kit-logo-link {
  width: max-content;
}

/* --------------------------------------------------------------- hero */
#hero {
  min-height: 820px;
  display: grid;
  grid-template-columns: 2fr 1fr;
}
.hero-main {
  padding: 60px 4vw;
  border-right: var(--rule);
  display: flex;
  align-items: center;
}
.hero-side {
  display: flex;
  flex-direction: column;
  padding: 30px;
}
.hero-brand-panel {
  margin: -30px -30px 30px;
  padding: 34px 25px;
  min-height: 235px;
  display: grid;
  place-items: center;
}
.hero-brand-panel img {
  width: 100%;
  display: block;
}
.hero-intro {
  font-size: 19px;
  line-height: 1.4;
  margin-top: auto;
}
.hero-cta {
  font: 12px var(--mono);
  border-top: var(--rule);
  padding-top: 15px;
  text-transform: uppercase;
}

/* ------------------------------------------------- 01 capability cards */
#work {
  display: grid;
  grid-template-columns: 1.2fr 1fr 1fr 1fr;
  border-top: var(--rule);
  border-bottom: var(--rule);
}
.card {
  min-height: 610px;
  padding: 28px;
  border-right: var(--rule);
  display: flex;
  flex-direction: column;
}
.card:last-child {
  border-right: 0;
}
.card h2 {
  font-size: clamp(38px, 3.8vw, 55px);
  line-height: 0.85;
  letter-spacing: -0.06em;
}
.card h3 {
  font-size: clamp(20px, 1.9vw, 26px);
  min-height: 80px;
  text-wrap: balance;
}
.card-lede {
  font: 400 14px/1.5 var(--sans);
  margin: auto 0 0;
  max-width: 280px;
}
/* Yacoda's own record, under the lede in the title card. Number left, label
   right on a shared baseline: three rows cost ~130px of a 610px card, where a
   number-over-label stack would cost twice that. */
.card-stats {
  list-style: none;
  margin: 26px 0 0;
  padding: 0;
  max-width: 280px;
  font-variant-numeric: tabular-nums;
}
.card-stats li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding: 9px 0;
  border-top: 1px solid currentColor;
}
.card-stats strong {
  font-size: clamp(26px, 2.2vw, 32px);
  line-height: 1;
  letter-spacing: -0.04em;
}
.card-stats small {
  font: 11px var(--mono);
  text-transform: uppercase;
  text-align: right;
}
/* anchored to the h3 above (uniform 80px), so the first line of copy sits at
   the same height in every card; the tags float to the bottom instead */
.card-copy {
  font: 400 16px/1.45 var(--sans);
  margin: 24px 0 0;
}
.marker {
  font: 60px var(--mono);
  text-align: right;
  opacity: 0.25;
  margin-top: 24px;
}

/* Inline tag list: reads as prose, marked up as the list it is. */
.tags {
  font: 12px/1.8 var(--mono);
  margin: auto 0 0;
  padding: 0;
  max-width: 250px;
  list-style: none;
}
.tags li {
  display: inline;
}
.tags a {
  border-bottom: 1px solid currentColor;
}
.tags li:not(:last-child)::after {
  content: " · ";
  opacity: 0.55;
}

/* -------------------------------------------------------- 02 casadi */
#casadi {
  display: grid;
  grid-template-columns: 22% 1fr 28%;
  /* the eyebrow is a section child (so it can lead on mobile); on wider screens
     it is placed back above the h2 in the middle column */
  grid-template-rows: auto 1fr;
  border-block: var(--rule);
}
#casadi > .eyebrow {
  grid-column: 2;
  grid-row: 1;
  padding: 90px 4vw 0;
}
.casadi-logo {
  grid-column: 1;
  grid-row: 1 / -1;
  display: grid;
  place-items: center;
  border-right: var(--rule);
  padding: 24px;
}
.casadi-logo img {
  width: 160px;
  max-width: 100%;
}
.casadi-copy {
  grid-column: 2;
  grid-row: 2;
  padding: 0 4vw 90px;
}
.casadi-copy h2 {
  font-size: clamp(40px, 5vw, 72px);
  line-height: 0.8;
  letter-spacing: -0.07em;
}
.casadi-copy p {
  line-height: 1.6;
}
.stats {
  grid-column: 3;
  grid-row: 1 / -1;
  border-left: var(--rule);
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0;
  padding: 0;
}
.stats li {
  flex: 1;
  padding: 16px 22px;
  font-size: clamp(26px, 2.4vw, 32px);
  border-bottom: var(--rule);
  font-variant-numeric: tabular-nums;
}
.stats li:last-child {
  border-bottom: 0;
}
.stats small {
  display: block;
  font: 11px var(--mono);
  text-transform: uppercase;
}

/* -------------------------------------------- 03 cost-free abstraction */
#method {
  display: grid;
  /* minmax(0,…) so a long unbreakable word can't widen the column past the
     viewport — "ABSTRACTION." is deliberately allowed to bleed instead. */
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
}
.method-title,
.method-copy {
  padding: 100px 5vw;
}
.method-title {
  border-right: var(--rule);
}
.method-title h2 {
  font-size: clamp(40px, 6.4vw, 92px);
  line-height: 0.8;
  letter-spacing: -0.07em;
}
.method-copy {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.method-copy > p:not(.lead) {
  font-size: 18px;
  line-height: 1.6;
}

/* The arrows are decoration, so they are generated rather than authored. */
.chain {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto 1fr;
  gap: 15px;
  border-top: var(--rule);
  padding: 20px 0 0;
  margin: 50px 0 0;
  font: 12px var(--mono);
  font-weight: 700;
  text-transform: uppercase;
  list-style: none;
}
.chain::before,
.chain::after {
  content: "→";
  grid-row: 1;
  font-weight: 400;
}
.chain::before {
  grid-column: 2;
}
.chain::after {
  grid-column: 4;
}
.chain li:nth-child(1) {
  grid-column: 1;
}
.chain li:nth-child(2) {
  grid-column: 3;
}
.chain li:nth-child(3) {
  grid-column: 5;
}

/* --------------------------------------------------------------- 04 ai */
#ai {
  padding: 110px 5vw;
}
.ai-title h2 {
  font-size: clamp(40px, 8vw, 120px);
  line-height: 0.8;
  letter-spacing: -0.07em;
}
.ai-lede {
  font: 17px/1.6 var(--mono);
  max-width: 760px;
  opacity: 0.75;
}
.ai-grid {
  display: grid;
  grid-template-columns: 1fr 1fr 1.4fr;
  border: 1px solid var(--grey);
  margin-top: 80px;
}
.ai-grid article {
  min-height: 260px;
  padding: 25px;
  border-right: 1px solid var(--grey);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.ai-grid article:last-child {
  border-right: 0;
}
.ai-grid h3 {
  font: 11px var(--mono);
  text-transform: uppercase;
}
.ai-grid ul {
  font-size: clamp(19px, 1.7vw, 24px);
  line-height: 1.3;
  font-weight: 700;
  text-transform: uppercase;
  list-style: none;
  margin: 0;
  padding: 0;
}
.ai-result p {
  font-size: 18px;
  line-height: 1.45;
  font-weight: 700;
  margin: 0;
  text-transform: uppercase;
}

/* -------------------------------------------------------- 05 industries */
#industries {
  display: grid;
  grid-template-columns: 32% 68%;
  border-block: var(--rule);
}
.industry-intro {
  padding: 80px 4vw;
  border-right: var(--rule);
  display: flex;
  flex-direction: column;
}
.industry-intro h2 {
  font-size: clamp(42px, 6vw, 86px);
  line-height: 0.82;
  letter-spacing: -0.06em;
}
/* The spatial axis, alongside the industry axis. */
.geo {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  list-style: none;
  margin: auto 0 0;
  padding: 32px 0 0;
}
.geo li {
  font: 11px var(--mono);
  text-transform: uppercase;
  border: 1px solid currentColor;
  padding: 7px 12px;
}
.industry-image {
  height: 540px;
  overflow: hidden;
}
.industry-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.industry-list {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: var(--rule);
  list-style: none;
  margin: 0;
  padding: 0;
}
.industry-list li {
  font: 12px var(--mono);
  text-transform: uppercase;
  padding: 20px;
  border-right: 1px solid;
  border-bottom: 1px solid;
}

/* ---------------------------------------------------------- 06 training */
#training {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
}
.training-intro,
.video {
  padding: 30px;
  border-right: 2px solid var(--ink);
}
.training-intro h2 {
  font-size: clamp(38px, 4vw, 58px);
  line-height: 0.8;
  letter-spacing: -0.07em;
}
.training-intro p {
  font-size: 14px;
  line-height: 1.6;
}
.training-cta {
  font: 12px var(--mono);
  color: var(--lime);
  border-bottom: 1px solid;
  text-transform: uppercase;
  width: max-content;
}
.video {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.video:last-child {
  border-right: 0;
}
.video img {
  width: 100%;
  aspect-ratio: 16/9;
  object-fit: cover;
  display: block;
  filter: saturate(0.5);
}
.video h3 {
  font: 12px var(--mono);
  margin: 15px 0 0;
  text-transform: uppercase;
}

/* ---------------------------------------------------------- 07 founder */
/* Head and hands are too far apart in the source frame for one crop to hold
   both, so they are two crops flanking the copy. */
#founder {
  display: grid;
  grid-template-columns: 1fr 1.6fr 1fr;
  grid-template-rows: auto 1fr;
}
#founder > .eyebrow {
  grid-column: 2;
  grid-row: 1;
  padding: 90px 5vw 0;
}
.portrait {
  grid-row: 1 / -1;
  /* stretches to the copy column's height, so all three columns bottom-align */
  min-height: 560px;
  position: relative;
  margin: 0;
  overflow: hidden;
}
.portrait picture {
  display: contents;
}
.portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.6);
  display: block;
}
.portrait figcaption {
  position: absolute;
  bottom: 0;
  right: 0;
  padding: 14px;
}
.founder-copy {
  grid-column: 2;
  grid-row: 2;
  padding: 0 5vw 90px;
}
.founder-copy blockquote {
  font-size: clamp(34px, 4.8vw, 70px);
  line-height: 1;
  letter-spacing: -0.05em;
  margin: 48px 0;
  text-wrap: balance;
}
.founder-copy p {
  line-height: 1.6;
}

/* ----------------------------------------------------- 08 collaboration */
#collaboration {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border-top: 2px solid var(--ink);
}
.collab-intro {
  padding: 90px 5vw;
  border-right: var(--rule);
}
.collab-intro h2 {
  font-size: clamp(46px, 7vw, 100px);
  line-height: 0.8;
  letter-spacing: -0.065em;
}
.collab-list {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: collab;
}
.collab-list li {
  min-height: 150px;
  padding: 30px;
  display: grid;
  grid-template-columns: 55px 1fr;
  align-items: center;
  border-bottom: var(--rule);
  font-size: 18px;
}
.collab-list li:last-child {
  border-bottom: 0;
}
.collab-list li::before {
  counter-increment: collab;
  content: counter(collab, upper-alpha);
  font: 12px var(--mono);
  color: var(--lime);
}

/* ---------------------------------------------------------- 09 contact */
#contact {
  padding: 100px 5vw;
  border-top: var(--rule);
}
/* The headline and the line that follows it are both inline, so they share a
   line box and the second one lands on the same baseline as the last line of
   the first. The margin lives on the wrapper because the h2 no longer has a
   block box of its own to carry it. */
.contact-head {
  margin: 70px 0;
}
#contact h2 {
  display: inline;
  font-size: clamp(64px, 16vw, 240px);
  line-height: 0.8;
  letter-spacing: -0.07em;
}
/* inline-BLOCK, not inline: as plain inline text it breaks at whatever word
   happens to reach the container edge and the remainder lands alone at the far
   left of the next line. As an inline-block it wraps inside its own box, which
   stays in the tail of the heading's last line — and an inline-block's baseline
   is its last line, so it sits on the heading's baseline either way. */
.contact-join {
  display: inline-block;
  max-width: 20ch;
  margin-left: 0.5em;
  font-size: clamp(18px, 1.5vw, 24px);
  line-height: 1.25;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
/* The map and the quote share a row under the headline; the mail address keeps
   the last word to itself. */
.contact-body {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  gap: 6vw;
  align-items: center;
}
.contact-map {
  margin: 0;
}
.contact-map svg {
  width: 100%;
  height: auto;
  display: block;
}
/* This section is the yellow block, so the map needs its own two colours: the
   separator between fills is the ground it sits on, and the rest of the world
   is ink held far enough back not to compete with the filled countries. */
#contact .map-rest path {
  fill: var(--ink);
  fill-opacity: 0.16;
  stroke: var(--lime);
}
#contact .map-served path {
  fill: var(--purple);
  stroke: var(--lime);
}
/* The oversized opening quote is the anchor, which is why there is no rule
   here: a border AND a 150px glyph both saying "a quotation starts" is one
   signal too many. */
.contact-quote {
  margin: 0;
}
.contact-quote::before {
  content: "\201C";
  display: block;
  font-size: clamp(96px, 9vw, 150px);
  line-height: 1;
  /* the glyph carries a lot of empty space below its bowl; collapsing its box
     to a fraction of the em pulls the first line up to where it looks attached
     to the mark rather than orphaned under it */
  height: 0.42em;
  color: var(--purple);
}
.contact-quote p {
  font-size: clamp(18px, 1.7vw, 24px);
  line-height: 1.35;
  text-wrap: balance;
  margin: 0 0 18px;
}
.contact-cite {
  font: 11px var(--mono);
  text-transform: uppercase;
  line-height: 1.7;
  margin-top: 26px;
}
.contact-cite a {
  border-bottom: 1px solid currentColor;
}
.contact-cite span {
  display: block;
  opacity: 0.7;
}
.contact-mail {
  display: block;
  width: max-content;
  margin: 60px 0 0 auto;
  font: 16px var(--mono);
  border-bottom: var(--rule);
  text-transform: uppercase;
}
@media (max-width: 760px) {
  .contact-body {
    grid-template-columns: 1fr;
    gap: 40px;
  }
  .contact-quote::before {
    /* stacked rather than side by side, so the mark needs its own air above */
    margin-top: 10px;
  }
}

/* ----------------------------------------------------------- footer */
footer {
  padding: 35px 3vw;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px 20px;
  font: 11px var(--mono);
  text-transform: uppercase;
  flex-wrap: wrap;
}
footer address {
  font-style: normal; /* <address> is italic by default */
  margin: 0;
}
.footer-links {
  margin: 0;
  white-space: nowrap;
}
.footer-links a + a::before {
  content: " / ";
}
footer a {
  border-bottom: 1px solid transparent;
}
footer a:hover,
footer a:focus-visible {
  border-bottom-color: currentColor;
}

/* ------------------------------------------------------------ tablet */
@media (max-width: 900px) {
  #hero {
    grid-template-columns: 1.5fr 1fr;
  }
  #work {
    grid-template-columns: 1fr 1fr;
  }
  .card {
    min-height: 520px;
    border-bottom: var(--rule);
  }
  .card:nth-child(even) {
    border-right: 0;
  }
  .ai-grid {
    grid-template-columns: 1fr 1fr;
  }
  .ai-result {
    grid-column: span 2;
  }
  #casadi {
    grid-template-columns: 25% 1fr;
  }
  .casadi-logo {
    grid-row: 1 / 3;
  }
  .stats {
    /* a band under the logo and the copy, not across them: the base rule spans
       every row, which at this width laid the figures on top of both */
    grid-column: 1 / -1;
    grid-row: 3;
    border-left: 0;
    border-top: var(--rule);
    flex-direction: row;
    /* four figures do not fit one tablet-width row: wrap to two of two */
    flex-wrap: wrap;
  }
  .stats li {
    flex: 1 1 45%;
    border-bottom: 0;
    border-right: var(--rule);
  }
  .stats li:nth-child(2n) {
    border-right: 0;
  }
  .stats li:nth-child(-n + 2) {
    border-bottom: var(--rule);
  }
  #training {
    grid-template-columns: 1fr 1fr;
  }
  .training-intro {
    grid-column: span 2;
  }
  #industries {
    grid-template-columns: 1fr;
  }
  .industry-intro {
    border-right: 0;
    border-bottom: var(--rule);
  }
  .industry-list {
    grid-template-columns: repeat(2, 1fr);
  }
  #method,
  #collaboration {
    grid-template-columns: 1fr;
  }
  #founder {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto auto auto;
  }
  .portrait-head {
    grid-area: 1 / 1;
  }
  .portrait-hands {
    grid-area: 1 / 2;
  }
  #founder > .eyebrow {
    grid-area: 2 / 1 / 3 / 3;
  }
  .founder-copy {
    grid-area: 3 / 1 / 4 / 3;
  }
  .method-title {
    border-right: 0;
    border-bottom: var(--rule);
  }
  .collab-intro {
    border-right: 0;
    border-bottom: var(--rule);
  }
}

/* ------------------------------------------------------------ mobile */
@media (max-width: 600px) {
  header {
    grid-template-columns: auto 1fr;
  }
  header nav {
    display: none;
  }
  #hero {
    grid-template-columns: 1fr;
    min-height: 0;
  }
  .hero-main {
    border-right: 0;
    padding: 48px 6vw;
  }
  .hero-side {
    border-top: var(--rule);
  }
  #work,
  .ai-grid,
  #method,
  #casadi,
  #training {
    grid-template-columns: 1fr;
  }
  /* single column: let DOM order stand, so the eyebrow leads instead of the image */
  #casadi,
  #founder {
    grid-template-rows: none;
  }
  #casadi > .eyebrow,
  #founder > .eyebrow,
  .casadi-logo,
  .casadi-copy,
  .stats,
  .founder-copy {
    grid-column: auto;
    grid-row: auto;
    grid-area: auto;
  }
  #casadi > .eyebrow,
  #founder > .eyebrow {
    padding: 60px 6vw 0;
  }
  .card {
    min-height: 0;
    border-right: 0;
  }
  .card h3 {
    min-height: 0;
  }
  .ai-result,
  .training-intro {
    grid-column: auto;
  }
  .ai-grid article {
    min-height: 0;
    border-right: 0;
    border-bottom: 1px solid var(--grey);
  }
  .method-title {
    border-right: 0;
    border-bottom: var(--rule);
  }
  .method-title,
  .method-copy {
    padding: 60px 6vw;
  }
  .chain {
    grid-template-columns: 1fr;
    gap: 6px;
  }
  /* the → separators only make sense on one line */
  .chain::before,
  .chain::after {
    display: none;
  }
  .chain li {
    grid-column: 1 !important;
  }
  .casadi-logo {
    border-right: 0;
    padding: 40px;
  }
  .casadi-copy {
    padding: 0 6vw 60px;
  }
  .stats {
    flex-direction: column;
    /* one figure per line again — undo the two-up wrapping of the wider band */
    flex-wrap: nowrap;
  }
  .stats li {
    flex: 1 1 auto;
    border-right: 0;
    border-bottom: var(--rule);
  }
  .stats li:last-child {
    border-bottom: 0;
  }
  #ai {
    padding: 70px 6vw;
  }
  .industry-list {
    grid-template-columns: 1fr 1fr;
  }
  .industry-image {
    height: 320px;
  }
  .training-intro,
  .video {
    border-right: 0;
    border-bottom: 2px solid var(--ink);
  }
  #founder {
    grid-template-columns: 1fr;
  }
  .portrait-hands {
    display: none;
  }
  .founder-copy {
    grid-area: auto;
  }
  /* whole figure, native ratio, so cover has nothing to crop */
  .portrait-head {
    grid-area: auto;
    min-height: 0;
    height: auto;
    aspect-ratio: 1200 / 1803;
  }
  .collab-intro {
    padding: 60px 6vw;
  }
  .founder-copy {
    padding: 0 6vw 60px;
  }
  .collab-list li {
    min-height: 0;
    font-size: 15px;
    padding: 24px;
  }
  #contact {
    padding: 70px 6vw;
  }
  #contact h2 {
    margin: 40px 0;
  }
  .contact-mail {
    margin-left: 0;
  }
  footer {
    justify-content: flex-start;
  }
}

/* ==================================================================
   /training — topic sections and offering tiers
   ================================================================== */

.page-hero {
  min-height: 0;
}
.page-hero .hero-main {
  padding: 90px 4vw;
}
.page-hero .hero-side {
  justify-content: center;
  padding: 90px 4vw;
}
.page-hero .hero-intro {
  margin-top: 0;
}
/* the subject's own mark, leading the intro box. align-self matters: the box is
   a flex column, and a stretched image ignores width:auto and distorts. */
.hero-mark {
  display: block;
  align-self: flex-start;
  height: 104px;
  width: auto;
  margin-bottom: 22px;
}
.page-hero .hero-cta {
  margin-top: 32px;
}

/* The complete-track section keeps the simple card layout. */
.topic {
  display: grid;
  grid-template-columns: 1.5fr 2fr;
  border-top: var(--rule);
}
.topic .card {
  min-height: 0;
}
.topic .card h2 {
  font-size: clamp(30px, 2.6vw, 40px);
  line-height: 0.95;
  letter-spacing: -0.04em;
}
.topic .card-copy {
  margin: 24px 0 0;
  max-width: 46ch;
}
.topic-meta {
  font: 12px/1.7 var(--mono);
  margin: 18px 0 0;
  max-width: 46ch;
  opacity: 0.72;
}

/* The curriculum is the substance — numbered by CSS, so reordering renumbers. */
.curriculum {
  counter-reset: mod;
  list-style: none;
  margin: 28px 0 0;
  padding: 0;
  font: 12px/1.7 var(--mono);
  max-width: 46ch;
}
.curriculum li {
  padding: 3px 0 3px 2.6em;
  text-indent: -2.6em;
}
.curriculum li::before {
  counter-increment: mod;
  content: counter(mod, decimal-leading-zero);
  display: inline-block;
  width: 2.6em;
  text-indent: 0;
  opacity: 0.5;
}

/* ---- offering tiers (complete-track card) ---- */
.tier {
  justify-content: flex-start;
}
/* a topic with a single flavour lets it take the remaining columns */
.topic .wide {
  grid-column: span 2;
}

.tier h3 {
  font-size: clamp(24px, 2.2vw, 32px);
  min-height: 0;
  margin-top: 4px;
}
.price {
  font: 12px var(--mono);
  text-transform: uppercase;
  margin: 14px 0 0;
  width: max-content;
}
.price.todo {
  background: var(--fluo, #d4ff00);
  color: var(--ink);
  padding: 5px 9px;
}

/* Checkmarks say what you get; a dash says what you don't. Both generated. */
.includes {
  list-style: none;
  margin: 28px 0 0;
  padding: 0;
  font: 400 14px/1.4 var(--sans);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.includes li {
  padding-left: 1.7em;
  text-indent: -1.7em;
}
.includes li::before {
  content: "✓";
  display: inline-block;
  width: 1.7em;
  text-indent: 0;
  font-family: var(--mono);
  font-size: 12px;
}
.includes li.out {
  opacity: 0.38;
}
.includes li.out::before {
  content: "–";
}

@media (max-width: 900px) {
  .topic {
    grid-template-columns: 1fr 1fr;
  }
  .topic .card-title {
    grid-column: 1 / -1;
    border-right: 0;
    border-bottom: var(--rule);
  }
}
@media (max-width: 600px) {
  .topic {
    grid-template-columns: 1fr;
  }
  .topic .card {
    border-right: 0;
    border-bottom: var(--rule);
  }
  .page-hero .hero-main,
  .page-hero .hero-side {
    padding: 60px 6vw;
  }
}

/* ---- buy control: the price IS the button ---- */
.buy {
  margin: 14px 0 0;
}
button.price {
  font: 12px var(--mono);
  text-transform: uppercase;
  display: inline-flex;
  align-items: baseline;
  gap: 10px;
  cursor: pointer;
  background: transparent;
  color: inherit;
  border: 1px solid currentColor;
  padding: 9px 12px;
  border-radius: 0;
}
button.price:hover,
button.price:focus-visible {
  background: currentColor;
}
button.price:hover > *,
button.price:focus-visible > * {
  /* invert against the block's own ground */
  filter: invert(1) grayscale(1) contrast(3);
}
.buy-cta {
  opacity: 0.62;
}

/* ==================================================================
   /training — pricing-comparison matrix
   Header row (topic | flavour A | flavour B), then one row per feature
   with the checkmarks aligned across columns, then a CTA row.
   ================================================================== */
.matrix {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
}
.matrix-3 {
  grid-template-columns: 1.7fr 1fr 1fr 1fr;
}
/* a value cell shows text (e.g. "2 years") instead of a check/dash */
.m-cell.val {
  font: 12px var(--mono);
  text-transform: uppercase;
}
/* faint separators drawn from the cell's own text colour, so they read on
   paper, lime and purple alike */
.matrix > * {
  border-top: 1px solid color-mix(in srgb, currentColor 16%, transparent);
}
/* the header row sits under the section's own top border only */
.m-head,
.m-flavour {
  border-top: 0;
}

.m-head {
  padding: 28px;
}
.m-head h2 {
  font-size: clamp(30px, 2.6vw, 40px);
  line-height: 0.95;
  letter-spacing: -0.04em;
}
.m-head .card-copy {
  margin: 24px 0 0;
  max-width: 46ch;
}

.m-flavour {
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-content: start;
}
/* the flavour name and its sticker share a line */
.m-flavour-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  /* if the column ever gets too narrow for name + sticker, the sticker drops
     to its own line rather than bleeding into the next column */
  flex-wrap: wrap;
}
/* "standard pick" as a sticker slapped on the column: filled lime, tilted,
   with a hard offset shadow so it sits on top of the block rather than in it */
.flavour-note {
  display: inline-block;
  flex: none;
  margin: 5px 6px 0 0; /* room for the tilt to stay inside its own column */
  padding: 7px 9px 6px;
  background: var(--fluo);
  color: var(--ink);
  font: 12px/1.2 var(--mono);
  text-transform: uppercase;
  text-align: center;
  /* two short lines rather than one long one: it stays beside the flavour name
     instead of pushing itself onto a line of its own */
  max-width: 7em;
  transform: rotate(-5deg);
  box-shadow: 3px 3px 0 var(--ink);
}
.cohort-start {
  font: 11px var(--mono);
  text-transform: uppercase;
  margin: 0;
  opacity: 0.85;
}
.m-cta-cell .flavour-note {
  margin: 0;
}
.m-flavour h3 {
  font-size: clamp(24px, 2.2vw, 32px);
  min-height: 0;
}
.m-price {
  margin-top: auto;
}
.m-price .price {
  font: 13px var(--mono);
  text-transform: uppercase;
  border: 1px solid currentColor;
  padding: 8px 12px;
  display: inline-block;
}
.m-price .price.todo {
  background: var(--fluo);
  color: var(--ink);
  border-color: var(--fluo);
}
/* "incl. VAT" sits under the amount, not beside it: the tiers are narrow
   columns and a wider chip would wrap mid-label. Quieter than the price
   without being decorative — it is a required disclosure, not a footnote. */
.m-price .price-vat {
  display: block;
  margin-top: 6px;
  font: 11px var(--mono);
  text-transform: uppercase;
  opacity: 0.75;
}

/* feature rows */
.m-label {
  padding: 13px 28px;
  display: flex;
  align-items: center;
  font: 400 15px/1.3 var(--sans);
}
.m-cell {
  display: grid;
  place-items: center;
  min-height: 46px;
  font: 14px var(--mono);
}
.m-cell.yes::before {
  content: "\2713";
}
.m-cell.no::before {
  content: "\2013";
  opacity: 0.45;
}

/* Row hover — the four cells of a row are separate grid items, so .m-row wraps
   them with display:contents: the grid still sees the cells, CSS gets one
   element to hover. The highlight is an inset shadow, not a border, so nothing
   reflows and the row cannot flicker under the cursor; currentColor keeps the
   line visible on the black column too. */
.m-row {
  display: contents;
}
/* a permanent rule under every feature row, in each column's own ink so it
   stays visible on the black one. Drawn as an inset shadow, like the hover
   highlight, so switching between them never changes a single box's size. */
.m-row > * {
  box-shadow: inset 0 -1px 0 color-mix(in srgb, currentColor 22%, transparent);
}
.m-row:hover > *,
.m-row:has(.m-label-btn:focus) > * {
  box-shadow:
    inset 0 1px 0 currentColor,
    inset 0 -1px 0 currentColor;
}
.m-label {
  position: relative;
}
.m-row:hover > .m-label,
.m-label:focus-within {
  z-index: 4;
}
/* the label is a button (see the generator): a tap on touch, Tab on a keyboard.
   It has to read as the label it replaced, so it inherits everything. */
.m-label-btn {
  all: unset;
  display: block;
  width: 100%;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.m-label-btn:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}
/* the detail panel drops out of the label column, over the page rather than in
   it — absolute, so no row below it moves. It is a CHILD of the label, which is
   what keeps it stable: moving the cursor onto the panel still counts as
   hovering the label, so the panel cannot flicker itself away, and it stays
   readable while you are on it (WCAG 1.4.13 "hoverable"). */
.m-info {
  position: absolute;
  top: 100%;
  /* starts halfway across the label column and runs to its right edge, so the
     panel reads as hanging off the row rather than replacing the label */
  left: 50%;
  width: 50%;
  z-index: 4;
  box-sizing: border-box;
  padding: 14px 28px 16px;
  background: var(--ink);
  color: var(--paper);
  font: 400 14px/1.5 var(--sans);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 120ms ease,
    visibility 0s linear 120ms;
}
.m-row:hover .m-info,
.m-label:focus-within .m-info {
  opacity: 1;
  visibility: visible;
  transition:
    opacity 120ms ease,
    visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
  .m-info {
    transition: none;
  }
}

/* footnote marker and its note under the matrix. The note sticks to the left
   edge so it stays put while a narrow screen scrolls the table sideways. */
.fn-ref {
  font-size: 0.62em;
  vertical-align: super;
  line-height: 0;
  margin-left: 1px;
}
/* the note lives in the table's last row, in the label column beside the
   buttons — not adrift under the whole section */
.matrix-note {
  margin: 0;
  max-width: 52ch;
  font: 400 13px/1.6 var(--sans);
  opacity: 0.75;
}
.matrix-note .fn-ref {
  vertical-align: baseline;
  font-size: 1em;
  margin: 0 4px 0 0;
}

/* CTA row */
.m-foot {
  border-top: 0;
  padding: 24px 28px 30px;
}
.m-cta-cell {
  padding: 24px 28px 30px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.m-cta-cell form {
  width: 100%;
}
.m-cta {
  display: block;
  width: 100%;
  text-align: center;
  padding: 15px;
  font: 12px var(--mono);
  text-transform: uppercase;
  border: 1px solid currentColor;
  cursor: pointer;
  background: transparent;
  color: inherit;
}
.m-cta:hover,
.m-cta:focus-visible {
  background: currentColor;
}
.m-cta:hover {
  filter: none;
}
/* the waitlist / cohort action stands out: filled lime */
.block-purple .m-cta {
  background: var(--fluo);
  color: var(--ink);
  border-color: var(--fluo);
}
.block-purple .m-cta:hover,
.block-purple .m-cta:focus-visible {
  filter: brightness(0.9);
}

/* A comparison table stays a table on mobile: the section scrolls sideways,
   the page never does. The label column sticks so the checkmarks stay legible. */
.matrix-section {
  overflow-x: auto;
  border-top: var(--rule);
  -webkit-overflow-scrolling: touch;
}
@media (max-width: 760px) {
  .matrix {
    min-width: 680px;
  }
  .matrix-3 {
    min-width: 900px;
  }
  .m-head,
  .m-label,
  .m-foot {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--paper);
  }
}

/* "See the course(s)" — the only survivor of the two-card course picker this
   page used to carry; the cards themselves are gone. */
.related-go {
  font: 12px var(--mono);
  text-transform: uppercase;
  border-bottom: 1px solid currentColor;
  width: max-content;
  padding-bottom: 3px;
}

/* ==================================================================
   /training — what is on offer (one card per subject)
   ================================================================== */
.offering {
  border-top: var(--rule);
}
.offering-card {
  display: block;
  padding: 48px 4vw 56px;
}
.offering-mark {
  display: block;
  height: 96px;
  width: auto;
}
.offering-card h2 {
  margin-top: 26px;
  font-size: clamp(34px, 4.4vw, 62px);
  line-height: 0.9;
  letter-spacing: -0.04em;
}
/* the two course slides, side by side: what the material actually looks like */
.offering-visual {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
  margin: 34px 0 30px;
  max-width: 900px;
}
/* Cropped to a band, the way the /training/casadi nodes crop theirs, rather
   than shown whole: two full slides at this width are a wall of unreadable
   6pt type, and the point is what the material looks like, not what it says.
   No mix-blend-mode — multiply was pulling the slides' white ground down into
   the lime block behind them, which is what made them read as yellow. */
.offering-visual img {
  display: block;
  width: 100%;
  height: 220px;
  object-fit: cover;
  object-position: top left;
  border: 1px solid color-mix(in srgb, currentColor 25%, transparent);
  filter: saturate(0.55) contrast(1.05);
}
.offering-card:hover h2,
.offering-card:focus-visible h2 {
  text-decoration: underline;
  text-underline-offset: 8px;
}

/* ==================================================================
   /training/casadi — the two courses as a graph, drawn the way the
   Yacoda mark draws one: nodes joined by a round-capped edge.
   ================================================================== */
.course-graph {
  border-top: var(--rule);
  padding: 64px 4vw 72px;
}
.graph {
  display: grid;
  justify-items: center;
  gap: 0; /* the edge is a row of its own, so it needs no gap around it */
  max-width: 760px;
  margin: 0 auto;
}
.graph-node {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  padding: 32px 34px 36px;
  border: 3px solid var(--ink);
  border-radius: 22px; /* the mark's nodes are soft squares */
  position: relative;
  z-index: 1;
}
.graph-node h2 {
  font-size: clamp(26px, 2.8vw, 40px);
  line-height: 0.92;
  letter-spacing: -0.04em;
}
.graph-node > p {
  font: 400 17px/1.4 var(--sans);
  margin: 0;
}
/* closes the node, under the slide */
.node-cohort {
  margin: 4px 0 0;
  font: 12px var(--mono);
  text-transform: uppercase;
}
.graph-node figure {
  margin: 6px 0 0;
  overflow: hidden;
  border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
}
.graph-node figure img {
  display: block;
  width: 100%;
  height: 220px;
  object-fit: cover;
  object-position: top left;
  filter: saturate(0.55) contrast(1.05);
}
.graph-node:hover h2,
.graph-node:focus-visible h2 {
  text-decoration: underline;
  text-underline-offset: 6px;
}
/* the edge: a round-capped stem down the middle of the gap, arrowhead at the
   bottom — basic feeds advanced, and the graph says so */
.graph-edge {
  position: relative;
  width: 6px;
  height: 78px;
  background: var(--ink);
  border-radius: 3px 3px 0 0;
}
.graph-edge::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 3px;
  width: 19px;
  height: 19px;
  transform: translateX(-50%) rotate(45deg);
  border-right: 6px solid var(--ink);
  border-bottom: 6px solid var(--ink);
  border-bottom-right-radius: 6px;
}
.graph-note {
  max-width: 760px;
  margin: 44px auto 0;
  font: 400 15px/1.6 var(--sans);
}
@media (max-width: 600px) {
  .graph-edge {
    height: 58px;
  }
}

/* ==================================================================
   course detail — 01 orientation, 02 contents, 04 related
   ================================================================== */
.course-hero {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
}
.course-intro {
  padding: 60px 4vw;
  border-right: var(--rule);
}
.course-intro h1 {
  font-size: clamp(38px, 5.2vw, 78px);
}
.course-intro .hero-intro {
  margin: 20px 0 0;
  max-width: 46ch;
}
.course-intro > p {
  max-width: 52ch;
  margin: 16px 0 0;
  line-height: 1.6;
}

/* spec list — fixes the abrupt mono jump the old topic-meta lines caused:
   a clean label/value grid, label in mono, value in sans */
.spec {
  margin: 32px 0 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 10px 20px;
  max-width: 60ch;
}
.spec dt {
  font: 11px var(--mono);
  text-transform: uppercase;
  color: var(--accent-soft, #8f508f);
  padding-top: 3px;
}
.spec dd {
  margin: 0;
  font: 400 15px/1.45 var(--sans);
}
/* closing line under the spec list — what you can do once the course is done */
.course-intro > p.course-outcome {
  margin-top: 28px;
  max-width: 60ch;
}
/* an example slide, closing the module list. The margin the slide used to carry
   as dead pixels is now the purple of the block itself: the image is cropped to
   its content and the breathing room comes from where it sits. */
.contents-slide {
  margin: 48px 0 0;
  max-width: 620px;
}
.contents-slide img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
}
.contents-slide figcaption {
  margin-top: 10px;
  font: 11px var(--mono);
  text-transform: uppercase;
  opacity: 0.7;
}
/* the pile of prints — assets/training/ocbt-pile.webp, built by
   landing_src/gen_pile.py. It arrives already framed, already shadowed and on a
   transparent ground, so the border that suits a bare slide would draw a
   rectangle around empty canvas. It also carries its own margin in the form of
   the space the rotated prints do not fill, which is why it sits wider and
   closer to the list above than a single slide would. */
.contents-pile {
  max-width: 760px;
  margin-top: 36px;
}
.contents-pile img {
  border: 0;
}
.contents-pile figcaption {
  margin-top: 4px;
}

.cite {
  margin: 28px 0 0;
  max-width: 60ch;
  font: 12px/1.6 var(--mono);
  opacity: 0.62;
}
.cite em {
  font-style: italic;
}

/* right-hand aside: one flowing panel — text and illustrative slides in the
   same column, no internal boxes or sub-dividers */
.course-aside,
.contents-aside {
  padding: 40px 4vw;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 18px;
  min-width: 0;
  overflow: hidden;
}
/* the logo IS the word in "What is <logo>?" — sized off the heading so it
   scales with the clamp, and nudged onto the text baseline */
.casadi-question {
  line-height: 1.15;
}
.casadi-mark {
  height: 1.35em;
  width: auto;
  display: inline-block;
  vertical-align: -0.42em;
}
/* the wordcloud reads as evidence, not decoration — no whitening filter, its
   thin red words would not survive one. Half width: a dense texture beside the
   figures rather than a second headline. */
.casadi-wordcloud {
  display: block;
  width: 70%;
  height: auto;
  margin: 6px 0;
  mix-blend-mode: multiply;
}
.casadi-stats {
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px 24px;
  width: 100%;
  font-variant-numeric: tabular-nums;
}
.casadi-stats strong {
  display: block;
  font-size: clamp(24px, 2.2vw, 30px);
  line-height: 1;
  font-weight: 400;
}
.casadi-stats small {
  display: block;
  margin-top: 5px;
  font: 11px/1.35 var(--mono);
  text-transform: uppercase;
}
/* every figure links to the page that backs it up. The outbound arrow belongs
   on the label, not on the block-level anchor, where it would take its own
   line under the figure. */
.casadi-stats a,
.stats a {
  display: block;
  color: inherit;
}
.casadi-stats a::after,
.stats a::after {
  content: none;
}
.casadi-stats a small::after,
.stats a small::after {
  content: "\00a0↗";
}
.casadi-stats a:hover small,
.casadi-stats a:focus-visible small,
.stats a:hover small,
.stats a:focus-visible small {
  border-bottom: 1px solid currentColor;
}
.course-aside h2 {
  font-size: clamp(28px, 3vw, 44px);
  line-height: 0.9;
  margin: 0;
}
.course-aside p {
  margin: 0;
  max-width: 44ch;
  line-height: 1.6;
}
/* an illustrative slide, flowing in the panel: shrunk and whitened, no frame */
.aside-slide {
  display: block;
  /* fill the content box plus the 4vw side padding, and bleed to the bottom
     edge — no blank margin frames the slide, and it never drives the column
     width (that is what min-width:0 on the aside guards) */
  width: calc(100% + 8vw);
  height: auto;
  margin: 10px -4vw -40px;
  opacity: 0.7;
  filter: saturate(0.5);
  mix-blend-mode: multiply;
}
.yacoda-lockup {
  width: min(300px, 100%);
  height: auto;
  margin-top: 8px;
}

/* 02 contents — head, collapsible body, tutor aside */
.course-contents {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  grid-template-rows: auto 1fr;
  border-top: var(--rule);
}
.contents-head {
  grid-column: 1;
  padding: 60px 4vw 20px;
}
.contents-head h2 {
  font-size: clamp(34px, 5vw, 68px);
  line-height: 0.85;
  letter-spacing: -0.05em;
}
.summary-line {
  margin: 20px 0 0;
  max-width: 54ch;
  font: 500 18px/1.4 var(--sans);
}
.contents-body {
  grid-column: 1;
  padding: 0 4vw 60px;
}
.contents-body .card-copy {
  margin: 0 0 20px;
  max-width: 54ch;
}
/* collapsible modules */
.mod {
  border-top: 1px solid color-mix(in srgb, currentColor 18%, transparent);
}
/* arrived from a deep link elsewhere on the site: say which lesson was meant.
   Browsers open a closed <details> when the fragment lands inside it, but the
   marker is what makes it obvious among a dozen siblings. */
.mod:has(:target) {
  box-shadow: inset 3px 0 0 var(--lime);
}
.mod:has(:target) > summary {
  padding-left: 14px;
}
/* The anchor is on the <ul>, so the browser parks the BULLETS at the top of the
   viewport and the lesson title lands just above it, off screen. Reserve room
   for the title — two lines of it, the longest any of them runs — so an
   arriving reader sees what they clicked on. */
.mod > ul {
  scroll-margin-top: 96px;
}
.mod summary {
  cursor: pointer;
  list-style: none;
  padding: 14px 28px 14px 0;
  font: 400 15px/1.35 var(--sans);
  position: relative;
  display: grid;
  grid-template-columns: 2.6em 1fr;
  gap: 6px;
}
.mod summary::-webkit-details-marker {
  display: none;
}
.mod summary::after {
  content: "+";
  position: absolute;
  right: 4px;
  font-family: var(--mono);
  opacity: 0.6;
}
.mod[open] summary::after {
  content: "\2013";
}
.mod-n {
  font: 12px var(--mono);
  opacity: 0.5;
}
.mod ul {
  margin: 0 0 16px;
  padding: 0 28px 0 2.6em;
  list-style: none;
  font: 400 14px/1.5 var(--sans);
}
.mod li {
  padding: 3px 0 3px 1.1em;
  position: relative;
}
.mod li::before {
  content: "\2013";
  position: absolute;
  left: 0;
  opacity: 0.5;
}
.contents-aside {
  grid-column: 2;
  grid-row: 1 / -1;
  border-left: var(--rule);
}
.tutor-photo {
  margin: 0;
  width: 100%;
}
.tutor-photo img {
  width: 100%;
  height: auto;
  display: block;
}
.tutor-photos img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  display: block;
}
.contents-aside p {
  margin: 0;
  max-width: 40ch;
  line-height: 1.55;
}

/* Sits between the white matrix foot and the white related card, so it is a
   dark block: without it those two whites run together into one long stretch
   and the page loses its section rhythm. */
.testimonials {
  padding: 60px 4vw;
}
.testimonials h2 {
  font-size: clamp(28px, 3.4vw, 52px);
  line-height: 0.9;
  letter-spacing: -0.04em;
  margin: 10px 0 0;
}
/* Two sentences: the headcount, then what the quotes below actually are. Held
   to a measure so the second sentence does not trail off across the block. */
.tm-standfirst {
  margin: 14px 0 0;
  max-width: 62ch;
  font: 500 15px/1.5 var(--sans);
  opacity: 0.7;
}
/* <em> takes the block's counter-colour on its own (see the block rules), so
   the figure lifts out of the sentence without a second colour decision. */
.tm-standfirst em {
  font-weight: 700;
}
.tm-grid {
  display: grid;
  /* min() rather than a bare 240px: below that the track would stop shrinking
     and push the card out of the block instead of collapsing to one column. */
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
  gap: 30px;
  margin-top: 40px;
}
/* Same device as .contact-quote: the oversized glyph is the only marker that a
   quotation starts, so the card carries no border or background of its own. */
.tm-card {
  margin: 0;
}
.tm-card::before {
  content: "\201C";
  display: block;
  font-size: 80px;
  line-height: 1;
  height: 0.42em;
  color: var(--lime);
}
.tm-card blockquote {
  margin: 0;
}
.tm-card blockquote p {
  margin: 0;
  font-size: clamp(16px, 1.3vw, 20px);
  line-height: 1.35;
  text-wrap: balance;
}
.tm-card figcaption {
  margin-top: 18px;
  font: 11px var(--mono);
  text-transform: uppercase;
  line-height: 1.7;
}
/* Name, then role, then year, each on its own line and each a step quieter —
   the eye takes the name first and the rest only if it is looking for it. */
.tm-card figcaption span {
  display: block;
  opacity: 0.7;
}
.tm-card figcaption span + span {
  opacity: 0.45;
}
/* Keeps the site-wide outbound ↗ from a[href^="http"]: these do leave for
   LinkedIn, and the mark is what tells a reader the name is checkable. */
.tm-who {
  display: block;
  width: max-content;
  max-width: 100%;
  border-bottom: 1px solid currentColor;
}
/* the receipt, next to the year: the words themselves, on LinkedIn. Quieter
   than the name — this is a citation, not the attribution. */
.tm-src {
  border-bottom: 1px solid currentColor;
  opacity: 0.7;
}
.tm-src:hover,
.tm-src:focus-visible {
  opacity: 1;
}

.related {
  padding: 60px 4vw;
  border-top: var(--rule);
}
.related-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 40px;
  margin-top: 24px;
}
/* the way back up: quieter than the card above it, in the eyebrow's voice, so
   it reads as navigation rather than a second offer */
.related-back {
  display: inline-block;
  margin-top: 22px;
  font: 12px var(--mono);
  text-transform: uppercase;
  border-bottom: 1px solid currentColor;
}
.related-card h2 {
  font-size: clamp(28px, 3.4vw, 52px);
  line-height: 0.9;
  letter-spacing: -0.04em;
}
.related-note {
  margin: 18px 0 0;
  font: 500 15px/1.4 var(--sans);
  display: flex;
  align-items: center;
  gap: 12px;
}
.related-note .tag {
  font: 12px var(--mono);
  background: var(--fluo);
  color: var(--ink);
  padding: 5px 9px;
}
.related-card:hover h2,
.related-card:focus-visible h2 {
  text-decoration: underline;
  text-underline-offset: 6px;
}

@media (max-width: 760px) {
  .course-hero,
  .course-contents {
    grid-template-columns: 1fr;
  }
  .course-intro,
  .contents-head {
    border-right: 0;
  }
  .course-aside,
  .contents-aside {
    border-left: 0;
    border-top: var(--rule);
  }
  .contents-aside {
    grid-column: 1;
    grid-row: auto;
  }
  .contents-body {
    grid-column: 1;
  }
  .course-intro {
    border-right: 0;
    border-bottom: var(--rule);
    padding: 48px 6vw;
  }
  .course-banner img,
  .contents-shot img {
    min-height: 220px;
  }
  .contents-head {
    padding: 48px 6vw 20px;
  }
  .course-contents .curriculum {
    padding: 0 6vw 48px;
  }
  .contents-shot {
    grid-column: 1;
    grid-row: auto;
    border-left: 0;
    border-top: var(--rule);
  }
}

/* ==================================================================
   /privacy (and other legal documents) — a readable prose column in
   the housestyle. Restrained on purpose: a legal page is read, not
   performed, so no giant hero — just the brand colour, type and rules.
   ================================================================== */
.legal {
  max-width: 760px;
  margin: 0 auto;
  padding: 64px 5vw 100px;
  counter-reset: sec;
}
.legal-head {
  border-bottom: var(--rule);
  padding-bottom: 30px;
  margin-bottom: 8px;
}
.legal-head .eyebrow {
  display: block;
  margin-bottom: 18px;
}
.legal-head h1 {
  font-size: clamp(40px, 7vw, 84px);
  line-height: 0.9;
  letter-spacing: -0.04em;
}
.legal-meta {
  font: 12px var(--mono);
  text-transform: uppercase;
  margin: 20px 0 0;
  opacity: 0.7;
}
.legal-intro {
  font-size: clamp(17px, 1.6vw, 20px);
  line-height: 1.55;
  margin: 34px 0 0;
}
/* Sentence-case headings read better than uppercase for long legal prose;
   the mono section number keeps the housestyle's counted-index motif. */
.legal h2 {
  font-size: clamp(21px, 2.3vw, 28px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-transform: none;
  margin: 54px 0 0;
  padding-top: 6px;
}
.legal h2::before {
  counter-increment: sec;
  content: counter(sec, decimal-leading-zero) "\00a0\00a0/\00a0\00a0";
  font: 13px var(--mono);
  color: var(--purple);
  vertical-align: 2px;
}
.legal h3 {
  font-size: 17px;
  text-transform: none;
  letter-spacing: 0;
  margin: 30px 0 0;
}
.legal p,
.legal li {
  font: 400 16px/1.65 var(--sans);
}
.legal p {
  margin: 14px 0 0;
}
.legal ul {
  margin: 14px 0 0;
  padding-left: 1.25em;
}
.legal li {
  margin: 7px 0 0;
}
.legal a {
  border-bottom: 1px solid currentColor;
}
/* processor list: name (+ location chip) over a one-line description */
.legal-parties {
  margin: 18px 0 0;
}
.legal-parties dt {
  font: 500 15px var(--mono);
  margin-top: 20px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
}
.legal-parties dt span {
  font: 11px var(--mono);
  text-transform: uppercase;
  border: 1px solid currentColor;
  padding: 3px 8px;
  opacity: 0.7;
}
.legal-parties dd {
  margin: 6px 0 0;
  font: 400 15px/1.55 var(--sans);
}

@media (max-width: 600px) {
  .legal {
    padding: 48px 6vw 72px;
  }
}

/* Cohort timing selector + alternative-dates list (training pages) */
.m-timing-label { display:block; font-size:.72rem; text-transform:uppercase; letter-spacing:.04em; margin-bottom:.5rem; opacity:.85; }
.m-timing { display:block; width:100%; margin-top:.25rem; padding:.4rem .5rem; border:1px solid currentColor; background:var(--paper); color:var(--ink); font:inherit; border-radius:2px; }
.cohort-alt { font-size:.8rem; margin:0 0 .35rem; opacity:.85; }
.cohort-alt-list { list-style:none; margin:0; padding:0; display:flex; flex-wrap:wrap; gap:.4rem; }
.cohort-alt-list li { font-size:.75rem; padding:.15rem .5rem; border:1px solid var(--purple); border-radius:999px; }
