/* =========================================================================
 * doctor.css — the shapes a DOCTOR page needs and no existing page has
 * =========================================================================
 * Loaded after site.css by /doctors/jayant-v-iyer/, the first of five doctor
 * pages. Everything the site already draws is reused unchanged — .hero,
 * .shell, .head, .crumb, .index, .provenance, .nearby, .btn, .label, .tnum,
 * .cclose are all site.css's — so this file holds only four new things:
 * the at-a-glance sheet, the qualifications and appointments lists, the
 * awards table and the Vision Mission band.
 *
 * IT LOADS /css/hero2.css AFTER THIS FILE, not instead of it. hero2.css owns
 * the fold's colour (--hero2-field, the Straits mark's own indigo) and the
 * hard seam at 84rem, both unscoped, so this page gets the same fold as
 * /clinics/bedok/ and the home page from one source. If the owner rejects that
 * field, dropping hero2.css from $page['css'] here returns this page to
 * site.css's near-black — nothing in this file depends on which one wins.
 *
 * NO NEW COLOUR VALUES. Every colour below resolves through a token from
 * tokens.css, per the standing rule.
 * ========================================================================= */

/* ---- The fold ----------------------------------------------------------
 * site.css tunes object-position to the specialists photograph's 1.97:1 and
 * bottom-aligns it (28% 100%) because that frame has a vertical crop to
 * place. This photograph is 4928x3264 — 1.51:1 — and needs its own value.
 *
 * BOTH AXES ARE LOAD-BEARING, AT DIFFERENT WIDTHS, which is the thing to know
 * before touching this. The hero's media box is half the viewport wide and a
 * fixed 576px tall, so its aspect ratio SWINGS ACROSS the source's 1.51 as the
 * viewport grows, and `cover` changes which axis it trims:
 *
 *     1024   box 512x576  (0.89:1, portrait)  -> 41.1% off the sides
 *     1344   box 672x576  (1.17:1)            -> 22.7% off the sides
 *     1440   box 720x576  (1.25:1)            -> 17.4% off the sides
 *     1920   box 960x576  (1.67:1)            ->  4.7% off top AND bottom
 *      390   box 390x293  (1.33:1, stacked)   -> 11.8% off the sides
 *
 * All five measured off the rendered page. At 1920 the box is WIDER than the
 * source, so the horizontal value goes inert and the vertical one takes over;
 * everywhere else it is the reverse. A single-axis value would therefore be
 * unset exactly where it was needed. 50% 50% is correct on both counts and
 * needs no breakpoint: Dr Jayant sits at x 0.48 of the frame, so centring the
 * window lands him mid-box at every width above — at 1440 the window is
 * x 0.086-0.914 and he falls at 0.47 of the box, nowhere near the seam.
 *
 * THE MONITOR'S WORDMARK IS CLIPPED AND THAT IS ACCEPTED, NOT AN OVERSIGHT.
 * The screen carrying the practice's own mark runs from about x 0.84 to the
 * right edge of the frame, so every crop that keeps him centred takes the
 * tail of it: the only object-position that saves the wordmark whole is about
 * 83%, which drags the window to x 0.145-0.971 and pushes him hard against
 * the left edge of the panel. A clipped background object reads as a
 * photograph continuing past its frame, which is ordinary; an off-centre
 * subject reads as a mistake. If the wordmark ever has to be whole, re-shoot
 * or re-crop the master rather than moving this value. */
.hero--doctor .hero__media img {
  object-position: 50% 50%;
}

/* /doctors/jason-lee/ — A SECOND DOCTOR PHOTOGRAPH NEEDS A SECOND VALUE, and
 * this is the rule CLAUDE.md's "object-position is one value with no
 * breakpoint on every hero, and which axis it acts on swings with the
 * viewport" exists to make survivable. Dr Jason's master is 1498x1192 —
 * 1.26:1, where Dr Jayant's is 1.51:1 — so the SAME media box crops the two
 * frames differently and 50% 50% is not transferable between them.
 *
 * The box is 50vw x 576px on the wide layout, 100vw x 4:3 below 48rem and
 * 100vw x 16:9 between, so its ratio crosses this source's 1.26 at about
 * 1450px. Above that the crop is vertical, below it sideways — a single-axis
 * value would be inert exactly where the other one bites. Measured off the
 * source, with the top of his head at y 0.098 of the frame:
 *
 *      390   box 390x293   (4:3)     ->  5.8% off top AND bottom
 *      768   box 768x432   (16:9)    -> 29.3% off top AND bottom
 *     1024   box 512x576   (0.89:1)  -> 29.3% off the sides
 *     1344   box 672x576   (1.17:1)  ->  7.2% off the sides
 *     1440   box 720x576   (1.25:1)  ->  0.5% off the sides — the full frame
 *     1920   box 960x576   (1.67:1)  -> 24.6% off top AND bottom
 *     2560   box 1280x576  (2.22:1)  -> 43.4% off top AND bottom
 *
 * 16% ON THE VERTICAL, NOT 50%, AND THE BINDING CASE IS 2560. At 50% the
 * top-of-head clearance goes negative from about 1600px up: the 2560 box
 * takes 221px off the ceiling where his hair starts at 100px, so the crown
 * is cut. 16% leaves 29px at 2560, 45px at 1920, 31px at 768 and 28px at 390
 * — clearance at every width, verified by cropping the master to each
 * rectangle and looking at it, not by reading the numbers back.
 *
 * WHAT IT SPENDS IS THE BOTTOM OF THE FRAME. Below ~1450 nothing is lost;
 * above it the desk and his hands leave the picture and the fold becomes
 * head-and-shoulders, which is the right half of that trade on a page whose
 * subject is the man. Re-derive both numbers if the master is ever replaced
 * — a re-crop changes 0.098 and every clearance above it. */
.hero--doctor.hero--jason .hero__media img {
  object-position: 50% 16%;
}

/* ---- Section rhythm ----------------------------------------------------
 * .shell carries no vertical padding — every section on the site supplies its
 * own, which is why clinic.css sets .visit and cataract.css sets .sec. This
 * page does the same through one class rather than five.
 *
 * .dsec--lead IS padding-top: 0 AND THAT IS LOAD-BEARING. The breadcrumb owns
 * both of its own paddings (--space-lg above, --space-xl below, both on
 * .crumb in site.css). A section padding-top stacked on top of that is
 * exactly the bug CLAUDE.md records against /cataract-surgery/: 48px above
 * the trail there against Bedok's 24, the same component at two heights on
 * the site's only two sub-pages. Do not re-introduce a gap here. */
/* --space-2xl (64px), NOT --space-3xl (96px), and the first value was simply
 * wrong rather than merely generous. Every other page below the home page
 * sets less: clinic.css's sections take --space-2xl and cataract.css's .sec
 * takes --space-xl. At 3xl this page spent 192px per section against Bedok's
 * 128 and the cataract page's 80 — up to 2.4x the rest of the site, on the
 * longest page of the three. Measured saving across the six .dsec sections:
 * 384px at 1440. Matching Bedok rather than cataract because this page's
 * sections carry more per section than that one's do. */
.dsec {
  padding-block: var(--space-2xl);
}

.dsec--lead {
  padding-top: 0;
}

/* .head carries no bottom margin of its own — it is a grid with an internal
 * gap and nothing else, so every page that uses it supplies the space beneath
 * (clinic.css does it on .visit__grid, cataract.css on its own sections).
 * Without this the section lede and the first line of body prose sit one
 * line-height apart and read as a single run-on paragraph, which is exactly
 * how the About section shipped for one round. */
/* --space-xl, down from --space-2xl: the heading block and the content under
 * it belong together, and 64px of air between a section's own lede and its
 * first row read as a gap rather than as a relationship. 24px x 6 sections =
 * 144px off the page. The gap still has to clear the lede's own descenders,
 * which is why it is not smaller. */
.dsec .head {
  margin-bottom: var(--space-xl);
}


/* AWARDS AND PUBLICATIONS ARE TWO HALVES OF ONE RECORD, so they sit closer to
 * each other than to their neighbours. Each .dsec carries --space-2xl top and
 * bottom, which put 128px between them — and both sections are a heading, one
 * line of lede and a single collapsed row, the two shortest on the page, so
 * that gap was taller than either block's own content. It read as two
 * half-empty sections either side of a void rather than as a matched pair.
 *
 * --space-lg each side, 48px between, down from 128. Both values were
 * rendered at 1440 and compared: at --space-xl (80px) the void was smaller
 * but still read as a gap between two half-empty sections rather than as one
 * record in two parts.
 *
 * 48px is LESS than .dsec .head's own 40px by only 8px, which would normally
 * flatten the hierarchy — it does not here because each block is closed by
 * the hairline above its own summary row, so the eye has a rule to read the
 * break from rather than only the space. IF THE DISCLOSURE EVER LOSES THAT
 * HAIRLINE, this gap has to go back up.
 *
 * The OUTER paddings are untouched — awards keeps 64px above it against
 * Qualifications and Publications keeps 64px below against the Vision Mission
 * band — so the pair groups without the page's rhythm moving anywhere else.
 *
 * The class goes on the FIRST of the pair and the sibling selector reaches the
 * second, so nothing has to be restated on both and the two cannot drift. */
.dsec--paired {
  padding-bottom: var(--space-lg);
}

.dsec--paired + .dsec {
  padding-top: var(--space-lg);
}

/* ---- The profile: prose beside the sheet -------------------------------
 * The same shape as /clinics/bedok/'s .visit__grid and for the same reason:
 * a block of prose and a block of facts, which are read differently and
 * should not be interleaved.
 *
 * THE PROSE COLUMN IS THE FLEXIBLE ONE. `minmax(0, 1fr)` and a fixed 21rem
 * sheet, not two fr units — the sheet's content is short labels and short
 * values, so an fr unit inflates it to half the row and strands the prose in
 * a column narrower than its own measure. 21rem holds the longest value
 * ("General eye care · Cataract · Glaucoma") on two lines at every width the
 * grid is live. */
.dprofile__grid {
  display: grid;
  gap: var(--space-xl);
}

@media (min-width: 64rem) {
  .dprofile__grid {
    grid-template-columns: minmax(0, 1fr) 21rem;
    gap: var(--space-3xl);
    align-items: start;
  }
}

.dprofile__body > * + * {
  margin-top: var(--space-md);
}

.dprofile__body p {
  max-width: var(--measure);
  color: var(--color-ink-2);
  text-wrap: pretty;
}

/* ---- The at-a-glance sheet ---------------------------------------------
 * A definition list, hairline between rows, mono uppercase labels — the same
 * register as .clinic__facts and the breadcrumb, so the sheet reads as a
 * spec sheet rather than as more prose. Rows stack rather than sitting in two
 * columns: the values here are phrases, not two-word labels, and .hours__day
 * already records what happens when a phrase meets a collapsed column. */
.dsheet {
  margin: 0;
  border-top: var(--rule-hair) solid var(--color-rule);
}

.dsheet > div {
  padding-block: var(--space-sm);
  border-bottom: var(--rule-hair) solid var(--color-rule);
}

.dsheet dt {
  margin: 0;
}

.dsheet dd {
  margin: var(--space-3xs) 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink);
  text-wrap: pretty;
}

/* ---- Profile links -----------------------------------------------------
 * Brand glyphs, not text, matching the specialists cards on the home page.
 * Filled glyphs only — a stroked icon dropped in beside these breaks the
 * optical weight, which is the rule .person__social already carries. 44x44
 * targets, pulled left by 10px so the first glyph optically aligns with the
 * label above rather than with its own bounding box. */
.dsocial {
  display: flex;
  gap: var(--space-2xs);
  margin: var(--space-3xs) 0 0;
  margin-left: -10px;
}

.dsocial a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: var(--color-ink-2);
  transition: color var(--dur-short) var(--ease-out);
}

.dsocial a:hover {
  color: var(--color-accent);
}

.dsocial svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
}

/* ---- Qualifications and appointments -----------------------------------
 * Two lists of the same shape, side by side on desktop. Each row is a fact
 * with a qualifier: the degree and where it was taken, the institution and
 * the role held there. The qualifier is the muted half, so the eye runs down
 * the institutions. */
.dcols {
  display: grid;
  gap: var(--space-2xl);
}

@media (min-width: 60rem) {
  .dcols {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3xl);
  }
}

.dlist {
  margin: var(--space-sm) 0 0;
  padding: 0;
  list-style: none;
  border-top: var(--rule-hair) solid var(--color-rule);
}

.dlist li {
  padding-block: var(--space-sm);
  border-bottom: var(--rule-hair) solid var(--color-rule);
}

.dlist__name {
  display: block;
  font-size: var(--text-base);
  color: var(--color-ink);
}

.dlist__note {
  display: block;
  margin-top: var(--space-3xs);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* The post-nominal is the thing a reader scans for, so it takes the mono
 * label register rather than being set in running prose beside the degree's
 * full name. Lining tabular figures are irrelevant here but the family is
 * the same one .label uses, which is the point — it marks these as
 * credentials rather than as sentences. */
.dlist__abbr {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--color-accent);
}

/* ---- Awards ------------------------------------------------------------
 * Thirteen of them, at the owner's explicit instruction, reproduced from
 * Dr Jayant's own published profile on straitseye.com.sg.
 *
 * THE YEAR LEADS AND IT IS A COLUMN, NOT A SENTENCE. Set as a fixed mono
 * column, the years form a scannable rail down the left and the reader can
 * see the span without reading a single award name; folded into the text
 * ("... in 2019") they are thirteen numbers buried mid-sentence, and the
 * oldstyle-figure rule would drop each 4 and 9 below the baseline while doing
 * it. .tnum in the markup keeps them lining and tabular so the column aligns.
 *
 * 2-UP FROM 64rem. Thirteen rows in one column is a 900px tower that reads as
 * a CV dump; in two it is seven rows and closes level with the section above.
 * The conferring body is the muted line under each, never omitted — an award
 * without the body that gave it is an assertion, and the whole reason this
 * section is defensible under the MOH rules is that every line names a
 * verifiable source. */
.awards {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  border-top: var(--rule-hair) solid var(--color-rule);
}

@media (min-width: 64rem) {
  .awards {
    grid-template-columns: 1fr 1fr;
    column-gap: var(--space-3xl);
  }
}

.awards__item,
.pubs__item {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  gap: var(--space-sm);
  padding-block: var(--space-sm);
  border-bottom: var(--rule-hair) solid var(--color-rule);
}

.awards__year,
.pubs__year {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: 0.04em;
  line-height: 1.9;
  color: var(--color-accent);
  white-space: nowrap;
}

.awards__name,
.pubs__title {
  font-size: var(--text-base);
  color: var(--color-ink);
  text-wrap: pretty;
}

.awards__by {
  display: block;
  margin-top: var(--space-3xs);
  font-size: var(--text-sm);
  color: var(--color-muted);
  text-wrap: pretty;
}

/* Below 30rem the year rail costs more than it earns — 4.5rem of a 280px
 * column is 16% spent on four characters, and every award name then sets to
 * four and five lines. The year moves above the name and the rail goes. */
@media (max-width: 30rem) {
  .awards__item,
  .pubs__item {
    grid-template-columns: 1fr;
    gap: var(--space-3xs);
  }

  .awards__year,
  .pubs__year {
    line-height: 1.4;
  }
}

/* ---- The publications list --------------------------------------------
 * A year, a thing, and the source that vouches for it — structurally the
 * SAME row as an award, so it takes the same rules rather than a lookalike
 * set of its own. `.awards__item` and `.pubs__item` are grouped above for
 * that reason: if the awards row changes, this one has to change with it or
 * the page grows two registers for one kind of fact.
 *
 * THE DISCLOSURE ITSELF IS NOT HERE ANY MORE. `.disclose`, `.disclose__toggle`,
 * `.disclose__chevron` and `.disclose__meta` moved to site.css when
 * /cataract-surgery/'s FAQ became the second page type to collapse something.
 * Both blocks on this page still use them unchanged; the reasoning that used
 * to sit here moved with the rules. */

.pubs__list {
  display: grid;
  margin: 0 0 var(--space-lg);
  padding: 0;
  list-style: none;
}

@media (min-width: 64rem) {
  .pubs__list {
    grid-template-columns: 1fr 1fr;
    column-gap: var(--space-3xl);
  }
}

/* The citation's own line: the journal is the source, exactly as the
 * conferring body is an award's source, so it takes .awards__by's colour and
 * size. Italic, because that is how a journal name is set in every citation
 * style there is — the one place on this site where italics carry meaning
 * rather than emphasis. */
.pubs__journal {
  display: block;
  margin-top: var(--space-3xs);
  font-size: var(--text-sm);
  font-style: italic;
  color: var(--color-muted);
  text-wrap: pretty;
}

.pubs__item a {
  color: var(--color-ink);
  text-decoration: none;
}

.pubs__item a:hover {
  color: var(--color-accent);
  text-decoration: underline;
}

/* ---- The Vision Mission ------------------------------------------------
 * A tinted band, --color-paper-2, one step off the paper. It is the same
 * device the exam-at-40 band uses and for the same reason: this is the one
 * section on the page that is not about treating the reader, so it needs a
 * boundary rather than another hairline.
 *
 * It is NOT a call to action and must not become one. The link leaves for an
 * NGO's own site; giving it a .btn would put a second action on the page
 * competing with the one that books an appointment. */
.mission {
  background: var(--color-paper-2);
  border-block: var(--rule-hair) solid var(--color-rule);
  padding-block: var(--space-3xl);
}

.mission__inner {
  display: grid;
  gap: var(--space-md);
}

.mission__lede {
  max-width: var(--measure-lede);
  margin: 0;
  font-size: var(--text-md);
  color: var(--color-ink-2);
  text-wrap: pretty;
}

.mission__note {
  max-width: var(--measure);
  margin: 0;
  font-size: var(--text-base);
  color: var(--color-muted);
  text-wrap: pretty;
}

/* ---- The mission figure ------------------------------------------------
 * A standalone numeral used as a graphic anchor, which is exactly what
 * --text-figure exists for — the device already carrying the "40" in the
 * exam-at-40 band. Reused rather than reinvented: a second stat component
 * with its own size and colour is how two bands stop looking like one site.
 * `.forty__figure`'s own declarations are matched here (display family,
 * display weight, lining tabular figures, -0.03em, accent) because the
 * oldstyle-figures rule covers prose and an oldstyle "2" and "0" would sit
 * below the baseline at this size.
 *
 * THE SIZE IS *NOT* --text-figure, AND THAT IS THE WHOLE POINT OF THIS
 * BLOCK. That token clamps to 7rem, which is tuned for the TWO characters of
 * "40". "20,000+" is seven, and Spectral's tabular figures measure about
 * 3.6em for that string — 405px at 112px type. Measured, it forces the text
 * beside it into a 640px column at 1440 and blows straight through a 280px
 * content box at 320. This clamp tops out at 5.5rem (88px, ~319px wide) and
 * bottoms at 3.25rem, which is the largest the string can be at 320 without
 * touching the gutter. **Re-derive it if the copy changes length** — a
 * numeral anchor is sized by its own string, not by a shared token.
 *
 * Hairline above and below, no box: an outlined badge here would be the
 * inset panel the owner already rejected on the home page as "a square box
 * suddenly in the middle of the design", and card-in-card is on the banned
 * list. The rules do the containing. */
/* --space-xl and not --space-2xl, and it is worth knowing why: .mission__inner
 * is a grid with its own --space-md gap, so this margin STACKS on top of it.
 * At 2xl the air above the top hairline came out ~100px against ~57px below
 * it, and the band read as a figure that had drifted away from the prose. At
 * xl both sides are md + xl and the rules sit level either side of it. */
.mission__stat {
  display: grid;
  gap: var(--space-xs) var(--space-xl);
  margin: var(--space-xl) 0 0;
  padding-block: var(--space-lg);
  border-block: var(--rule-hair) solid var(--color-rule);
}

@media (min-width: 40rem) {
  .mission__stat {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
  }
}

.mission__figure {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: clamp(3.25rem, 5vw + 0.5rem, 5.5rem);
  font-variant-numeric: lining-nums tabular-nums;
  line-height: 0.86;
  letter-spacing: -0.03em;
  color: var(--color-accent);
  white-space: nowrap;
}

/* The wrapper is load-bearing, not tidiness. .mission__stat is a two-column
 * grid from 40rem (auto minmax(0, 1fr)) with align-items: center, so a third
 * child auto-places into row 2 column 1 — underneath the numeral — and the
 * numeral stops centring against the text it annotates. Wrapped, the note and
 * the procedure strip are one grid item and the figure centres on the pair.
 * 2xs and not the row's own xs: these two lines are one block, and at xs they
 * separated far enough to read as two unrelated notes. */
.mission__stat-text {
  display: grid;
  gap: var(--space-2xs);
}

/* Capped by --measure-note and not by the wrapper: this line is the only
 * prose in the row, and left uncapped beside a nowrap numeral it runs the
 * full remaining track — 700px at 1440, which reads as a paragraph that has
 * lost its column rather than as a caption to the figure. */
.mission__stat-note {
  max-width: var(--measure-note);
  margin: 0;
  font-size: var(--text-md);
  color: var(--color-ink-2);
  text-wrap: pretty;
}

/* The clause the note exists for, at the owner's request — the same device
 * .assure__urgent uses on the assurances strip: 500 and --color-ink against
 * a note set at --color-ink-2, so the eye lands on what the figure counts
 * before it reaches the attribution.
 *
 * 500 STATED EXPLICITLY, AND THAT IS NOT TIDINESS. Only .hero__lede strong
 * is styled in site.css, so a bare <strong> here would fall to the browser
 * default 700 — a weight IBM Plex Sans does not load, so it renders as
 * synthesised fake bold beside real weights everywhere else on the page.
 * 500 is loaded and real. Not 600: at --text-md that is the .values__name
 * step, and a note as heavy as a name stops being a note.
 *
 * THE WHOLE CLAUSE, NEVER "BLINDNESS" ALONE. A single emphasised word
 * mid-sentence reads as keyword bolding; the phrase is the unit that carries
 * the meaning, which is how .assure__urgent and .forty__symptoms are scoped
 * too. One emphasised run in this note, and that is the ceiling. */
.mission__stat-note strong {
  font-weight: 500;
  color: var(--color-ink);
}

/* The procedure mix under the note. Sans and muted, NOT the mono .label
 * register: mono on this site is reserved for labels and credentials, and
 * these are values — the same reason the spec sheet's `·` runs ("General eye
 * care · Cataract · Glaucoma") are set in the body face and only their <dt>s
 * are mono.
 *
 * --measure-note, matching the line above it, so the two share one column
 * edge; a wider cap here would step the block out from under its own note.
 * MEASURED: the string holds one line from 375 up (335px content column) and
 * takes a second only at 320, where the column is 280px — the note beside it
 * is already 5 lines there, so the strip costs the band ~23px at the one
 * width it wraps. Re-measure if a fourth procedure is added; the wrap point
 * sits between 280px and 335px of column, with very little slack. */
.mission__mix {
  max-width: var(--measure-note);
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-muted);
  text-wrap: pretty;
}

/* ---- The mission photographs -------------------------------------------
 * The page's second and third photographs, and the reason the band exists as
 * a band rather than a paragraph. Both are outreach: the work this section
 * describes, actually happening.
 *
 * THEY ARE FIGURES WITH CAPTIONS, NOT DECORATION. CLAUDE.md's rule is that
 * nothing on this site is ornament — every element does work. An uncaptioned
 * pair here would be mood photography; captioned, each one tells the reader
 * where it was taken and who is in it, which is what makes the Vision Mission
 * a verifiable activity rather than a claim. If a caption ever has to be
 * removed, remove the photograph with it.
 *
 * BOTH SOURCES ARE EXACTLY 4:3, so `aspect-ratio: 4 / 3` with `object-fit:
 * cover` crops nothing at all — the ratio is declared to hold the grid's
 * shape while the images load and to keep the row honest if a future
 * photograph arrives at a different ratio. It is a reservation, not a crop.
 *
 * 2-up from 48rem. Below that they stack, because two 4:3 frames side by side
 * on a phone are 160px wide and the faces — which are the entire content of
 * both pictures — stop being legible. */
.mission__figures {
  display: grid;
  gap: var(--space-xl);
  margin: var(--space-xl) 0 0;
}

@media (min-width: 48rem) {
  .mission__figures {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-2xl);
  }
}

.mission__fig {
  margin: 0;
}

.mission__fig img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  /* --radius-sm, the site's only corner softening, and the same value the
   * Bedok map frame carries. --radius-pill belongs to the floating widget
   * alone and must not be reached for here. */
  border-radius: var(--radius-sm);
  background: var(--color-paper-3);
}

.mission__fig figcaption {
  margin-top: var(--space-sm);
  font-size: var(--text-sm);
  color: var(--color-muted);
  text-wrap: pretty;
}
