/* ---------- Modals (side drawers) ---------- */
.modal-scrim {
  position: fixed;
  inset: 0;
  background: rgba(22, 19, 16, 0.5);
  z-index: 95;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
.modal-scrim.is-visible { opacity: 1; pointer-events: auto; }

.modal {
  position: fixed;
  top: 0;
  right: 0;
  height: 100dvh;
  width: min(420px, 100vw);
  background: var(--bg);
  color: var(--ink);
  z-index: 100;
  /* No top padding here — it moved onto .modal-head itself. A sticky child's
     "top: 0" offset resolves against the padding edge of its scrolling
     ancestor, so padding-top on .modal would leave that gap permanently
     unfilled above the stuck header (confirmed in Chrome: scrolled content
     was visible poking through it) rather than scrolling away like ordinary
     padding does. Moving the same amount onto .modal-head's own padding-top
     keeps the resting look identical while putting it inside the header's
     own background. */
  padding: 0 clamp(1.25rem, 4vw, 2rem) 2rem;
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform 0.3s ease;
}
/* box-shadow lives here, not on the base .modal rule: closed, the drawer sits
   flush against the viewport's right edge (translateX(100%) shifts it by its
   OWN width, landing its left edge exactly on the viewport boundary) — a
   shadow on the base rule would still blur back into the visible page the
   full height of the drawer, on every page, all the time, even fully closed.
   Scoping it to .is-open means it only exists while the drawer is actually
   on-screen to cast it. */
.modal.is-open { transform: translateX(0); box-shadow: -8px 0 30px rgba(0,0,0,0.12); }

/* Sticky to the top of .modal itself — .modal is the scrolling element
   (overflow-y: auto), not a nested "body" div, so this sticks directly
   against it with no wrapper needed. Own background so content scrolling
   underneath doesn't show through, and a z-index above ordinary form content
   (the HS-code combobox's popup list is the highest of those, at z-index: 5,
   and shares this same local stacking context since nothing between it and
   .modal establishes a new one). */
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 1.75rem;
  margin-bottom: 1.5rem;
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg);
}
.modal-head h2 { font-size: var(--h3); margin: 0; }
.modal-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  margin-right: -0.5rem;
  background: none;
  border: none;
  font-size: 1.75rem;
  line-height: 1;
  color: var(--ink);
  padding: 0;
}

/* ---------- Auth modal (login / register / forgot) ----------
   Absorbed from the deleted assets/css/auth.css when auth moved from a
   standalone page back into this drawer. Only the page-level rules were
   dropped (.auth-page/.auth-card/.auth-header/.auth-head) — the notice,
   divider and Google button below are unchanged, so the surface looks the
   same as the page did. Form primitives (.field, .field-error, .btn,
   .field-honeypot) are reused from this file verbatim, as before. */
/* --muted at body size fails WCAG AA 4.5:1 (docs/palette.md's own contrast note
   reserves --muted for labels/captions, not instructional sentences like this). */
.modal-context { font-size: var(--body); color: var(--ink); margin: 0 0 1.5rem; }
.auth-forgot {
  align-self: flex-start;
  font-size: var(--small);
  color: var(--muted);
  margin-top: -0.4rem;
}
.auth-forgot:hover { color: var(--terracotta); }
.auth-switch { font-size: var(--small); color: var(--muted); text-align: center; margin: 0.25rem 0 0; }
/* Panel switches are <button class="btn btn-link"> now, not <a> — a button is
   the honest element for something that swaps a panel rather than navigating,
   and it stops a dead href="#" existing for JS to have to cancel. */
.auth-switch a,
.auth-switch .btn-link { color: var(--terracotta); font-weight: 500; }
.auth-switch a:hover,
.auth-switch .btn-link:hover { text-decoration: underline; }

/* The Tools gate panel (asta_tool_gate() in functions.php) — its two stacked
   buttons, same layout as .modal-result-actions below. */
.auth-modal-actions { display: flex; flex-direction: column; gap: 0.75rem; margin-bottom: 1rem; }

.auth-notice {
  font-size: var(--small);
  border-radius: var(--radius);
  padding: 0.75em 1em;
  margin: 0 0 1.25rem;
  color: var(--terracotta);
  border: 1px solid var(--terracotta);
}
.auth-notice--success {
  color: #2f6b3f;
  border-color: #2f6b3f;
  background: color-mix(in srgb, #2f6b3f 8%, transparent);
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.25rem 0;
  color: var(--muted);
  font-size: var(--small);
}
.auth-divider::before,
.auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: color-mix(in srgb, var(--ink) 12%, transparent);
}

/* Self-hosted Google mark (assets/img/google-g.svg) per Google's branding
   requirements, and to keep the no-external-origins rule. */
.btn-google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  width: 100%;
  background: #fff;
  color: var(--ink);
  border: 1px solid color-mix(in srgb, var(--ink) 20%, transparent);
  border-radius: var(--radius);
  padding: 0.75em 1.2em;
  font-size: var(--btn-size);
  font-family: var(--font-body);
  font-weight: 500;
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.btn-google:hover { background: color-mix(in srgb, var(--ink) 4%, transparent); }
.btn-google img { display: block; width: 1.15em; height: 1.15em; }

.modal-flag {
  font-size: var(--small);
  font-weight: 500;
  color: var(--terracotta);
  border: 1px solid var(--terracotta);
  border-radius: var(--radius);
  padding: 0.75em 1em;
  margin-bottom: 1.5rem;
}


/* ---------- Collapsible disclosure ----------
   One pattern, used twice in the Duty drawer: the red notice at the top and
   the "Freight Estimate (Per KG)" rate card. chrome.js's initDisclosures()
   drives both; see its comment for the ARIA and Escape handling.

   The open/closed state lives as .is-open on the ROOT rather than on the
   panel, because one rule has to reach both the panel's row track and the
   caret, and CSS cannot select upwards from the panel to the caret's sibling. */
.asta-disclosure-toggle {
  display: flex;
  align-items: center;
  /* Marker first and hard left, matching the <details>/<summary> "Rate
     breakdown" panel in the result view — that is the disclosure idiom this
     drawer already had, and two different ones on one screen read as two
     different kinds of control. */
  justify-content: flex-start;
  gap: 0.5em;
  width: 100%;
  padding: 0;
  background: none;
  border: none;
  font-family: inherit;
  font-size: inherit;
  font-weight: 600;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
/* The disclosure triangle.

   Drawn from borders rather than set as a Phosphor glyph or a ::marker,
   because it has to match the native <summary> marker on .result-breakdown
   and neither of those does: the icon font would be a chevron at whatever
   weight Phosphor draws, and ::marker is only available on list-item
   display, which a <button> is not. A border triangle in currentColor at
   ~0.4em is what the UA marker actually looks like.

   Points right when shut, down when open — the same 90deg turn the native
   one makes, rather than the 180deg flip a chevron wants. */
.asta-disclosure-marker {
  flex: none;
  width: 0;
  height: 0;
  border-left: 0.42em solid currentColor;
  border-top: 0.3em solid transparent;
  border-bottom: 0.3em solid transparent;
  /* The triangle's optical centre sits left of its box centre, so rotating
     about the default 50% 50% makes it wobble. Pulling the origin left keeps
     the tip on one spot through the turn. */
  transform-origin: 30% 50%;
  transition: transform 0.25s ease;
}
.asta-disclosure.is-open .asta-disclosure-marker { transform: rotate(90deg); }

/* The height animation, with no JavaScript measuring anything.

   grid-template-rows 0fr -> 1fr animates against the content's OWN height,
   which is the whole reason for the nested .asta-disclosure-inner: a grid item
   has an automatic minimum size, so without `min-height: 0` on the child the
   track refuses to shrink below the content and the panel never closes.
   `overflow: hidden` is what clips it on the way.

   The alternative — transitioning max-height to a guess — either clips long
   copy or eases from a number far larger than the real height, which reads as
   a pause before anything visibly moves. */
.asta-disclosure-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.28s ease;
}
.asta-disclosure.is-open .asta-disclosure-panel { grid-template-rows: 1fr; }
.asta-disclosure-inner {
  min-height: 0;
  overflow: hidden;
}
/* Space that belongs to the OPEN state only. Padding on .asta-disclosure-inner
   itself would be part of the collapsed box too, leaving a sliver of gap under
   the toggle when shut. */
.asta-disclosure.is-open .asta-disclosure-inner { padding-top: 0.6em; }

@media (prefers-reduced-motion: reduce) {
  .asta-disclosure-panel,
  .asta-disclosure-marker { transition: none; }
}

/* ---------- Freight rate card ----------
   Collapsed by default. Sits between the weight field and the derived
   freight/insurance row, so the rates are one click away from the field that
   uses them without occupying the form when nobody has asked. */
.asta-rate-card {
  border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  border-radius: var(--radius);
  padding: 0.7em 0.9em;
}
.asta-rate-card .asta-disclosure-toggle { font-size: var(--small); }
.asta-rate-card-list > div {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.4em;
  border-top: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
  font-size: var(--small);
}
.asta-rate-card-list > div:first-child { border-top: none; }
.asta-rate-card-list dt { color: var(--muted); }
.asta-rate-card-list dd { margin: 0; font-weight: 500; white-space: nowrap; }

/* The band the entered weight actually falls into. Marked by chrome.js on
   every keystroke, so the panel answers "which rate am I on" rather than
   leaving the reader to work it out against the boundaries. */
.asta-rate-card-list > div.is-active dt,
.asta-rate-card-list > div.is-active dd {
  color: var(--terracotta);
  font-weight: 600;
}

/* ---------- Derived (readonly) fields ----------
   Freight and insurance are computed, not entered. They have to read as
   "worked out for you" rather than as "broken" or "disabled", so: a tinted
   fill and a softened border to say not-editable, but full-strength text,
   because the number is the point. A greyed value would look unavailable.

   Specificity, not !important: `.field input.asta-derived-field` is (0,2,1)
   against `.field input` at (0,1,1). */
.field input.asta-derived-field {
  background: color-mix(in srgb, var(--ink) 4%, #fff);
  border-color: color-mix(in srgb, var(--ink) 10%, transparent);
  color: var(--ink);
  font-weight: 500;
  cursor: default;
}
/* Still focusable (readonly, not disabled — see parts/modals.php), so the
   focus ring has to survive; it is just calmer than an editable field's. */
.field input.asta-derived-field:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--ink) 25%, transparent);
  box-shadow: none;
}
.field input.asta-derived-field + .field-hint { color: var(--muted); }

.modal-form { display: flex; flex-direction: column; gap: 1.1rem; }
/* Pinned to the bottom of .modal (the same scrolling element .modal-head
   sticks to). On a short form this does nothing — its natural position is
   already above the sticky threshold, so it just sits in flow directly under
   the last field, per .modal-form's own `gap`. On a form tall enough to
   scroll, its natural position starts below the visible drawer, which is
   already past the "stick" threshold, so it's pinned to the bottom from the
   first frame and stays there until its own trailing space (.modal's
   bottom padding) scrolls into view at the very end. No JS overflow check
   needed either way — verified at both content heights in Chrome. */
.modal-form-footer {
  position: sticky;
  bottom: 0;
  z-index: 10;
  background: var(--bg);
  padding-top: 0.5rem;
  padding-bottom: 0.25rem;
}
.field { display: flex; flex-direction: column; gap: 0.4rem; }
/* min-width: 0 on the grid items - without it, a grid track can't shrink below its
   content's intrinsic width (the default `min-width: auto`), so a wide-enough input
   pushes its column past the modal's edge instead of actually filling 1fr. */
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.field-row > .field { min-width: 0; }
.field label { font-size: var(--small); font-weight: 500; }
.field input, .field textarea, .field select {
  font-family: inherit;
  font-size: var(--body);
  color: var(--ink);
  background: #fff;
  border: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
  border-radius: var(--radius);
  padding: 0.7em 0.85em;
}
/* Custom arrow, drawn ourselves rather than left to the native control: a
   native select's own arrow sits flush against the border with no
   configurable margin, and a browser-drawn one can't be pushed in from the
   edge or accounted for in padding-right. appearance: none removes it so
   this chevron (--muted, matching .field-hint) and the padding below are the
   only things reserving that space — 2.5rem of padding-right, wider than the
   1rem gap + icon, so long option text (e.g. "China — overland via the Tibet
   border") never runs under it. */
.field select {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: 2.5rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23797166' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  background-size: 0.7rem auto;
}
.field input:focus, .field textarea:focus, .field select:focus {
  outline: 2px solid var(--terracotta);
  outline-offset: 1px;
  border-color: var(--terracotta);
}
.field-error { font-size: var(--small); color: var(--terracotta); margin: 0; }
/* Collapses to nothing when there's no message — errorEl.textContent is always
   set to '' to clear it (validateRequiredFields(), chrome.js), never left with
   whitespace, so :empty tracks that reliably with no JS class toggle needed.
   display: none (not just zero height) so the .field flex gap also collapses;
   a zero-height empty box would still cost a full `gap` of dead space. */
.field-error:empty { display: none; }
.field-hint { font-size: var(--small); color: var(--muted); margin: 0.3em 0 0; }
.form-note { font-size: var(--small); color: var(--muted); margin-top: 0.5rem; }

/* Amounts group (Duty Calculator) - a native fieldset so the currency selector's
   scope over invoice/freight/insurance is structural, not just implied by proximity.
   No border/box - just the title, so it reads as a section label within the form
   rather than a nested card. */
.field-group {
  border: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  min-width: 0;
}
.field-group legend {
  padding: 0;
  font-size: var(--small);
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
}

.modal-success { text-align: center; padding-top: 3rem; }
.modal-success h3 { font-size: var(--h3); }

.result-lines { margin-top: 1rem; }
.result-lines > div { display: flex; justify-content: space-between; padding-block: 0.6em; border-top: 1px solid color-mix(in srgb, var(--ink) 10%, transparent); }
.result-lines > div:first-child { border-top: none; }
.result-lines dt { color: var(--muted); font-size: var(--small); }
.result-lines dd { margin: 0; font-weight: 500; }
.modal-result-actions { display: flex; flex-direction: column; gap: 0.75rem; margin-top: 1.5rem; }

/* ---------- Costing breakdown ----------
   The supplementary customs+excise+service panel below the published-rate
   result (parts/modals.php, rendered by renderCostingBreakdown()).

   Separated from the panel above by a rule and real space, not just a
   heading: the two show different totals on purpose, and reading as one
   continuous list is exactly the misreading the copy then has to undo. */
.modal-costing {
  margin-top: 1.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
}
.modal-costing-title {
  font-family: var(--font-display);
  font-size: var(--h4, 1.05rem);
  margin: 0;
}
.costing-lines { margin-top: 0.75rem; }
.costing-note { margin-top: 0.85rem; }

/* Emphasis. Specificity, not !important: .result-lines dd sets the base
   weight, so each of these matches it on element count and wins on source
   order by being declared later in this same sheet. */
.costing-lines > div.costing-line--strong dt,
.costing-lines > div.costing-line--strong dd {
  font-weight: 600;
  color: var(--ink);
}
.costing-lines > div.costing-line--italic dt,
.costing-lines > div.costing-line--italic dd { font-style: italic; }

/* The final row: a double bottom rule. Drawn as border-bottom plus an
   outset box-shadow rather than as two borders, because an element has only
   one border-bottom - the shadow is a second hairline 3px below it, with no
   blur or spread so it renders as a crisp line rather than a shadow. */
.costing-lines > div.costing-line--final {
  margin-top: 0.25rem;
  padding-bottom: 0.75em;
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 55%, transparent);
  box-shadow: 0 3px 0 -1px color-mix(in srgb, var(--ink) 55%, transparent);
}
.costing-lines > div.costing-line--final dt,
.costing-lines > div.costing-line--final dd {
  font-weight: 700;
  color: var(--ink);
}
.costing-lines > div.costing-line--final dd { color: var(--terracotta); }

/* ---------- Track Shipment: carrier picker ---------- */
/* Cards, not radio buttons, so the whole 3rem-tall slot is clickable - the input
   itself is visually hidden but stays in the tab order and keyboard-operable
   (native radio semantics), matching this project's accessibility-parity rule. */
.carrier-picker { display: flex; gap: 0.75rem; }
.carrier-option { flex: 1; min-width: 0; position: relative; }
.carrier-option input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
/* height: 3rem per the client's logo spec - a real <img src="carrier-logo.svg">
   drops into this exact slot once the client sends logo files, no layout
   change needed. */
.carrier-option-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  height: 3rem;
  padding: 0 1rem;
  border: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
  border-radius: var(--radius);
  font-weight: 600;
  color: var(--ink);
  transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.carrier-option-mark i { font-size: 1.35rem; }
.carrier-option input:checked + .carrier-option-mark {
  border-color: var(--terracotta);
  color: var(--terracotta);
  background: color-mix(in srgb, var(--terracotta) 6%, transparent);
}
.carrier-option input:focus-visible + .carrier-option-mark {
  outline: 2px solid var(--terracotta);
  outline-offset: 2px;
}

.track-fallback-note { margin-top: 1.5rem; margin-bottom: 0; }
.track-fallback-note p { margin: 0 0 0.75em; }
.track-number-copy { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.track-number-copy code {
  font-family: var(--font-body);
  font-size: var(--small);
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  padding: 0.4em 0.7em;
  border-radius: var(--radius);
}
.track-copy-btn { padding: 0.5em 1em; font-size: var(--small); flex-shrink: 0; }

/* ---------- Order request: repeatable product-URL rows ---------- */
.field-list { display: flex; flex-direction: column; gap: 0.9rem; }
.field-link { position: relative; }
.field-link .field-remove-link {
  position: absolute;
  top: 0;
  right: 0;
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1.1rem;
  padding: 0.2rem;
  line-height: 1;
}
.field-link .field-remove-link:hover { color: var(--terracotta); }
[data-add-link] { align-self: flex-start; margin-top: -0.2rem; }

/* Honeypot for the quote form (astamangala-core REST endpoint) — off-screen
   rather than display:none, since some screen readers still surface the
   latter. Real visitors never see or reach it; only automated fill-everything
   bots do. */
.field-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---------- Contact drawer (#contact-modal) ----------
   Static info, no form — so no .modal-form/.field primitives apply here. The
   row shape deliberately mirrors .result-lines (icon/label in --muted, value
   at body weight, hairline rule between rows) so a drawer with nothing to
   submit still reads as the same component as the ones that do. */
.contact-lines { list-style: none; margin: 0; padding: 0; }
.contact-lines > li {
  display: flex;
  align-items: flex-start;
  gap: 0.9rem;
  padding-block: 1rem;
  border-top: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
}
.contact-lines > li:first-child { border-top: none; padding-top: 0; }
.contact-lines > li > i {
  font-size: 1.25rem;
  line-height: 1.2;
  color: var(--terracotta);
  flex-shrink: 0;
}
.contact-line-body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.contact-line-label { font-size: var(--small); color: var(--muted); }
/* overflow-wrap, not word-break: a long address should only break when it
   genuinely cannot fit the 420px drawer (narrower still on mobile, where the
   drawer is 100vw), not mid-word wherever the line happens to end. */
.contact-line-value {
  font-size: var(--body);
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.contact-line-value:hover,
.contact-line-value:focus-visible { color: var(--terracotta); text-decoration: underline; }

/* ---------- HS code combobox (Duty Calculator) ----------
   Search-as-you-type list under the HS Code / Product field. Border, radius and
   the terracotta accent are lifted from `.field input` / `:focus` above so the
   popup reads as part of that control rather than a separate widget.

   The list is absolutely positioned inside `.asta-combo` (which is the `.field`
   itself, made relative) so it OVERLAPS what follows instead of pushing the
   rest of the form down each keystroke — the drawer is only ~420px wide and
   scrolls, and a list that reflowed the form would move the field out from
   under the cursor mid-type. */
.asta-combo { position: relative; }
/* The popup anchors to THIS, not to .field — .field also contains the status
   and error lines, so `top: 100%` measured against it would drop the list
   below those instead of flush under the input. This wrapper is exactly the
   input's box. `display: block` (not flex) keeps the input full-width. */
.asta-combo-anchor { position: relative; display: block; }
.asta-combo-anchor > input[type="text"] { width: 100%; }
.asta-combo-list {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 5;
  margin: 0.25rem 0 0;
  padding: 0;
  list-style: none;
  max-height: 16rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--bg);
  border: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
  border-radius: 8px;
  box-shadow: 0 8px 24px color-mix(in srgb, var(--ink) 14%, transparent);
}
.asta-combo-option {
  display: flex;
  flex-direction: column;
  gap: 0.15em;
  padding: 0.55em 0.85em;
  cursor: pointer;
  border-top: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
}
.asta-combo-option:first-child { border-top: none; }
/* Hover and keyboard highlight share one style: the active option must look
   the same whichever way the visitor is driving the list. */
.asta-combo-option:hover,
.asta-combo-option[aria-selected="true"] {
  background: color-mix(in srgb, var(--terracotta) 10%, transparent);
}
.asta-combo-code { font-weight: 600; font-size: var(--small); }
.asta-combo-desc {
  font-size: var(--small);
  color: var(--muted);
  line-height: 1.35;
  overflow-wrap: anywhere;
}
/* The typed-substring highlight inside a result (comboFillHighlighted() in
   chrome.js emits <b class="asta-combo-match">).

   Weight and colour only, no background: an option already uses a tinted
   background for hover/keyboard-active, and a second highlight colour inside
   it would compete with that state rather than read as emphasis within it.

   .asta-combo-code is already 600, so the match there goes to 700 to stay
   visible against its own baseline - the same bump the description gets from
   400, not a flat weight both would share. */
.asta-combo-match {
  font-weight: 700;
  color: var(--ink);
}
.asta-combo-code .asta-combo-match { font-weight: 700; }
.asta-combo-desc .asta-combo-match { font-weight: 600; }

.asta-combo-status {
  font-size: var(--small);
  color: var(--muted);
  margin: 0.3em 0 0;
}
/* Same :empty collapse as .field-error above — comboSetStatus() (chrome.js)
   always writes textContent, including '' to clear, so this stays in sync
   with no extra JS. role="status"/aria-live="polite" stay on the element
   itself and are unaffected by display toggling on an already-empty node. */
.asta-combo-status:empty { display: none; }

/* ---------- Duty result: caveat, breakdown, source note ---------- */
/* The Item line carries a full breadcrumb, which wraps to two or three lines;
   the shared space-between row crushes its label against that. Stack it. */
.result-lines > div.result-line-stacked {
  display: block;
}
.result-lines > div.result-line-stacked dd {
  margin-top: 0.25em;
  line-height: 1.4;
  overflow-wrap: anywhere;
}

.result-caveat { margin-top: 1rem; }
.result-breakdown {
  margin-top: 1rem;
  border-top: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  padding-top: 0.75rem;
}
.result-breakdown summary {
  cursor: pointer;
  font-size: var(--small);
  font-weight: 500;
}
.result-breakdown .result-lines { margin-top: 0.5rem; }
.result-notes { margin-top: 0.75rem; }
.result-notes .field-hint + .field-hint { margin-top: 0.4em; }
.result-source { margin-top: 1rem; }

@media (max-width: 768px) {
  .modal { width: 100vw; }
}

@media (max-width: 480px) {
  .field-row { grid-template-columns: 1fr; }
}

/* ---------- Page-behind suppression while a drawer is open ----------
   chrome.js's setNavSuppressed() puts .asta-modal-open on <body> and `inert`
   on .site-header / #mobile-nav. This is the styling half; `inert` is the
   semantic half (tab order + accessibility tree), and neither substitutes
   for the other.

   Needed at all because .site-header is z-index 100 against the scrim's 95 -
   the nav is genuinely ON TOP of the dimming layer, not merely visible
   through it, so without this its links stay hoverable and clickable while a
   drawer covers the rest of the page.

   Scoped to the nav containers. The drawers are siblings of <header> (see
   parts/site-header.php), so nothing here can reach a modal's own content,
   and .modal / .modal-scrim are deliberately never descendants of either
   selector below.

   Overridden by specificity rather than !important throughout: each rule
   below carries the body class in addition to the selector it is overriding,
   so it outweighs the base rule on class count alone. */
body.asta-modal-open .site-header,
body.asta-modal-open #mobile-nav {
  pointer-events: none;
}

/* Hover styling, suppressed. The cursor can still travel across the header
   on its way to the drawer, and a link lighting up under a scrim reads as an
   invitation to click something that will not respond. */
body.asta-modal-open .site-nav a:hover,
body.asta-modal-open .nav-dropdown-trigger:hover,
body.asta-modal-open .nav-dropdown-item:hover,
body.asta-modal-open .account-trigger:hover,
body.asta-modal-open .header-actions .btn:hover {
  color: inherit;
  border-color: transparent;
  background: transparent;
}

/* A submenu already open when the drawer opened (the Tools menu opens on
   hover, so it is often mid-open at exactly that moment) is hidden outright
   rather than left floating over the scrim. `hidden` is the attribute
   chrome.js toggles; this covers the frames before any handler runs and the
   case where the pointer never leaves to trigger mouseleave. */
body.asta-modal-open .nav-dropdown-menu,
body.asta-modal-open .account-menu-dropdown {
  display: none;
}

/* Fallback for browsers without `inert`. The attribute is set by JS either
   way; where it is unsupported it is inert as far as the tab order goes, so
   this at least restores the visual and pointer half. Harmless where `inert`
   IS supported - the UA stylesheet already does this. */
body.asta-modal-open [inert] {
  pointer-events: none;
  user-select: none;
}
