/* =====================================================================
   calculator.css — owner: CALCULATOR
   Everything under the instrument (#quote-instrument.instrument, rendered
   by HOME): the three-tap quick view (country tiles with their from-price,
   weight buttons, the two service rows, the price bar with the one green
   button), the detailed view (back button, three panels, the service rows,
   the quote card) and the fine print at the detailed view's end. Shared
   skins (.panel, .field, inputs, .btn) are overridden only under
   `.instrument`; the calculator's own class names are unscoped because
   nothing else uses them. Brief v3 §4–6 is the spec.
   No mono, no uppercase, nothing under 14.9px for the customer.
   Motion policy (v3 §5): colour on hover / focus / checked ≤ 80ms, :active
   feedback at 0ms, the price bar's 120ms first-mount entrance — nothing
   else moves, nothing counts, nothing glows. Dark-mode rules use
   :root[data-theme="dark"] only.
   Stillness (v3 §4 rule 4): the result block is in the page from the first
   paint at the height it has once live — rows + lines + bar — so the panel
   measures the same in all five states and nothing under it moves when the
   price lands. On a phone the whole live result ends above the sticky bar:
   the two wide weight buttons share a row, the two service rows stand side
   by side (name / days / price stacked beside the radio, 70px), the lines
   take 44px, and the bar's note drops the country.
   ===================================================================== */

/* ---------- the instrument's own tokens ---------- */
.instrument {
  --i-edge: var(--edge, var(--dash));
  --i-tap: var(--tap, 48px);
  --i-tap-big: var(--tap-big, 56px);
  --i-tap-giant: var(--tap-giant, 64px);
  --i-label: var(--fs-label, 0.95rem);
  --i-small: var(--fs-small, 0.875rem);
  --i-button: var(--fs-button, 1.1rem);
  --i-kg: var(--fs-kg, 1.35rem);
  --i-price: var(--fs-price, clamp(2.6rem, 1.4rem + 4vw, 4.25rem));
  --i-fast: 80ms; /* the one transition length inside the instrument: colour on hover / focus / checked */
  --i-gap: 12px; /* between a step's block and the next head */
  --i-tile: 64px; /* country tile */
  --i-kgh: var(--i-tap-big); /* weight button */
  --i-wide: var(--i-tap); /* "Over N kg" / "Documents · box size" */
  --i-opt: 70px; /* service row: three lines beside the radio on phones (69px of content + borders); 64px two-line rows from 560 */
  --i-lines: 44px; /* the two lines under the rows: 44px on phones (and the density tier), 48px from 560 */
  /* The price bar's height. Below 1024 it is the sticky bar: the 56px button, 8px above and below, the 1px
     hairline and the safe-area inset — written out so the result block can reserve exactly that. */
  --i-bar: calc(var(--i-tap-big) + 17px + env(safe-area-inset-bottom, 0px));
  --i-book: var(--i-tap-giant); /* the green button from 1024 */
  /* How many rows the service rows take: two services stack under 360, share a row from 360 (see .result). */
  --i-rows: 2;
}

/* ---------- the instrument skin: overrides of the shared skins ---------- */
/* The panel is the page below 1024 (no chrome; the wrap's gutter is its margin, which the sticky bar's negative
   margin assumes); from 1024 it is a bordered box with --r3 and --panel-pad. Never a shadow. The shared skin's
   --surface fill goes with the chrome: in light mode --surface is the page white, but in dark mode it is a shade
   lighter than --bg, and a square, unpadded fill from "1 Country" down through the result block's reserved room
   read as an empty frame with a hard bottom edge — and the sticky bar (--bg) as a darker strip inside it. */
.instrument .panel {
  padding: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
  margin-bottom: 24px;
}
.instrument .field {
  gap: 8px;
  margin-bottom: 16px;
}
/* Field labels: Barlow 600, sentence case, ink — never the mono eyebrow. */
.instrument .field > span {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--i-label);
  line-height: 1.2;
  text-transform: none;
  letter-spacing: 0;
  color: var(--ink);
}
.instrument .field > span.hint,
.instrument .field > span .hint {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: var(--i-small);
  text-transform: none;
  letter-spacing: 0;
  color: var(--muted);
}
.instrument .hint {
  font-size: var(--i-small);
  line-height: 1.45;
  color: var(--muted);
}
.instrument input[type="number"],
.instrument input[type="text"],
.instrument input[type="date"],
.instrument select {
  min-height: 52px;
  padding: 0 14px;
  border: 1.5px solid var(--i-edge);
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: 1.05rem;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  transition: border-color var(--i-fast);
}
.instrument input:hover,
.instrument select:hover,
.instrument input:focus,
.instrument select:focus {
  border-color: var(--ink);
}
.instrument select {
  padding-right: 40px;
}
/* Buttons inside the instrument: 48px, control edges; the green Book button; outline Back / Share / Copy. A button
   is a rectangle that does not move: colour only on hover, a darker fill at 0ms while pressed. */
.instrument .btn {
  min-height: var(--i-tap);
  font-size: 1rem;
  border-color: var(--i-edge);
  transition:
    background-color var(--i-fast),
    border-color var(--i-fast),
    color var(--i-fast);
}
.instrument .btn:hover {
  border-color: var(--ink);
}
.instrument .btn:active {
  transform: none;
  filter: brightness(0.92);
  transition: none;
}
.instrument .btn svg {
  width: 20px;
  height: 20px;
}
.instrument .btn.outline {
  background: transparent;
  color: var(--ink);
  border-color: var(--i-edge);
}
.instrument .btn.outline:hover {
  background: var(--paper-2);
  border-color: var(--ink);
}
.instrument .btn.giant {
  min-height: var(--i-tap-giant);
  padding: 0 24px;
  font-size: var(--i-button);
}
.instrument .btn.giant svg {
  width: 24px;
  height: 24px;
}
/* Book is green on customer pages: the one button that finishes the job. It exists only with somewhere to go. */
.instrument .btn.book {
  background: var(--wa);
  color: var(--wa-ink);
  border-color: var(--wa);
  transition: none;
}
.instrument .btn.book:hover {
  background: var(--wa-hover);
  border-color: var(--wa-hover);
}
.instrument .btn.book:active {
  filter: brightness(0.92);
}

/* ---------- step heads: "1 Country", "2 Weight" — a 24px text row, the choice at its right end ---------- */
.step-head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 24px;
  margin: 0 0 8px;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--i-label);
  line-height: 1.2;
  letter-spacing: 0;
  color: var(--ink);
}
.step-name {
  flex: none;
}
/* What has been chosen, in the step's own head: the flag and the name, or the kilograms. */
.step-done {
  margin-left: auto;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
/* The detailed view's panel titles: the same words, in the display face at h3 size, no numeral circle. */
.instrument h2.step-title {
  min-height: auto;
  margin: 0 0 14px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 600;
  font-size: var(--fs-h3);
  line-height: 1.15;
  letter-spacing: 0;
}

/* ---------- step 1: country tiles ----------
   A tile is the flag and the name on one line, the from-price under them across the tile's whole width: "from
   PKR 5,220", and "· 4–6 days" after it when the tile is wide enough for both (the days are on the rates board
   in any case). Fixed height, so the grid measures the same cold and live. Checked = ink fill, paper text. */
.ctiles {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
  margin: 0 0 var(--i-gap);
}
.ctile {
  appearance: none;
  min-width: 0;
  min-height: var(--i-tile);
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr);
  grid-template-rows: auto auto;
  align-content: center;
  align-items: center;
  gap: 2px 10px;
  padding: 8px 12px;
  background: var(--surface);
  border: 1.5px solid var(--i-edge);
  border-radius: var(--r3);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.2;
  color: var(--ink);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--i-fast),
    border-color var(--i-fast),
    color var(--i-fast);
}
.ctile .flag {
  grid-row: 1;
  grid-column: 1;
}
.ctile-name {
  grid-row: 1;
  grid-column: 2;
  min-width: 0;
  /* The name column is ~82px at 320 (two-up, beside the flag), where "Australia"/"Germany" sit on one line and
     "United Kingdom" wraps at its space. `anywhere` is kept only as the net for truly tiny widths, so a name can
     never force page overflow — the audit's cardinal mobile bug. It broke mid-letter ("Australi-a", "German-y",
     one orphaned letter) once usable width dipped under ~310 (a legacy non-overlay scrollbar, or a sub-320
     device). At every real phone width the override below keeps whole words instead. */
  overflow-wrap: anywhere;
  hyphens: auto;
}
/* 300 and up — every real phone, and the ~305 legacy-scrollbar edge the audit flagged — keep words whole rather
   than let `anywhere` break mid-letter: "Australia"/"Germany" stay on one line and multi-word names wrap at the
   space. A long word can spill a pixel or two past its ~82px box, which the tile's 12px side padding absorbs (no
   tile or page overflow, checked). Under 300 the `anywhere` net above takes back over, trading a tidy break for
   the guarantee of no overflow on a width no real device reports. */
@media (min-width: 300px) {
  .ctile-name {
    overflow-wrap: normal;
  }
}
.ctile-from {
  grid-row: 2;
  grid-column: 1 / -1;
  min-width: 0;
  font-weight: 500;
  font-size: var(--i-small);
  line-height: 1.2;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* The days join the from-line only where they physically fit: each tile is its own inline-size container, and the
   days show once its content box is 196px wide — "from PKR 5,850 · 7–10 days" measures 172.5px at 14.9px, so a
   five-digit price or two-digit days still fit and the line never ellipsizes. That is 3-up from about 720 wide
   and 2-up phones in landscape; a 4-up tile beside the hero (138px at 1366, 145px at 1920) keeps "from PKR 5,850"
   alone, and the days stand on the rates board next to it. The panel stays a container for the narrow-desktop
   3-up rule at the end of the widths section. */
.ctile-days {
  display: none;
}
.instrument .panel.quick {
  container-type: inline-size;
}
.ctile {
  container-type: inline-size;
}
@container (min-width: 196px) {
  .ctile-days {
    display: inline;
  }
}
.ctile:hover {
  border-color: var(--ink);
}
.ctile:active {
  background: var(--paper-2);
  transition: none;
}
.ctile[aria-checked="true"] {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}
.ctile[aria-checked="true"]:active {
  background: var(--ink);
}
.ctile[aria-checked="true"] .ctile-from {
  color: inherit;
  opacity: 0.8;
}
/* The "More countries" picker: the grid's last cell when the destinations outnumber it. It looks like a tile,
   opens the native list, and once a country is chosen there it reads that name and fills ink like a checked tile —
   so the grid keeps its two rows and nothing is added under it. */
.instrument select.ctile-more {
  min-width: 0;
  min-height: var(--i-tile);
  padding: 0 26px 0 12px; /* the chevron sits 8–22px from the right edge */
  border: 1.5px solid var(--i-edge);
  border-radius: var(--r3);
  background-color: var(--surface);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--i-small); /* "More countries" (98px) fits the narrowest cell: 141px at 1200, 143px on a 360px phone */
  line-height: 1.2;
  letter-spacing: 0;
  color: var(--ink);
  text-overflow: ellipsis;
  cursor: pointer;
  transition:
    background-color var(--i-fast),
    border-color var(--i-fast),
    color var(--i-fast);
}
.instrument select.ctile-more:hover {
  border-color: var(--ink);
}
.instrument select.ctile-more[data-chosen="true"] {
  background-color: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
  background-image: linear-gradient(45deg, transparent 50%, var(--bg) 50%), linear-gradient(135deg, var(--bg) 50%, transparent 50%);
}

/* ---------- step 2: weight buttons ---------- */
.kgs {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin: 0 0 var(--i-gap);
}
.kg {
  appearance: none;
  min-width: 0;
  min-height: var(--i-kgh);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  padding: 4px 6px;
  background: var(--surface);
  border: 1.5px solid var(--i-edge);
  border-radius: var(--r3);
  color: var(--ink);
  font-family: var(--font-sans);
  cursor: pointer;
  transition:
    background-color var(--i-fast),
    border-color var(--i-fast),
    color var(--i-fast);
}
.kg strong {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 700;
  font-size: var(--i-kg);
  line-height: 1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}
.kg span {
  font-size: var(--i-small);
  line-height: 1.2;
  color: var(--muted);
}
.kg:hover {
  border-color: var(--ink);
}
.kg:active {
  background: var(--paper-2);
  transition: none;
}
.kg[aria-pressed="true"] {
  background: var(--ink);
  color: var(--bg);
  border-color: var(--ink);
}
.kg[aria-pressed="true"]:active {
  background: var(--ink);
}
.kg[aria-pressed="true"] span {
  color: inherit;
}
/* The two wide buttons: "Over N kg" and "Documents · box size" (the way out to the detailed view) — side by
   side from 360 (half a row each: two of the four weight columns, then four of the eight from 560), so the
   phone's live result ends above the sticky bar; a full row each only on the narrowest phones. Words only, no
   glyph. The padding is 4px because "Documents · box size" is 149px and the half-row cell is 160px at 360. */
.kg.more,
.kg.other {
  grid-column: 1 / -1;
  min-height: var(--i-wide);
  flex-direction: row;
  gap: 8px;
  padding: 6px 4px;
  font-weight: 600;
  font-size: var(--i-label);
  line-height: 1.2;
  text-align: center;
}
@media (min-width: 360px) {
  .kg.more,
  .kg.other {
    grid-column: span 2;
  }
  .kgs:not(:has(.kg.more)) .kg.other {
    grid-column: 1 / -1;
  }
}

/* ---------- the line that waits: "Tap a country" / "Now tap a weight" ----------
   The result block's only child while it waits, at the block's TOP (margin-bottom auto): 12px under the weights,
   where the eye is after the last tap, in the step heads' rhythm — "1 Country", "2 Weight", "Now tap a weight".
   The reserved room is under the line, so it reads as page space; pushed to the block's end the line stood 120px
   (1366) / 146px (375) below the controls it named, behind a blank that read as a loading state, and below the
   fold of a 375×667 phone. Still at least as tall as the bar it stands in for, so the block can never be shorter
   waiting than live even if the reservation above were wrong; the words sit at its top. No box, no border. */
.wait {
  min-height: var(--i-bar);
  display: flex;
  align-items: flex-start;
  margin: 0 0 auto;
  padding: 0;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 1.1765rem; /* 20px */
  line-height: 1.2;
  color: var(--ink);
}

/* ---------- the result: the service rows, the two lines under them and the bar ----------
   Always rendered, always this tall: the rows (as many as the services need), 8px, the lines, 12px, the bar. So
   the block measures the same whether it holds the waiting line, the live result or the cargo bar, and the
   third tap moves nothing under the calculator. A column, with the bar pushed to its end (the bar's top margin
   is auto), so in the cargo and no-service states — no rows, no lines — the bar still sits exactly where it
   sits live; the waiting line, the other way round, is pinned to the block's start (see .wait). The row count
   comes from data-n (how many services the document has, capped at 4). */
.result {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  min-height: calc(var(--i-rows) * var(--i-opt) + (var(--i-rows) - 1) * 8px + 8px + var(--i-lines) + 12px + var(--i-bar));
  margin: 12px 0 0;
}
.result[data-n="1"] {
  --i-rows: 1;
}
.result[data-n="3"] {
  --i-rows: 3;
}
.result[data-n="4"] {
  --i-rows: 4;
}
@media (min-width: 360px) {
  .result {
    --i-rows: 1;
  }
  .result[data-n="3"],
  .result[data-n="4"] {
    --i-rows: 2;
  }
}
.opts {
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}
/* The pair the visitor compares is level: fixed-height rows, side by side from 360 so both fit above the sticky
   bar on a phone (stacked only on the narrowest phones). */
.opts > div {
  display: grid;
  gap: 8px;
}
@media (min-width: 360px) {
  .opts > div {
    grid-template-columns: 1fr 1fr;
  }
}
/* A service row. On a phone the cell is 160–168px wide, so the name, the days and the price stack beside the
   drawn radio (17px + 15px + 24px lines, 2px gaps, 2px padding and the 1.5px borders: 69px in a 70px row); from 560 the name and the price share the first line
   and the days sit under the name. Only priced services render, so a row is never dim and never empty. Checked:
   ink border and the 3px orange bar. */
.opt {
  position: relative;
  min-height: var(--i-opt);
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  grid-template-areas:
    "radio name"
    "radio eta"
    "radio price";
  gap: 2px 8px;
  align-items: center;
  align-content: center;
  padding: 2px 8px;
  border: 1.5px solid var(--i-edge);
  border-radius: var(--r3);
  background: var(--surface);
  cursor: pointer;
  transition:
    border-color var(--i-fast),
    box-shadow var(--i-fast);
}
.opt:hover {
  border-color: var(--ink);
}
.opt:active {
  background: var(--paper-2);
  transition: none;
}
.opt input {
  grid-area: radio;
  justify-self: center;
  align-self: center;
  appearance: none;
  -webkit-appearance: none;
  width: 24px;
  height: 24px;
  margin: 0;
  border: 2px solid var(--i-edge);
  border-radius: 50%;
  background: var(--surface);
  cursor: inherit;
  flex: none;
  transition:
    border-color var(--i-fast),
    background-color var(--i-fast);
}
.opt input:checked {
  border-color: var(--hot-a);
  background: radial-gradient(circle, var(--hot-a) 0 5.5px, transparent 6.5px) var(--surface);
}
.opt-name {
  grid-area: name;
  min-width: 0;
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.1;
}
.opt-eta {
  grid-area: eta;
  min-width: 0;
  color: var(--muted);
  font-size: var(--i-small);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.opt-eta:empty {
  display: none;
}
.opt-price {
  grid-area: price;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 700;
  font-size: 1.41rem; /* 24px */
  line-height: 1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  white-space: nowrap;
  text-align: left;
}
.opt:has(input:checked) {
  border-color: var(--ink);
  box-shadow: inset 3px 0 0 var(--hot-a);
}
/* Under the rows: when the parcel arrives, and what the figure includes. Two 16px lines (1.3 keeps two inside the
   44px tiers); fixed height, and the 12px under them is the bar's gap, so the bar never moves when a service or
   a weight changes. */
.lines {
  min-height: var(--i-lines);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  margin: 8px 0 12px;
  font-size: var(--i-label);
  line-height: 1.3;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.lines > span {
  display: block;
  min-width: 0;
}

/* ---------- the quick view's "What is inside?" line (owner, 2026-10-05) ----------
   Optional, and in the page only once there is a price: it sits between the lines and the price bar, the one
   block that grows the live result past its reserved room. Below 1024 the price bar is fixed to the screen, so
   nothing it covers moves; the only thing under the growing result is the panel's own end. */
.result .contents {
  margin: 4px 0 12px;
}
.result .contents > span {
  margin-bottom: 2px;
}

/* ---------- the price bar: the figure and the one green button ----------
   Below 1024 it is PINNED to the bottom of the screen; from 1024 it sits under the rows. Its top margin is auto
   (from 1024, where it is in flow): it is pushed to the block's end, so it sits in the same place whether or not
   rows are above it. The figure is printed at once and left-aligned, so a changing width only moves its own right
   edge. No shadow: the 1px hairline is its whole edge — a rule, like every other separation on the page, never a
   floating card.

   WHY PINNED AND NOT STUCK. It used to be `position: sticky; bottom: 0` inside the result block, which pins it
   to the screen only while THAT block overlaps the viewport. The block is the last thing in the panel, so with a
   long destination list it starts below the fold and the bar went with it: at 375x812 with ten destinations the
   tiles take four rows, the result block begins at y=792, and after tapping a country and a weight the price
   landed at 804–832 and the green button at 801–857 — both under the edge of the screen, with nothing on it to
   say a price had been worked out. Scrolling the page to the answer was tried and the owner rejected it, so the
   answer comes to the visitor instead: below 1024 the bar is fixed to the bottom edge from the moment it exists
   (it does not exist before the second tap — the waiting line stands in its place). The result block still
   RESERVES its height (see .result's min-height), so taking the bar out of flow moves nothing, and the bar comes
   to rest over that reserved slot when the calculator's end is on screen — which is where it used to sit.

   The price bar is 73px of the screen it is on. The only place that could hide for good is the last 73px of the
   document, so the footer gets that much room back below it — site.css owns .site-foot, so that rule lives
   there, keyed on :has(.pricebar). */
.pricebar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  min-height: var(--i-bar);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 12px;
  align-items: center;
  margin: 0;
  padding: 8px var(--gutter) calc(8px + env(safe-area-inset-bottom, 0px));
  background: var(--bg);
  border-top: 1px solid var(--line);
}
.pricebar-sum {
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
}
.tval {
  display: block;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 700;
  font-size: 1.65rem; /* 28px */
  line-height: 1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  white-space: nowrap;
}
.tnote {
  display: block;
  min-width: 0;
  font-size: var(--i-small);
  line-height: 1.3;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* "· Canada" after "Express · 5 kg": the country is in the step head, and the phone bar keeps ~125px beside the
   green button, so it joins the note only from 560. */
.tnote-to {
  display: none;
}
/* Cargo or no service: the bar has a sentence and the button, no figure. The same two columns as live (rule 4:
   the bar is ONE height in every state, and the green button stands where it stands live, at the same width its
   label needs — stacking the sentence over a full-width button made the bar 106px against 73 and the button
   343px against 206). The sentence is the line that waits, in ink, wrapping beside the button: the button is
   ~200px wide with "Ask for a cargo rate", so a 360–375 phone keeps 110–130px for it, and "Over 25 kg? We price
   it on WhatsApp" takes three lines there — 14.9px (the live bar's second line's size) at 1.2 is 53.5px, inside
   the 56px row. 16px from 560, where the column is ≥ 300px and two lines fit; 20px from 1024 (below). Only under
   360 (a 320px phone would keep 67px) does the button take the row below. */
.pricebar:not([data-state="live"]) {
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 360px) {
  .pricebar:not([data-state="live"]) {
    grid-template-columns: minmax(0, 1fr) auto;
  }
}
.pricebar:not([data-state="live"]) .tnote {
  font-weight: 600;
  font-size: var(--i-small);
  line-height: 1.2;
  color: var(--ink);
  white-space: normal;
  overflow: visible;
}
/* The phone button: 56px tall, 17px words, so a 343px bar keeps ≥ 136px for the figure beside it. */
.instrument .pricebar .btn.book {
  min-height: var(--i-tap-big);
  min-width: 168px;
  padding: 0 16px;
  border-radius: var(--r3);
  font-size: 1rem;
}
.instrument .pricebar .btn.book svg {
  width: 22px;
  height: 22px;
}
/* The narrowest phones (< 360): the LIVE price bar stacks too, the way the cargo bar already does. In two
   columns the figure is left a ~70px slot beside the ~206px green button, so "PKR 8,070" (105px) overflows and
   its tail is painted BEHIND the button, and the note ellipsizes to "Express ·…". Shrinking will not save it —
   "Book on WhatsApp" needs ~142px and a 5-/6-digit price ~105–127px, past the ~276px a 320px bar has on one
   row — so the summary takes the full width on top and the button drops to a full-width row under it, where the
   price is whole and the note fits. --i-bar grows to the stacked height so the result block and the footer
   reserve exactly what the bar occupies and it still comes to rest over empty space (rule 4: one height per
   state within this width — the cargo bar is 103px here, the live bar 129px, both inside the reservation). */
@media (max-width: 359.98px) {
  .instrument {
    /* 56px button + above it: 8 pad + 28 figure + 2 + 19 note + 8 row-gap + 8 pad = 73 */
    --i-bar: calc(var(--i-tap-big) + 73px + env(safe-area-inset-bottom, 0px));
  }
  .pricebar[data-state="live"] {
    grid-template-columns: minmax(0, 1fr);
    gap: 8px;
  }
  .instrument .pricebar[data-state="live"] .btn.book {
    width: 100%;
  }
}

/* ---------- detailed view: back, type, units, pieces ---------- */
.instrument .btn.back {
  margin: 0 0 16px;
}
.static {
  min-height: 52px;
  display: flex;
  align-items: center;
  padding: 0 2px;
  font-weight: 500;
  font-size: 1.05rem;
}
.seg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  width: 100%;
  max-width: 420px;
  padding: 4px;
  margin-bottom: 10px;
  background: var(--surface);
  border: 1.5px solid var(--i-edge);
  border-radius: var(--r3);
}
.segbtn {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--muted);
  border-radius: var(--r2);
  padding: 0 16px;
  min-height: var(--i-tap);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 1.05rem;
  line-height: 1.2;
  cursor: pointer;
  transition:
    background-color var(--i-fast),
    color var(--i-fast);
}
.segbtn:hover {
  color: var(--ink);
}
.segbtn:active {
  background: var(--paper-2);
  transition: none;
}
.segbtn[aria-checked="true"] {
  background: var(--ink);
  color: var(--bg);
}
.units {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 10px 0 14px;
}
.units .hint {
  margin-right: 4px;
}
.ubtn {
  appearance: none;
  min-height: var(--i-tap);
  padding: 0 18px;
  border: 1.5px solid var(--i-edge);
  border-radius: var(--r3);
  background: var(--surface);
  color: var(--muted);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--i-label);
  cursor: pointer;
  transition:
    background-color var(--i-fast),
    border-color var(--i-fast),
    color var(--i-fast);
}
.ubtn:hover {
  border-color: var(--ink);
  color: var(--ink);
}
.ubtn:active {
  background: var(--paper-2);
  transition: none;
}
.ubtn[aria-pressed="true"] {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--bg);
}
.pieces.docs .dims-field {
  display: none;
}
.pieces.docs .row.three {
  grid-template-columns: 1fr 1fr;
}
.dims {
  display: grid;
  grid-template-columns: 1fr auto 1fr auto 1fr;
  gap: 4px;
  align-items: center;
}
.instrument .dims input {
  min-width: 0;
  padding: 0 8px;
  font-size: 1rem;
}
.dims .x {
  color: var(--muted);
  font-size: 1rem;
  text-align: center;
}
.piece + .piece {
  border-top: 1px solid var(--line);
  padding-top: 16px;
  margin-top: 4px;
}
.piece .ptitle {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}
.piece .ptitle .pnum {
  font-weight: 600;
  font-size: var(--i-label);
  color: var(--ink);
}
.instrument .btn.p-remove {
  padding: 0 14px;
  font-size: var(--i-small);
}
.addrow {
  margin: -2px 0 16px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px 14px;
  align-items: center;
}
.addrow .hint {
  flex: 1 1 240px;
}
.instrument .field.date {
  max-width: 260px;
  margin-top: 6px;
}

/* ---------- the add-on row (the detailed view only: the quick view names what is included under its rows) ---------- */
.addons {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.addons label {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: var(--i-tap);
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--i-label);
  line-height: 1.3;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  cursor: pointer;
}
.addons input {
  appearance: none;
  -webkit-appearance: none;
  width: 24px;
  height: 24px;
  margin: 0;
  flex: none;
  border: 2px solid var(--i-edge);
  border-radius: var(--r1);
  background: var(--surface);
  display: grid;
  place-content: center;
  cursor: inherit;
  transition:
    border-color var(--i-fast),
    background-color var(--i-fast);
}
.addons input::before {
  content: "";
  width: 6px;
  height: 12px;
  margin-top: -2px;
  border: solid var(--book-ink);
  border-width: 0 2.5px 2.5px 0;
  transform: rotate(45deg) scale(0);
}
.addons input:checked {
  background: var(--hot-a);
  border-color: var(--hot-a);
}
.addons input:checked::before {
  transform: rotate(45deg) scale(1);
}

/* ---------- results: the service rows, the add-on, the status sentence ---------- */
.results {
  margin: 0 0 24px;
}
.results h2.step-title {
  margin-bottom: 12px;
}
.svcs {
  display: grid;
  gap: 8px;
}
.svc {
  appearance: none;
  width: 100%;
  min-height: var(--i-opt);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 2px 12px;
  align-items: center;
  padding: 8px 14px;
  border: 1.5px solid var(--i-edge);
  border-radius: var(--r3);
  background: var(--surface);
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition:
    border-color var(--i-fast),
    box-shadow var(--i-fast);
}
.svc:hover:not(:disabled) {
  border-color: var(--ink);
}
.svc:active:not(:disabled) {
  background: var(--paper-2);
  transition: none;
}
.svc:disabled {
  border-color: var(--line);
  cursor: default;
}
.svc:disabled .svc-name,
.svc:disabled .svc-eta {
  color: var(--muted);
}
.svc[aria-pressed="true"] {
  border-color: var(--ink);
  box-shadow: inset 3px 0 0 var(--hot-a);
}
.svc-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.svc-name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.25;
}
/* "Choose" / "Chosen": one plain word after the name, never a pill. */
.svc-cta {
  color: var(--muted);
  font-weight: 500;
  font-size: var(--i-small);
  line-height: 1.3;
}
.svc[aria-pressed="true"] .svc-cta {
  color: var(--ink);
  font-weight: 600;
}
.svc-eta {
  color: var(--muted);
  font-size: var(--i-small);
  line-height: 1.35;
}
.svc-eta strong {
  color: var(--ink);
  font-weight: 600;
}
.svc-price {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 700;
  font-size: 1.41rem;
  line-height: 1;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: right;
}
.svc-price:empty {
  display: none;
}
/* The status sentence: plain text, no box; a warning reads in the warning colour. */
.status {
  margin-top: 12px;
  color: var(--ink);
  font-size: 1rem;
  line-height: 1.5;
  max-width: 66ch;
}
.status.warn {
  color: var(--warn);
}
.status a {
  color: inherit;
  font-weight: 600;
}
.addons-detail {
  margin-top: 12px;
  padding-top: 8px;
  border-top: 1px solid var(--line);
}
.addons-detail .addons {
  margin: 0;
}

/* ---------- the quote card ---------- */
.quote {
  position: relative;
  margin: 0 0 24px;
}
.quote h2 {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  margin: 0;
  font-size: var(--fs-h3);
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: 0;
}
/* "Document rate": a plain muted word after the title, never a pill. */
.quote h2 .tag {
  margin: 0;
  padding: 0;
  background: none;
  border-radius: 0;
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--i-label);
  line-height: 1.3;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
}
.quote dl {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0 16px;
  margin: 16px 0;
  border-bottom: 1px solid var(--line);
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
}
.quote dt,
.quote dd {
  margin: 0;
  padding: 10px 0;
  border-top: 1px solid var(--line);
  line-height: 1.35;
}
.quote dt {
  color: var(--muted);
}
.quote dd {
  color: var(--ink);
}
.quote dd .hint {
  font-size: var(--i-small);
}
.quote dd.display {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 700;
  font-size: var(--fs-h2);
  line-height: 1;
  letter-spacing: -0.01em;
  padding: 10px 0 12px;
}
@supports selector(:has(a)) {
  .quote dt:has(+ dd.display) {
    align-self: center;
    color: var(--ink);
    font-weight: 600;
  }
}
.qid {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* "How this price is calculated": a disclosure row (48px, a chevron at the right end) with the working inside;
   the one accordion in the calculator, and it lives on the detailed view only. */
details.how {
  margin: 0 0 16px;
  border-top: 1px solid var(--line);
  padding-top: 6px;
}
details.how summary {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: var(--i-tap);
  padding: 4px 0;
  list-style: none;
  cursor: pointer;
  font-weight: 600;
  font-size: var(--i-label);
  line-height: 1.4;
  color: var(--ink);
}
details.how summary::-webkit-details-marker {
  display: none;
}
details.how summary::after {
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  margin-left: auto;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
details.how[open] summary::after {
  transform: translateY(2px) rotate(-135deg);
}
details.how summary:hover {
  color: var(--orange-ink);
}
details.how ul {
  margin: 4px 0 8px;
  padding-left: 18px;
  list-style: disc;
  max-width: 66ch;
  color: var(--muted);
  font-size: var(--i-small);
  line-height: 1.5;
}
details.how li {
  margin: 2px 0;
}
.quote details.how {
  border-top: 0;
  padding-top: 0;
}
.quote .field {
  margin: 4px 0 16px;
}
.quote .actions {
  display: grid;
  gap: 10px;
}
.quote .actions .btn.book {
  width: 100%;
}
.actions-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.actions-2 > .btn:only-child {
  grid-column: 1 / -1;
}

/* ---------- the fine print at the end of the detailed view: what is included, the notes, the disclaimer ---------- */
.includes,
.notes,
.disclaimer {
  margin: 0 0 8px;
  max-width: 66ch;
  color: var(--muted);
  font-size: var(--i-small);
  line-height: 1.5;
}
.includes {
  margin-top: 16px;
}
.notes {
  padding-left: 18px;
  list-style: disc;
}
.notes li {
  margin: 2px 0;
}
/* "Sample prices for now." — one muted line, no box (SampleNotice.tsx: under the home calculator and under the
   /services board). Sized on the root token, not --i-label: it also prints outside .instrument. */
.sample {
  margin: 0 0 12px;
  font-size: var(--fs-label);
  line-height: 1.45;
  color: var(--muted);
}

/* ---------- widths ----------
   375: tiles 2-up (the picker cell included), weights 4-up + the two wide buttons side by side, service rows side
   by side with the name / days / price stacked. From 560: tiles 3-up (the days join the from-line once a tile is
   196px wide inside, see .ctile-days), weights 8 + 2, two-line service rows, the country back in the bar's note.
   From 1024: the panel is a bordered box, the bar is static under the rows, tiles 4-up. */
@media (min-width: 560px) {
  .instrument {
    --i-opt: 64px;
    --i-lines: 48px;
  }
  .ctiles {
    grid-template-columns: repeat(3, 1fr);
  }
  .kgs {
    grid-template-columns: repeat(8, 1fr);
  }
  .kg.more,
  .kg.other {
    grid-column: span 4;
    padding: 6px 10px;
  }
  .kgs:not(:has(.kg.more)) .kg.other {
    grid-column: 1 / -1;
  }
  .svcs {
    grid-template-columns: 1fr 1fr;
  }
  .opt {
    grid-template-columns: 24px minmax(0, 1fr) auto;
    grid-template-areas:
      "radio name price"
      "radio eta price";
    gap: 2px 12px;
    padding: 8px 14px;
  }
  .opt-name,
  .opt-eta {
    line-height: 1.2;
  }
  .opt-price {
    text-align: right;
  }
  .tnote-to {
    display: inline;
  }
  .pricebar:not([data-state="live"]) .tnote {
    font-size: var(--i-label);
    line-height: 1.3;
  }
  .quote .actions {
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
  }
  .quote .actions .btn.book {
    width: auto;
    min-width: 260px;
  }
}
/* Below 1024 the detailed view's sections are separated by a hairline and whitespace, never a box. */
@media (max-width: 1023px) {
  .instrument .panel + .panel,
  .results,
  .quote {
    padding-top: 20px;
    border-top: 1px solid var(--line);
  }
}
@media (min-width: 1024px) {
  .instrument .panel,
  .results,
  .quote {
    padding: var(--panel-pad);
    border: 1px solid var(--line);
    border-radius: var(--r3);
    margin-bottom: 16px;
  }
  /* The bordered box gets the shared skin's fill back (transparent below 1024, where the panel is the page). */
  .instrument .panel {
    background: var(--surface);
  }
  .instrument .panel > :last-child,
  .results > :last-child,
  .quote > :last-child {
    margin-bottom: 0;
  }
  .instrument {
    --i-bar: 88px;
  }
  .ctiles {
    grid-template-columns: repeat(4, 1fr);
  }
  /* The bar sits under the rows: static, unboxed, the figure at its full size, the button 64px and 260px wide.
     A fixed height, so it never changes between states. */
  .pricebar {
    position: static;
    min-height: var(--i-bar);
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 16px;
    margin: auto 0 0;
    padding: 0;
    border: 0;
    background: transparent;
  }
  .pricebar:not([data-state="live"]) .tnote {
    font-size: 1.1765rem; /* 20px, the line that waits */
  }
  /* The 72px figure at 0.9 plus the 21px note is 86px: no gap, so the pair stays inside the 88px bar and the bar
     never grows by the fraction that would move the panel's end. */
  .pricebar-sum {
    gap: 0;
  }
  .tval {
    font-size: var(--i-price);
    line-height: 0.9;
    letter-spacing: -0.01em;
  }
  .tnote {
    font-size: var(--i-label);
  }
  .instrument .pricebar .btn.book {
    min-height: var(--i-book);
    min-width: 260px;
    padding: 0 28px;
    font-size: var(--i-button);
  }
  .instrument .pricebar .btn.book svg {
    width: 24px;
    height: 24px;
  }
  /* Detailed quote: weight | quantity | L × W × H. The quantity holds one or two digits, so it gives its width
     to the weight ("e.g. 2.5" plus Chrome's spinner) and the three dimension boxes. */
  .instrument .row.three {
    grid-template-columns: 1.2fr 0.8fr 2fr;
  }
  .instrument .dims input {
    padding: 0 6px;
  }
}
/* The 1366×768 density tier: beside the hero, the green button must sit inside the first screen once a weight is
   chosen — with eight country cells (two rows). Budget: 64 header + 24 + 16 panel + (24 + 8 + 120 + 12) tiles +
   (24 + 8 + 100 + 12) weights + (56 + 8 + 44) rows and lines + 12 + 80 bar + 16 = 688. Only the heights of the
   controls and the figure shrink; nothing under 44px. */
@media (min-width: 1024px) and (max-height: 820px) {
  .instrument {
    --i-price: 3.4rem; /* 58px */
    --i-tile: 56px;
    --i-kgh: 48px;
    --i-wide: 44px;
    --i-opt: 56px;
    --i-lines: 44px;
    --i-bar: 80px;
    --i-book: 60px;
  }
  .instrument .panel.quick {
    padding: 16px;
  }
  .ctile {
    padding: 4px 12px;
  }
  .kg {
    padding: 2px 4px;
  }
  .kg span {
    line-height: 1.1;
  }
  .opt {
    padding: 4px 14px;
  }
}
/* 4-up needs a 141px tile ("Australia" beside a 32px flag, "from PKR 5,850" under them, whole): 588px of panel.
   Narrower — 1024 to about 1200 wide, iPads in landscape, where the panel is 509–585px — the tiles go 3-up, the
   same 137px tile as at 1366, instead of breaking a country's name mid-word. Eight cells make three rows there
   (+64px on the density tier's 688px budget: the green button still ends inside 768). The panel is the query
   container (a tile cannot query its own width); after the 1024 block, whose 4-up this narrows. */
@media (min-width: 1024px) {
  @container (max-width: 592px) {
    .ctiles {
      grid-template-columns: repeat(3, 1fr);
    }
  }
}

/* ---------- motion: the price bar arrives once ----------
   The only animation in the instrument (v3 §5): the bar fades in over 120ms on its first mount and, below 1024,
   rises from the bottom edge. Nothing else moves; reduced motion gets nothing at all. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes bar-in {
    from {
      opacity: 0;
      transform: translateY(100%);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }
  @keyframes bar-fade {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
  .pricebar {
    animation: bar-in 120ms var(--ease-out) backwards;
  }
  @media (min-width: 1024px) {
    .pricebar {
      animation: bar-fade 120ms var(--ease-out) backwards;
    }
  }
}
