/* ============================================================================
   page.css — the five inner pages

   Same design system as the homepage, lighter motion. A pricing table is for
   scanning, not scrubbing: nothing here pins, nothing scrubs, and the only
   scroll device is a reveal on entry. Where the homepage refuses cards because
   its grammar forbids them, these pages refuse them for the ordinary reason —
   a grid of identical boxes is the most recognisable machine-made page there
   is. Structure comes from hairlines, asymmetry and space.
   ========================================================================== */

body { padding-top: 3.4rem; }         /* the topline is fixed and always on here */

/* ------------------------------------------------------------- page head -- */
.phead { padding-block: clamp(3rem, 9vh, 6rem) clamp(2.5rem, 6vh, 4rem); }
.phead h1 { font-size: clamp(2.4rem, 1.5rem + 4.4vw, 5rem); line-height: 1; margin-top: 1.1rem; max-width: 17ch; }
.phead .lede { margin-top: 1.5rem; max-width: 46ch; color: var(--ink-soft); font-size: clamp(1rem, 0.95rem + 0.4vw, 1.2rem); }
.phead__meta { margin-top: 2rem; display: flex; flex-wrap: wrap; gap: 0.5rem 1.75rem; font-size: var(--t-quiet); color: var(--ink-dim); }

/* ---------------------------------------------------------------- blocks -- */
.sec { padding-block: clamp(3rem, 8vh, 5.5rem); }
.sec--tight { padding-block: clamp(2rem, 5vh, 3.5rem); }
.sec__head { max-width: 54ch; margin-bottom: clamp(1.5rem, 4vh, 2.5rem); }
.sec__head h2 { margin-top: 0.9rem; }
.sec__head .prose { margin-top: 1rem; }

.split {
  display: grid; gap: clamp(1.75rem, 4vw, 4rem);
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); align-items: start;
}
.split--even { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.split--flip > :first-child { order: 2; }

/* Definition rows: the workhorse. A hairline between two things beats a box
   around each of them, and it never turns into a grid of identical cards. */
.rows { list-style: none; margin: 0; padding: 0; }
.rows > li, .rows > div {
  display: grid; gap: 0.35rem 2rem; align-items: baseline;
  grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
  padding: clamp(0.9rem, 2.2vh, 1.3rem) 0; border-top: 1px solid var(--rule);
}
.rows > :last-child { border-bottom: 1px solid var(--rule); }
.rows dt, .rows .rows__k {
  font-family: var(--sc-font-text); font-size: 0.8rem; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); margin: 0;
}
.rows dd, .rows .rows__v { margin: 0; color: var(--fg-soft); font-size: 0.98rem; max-width: 62ch; }
.rows .rows__v strong { color: var(--fg); font-weight: 500; }

/* ------------------------------------------------------------- packages --- */
.pkg { border-top: 1px solid var(--rule); padding-block: clamp(1.5rem, 4vh, 2.5rem); }
.pkg:last-of-type { border-bottom: 1px solid var(--rule); }
.pkg__top { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.5rem 2rem; align-items: baseline; }
.pkg__name { font-family: var(--sc-font-display); font-size: clamp(1.7rem, 1.25rem + 2vw, 2.8rem); letter-spacing: -0.03em; color: var(--fg); margin: 0; }
.pkg__pages { font-family: var(--sc-font-text); font-size: 0.82rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-dim); margin: 0.3rem 0 0; }
.pkg__price { font-family: var(--sc-font-text); font-size: clamp(1.3rem, 1rem + 1.4vw, 2rem); font-weight: 600; color: var(--fg); white-space: nowrap; }
.pkg__price small { display: block; font-size: 0.75rem; font-weight: 400; letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-dim); text-align: right; margin-top: 0.2rem; }
.pkg__body { margin-top: 1.1rem; display: grid; gap: 1rem 2.5rem; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.pkg__inc { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: 2rem; font-size: 0.92rem; color: var(--fg-soft); }
.pkg__inc li { break-inside: avoid; padding-left: 1.1rem; position: relative; margin-bottom: 0.35rem; }
.pkg__inc li::before { content: ""; position: absolute; left: 0; top: 0.62em; width: 0.5rem; height: 1px; background: var(--accent); }
.pkg--flag { color: var(--accent); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; }

/* ------------------------------------------------------------ real table -- */
.tablewrap { overflow-x: auto; margin-top: 1.5rem; -webkit-overflow-scrolling: touch; }
table.matrix { width: 100%; border-collapse: collapse; font-size: 0.88rem; min-width: 42rem; }
table.matrix th, table.matrix td { text-align: left; padding: 0.7rem 0.75rem; border-bottom: 1px solid var(--rule); vertical-align: top; }
table.matrix thead th { font-family: var(--sc-font-text); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-dim); border-bottom-color: var(--rule-firm); }
table.matrix tbody th { font-weight: 400; color: var(--fg); }
table.matrix td { text-align: center; color: var(--fg-soft); }
table.matrix td.yes { color: var(--accent); }
table.matrix caption { text-align: left; font-size: var(--t-quiet); color: var(--fg-dim); padding-bottom: 0.75rem; }

/* ------------------------------------------------------------ calculator -- */
.calc { border: 1px solid var(--rule-firm); padding: clamp(1.25rem, 3vw, 2rem); border-radius: var(--sc-r-lg); }
.calc__row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0.6rem 1.5rem; align-items: center; padding: 0.6rem 0; border-bottom: 1px solid var(--rule); }
.calc__row:last-of-type { border-bottom: 0; }
.calc__row label { font-size: 0.92rem; color: var(--fg-soft); }
.calc select, .calc input[type="number"] {
  background: color-mix(in oklab, var(--fg) 6%, transparent);
  border: 1px solid var(--rule-firm); color: var(--fg);
  padding: 0.45rem 0.6rem; border-radius: var(--sc-r-sm); font-size: 0.92rem; min-width: 8rem;
}
.calc__check { display: flex; align-items: center; gap: 0.6rem; }
.calc__out { margin-top: 1.25rem; padding-top: 1.25rem; border-top: 1px solid var(--rule-firm); display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; align-items: baseline; justify-content: space-between; }
.calc__total { font-family: var(--sc-font-text); font-size: clamp(1.5rem, 1.2rem + 1.6vw, 2.3rem); font-weight: 600; color: var(--accent); }
.calc__note { font-size: var(--t-quiet); color: var(--fg-dim); max-width: 40ch; }

/* ----------------------------------------------------------------- work --- */
.proj { padding-block: clamp(2.5rem, 7vh, 4.5rem); border-top: 1px solid var(--rule); }
/* aspect-ratio is stated explicitly rather than left to the UA's
   attr(width)/attr(height) mapping. Lighthouse charged 0.115 CLS against this
   image as "media element lacking an explicit size" even with both attributes
   present and both overridden in CSS — the reserved box is not worth arguing
   about, so it is declared. */
.proj__shot { width: 100%; height: auto; aspect-ratio: 1200 / 750;
  border-radius: var(--sc-r-lg); border: 1px solid var(--rule); }
.proj__head { display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; align-items: baseline; margin-bottom: 0.75rem; }
.proj h2 { font-size: clamp(1.7rem, 1.3rem + 2vw, 2.8rem); }
.proj h2 em { font-style: italic; color: var(--fg-soft); font-size: 0.55em; display: block; margin-top: 0.4rem; letter-spacing: -0.01em; }
.status { font-family: var(--sc-font-text); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.13em; text-transform: uppercase; padding: 0.25rem 0; border-bottom: 1px solid currentColor; }
.status--live { color: var(--accent); }
/* ROUND 13 — QC W4. GLD Alloys carried "Live client site" while its own status
   row said the public launch was still pending the client's sign-off, and the
   build itself is a noindex preview. Two claims, one page, contradicting. This
   one is deliberately quieter than --live: it is not an achievement badge. */
.status--preview { color: var(--fg-dim); }
.status--build, .status--concept { color: var(--fg-dim); }

/* ------------------------------------------------------------- sectors ---- */
.ind { padding-block: clamp(2rem, 5vh, 3.25rem); border-top: 1px solid var(--rule); scroll-margin-top: 5rem; }
.ind__grid { display: grid; gap: 1rem 3rem; grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr); align-items: start; }
.ind h2 { font-size: clamp(1.5rem, 1.15rem + 1.6vw, 2.3rem); }
.ind__lands { margin-top: 1rem; font-size: var(--t-quiet); color: var(--fg-dim); }
.ind__lands b { color: var(--accent); font-weight: 600; }

/* ------------------------------------------------------- about · the head --
   The same rig as the homepage, given room to be the page's subject rather
   than a corner watcher. There is no canvas here any more: the portrait is a
   finished offline render and the depth is CSS — `perspective` on the frame,
   rotateY/rotateX on `.head__tilt`, the two irises tracking inside a mask.

   THE PERSPECTIVE MUST LIVE ON THE FRAME. core.css scopes `perspective` to
   `.head`, and this element carries both `.viewer` and `.head`, so it is
   already the containing block — the trap the homepage hit was a dock that had
   `[data-head]` but not `.head`, which left the turn a flat affine squash. The
   value is restated here because About shows the head far larger than the dock
   does, and 900px of perspective on a 40vw portrait is a caricature. */
.viewer {
  position: relative; aspect-ratio: 4 / 5;
  display: grid; place-items: center;
  perspective: 1800px;
}
.viewer .head__img { width: 100%; height: 100%; object-fit: contain; }
.viewer__hint { margin-top: 0.7rem; font-size: var(--t-quiet); color: var(--ink-dim); }
/* No pointer, no invitation. head.js adds `.head--still` whenever the rig
   declines — a phone, reduced motion, a script that never arrived — and in
   that state there is nothing to move a cursor across. Keyed on the class
   rather than the media query, because the rig declines for several reasons
   and only one of them makes that query match. */
.head--still + .viewer__hint { display: none; }

/* --------------------------------------------------------------- contact -- */
.reach { display: grid; gap: 1rem; margin-top: 1.5rem; }
.reach a { font-size: clamp(1.1rem, 0.95rem + 0.8vw, 1.5rem); font-family: var(--sc-font-display); letter-spacing: -0.02em; }

/* -------------------------------------------------------------- 404 ------- */
.oops { min-height: 62svh; display: grid; align-content: center; }

/* ---------------------------------------------------------------- narrow -- */
@media (max-width: 900px) {
  .split, .split--even, .pkg__body, .ind__grid { grid-template-columns: minmax(0, 1fr); }
  .split--flip > :first-child { order: 0; }
  .rows > li, .rows > div { grid-template-columns: minmax(0, 1fr); }
  .pkg__inc { columns: 1; }
  .pkg__top { grid-template-columns: minmax(0, 1fr); }
  .pkg__price small { text-align: left; }
}


/* ============================================================================
   ROUND 12 — WHAT THE INNER PAGES LOOK AT

   Round 11 gave the homepage three photographs and argued that eight stills was
   the right number for the whole site. It was not: the five inner pages had
   ZERO between them — services.html was 7,216px of dark grey with type on it,
   pricing.html 8,496px, contact.html 3,921px, and every one of them had exactly
   as many pictures as a text file. Daniel's note, for the second time, was
   "there are still a lot of blank and no graphic in the website". That is the
   blank he is looking at.

   THE RULE ROUND 11 SET STILL HOLDS AND IS NOT RELAXED. The grammar is
   typographic; no photograph goes behind a headline. Every plate below sits in
   its OWN space — a column the type does not occupy, or a band the type has
   already finished — and the one full-bleed frame is masked at both edges
   rather than scrimmed. More imagery does not mean photographs under text.

   Everything here is one shoot with the eight already placed: same 35mm f/2
   preamble, same warm-tungsten-against-cool-blue split, same unpeopled
   documentary framing, same $0.035 rung on fal.ai. The seeds are in
   _v2-work/gen12/out/fal-ledger.jsonl.

   WEIGHT. Every plate ships two widths and every one is lazy. A 360px phone
   pulls 480-560w variants: 121KB across the whole of services.html, 26-30KB on
   the others. `aspect-ratio` is declared on every rule rather than left to the
   UA's attr() mapping, because Lighthouse charged this build CLS for exactly
   that once already (see .proj__shot above).
   ========================================================================== */

/* ---------------------------------------------------------- shared plate --
   One treatment, held across every page: hard 4px corner (the steel-rule
   radius, no fillet), an edge light on the lip and a cast shadow with real
   downward offset. Depth by two of the five tools rather than by a halo. */
.plate-fig { margin: 0; }
.plate-fig img,
.ind__plate img,
.band-fig img,
.oops__ground img {
  display: block; width: 100%; height: auto; object-fit: cover;
}
.plate-fig img,
.ind__plate img {
  aspect-ratio: 16 / 9;
  border-radius: var(--sc-r-lg);
  box-shadow: 0 1px 0 var(--edge-lip) inset, 0 20px 50px -24px var(--shadow-cast-soft);
}
.plate-fig--tall img { aspect-ratio: 3 / 2; }
.plate-fig figcaption {
  margin-top: 0.9rem; max-width: 40ch;
  font-size: var(--t-quiet); color: var(--fg-dim);
}

/* ------------------------------------------------------- industry plates --
   In the LEFT column, under the trade's own name — the column that until now
   held one heading and then several hundred pixels of nothing while the prose
   beside it ran on. So the picture costs the page almost no extra height on a
   desktop and gives the seven-block list something to recognise yourself in.
   These are the same six frames the homepage's maker act uses, which is the
   point: pick "Clinic" on the homepage and the services page shows you the
   same room. Hospitality is the seventh and was shot for this page.

   Note `firms` is NOT the office frame the homepage uses. That one has a city
   dusk beyond glass, and this block's own copy promises "no glass towers, no
   borrowed skyline" — so it got a windowless one. An image that contradicts
   the sentence beside it is worse than no image. */
.ind__lead { min-width: 0; }
.ind__plate { margin: 1.25rem 0 0; }
@media (min-width: 901px) {
  .ind__plate { max-width: 30rem; }
}
@media (max-width: 900px) {
  .ind__plate { margin-block: 1rem 1.25rem; }
}

/* ----------------------------------------------------------- the band ----
   One full-bleed frame per site, on the services page head: a shophouse
   terrace where every unit is a different trade and one is still open. That IS
   the page's argument ("arranged by what your business actually is"), so it
   carries a caption and real alt text rather than being mood.

   It is masked top and bottom so it rises out of the page ground and settles
   back into it instead of sitting in a box — the same undertow treatment as
   the homepage's faq ground, for the same reason. */
.band-fig { margin: 0 0 clamp(1.5rem, 5vh, 3.5rem); }
.band-fig__shot {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 16%, #000 84%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 16%, #000 84%, transparent 100%);
}
.band-fig img { aspect-ratio: 1400 / 788; max-height: 62vh; }
/* The caption carries `.wrap` so it lines up with the page gutter. Its MEASURE
   therefore has to live on an inner span: putting `max-width: 44ch` on the
   .wrap box itself beats .wrap's own max-width on source order, and the auto
   margins then centre a 420px box in the viewport — measured at 1440, the
   caption started at x=597 instead of x=176. */
.band-fig figcaption { margin-top: 1rem; font-size: var(--t-quiet); color: var(--fg-dim); }
.band-fig figcaption > span { display: block; max-width: 44ch; }

/* --------------------------------------------------------------- 404 -----
   The emptiest page on the site by a distance: 909px, one heading, two links.
   A shutter down at night is a dead end that is at least somewhere. Decorative
   — every word a lost visitor needs is in the text beside it — so it is
   aria-hidden and masked away from the column the links sit in, on the same
   right-hand rule the FAQ ground uses, and only once there is provably room. */
.oops { position: relative; overflow: clip; }
.oops > .wrap { position: relative; z-index: 1; }
.oops__ground {
  position: absolute; inset: auto 0 0 0;
  height: clamp(8rem, 24vh, 12rem); pointer-events: none; z-index: 0;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 60%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 60%);
}
.oops__ground img { height: 100%; opacity: 0.4; }
@media (min-width: 1000px) {
  .oops__ground {
    inset: 0 0 0 56%; height: auto;
    -webkit-mask-image: linear-gradient(to right, transparent 0%, hsl(0 0% 0% / 0.5) 24%, #000 68%);
    mask-image: linear-gradient(to right, transparent 0%, hsl(0 0% 0% / 0.5) 24%, #000 68%);
  }
  .oops__ground img { opacity: 0.5; }
}


/* ============================================================================
   ROUND 14 - the two devices the inner pages did not have.

   QC13 blocked on the anti-dry gate: the seven inner documents' entire motion
   budget was 19 x data-sc-in + 3 x data-sc-stagger. Things faded in once and
   the page was inert for the rest of the visit. Two engine devices close it.

     .kin      the page's argument heading arrives by AUTHORED line, each line
               inside its own clip box, staggered. Authored rather than
               measured: see the swapText() note in i18n.js, and because it is
               the only way a Chinese heading breaks where a reader would.
     .pf-crop  every photograph sits in a crop SHORTER than itself and travels
               through it as its block passes (data-sc-parallax), so it reads
               as lying behind the page rather than pasted onto it.

   Neither may ever be the only copy of anything: both degrade to "simply
   there" with no JavaScript and under reduce. Contracts are in reduce.css.

   COMMENT VOLUME IS A MEASURED COST IN THIS FILE. page.css is render-blocking
   on all seven inner pages. This round's first draft added 8.65 KB of comment
   here and in reduce.css, and an interleaved same-session A/B (ab-round14.sh)
   charged services.html a reproducible LCP 3.17s -> 3.31s and one Lighthouse
   point for it while the REAL LCP phases stayed identical - Lantern billing
   the bytes on a throttled link. Keep the facts, cut the prose, until the
   deploy step minifies.
   ========================================================================== */

/* 1 - the heading. Structure only here, in both motion modes and with no JS:
   the masks exist and the lines sit in them at rest. The rise lives in the
   no-preference block below, so reduce is not a special case - it is this. */
.kin .ln {
  display: block;
  /* Descender room bought back inside the clip, then removed from flow: a mask
     cut to the line box shaves the tail off every g, y, p and j. */
  overflow: hidden;
  padding-block-end: 0.1em;
  margin-block-end: -0.1em;
}
.kin .ln__i { display: block; }
/* An authored break beats `balance`, and the two fight. */
.kin { text-wrap: wrap; }

/* 2 - the crop owns the box, ratio, corner, lip and cast shadow; the picture
   owns none of them, because it is the thing that moves and a moving element
   carrying a radius drags the corner with it. */
.pf-crop {
  display: block; position: relative; overflow: hidden;
  border-radius: var(--sc-r-lg);
  box-shadow: 0 1px 0 var(--edge-lip) inset, 0 20px 50px -24px var(--shadow-cast-soft);
}
/* THE SLACK IS A PERCENTAGE PLUS A FIXED FLOOR, AND THE FLOOR IS THE POINT.
   The engine writes `rate * (p - 0.5) * 100px` - a FIXED pixel travel at every
   viewport (rate 0.3 => +/-15px). Slack written as a percentage alone is not
   fixed: a 16/9 crop is ~340px tall on a desktop (6% = 20px, fine) and ~180px
   on a 360px phone (6% = 10.8px), where the picture pulls a 4.2px hole in its
   own frame at both ends of the act - measured on 54 of 243 samples by
   verify-pan-budget.mjs. Percentage for the look, 14px for the guarantee: the
   worst case on the site is now 24.8px of slack against 15px of travel. Raise
   a rate above 0.4 and this rule changes with it. Run the probe. */
.pf-crop > img {
  position: absolute; inset: calc(-6% - 14px) 0;
  width: 100%; height: calc(112% + 28px);
  aspect-ratio: auto;
  object-fit: cover;
  border-radius: 0; box-shadow: none; border: 0;
}
.plate-fig > .pf-crop,
.ind__plate > .pf-crop { aspect-ratio: 16 / 9; }
.plate-fig--tall > .pf-crop { aspect-ratio: 3 / 2; }
/* A screenshot is 1200x750 (1.6) in a 16/9 crop (1.778), so it is already 11%
   taller than its box. The extra slack returns the WHOLE screenshot across the
   block - nothing is permanently cropped away, it is scrolled - which is also
   why this one alone takes a negative rate: on a picture of a web page the
   browsing read beats the receding read. */
.pf-crop--shot { aspect-ratio: 16 / 9; border: 1px solid var(--rule); }
.pf-crop--shot > img { inset: calc(-11% - 14px) 0; height: calc(122% + 28px); }

@media (prefers-reduced-motion: no-preference) {
  /* The heading opts out of the ordinary flow reveal: the lines ARE the
     entrance, and a wrapper fading underneath them wastes the rise. */
  [data-sc-in].kin,
  [data-sc-stagger] > .kin { opacity: 1; transform: none; transition: none; }

  /* Keyed on .sc-ready, never on the media query alone: a visitor with no
     JavaScript never receives .sc-in, and a line parked inside its own clip
     box is a line that is not on the page. */
  html.sc-ready .kin[data-sc-in]:not(.sc-in) .ln__i { transform: translateY(118%); }
  /* No `will-change`: a permanent compositing layer per line, on a heading that
     moves once, is memory for nothing. A transform transition promotes itself
     for its own duration. */
  .kin .ln__i { transition: transform 780ms var(--sc-ease-out); }
  /* 80ms - the top of the house stagger range. */
  .kin .ln:nth-child(2) .ln__i { transition-delay: 80ms; }
  .kin .ln:nth-child(3) .ln__i { transition-delay: 160ms; }
  .kin .ln:nth-child(4) .ln__i { transition-delay: 240ms; }
  .kin .ln:nth-child(5) .ln__i { transition-delay: 320ms; }
  .kin .ln:nth-child(n+6) .ln__i { transition-delay: 400ms; }
}

/* ============================================================= price table ==
   ROUND 15. The five prose package cards and the separate comparison matrix
   were stating the same facts twice — the cards' `.pkg__inc` lists WERE the
   table rows. They are now one artefact: a table whose header is the package.

   Three moving parts, in order of how much they matter:
     1. a sticky label column, so a phone scrolling sideways never loses the
        row it is reading. Without it a comparison table on a 360px screen is
        five columns of numbers with nothing to attach them to.
     2. a spotlight, so one tier can be picked out of five WITHOUT hiding the
        other four — the whole value of a comparison is the comparison. Chips
        drive it on touch, hover drives it on a mouse, both set the same state.
     3. ticks that draw and prices that count, once, on entry.
   -------------------------------------------------------------------------- */

/* The ticks are SVG and say nothing out loud, so every one carries a word
   behind it. Not display:none — that would take it from the accessibility
   tree too, which is the entire point of the span. */
.u-sr {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}
svg.u-sr { position: absolute; }

/* ---------------------------------------------------------------- chips -- */
.tiers { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1.6rem; }
.tiers__b {
  font-family: var(--sc-font-text); font-size: 0.8rem; font-weight: 500;
  letter-spacing: 0.05em; color: var(--fg-soft);
  background: transparent; border: 1px solid var(--rule-firm);
  border-radius: 100px; padding: 0.36rem 0.95rem; cursor: pointer;
  transition: color 140ms var(--sc-ease-out), border-color 140ms var(--sc-ease-out),
              background-color 140ms var(--sc-ease-out);
}
.tiers__b[aria-pressed="true"] {
  color: var(--ember-ink); background: var(--accent); border-color: var(--accent);
}
@media (hover: hover) and (pointer: fine) {
  .tiers__b:hover { color: var(--accent); border-color: var(--accent); }
  .tiers__b[aria-pressed="true"]:hover { color: var(--ember-ink); }
}

/* ---------------------------------------------------------------- table -- */
table.matrix--price { min-width: 52rem; font-size: 0.86rem; }

/* The row label rides along. z-index so a lit column passes UNDER it, and an
   opaque ground because a transparent sticky cell smears its own text. */
table.matrix--price th[scope="row"],
table.matrix--price .matrix__corner {
  position: sticky; left: 0; z-index: 2; background: var(--sc-canvas);
}
table.matrix--price th[scope="row"] { min-width: 10.5rem; padding-right: 1rem; }
table.matrix--price .matrix__corner { border-bottom: 1px solid var(--rule); }

table.matrix--price thead th.tier {
  text-align: center; vertical-align: bottom; padding: 1.1rem 0.8rem 0.95rem;
  text-transform: none; letter-spacing: normal; font-weight: 400; color: var(--fg);
  border-bottom-color: var(--rule-firm);
  transition: background-color 220ms var(--sc-ease-out);
}
.tier__flag {
  display: block; font-size: 0.62rem; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--accent); margin-bottom: 0.32rem;
}
.tier__name {
  display: block; font-family: var(--sc-font-display);
  font-size: clamp(1.1rem, 0.9rem + 0.6vw, 1.5rem);
  letter-spacing: -0.02em; color: var(--fg);
}
.tier__price {
  display: block; margin-top: 0.22rem; font-weight: 600; color: var(--accent);
  font-size: clamp(0.95rem, 0.85rem + 0.4vw, 1.15rem);
  /* tabular figures, or the count-up reflows the column on every frame */
  font-variant-numeric: tabular-nums;
}
.tier__meta {
  display: block; margin-top: 0.28rem; font-size: 0.68rem; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--fg-dim); font-weight: 400;
}
.tier__cta { text-align: center; padding-top: 1rem; }

/* The table's own <caption> lives inside a box that is 52rem wide and clipped
   by the scrolling wrapper, so on a phone the sentence was cropped mid-word.
   It sits in page flow instead, where it wraps to the viewport; the caption
   that remains is a short name for assistive tech. */
.matrix__note {
  margin: 1.25rem 0 0; max-width: 62ch;
  font-size: var(--t-quiet); color: var(--fg-dim);
}
.ghost-cta--sm { font-size: 0.8rem; }

/* The two orientation rows read as prose, not as data. */
.matrix__who td { text-align: center; color: var(--fg-soft); font-size: 0.82rem; line-height: 1.45; }
.matrix__pay td { text-align: center; color: var(--fg-dim); font-size: 0.78rem; }
.matrix__pay th, .matrix__pay td { border-bottom-color: var(--rule-firm); }
table.matrix--price td.no { color: var(--fg-dim); }

/* ------------------------------------------------------------- the tick -- */
/* stroke-dasharray/offset are INHERITED SVG properties, so setting them on
   the <svg> reaches the <path> inside the <use> shadow tree — which ordinary
   selectors cannot. pathLength="1" makes one length work for any path. */
.tick {
  width: 1.05rem; height: 1.05rem; display: inline-block; vertical-align: -0.2em;
  fill: none; stroke: var(--accent); stroke-width: 2.4;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 1; stroke-dashoffset: 1;
  transition: stroke-dashoffset 420ms var(--sc-ease-out);
}
.tablewrap.sc-in .tick { stroke-dashoffset: 0; }
/* down the table, row by row, so it reads as filling in rather than flashing */
.matrix--price tbody tr:nth-child(1) .tick { transition-delay: 40ms; }
.matrix--price tbody tr:nth-child(2) .tick { transition-delay: 80ms; }
.matrix--price tbody tr:nth-child(3) .tick { transition-delay: 120ms; }
.matrix--price tbody tr:nth-child(4) .tick { transition-delay: 160ms; }
.matrix--price tbody tr:nth-child(5) .tick { transition-delay: 200ms; }
.matrix--price tbody tr:nth-child(6) .tick { transition-delay: 240ms; }
.matrix--price tbody tr:nth-child(7) .tick { transition-delay: 280ms; }
.matrix--price tbody tr:nth-child(8) .tick { transition-delay: 320ms; }
.matrix--price tbody tr:nth-child(9) .tick { transition-delay: 360ms; }
.matrix--price tbody tr:nth-child(10) .tick { transition-delay: 400ms; }
.matrix--price tbody tr:nth-child(n+11) .tick { transition-delay: 440ms; }

/* ---------------------------------------------------------- the spotlight -- */
/* One tier lit, the other four still legible. data-hot is set by pages.js from
   a chip, a hover or keyboard focus, so touch and mouse share one state. */
.matrix--price [data-tier] { transition: background-color 220ms var(--sc-ease-out); }
.matrix--price[data-hot] tbody td,
.matrix--price[data-hot] tfoot td { transition: opacity 220ms var(--sc-ease-out); }

.matrix--price[data-hot="1"] :is(thead th, tbody td, tfoot td):nth-child(2),
.matrix--price[data-hot="2"] :is(thead th, tbody td, tfoot td):nth-child(3),
.matrix--price[data-hot="3"] :is(thead th, tbody td, tfoot td):nth-child(4),
.matrix--price[data-hot="4"] :is(thead th, tbody td, tfoot td):nth-child(5),
.matrix--price[data-hot="5"] :is(thead th, tbody td, tfoot td):nth-child(6) {
  background: color-mix(in oklab, var(--accent) 13%, transparent);
}
/* the flagged tier keeps a permanent, quieter version of the same treatment */
.matrix--price thead th.tier--flagged { background: color-mix(in oklab, var(--accent) 7%, transparent); }

@media (hover: hover) and (pointer: fine) {
  .matrix--price tbody tr:hover td { background: color-mix(in oklab, var(--fg) 4%, transparent); }
}

/* ------------------------------------------------------------- narrow ----- */
@media (max-width: 640px) {
  table.matrix--price { min-width: 44rem; font-size: 0.82rem; }
  table.matrix--price th[scope="row"] { min-width: 8.5rem; }
  .tier__meta { display: none; }   /* the chips and the pages row carry this */
}
