/* eyespecialist.sg — cataract pages, loaded after site.css
 * =======================================================================
 * Draft A's language, page-scoped, shared by the whole cataract cluster:
 * /cataract-surgery/ and its four spokes, /cataract/ and
 * /community-cataract-surgery/.
 *
 * site.css carries the system and most of what these pages need is already
 * in it — .hero, .shell, .head, .index, .clinic, .sheet, .crumb, .btn,
 * .label, .tnum, .measure, .tick, .provenance__list are all the homepage's
 * and are used here unchanged. This file adds only the four shapes none of
 * the existing pages has: a numbered sequence, a clickable index, a question
 * and answer list, and a prose block with subheadings.
 *
 * Every colour resolves through a token from tokens.css. No inline hex,
 * oklch() or rgb() — the house rule, no exceptions.
 */

/* ---- The fold ----------------------------------------------------------
 * NOTHING HERE, DELIBERATELY, SINCE 23 AUG 2026. The whole cataract cluster —
 * /cataract-surgery/ and its four spokes — now carries the home page's close
 * crop of all three surgeons, so every one of them loads /css/hero3.css, the
 * sheet written for that crop, and none of them needs a rule of its own.
 *
 * .hero--cataract LIVED HERE AND IS GONE. It set object-position: 50% 50% for
 * the two-surgeon reception frame (2809x1717), which the media box crosses at
 * 1884px — the value existed because site.css's inherited 28% 100% decapitated
 * both doctors at 2560. That photograph is published nowhere now.
 *
 * DO NOT PUT IT BACK ON A PAGE THAT LOADS hero3.css: it is (0,2,1) against
 * that sheet's (0,1,1), so it wins on specificity whatever the load order and
 * would pin the crop to a value derived for a different photograph. A NEW hero
 * photograph needs its own object-position derived from its own master and its
 * own media box, on BOTH axes — the box swings across every source's aspect
 * ratio as the viewport grows, so a single-axis value is inert exactly where
 * the other one bites. hero3.css carries the worked example. */

/* ---- Numbered sequence -------------------------------------------------
 * "What happens on the day". A list of steps, each numbered in the mono
 * face the spec sheet and the credentials use, so the numeral reads as
 * metadata rather than as a heading.
 *
 * The counter is CSS, not markup: an <ol> already carries the ordinal
 * semantically, and hand-typing "1." into the heading would say it twice to
 * a screen reader. list-style is off and ::before draws it instead, because
 * a list marker cannot be given its own font and colour. */
.steps {
  counter-reset: step;
  margin-top: var(--space-xl);
  border-top: var(--rule-hair) solid var(--color-rule);
}

.step {
  counter-increment: step;
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr);
  gap: var(--space-2xs) var(--space-md);
  padding-block: var(--space-lg);
  border-bottom: var(--rule-hair) solid var(--color-rule);
}

.step::before {
  content: counter(step, decimal-leading-zero);
  grid-row: 1 / span 2;
  font-family: var(--font-mono);
  font-variant-numeric: lining-nums tabular-nums;
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--color-accent);
}

.step__name {
  font-size: var(--text-md);
  font-weight: var(--weight-subhead);
  line-height: 1.3;
}

.step__note {
  font-size: var(--text-sm);
  color: var(--color-ink-2);
  max-width: var(--measure-note);
}

/* 500 AND STATED, not left to the browser. `.copy strong` does not reach this
 * block, and a bare <strong> falls to the UA's 700 — a weight IBM Plex Sans
 * does not load, so it renders as synthesised fake bold. Same trap the
 * .assure__urgent comment in site.css records.
 *
 * 500 rather than .copy strong's 600, because .step__name directly above is
 * 500: a note bolder than the name it sits under inverts the pair. The colour
 * lift to --color-ink is what actually does the work at this size, exactly as
 * it does in .assure__urgent. */
.step__note strong {
  font-weight: 500;
  color: var(--color-ink);
}

/* Below 30rem the 2.5rem numeral column costs a tenth of the width and the
 * note wraps to five lines. The numeral goes above the name instead. */
@media (max-width: 30rem) {
  .step {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-2xs);
    padding-block: var(--space-md);
  }

  .step::before {
    grid-row: auto;
  }
}

/* ---- .steps--when — the same sequence, drawn as a timeline -------------
 * /cataract-surgery/recovery/ only. The hub's plain .steps is untouched and
 * must stay that way: "what happens on the day" is a sequence of steps
 * inside one morning, where an ordinal is the right marker, and this is four
 * periods spread over six weeks, where the ordinal was the only thing on the
 * page not saying WHEN.
 *
 * SO THE MODIFIER TRADES THE ORDINAL FOR THE TIME. .step::before keeps its
 * counter-increment — the <ol> still carries the ordinal semantically, which
 * is the whole reason the number was CSS in the first place — but its content
 * goes and the box becomes the node on the rail. The visible time is real
 * markup, .step__when, because it is content and not a counter.
 *
 * THE RAIL IS NOT DECORATION, WHICH IS THE ONLY REASON IT IS ALLOWED. It says
 * the four periods are one continuous recovery rather than four separate
 * appointments — the argument the section's own lede makes in words. A
 * hairline in --color-rule, the same weight as the row separators it crosses,
 * so it reads as structure and not as a graphic.
 *
 * IT IS A BACKGROUND, NOT AN ELEMENT, and that is deliberate: an absolutely
 * positioned rule would need a height, and a step's height is set by its own
 * note wrapping. A background sized `--rule-hair 100%` is exactly as tall as
 * whatever the row turns out to be, at every width, with no measurement to
 * keep in step.
 *
 * THE NODE IS A SQUARE AND MUST STAY ONE. The site is square-cornered, with
 * exactly two exceptions — the pill on the WhatsApp widget and --radius-panel
 * on an inset CTA — and a round node here would be a third, which is how an
 * identity becomes a coincidence. A square also sits on a 1px rail more
 * honestly than a circle does at these sizes.
 *
 * --rail-x IS DERIVED FROM THE COLUMN AND THE GAP, NOT TYPED. The rail has to
 * land in the middle of the gutter between the time and the text; written as
 * a third number it would drift the moment either of the other two moved.
 * 6.5rem is set by the longest string the column holds. "WEEKS 2-6" measures
 * 88.9px at --text-xs with .label's 0.16em tracking, against the column's
 * 104px, so the label sets on one line at every width. Re-measure it if
 * --text-xs moves or a longer period is added. */
.steps--when {
  --when-col: 6.5rem;
  --when-gap: var(--space-lg);
  --rail-x: calc(var(--when-col) + var(--when-gap) / 2);
}

.steps--when .step {
  position: relative;
  grid-template-columns: var(--when-col) minmax(0, 1fr);
  column-gap: var(--when-gap);
  background-image: linear-gradient(var(--color-rule), var(--color-rule));
  background-repeat: no-repeat;
  background-size: var(--rule-hair) 100%;
  background-position: var(--rail-x) 0;
}

/* The node. content is emptied rather than the pseudo being display:none,
 * because the box IS the node — and `position: absolute` is what takes it out
 * of the grid, so the inherited `grid-row: 1 / span 2` cannot open a third
 * track.
 *
 * `top` RESOLVES AGAINST THE PADDING BOX, NOT THE BORDER BOX, and that is the
 * trap this rule already fell into: written as `--space-lg + 0.35em` the node
 * landed 29px low, because .step's own --space-lg of padding had already been
 * counted and the offset added it a second time. Measured at 58px below the
 * row's top edge against the label's 34px. The origin is INSIDE the padding,
 * so the offset is the label's own metrics and nothing else.
 *
 * font-size IS RESTATED so `em` means the label's size here. .step::before
 * sets --text-sm for the ordinal this modifier no longer draws; left alone,
 * every em below would silently measure against the wrong face. The label's
 * first line is 1.4em tall (.label's line-height) and sits 0.15em down on its
 * own margin, so the node's top is that margin plus half the difference
 * between the line box and the node. It follows --text-xs on its own. */
.steps--when .step::before {
  content: '';
  position: absolute;
  top: calc(0.15em + (1.4em - 9px) / 2);
  left: var(--rail-x);
  width: 9px;
  height: 9px;
  font-size: var(--text-xs);
  margin-left: calc((9px - var(--rule-hair)) / -2);
  background: var(--color-accent);
}

/* .label is (0,1,0) and sets --color-muted; this is (0,2,0) and takes the
 * accent the ordinal had, so the column still reads as the marker it
 * replaced. margin-top matches the node's own optical drop. */
.steps--when .step__when {
  grid-row: 1 / span 2;
  margin-top: 0.15em;
  color: var(--color-accent);
}

/* BASE RULES ABOVE, THE QUERY HERE. Both sets are (0,2,0), so this one wins
 * on source order alone — written above, it would still match and still lose,
 * which is the .person__portrait bug this project has already paid for.
 *
 * Below 30rem the 6.5rem column costs a third of the width. The time goes
 * above the name, and the rail and node go with it: a rail with nothing to
 * the left of it is the decoration this component is not allowed to be. */
@media (max-width: 30rem) {
  .steps--when .step {
    grid-template-columns: minmax(0, 1fr);
    background-image: none;
  }

  .steps--when .step::before {
    display: none;
  }

  .steps--when .step__when {
    grid-row: auto;
    margin-top: 0;
  }
}

/* ---- Clickable index ---------------------------------------------------
 * The homepage's .index is a static hairline grid of conditions. Here the
 * same shape carries the four spoke links, so each item is an <a>. It takes
 * .index's own geometry unchanged and adds only what a link needs: the
 * whole cell is the target, the name takes the accent on hover, and an
 * arrow marks it as a destination the way .tlink does.
 *
 * The arrow is on the name rather than the cell so it sits against the
 * text's own right edge instead of floating in the column's slack. */
.index--links .index__item {
  color: inherit;
  text-decoration: none;
  /* The hover fill bleeds to the hairline on both sides, so the padding has
   * to come inside the cell rather than off the grid's column gap. */
  padding-inline: var(--space-md);
  margin-inline: calc(var(--space-md) * -1);
  transition: background-color var(--dur-micro) var(--ease-out);
}

.index--links .index__item:hover {
  background: var(--color-paper-2);
}

.index--links .index__name {
  transition: color var(--dur-micro) var(--ease-out);
}

.index--links .index__item:hover .index__name {
  color: var(--color-accent);
}

.index--links .index__name::after {
  content: "→";
  margin-left: auto;
  padding-left: var(--space-sm);
  font-size: var(--text-base);
  color: var(--color-rule);
  transition: transform var(--dur-micro) var(--ease-out),
              color var(--dur-micro) var(--ease-out);
}

.index--links .index__item:hover .index__name::after {
  color: var(--color-accent);
  transform: translateX(3px);
}

/* .index goes to three columns at 64rem because the homepage's conditions
 * index carries nine items and three columns bring it from five rows to
 * three. This one carries four, and 3-up leaves a single orphan on a second
 * row — which reads as a fifth item that failed to load. Two columns give a
 * square 2x2 at every width from 48rem up. */
@media (min-width: 64rem) {
  .index--links {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ---- .ledger — a fee breakdown, or any label-and-phrase list -----------
 * A hairline definition list: a short label, a phrase under or beside it.
 * Built for /cataract-surgery/cost/, where "what the fee covers" and "what is
 * billed separately" have to be read AGAINST each other, and reused on
 * /cataract-surgery/lens/ for what each lens still needs glasses for.
 *
 * IT IS NOT A TABLE, and that is the point. A table forces both columns to
 * one grid, so the widest phrase in either sets the label column for both —
 * the `width: 1%` trap CLAUDE.md records, where "Sunday & public holidays"
 * went to three lines beside a one-line time. Here each row owns its own
 * height and the label column is fixed, so a long phrase wraps under itself.
 *
 * STACKED BELOW 40rem, two columns above. The label is the mono uppercase
 * register the spec sheets use, so a row reads as a fact rather than as more
 * prose; the phrase stays in the body face at --text-sm.
 *
 * IF A THIRD PAGE TYPE WANTS THIS, MOVE IT TO site.css. .crumb, .nearby,
 * .cclose, .assure and .disclose each made that journey and four of them only
 * after a second copy had already drifted. Two uses in one cluster is still
 * one page type; three is not. */
.ledger {
  margin: 0 0 var(--space-xl);
  border-top: var(--rule-hair) solid var(--color-rule);
}

.ledger > div {
  display: grid;
  gap: var(--space-3xs);
  padding-block: var(--space-sm);
  border-bottom: var(--rule-hair) solid var(--color-rule);
  /* auto rows would stretch to the tallest item in the row and pad the label
   * away from its own phrase — the align-content lesson, in a grid that has
   * exactly the shape it bit on before. */
  align-content: start;
}

.ledger dt {
  margin: 0;
  font-weight: var(--weight-subhead);
  color: var(--color-ink);
}

.ledger dd {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-ink-2);
  text-wrap: pretty;
}

/* The heading above each list. It is an h3 so the outline is right, and .label
 * gives it the mono uppercase register — the heading reset covers h1-h6, so
 * without .label it would arrive in Spectral and read as a section head
 * rather than as a column label. */
.ledger__head {
  margin-top: var(--space-lg);
  margin-bottom: var(--space-xs);
  color: var(--color-muted);
}

@media (min-width: 40rem) {
  .ledger > div {
    grid-template-columns: 14rem minmax(0, 1fr);
    gap: var(--space-md);
    align-items: baseline;
  }

  .ledger dd {
    max-width: var(--measure);
  }
}

/* ---- .ledger--marked — a ledger whose labels carry glyphs ---------------
 * TWO PAGES, AND IT WAS DEFINED TWICE IN THIS FILE FOR A WHILE. Two sessions
 * built the same component hours apart — one for /cataract-surgery/bladeless/
 * and one for /recovery/ — and the second set of rules sat 400 lines below
 * the first, quietly winning on source order. That is the exact rot CLAUDE.md
 * describes: two blocks describing one component in different states. They
 * are merged here, and a fourth caller edits THIS block rather than adding a
 * fifth.
 *
 * WHERE IT IS USED, AND WHY ONLY THERE:
 *   /bladeless/  "What the laser does not do". That page carries two ledgers
 *                a screen apart and undifferentiated they read as the same
 *                list printed twice. The glyph is only honest on this one —
 *                its four rows name OBJECTS a reader can picture, where the
 *                other's name conditions.
 *   /recovery/   Both activity lists. A reader arrives looking for ONE
 *                activity, so the glyph is what lets them find their own row
 *                without reading the other nine — the job .marks does on the
 *                hub, in a component that also has to carry a note.
 *   /cost/       Both fee ledgers, from 23 Aug 2026 at the owner's request
 *                for a more visual page. THIS REVERSES WHAT THIS COMMENT USED
 *                TO SAY — that /cost/'s rows are "read as a set rather than
 *                scanned for a single entry" — and the reasoning it reversed
 *                still holds for /lens/, which keeps the plain .ledger. What
 *                made it survivable on /cost/ is that eleven of its rows do
 *                name objects or acts a reader can picture (a lens, a bed, a
 *                calendar, a magnifier); the four that did not were re-cut
 *                until they did, not drawn abstractly. If a future edit adds
 *                a row that cannot be drawn honestly, the glyph does not go
 *                blank on that ONE row — see the all-or-none rule below.
 * /lens/ keeps the plain .ledger: its rows are lens types, read as a set.
 *
 * IT IS ALL THE ROWS IN A LIST OR NONE OF THEM. One unmarked row in a marked
 * ledger reads as a missing image, not as a row that needed no picture, and
 * the label column is indented for the glyph whether or not one arrives.
 *
 * EVERY GLYPH IN BOTH SETS WAS RENDERED AT 56px AND 24px AND LOOKED AT, and
 * both pages threw candidates away at 24: the three laser steps all died
 * there (the capsulotomy read as a COPYRIGHT SYMBOL, the divided lens as a
 * crosshair, the cornea in section as "(-"), and so did a mascara wand that
 * read as a lollipop and a top-down aeroplane whose wings filled in.
 * Surgical sub-steps have no silhouette at 24px; everyday objects do.
 * previews/_bladeless-marks.html and _recovery-marks*.html keep both sets
 * beside their rejects.
 *
 * THE dt BECOMES ITS OWN GRID, and it has to. A glyph set inline would let
 * the second line of a wrapping name run back underneath it — "Heavy lifting
 * and strenuous exercise" is 36 characters and wraps in this column at every
 * width from 40rem up. auto + minmax(0, 1fr) keeps every line of a name on
 * one left edge.
 *
 * align-items: start ON THE ROW, NOT the plain .ledger's baseline. Baseline
 * is right for two runs of text; here the dt's first grid item is a replaced
 * element with no baseline, so the row synthesises one from the svg's margin
 * box and drops the note. Measured, not guessed.
 *
 * THE COLUMN GOES 14rem -> 16rem WITH THE GLYPH, and the bump is the glyph.
 * The mark and its 12px gutter take 36px off the front of the label column,
 * so 16rem leaves a name 220px against the plain ledger's 224px. It is not
 * room for longer names: "Removing the cataract" set to two lines at 14rem.
 * Four of /recovery/'s ten names still wrap, and that is the component
 * working — .ledger's own note records that a long phrase wraps UNDER ITSELF
 * here rather than widening a shared column, which is the table behaviour it
 * exists to avoid. Measured at 1440. */
.ledger--marked dt {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  column-gap: var(--space-sm);
}

/* 24px and the house stroke — the same box, weight, caps and joins as
 * .marks__icon and .assure__icon, which is what keeps one stroke voice; a
 * second weight beside those is the fastest way to make a page look
 * assembled. The 0.15em nudge puts the box on the label's optical centre,
 * because the cap height of 18px text does not start at the line box's top. */
.ledger__icon {
  width: 24px;
  height: 24px;
  margin-top: 0.15em;
  flex: none;
  fill: none;
  stroke: var(--color-accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

@media (min-width: 40rem) {
  .ledger--marked > div {
    grid-template-columns: 16rem minmax(0, 1fr);
    align-items: start;
  }
}

/* ---- .copy__sub — a prose subheading with a mark ----------------------
 * /cataract-surgery/recovery/'s "Drops, the shield and your glasses", whose
 * h2 names three subjects that four paragraphs then ran together. It takes
 * .copy h3's size, weight and tracking unchanged from site.css and adds only
 * the glyph column, so a subhead cannot change size because it grew a mark.
 *
 * IF A SECOND PAGE TYPE WANTS THIS, MOVE IT TO site.css beside .copy itself —
 * .copy__list made exactly that journey out of clinic.css when /fees/
 * shipped, and .crumb, .nearby, .cclose, .assure and .disclose each made it
 * only after a second copy had already drifted.
 *
 * align-items: center is right here and wrong on .marks. A mark beside a
 * single line of --text-lg is a pair; a mark beside a label that may wrap to
 * three lines has to sit on the first line, which is why .marks and
 * .ledger--marked both use start. A subhead here is one line at every width
 * the page has — the longest is "Your glasses". */
.copy__sub {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  column-gap: var(--space-2xs);
}

/* The heading's own top margin comes from .copy h3 and is --space-xl, which
 * is the gap BETWEEN two subjects and far too much under the .head that has
 * just named all three. Zero was worse: measured 0px between the h2's bottom
 * and the h3's top, two Spectral headings stacked so close they read as a
 * title and its subtitle rather than as a section and its first part. */
.copy .copy__sub:first-child {
  margin-top: var(--space-md);
}

.copy__mark {
  width: 24px;
  height: 24px;
  fill: none;
  stroke: var(--color-accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---- Two-up people grid ------------------------------------------------
 * NOTHING RENDERS THIS TODAY. /cataract-surgery/ used it while it named two
 * cataract surgeons; the owner confirmed on 22 Aug 2026 that Dr Audrey Pang
 * performs cataract surgery, the page now names three, and its <ul> is a
 * plain .people — the home page's grid, three columns at 60rem.
 *
 * IT IS KEPT RATHER THAN DELETED because includes/clinic-doctors.php names it
 * as the documented revert path for the surgeons row it turned into .pcp
 * rows: two .person cards in a bare .people would take a third of the row and
 * read as a missing person, which is the failure this modifier exists to
 * prevent. Delete it only together with that comment.
 *
 * .person itself is untouched — same portrait ratio, same row structure,
 * same profile-link baseline — so every page describes the same doctors with
 * the same typography. */
@media (min-width: 60rem) {
  .people--pair {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: 46rem;
  }
}


/* ---- .crange — the fee as a scale --------------------------------------
 * /cataract-surgery/cost/ only. The page states its range four times in
 * prose and never once as a picture, and the range is the whole subject: a
 * reader who came from "cataract surgery cost singapore" is looking for two
 * numbers and what puts them at one end rather than the other.
 *
 * IT IS A FIGURE, NOT A CARD. No fill, no border, no enclosing box — a rule
 * with a number at each end and a caption under each. Cards and three-up
 * icon tiles are on the banned list and this is exactly the content that
 * attracts them; the type and one bar carry it.
 *
 * BOTH FIGURES AND BOTH CAPTIONS ARE THE PAGE'S OWN COPY, not new claims.
 * The captions restate the FAQ answer "Why is the range so wide?" word for
 * word — a monofocal lens with standard surgery at the low end, a premium
 * lens or laser assistance at the high end — so the graphic cannot say
 * something the prose does not. THE FIGURES READ $cat_fee_low / _high from
 * includes/cataract-cost.php like every other surface; never type one here.
 *
 * THE BAR CARRIES NO POSITIONS AND MUST NOT LEARN ANY. Marking where a toric
 * or a multifocal lens "sits" on it would publish a price per lens type,
 * which nobody has supplied — the same rule that keeps the MediSave limit to
 * the one figure the owner gave. Two ends, nothing between them.
 *
 * STACKED BELOW 30rem, AND THAT IS THE FIGURE'S OWN WIDTH DECIDING IT. At
 * 320 the .copy column is 280px, so a two-column form leaves each end 140px
 * and "$12,000" sets to 147px at --text-2xl — the number wraps inside its
 * own column. Stacked, each end has the full column and the pair reads as
 * "from … to …". Re-derive this if --text-2xl or --page-gutter moves. */
.crange {
  max-width: var(--measure);
  margin: var(--space-lg) 0 var(--space-xl);
  /* The UA gives a bare <figure> `margin: 1em 40px` and the reset does not
   * clear it — the line .vid, .pairfig and .finding all carry, and the reason
   * the cataract video once rendered 185px wide in a 265px column while the
   * document reported no overflow. */
  margin-inline: 0;
}

/* Square, like everything else on this site bar the WhatsApp widget and one
 * inset panel. 8px rather than a hairline because at hairline weight it reads
 * as a divider between the two figures rather than as the span joining
 * them. */
.crange__bar {
  height: 8px;
  background: var(--color-accent);
}

.crange__ends {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* normal computes to stretch, so the low end would grow to the high end's
   * height and push its own figure off the shared line — the grid rule that
   * has bitten .index__item, .marks and both .ledger variants here. */
  align-content: start;
  gap: var(--space-md);
  margin-top: var(--space-sm);
}

/* Lining tabular figures explicitly. The oldstyle rule covers running prose
 * and an oldstyle numeral sits at x-height, which is wrong for a numeral used
 * as a graphic anchor — the argument .forty__figure and .finding__num both
 * record. NOT --text-figure: that token is for a numeral that is the whole of
 * its column, and "$12,000" is seven characters where the "40" it was written
 * for is two.
 *
 * DOUBLE-SCOPED, AND IT SHIPPED WRONG ONCE FOR EXACTLY THE REASON THE FILE
 * KEEPS RECORDING. Every element here is a <p> inside .copy, and `.copy p` is
 * (0,1,1) against a single class's (0,1,0) — so the figure rendered at .copy's
 * 18px while this rule sat right there specifying 36. font-family came
 * through, which is what makes it hard to see: the numeral is in the display
 * face and merely the wrong size, so it reads as a design choice rather than
 * as a rule losing. Measure the computed font-size, do not look at it. */
.crange .crange__fig {
  max-width: none;
  margin: 0;
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-2xl);
  font-variant-numeric: lining-nums tabular-nums;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--color-accent);
}

/* Double-scoped, because the figure sits inside .copy whose `.copy p` is
 * (0,1,1) and would otherwise win on both size and colour — how the
 * assurances CTA band once rendered near-black type on navy while its own
 * stylesheet was correct. */
.crange .crange__cap {
  max-width: none;
  margin: var(--space-3xs) 0 0;
  font-size: var(--text-sm);
  line-height: 1.4;
  color: var(--color-ink-2);
  text-wrap: pretty;
}

.crange .crange__basis {
  margin-top: var(--space-md);
  font-size: var(--text-sm);
  color: var(--color-muted);
  max-width: var(--measure-note);
}

@media (min-width: 30rem) {
  .crange__ends {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-lg);
  }

  /* The high end is anchored to the bar's right end, which is the whole
   * reason the pair reads as a scale rather than as two prices. */
  .crange__end--high {
    text-align: right;
  }
}

/* ---- .ctab — the cost comparison table ---------------------------------
 * Three columns: where it is done, what the wait is, what it costs. The wait
 * column is the reason the table exists rather than a sentence.
 *
 * IT DOES NOT REUSE .fees, AND THAT WAS CHECKED RATHER THAN ASSUMED. .fees is
 * a TWO-column price list whose tier rows are <th colspan="2"> and whose price
 * column is width: 1%; a third column breaks both. It also lives in clinic.css,
 * which this page does not load, so reuse meant moving a component three live
 * clinic pages depend on in order to widen it. Different shape, not a variant.
 * What is borrowed is its mechanics, which are proven: collapsed borders,
 * hairline rows, mono .label column heads, .tnum figures, and a stack below
 * 30rem. IF A THIRD PAGE EVER NEEDS A THREE-COLUMN COMPARISON, this moves to
 * site.css — that is the journey .crumb, .nearby and .assure each made, and
 * each of them only after a second copy had already drifted.
 *
 * BOTH FIGURE COLUMNS ARE .tnum. They are ranges in two columns that a reader
 * compares down rather than across, so the digits have to align; oldstyle
 * figures from .copy's prose setting would stagger them. --measure does not
 * apply — a table is not prose and the cap would squeeze the third column.
 */
.ctab {
  width: 100%;
  max-width: 42rem;
  margin-top: var(--space-lg);
  border-collapse: collapse;
}

/* text-align: left IS REQUIRED AND IS NOT THE BROWSER DEFAULT. The UA gives
 * <th> text-align: center, so dropping this line centred every setting name
 * and the first column head — it looked like an indent bug, not an alignment
 * one. The cost column overrides it below at (0,2,1), which clears this
 * rule's (0,1,1); a bare `.ctab__cost` is only (0,1,0) and silently loses. */
.ctab th,
.ctab td {
  padding-block: var(--space-sm);
  border-bottom: var(--rule-hair) solid var(--color-rule);
  font-size: var(--text-md);
  line-height: 1.4;
  text-align: left;
  vertical-align: baseline;
}

/* The head row is a register, not a row: more air beneath it than the rows
 * carry between them, so the eye lands on the first setting rather than on
 * the labels. .label already supplies the mono caps and the muted ink. */
/* nowrap ON THE HEAD ROW ONLY, AND THE FIRST LABEL IS "Where" BECAUSE OF IT.
 * A table cannot be squeezed below its min-content, so nowrap on the original
 * "Where it is done" — 16 tracked mono characters, ~158px — pushed the whole
 * table to 722px, past its own 42rem cap and 8px past the .shell content edge
 * at 768, which max-width cannot claw back. Five characters buys ~108px and
 * the cap holds at 672. The column reads without the extra words: its cells
 * say "Subsidised / public hospital", "Class A / public hospital", "Private",
 * and the section lede already says "depends on where it is done".
 *
 * RE-MEASURE THE TABLE'S OWN WIDTH, not just the viewport, if a head label is
 * ever lengthened — the viewport check passes while the table overhangs its
 * column. The body cells still wrap freely; only the labels are pinned, and
 * below 34rem the head row is display: none so none of this reaches a phone. */
.ctab thead th {
  padding-top: 0;
  padding-bottom: var(--space-sm);
  white-space: nowrap;
}

/* THE COLUMN GUTTER IS --space-xl AND IT IS DOING TYPOGRAPHIC WORK, not
 * spacing. The three column heads are .label — mono caps at 0.16em — and at
 * --space-md they sat ~20px apart while the tracked word-space INSIDE each
 * head is ~10px. Two gaps of the same order read as one continuous string:
 * "WHERE IT IS DONE TYPICAL WAIT ESTIMATED COST". 40px is 4x the word-space,
 * which is the ratio that makes the eye break them into three. Re-measure the
 * PAINTED gap between heads, not the padding, if --text-xs or the .label
 * tracking moves. */
.ctab__where {
  padding-right: var(--space-xl);
}

.ctab tbody .ctab__where {
  font-weight: 500;
  color: var(--color-ink);
}

/* The setting's qualifier, on its own line. "Subsidised" and "Class A" are the
 * words a reader recognises; "public hospital" is what they mean. Inline it
 * would double the column's width and force the two figure columns to wrap. */
.ctab__sub {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--weight-body);
  color: var(--color-muted);
  line-height: 1.35;
}

.ctab__wait {
  padding-right: var(--space-xl);
  white-space: nowrap;
}

.ctab tbody .ctab__wait {
  color: var(--color-ink-2);
}

/* width: 1% collapses the column to its own content — right here, because
 * every cell in it is a price and none is a phrase. Check that again if a
 * row ever carries words. */
/* THE WEIGHT AND INK ARE SCOPED TO tbody. On the bare class they also hit the
 * <th class="label ctab__cost"> in the head, and one of three mono column
 * heads set 500 in --color-ink against two at --color-muted reads as a
 * heading for a heading. All three column heads are .label and nothing else. */
/* th AND td spelled out so this is (0,2,1) and clears `.ctab th`. The head
 * sits flush over its own figures, which separates it from "TYPICAL WAIT"
 * and tells the reader the column is numeric before they read a digit. */
.ctab th.ctab__cost,
.ctab td.ctab__cost {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}

.ctab tbody .ctab__cost {
  font-weight: 500;
  color: var(--color-ink);
}

/* p.ctab__basis, NOT .ctab__basis. `.copy p` is (0,1,1) and sets --text-base,
 * --color-ink-2 and --measure; a bare class is (0,1,0) and lost silently. The
 * footnote therefore rendered at full body size in body colour — seven lines
 * at 375 — so it read as another paragraph rather than as a note, which is
 * what made the foot of this section look unformatted.
 *
 * IT TOOK THE MEASURE DOWN WITH IT. --measure-note is a ch value and ch
 * resolves against the ELEMENT'S OWN font-size, so at the inherited 18px the
 * cap computed to 626px instead of ~370. Fixing the size fixes the box; this
 * is the trap CLAUDE.md records about mixed-size groups.
 *
 * --color-muted, one step past --color-ink-2, so the note sits below the body
 * text it follows rather than level with it. Measured against paper, not
 * assumed. */
.copy p.ctab__basis {
  margin-top: var(--space-sm);
  max-width: var(--measure-note);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--color-muted);
  text-wrap: pretty;
}

/* NOT AN ADJACENCY SELECTOR. This gap was `.ctab__basis + p`, which stopped
 * matching the moment the callout was inserted between them and left the
 * paragraph flush against the rule at margin-top: 0. Spacing that depends on
 * what happens to sit next to something fails silently when the order
 * changes; a class on the element itself does not. */
.copy p.ctab__more {
  margin-top: var(--space-lg);
}

/* ---- .ctab__note — the waiting-time callout ----------------------------
 * ONE ACCENT RULE, NO BOX, NO TINT. A bordered or tinted panel here would be
 * a card sitting directly under a table inside a band that already has its
 * own surface — the card-in-card shape the design system bans outright. A
 * 2px accent rule on the leading edge is the same move .toc__list uses to
 * mark the current row and the nav's trigger uses for its underline: the
 * page thickening a line rather than a new object appearing.
 *
 * IT IS AN <aside>, NOT A <p>, because it is genuinely tangential to the
 * table — everything above it describes the sector, this describes us.
 *
 * --measure-lede, NOT --measure. It is three short sentences read as a unit
 * rather than continuous prose, and the wider cap runs them past the table
 * they annotate, which makes the callout look like the section's main text.
 */
/* GREEN, AND IT IS --color-tick — THE SAME TOKEN THE GLYPH TAKES. The rule
 * and the tick are one object, so they cannot drift apart into two greens.
 * This is the site's third green and it is not a collapse of the other two:
 * --color-whatsapp is the icon-only widget value and --color-whatsapp-btn is
 * for a white label on a green field. Neither applies to a stroked mark on
 * paper, which is exactly what --color-tick already exists for.
 *
 * A RULE AND A MARK, NOT A TINTED PANEL. A filled box here would be a card
 * inside a band that already has its own surface — the card-in-card shape
 * the design system bans — and the site's tints (--color-paper-2/3) are a
 * page-level run down full-bleed bands, not an inline device. The green is
 * carried by the two things that can hold it without a background.
 *
 * FLEX ROW, mirroring .hero__points: .tick is already flex: none with its own
 * 0.12em nudge onto the first line's optical centre, so it needs nothing
 * added here. --measure-lede rather than --measure because this is three
 * short sentences read as a unit, not continuous prose. */
.ctab__note {
  display: flex;
  align-items: start;
  gap: var(--space-sm);
  margin-top: var(--space-lg);
  padding-left: var(--space-md);
  border-left: 2px solid var(--color-tick);
  max-width: var(--measure-lede);
}

.ctab__note p {
  margin: 0;
  font-size: var(--text-base);
  line-height: 1.55;
  color: var(--color-ink-2);
  text-wrap: pretty;
}

/* .inline-cta IS white-space: nowrap AND IT HAS TO BE UNPICKED HERE. The
 * nowrap stops a two-or-three-word CTA breaking across a line; "Talk to our
 * friendly staff and doctors" is 37 characters and measures ~315px at
 * --text-base, against a 264px column at 320 once the callout's own rule and
 * padding are taken off the 280px content width. It painted 15.5px past the
 * right edge — and html/body carry overflow-x: clip, so scrollWidth still
 * reported 320 and the document claimed no overflow. Measured the element's
 * own right against innerWidth, which is the only way this shows up.
 *
 * A WRAPPING PROSE LINK IS FINE; a wrapping BUTTON LABEL is not. The rule
 * about labels never taking two lines belongs to .btn/.btn--solid, where the
 * label is the whole object. Here the link is a phrase inside a sentence and
 * WCAG 2.5.8 exempts it, the same reasoning recorded for the .inline-cta on
 * /doctors/jayant-v-iyer/. If the wording is ever shortened under ~30
 * characters this override can go — re-measure before removing it. */
.ctab__note .inline-cta {
  white-space: normal;
}

/* The one emphasised run in the callout, and the ceiling is one: the claim is
 * the wait, and a second bold would put it in competition with the falls
 * sentence, which must stay a plain statement of fact about poor vision. */
.ctab__note strong {
  font-weight: 600;
  color: var(--color-ink);
}

/* Below 34rem three columns cannot hold: at 280px of content the setting name
 * wraps to three lines beside two nowrap figures. The row becomes a stacked
 * block with the cost lifted onto the setting's line, which is the pair a
 * reader is actually comparing; the wait follows beneath it. The thead goes,
 * because three column heads above a stacked list label nothing — so each
 * stacked wait carries its own word instead. */
@media (max-width: 34rem) {
  .ctab thead {
    display: none;
  }

  .ctab tr {
    display: grid;
    grid-template-columns: 1fr auto;
    align-content: start;
    gap: var(--space-3xs) var(--space-sm);
    padding-block: var(--space-sm);
    border-bottom: var(--rule-hair) solid var(--color-rule);
  }

  .ctab th,
  .ctab td {
    width: auto;
    padding: 0;
    border: 0;
  }

  /* EVERY CELL IS PLACED EXPLICITLY. The DOM order is where / wait / cost, so
   * auto-placement puts the full-width wait on row 2 and pushes the cost to
   * row 3 — the cost has to sit beside the setting, because that is the pair
   * being compared. align-content: start because normal computes to stretch
   * and the rows would grow to fill the tallest row in the table. */
  .ctab__where {
    grid-row: 1;
    grid-column: 1;
  }

  .ctab__cost {
    grid-row: 1;
    grid-column: 2;
    text-align: right;
  }

  .ctab__wait {
    grid-row: 2;
    grid-column: 1 / -1;
    font-size: var(--text-sm);
  }

  .ctab__wait::before {
    content: "Typical wait: ";
    color: var(--color-muted);
  }
}

/* ---- .finding — a cited result, pulled out of the prose ------------------
 * /cataract-surgery/bladeless/'s Cochrane review, and nothing else today. The
 * evidence section was four paragraphs of unbroken prose carrying the two
 * numbers that make it evidence rather than assertion; a reader scanning the
 * page went past them.
 *
 * IT IS AN ENCLOSED PANEL AND THAT IS NOT "CARD-IN-CARD". .forty__screening
 * does the same thing inside the exam-at-40 band and its comment records the
 * same defence: the thing around it is a section, not a card, and this is the
 * only enclosed object in it. Paper-2 against the section's paper, so it
 * lifts without a shadow, and a 2px accent edge on the leading side carrying
 * the emphasis a fill would.
 *
 * THE NUMBERS ARE A PROPERTY OF THE LITERATURE, NEVER OF THIS PRACTICE — the
 * distinction between "vision loss can worsen with delay" and "we get better
 * outcomes", which is the whole reason this page can cite anything. 42 trials
 * and 7,298 eyes are the size of the evidence base; setting them large says
 * the question is well studied, and the sentence beneath says the answer went
 * against the technique the page is about. IF A LATER EDIT EVER LEAVES THE
 * FIGURES WITHOUT THAT SENTENCE, the panel becomes a selling device.
 *
 * A BARE <figure> IN PROSE NEEDS margin-inline: 0. The UA gives it
 * `margin: 1em 40px` and the reset does not clear it — the line .vid and
 * .pairfig both carry, and the reason the cataract video once rendered 185px
 * wide in a 265px column while the document reported no overflow.
 *
 * EVERY TEXT RULE IS DOUBLE-SCOPED because the panel sits inside .copy, whose
 * `.copy p` is (0,1,1). A single class at (0,1,0) loses to it, which is how
 * the assurances CTA band once rendered near-black type on navy while its own
 * stylesheet was correct. */
.finding {
  max-width: var(--measure);
  margin: var(--space-lg) 0 var(--space-xl);
  margin-inline: 0;
  padding: var(--space-md) var(--space-lg) var(--space-lg);
  background: var(--color-paper-2);
  border: var(--rule-hair) solid var(--color-rule);
  border-inline-start: var(--rule-thick) solid var(--color-accent);
}

/* Flex and wrapping rather than two fixed columns: at 320 the content box is
 * 232px and "7,298" alone is ~100px at --text-2xl, so a two-column grid puts
 * the caption under it on two lines. Wrapped, the second figure drops to its
 * own row intact. */
.finding__figs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md) var(--space-2xl);
  margin-bottom: var(--space-md);
}

.finding .finding__fig {
  display: grid;
  align-content: start;
  gap: var(--space-3xs);
  max-width: none;
  margin: 0;
}

/* Lining tabular figures explicitly. The oldstyle rule covers running prose,
 * and an oldstyle numeral sits at x-height — wrong for a numeral used as a
 * graphic anchor, which is the argument .forty__figure records. NOT
 * --text-figure: that token is for a numeral that is the whole of its column,
 * and "7,298" is five characters where the "40" it was written for is two. */
.finding__num {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  font-size: var(--text-2xl);
  font-variant-numeric: lining-nums tabular-nums;
  line-height: 0.95;
  letter-spacing: -0.02em;
  color: var(--color-accent);
}

/* --color-ink-2, NOT --color-muted, AND THAT IS A MEASURED DECISION. Muted on
 * this panel's paper-2 measures 4.92 off painted pixels — over the 4.5 floor
 * and still the exact shape CLAUDE.md warns about: a small line, on a tinted
 * field, carrying meaning. It is 13px through .label and it is what says WHAT
 * the numeral counts, so it takes the brighter value at 8.99. The source line
 * below keeps muted: that one is a citation, the same job and the same
 * register as .cite, and it is 14.4px. */
.finding__cap {
  color: var(--color-ink-2);
}

.finding .finding__quote {
  max-width: none;
  margin: 0;
  font-size: var(--text-base);
  color: var(--color-ink);
}

.finding .finding__quote strong {
  font-weight: 500;
  color: var(--color-ink);
}

.finding__src {
  margin-top: var(--space-sm);
  font-size: var(--text-sm);
  color: var(--color-muted);
}

/* ---- Concern picker ----------------------------------------------------
 * site.css draws the whole component; this page adds one rule and it is the
 * same one clinic.css makes, for the same reason.
 *
 * THE TYPING BUBBLE IS HIDDEN BELOW 48rem. It is decoration — already
 * aria-hidden, so nothing is lost to a screen reader — and the chips beneath
 * it are the functional half, with the WhatsApp glyph on each saying what it
 * does. It costs ~132px at every width it is removed at, on a page that is
 * being shortened deliberately. display: none also stops the caret blink,
 * which is the only infinite animation on the page.
 *
 * The JS typer keeps mutating the hidden text, which is cheap; guarding it
 * would mean editing footer.php, which every page shares. */
@media (max-width: 47.99rem) {
  .picker__bubble {
    display: none;
  }
}
