/*!
 * ─────────────────────────────────────────────────────────────────────────────
 *  Arbdar Calculator — assets/css/main.css   (single stylesheet, no build step)
 *  Theme: "arbdar" · calculator.arbdar.com
 *
 *  Merged from 00-foundation + 01-chrome + 02-home + 03-calculator + 04-archives.
 *  ONE class API. Names follow the MARKUP (.ab-header/.ab-footer/.ab-burger/
 *  .ab-mpanel/.ab-skip-link/.ab-container), not the old foundation names.
 *
 *  Theme values live in EXACTLY three blocks (§02). No component anywhere else
 *  redeclares a colour that has to flip — that is what guarantees dark-mode
 *  coverage is complete.
 *
 *  THREE SIGNATURE SURFACES (header, footer, homepage) replace the plain ones.
 *  Their component CSS lives in §09 (footer, replaced in place), §20 (header)
 *  and §21 (homepage). Every token those surfaces add that must flip with the
 *  theme has been moved into the three §02 blocks — nothing below §02 declares
 *  a colour that changes with the theme, with ONE deliberate exception: the
 *  footer's --ab-fx-* set is scoped to .ab-fx (§09) because the plinth is dark
 *  in BOTH themes and one of those values is a scoped --ab-focus override that
 *  must not leak to :root.
 *
 *  Contents
 *   01 Reset & document defaults
 *   02 Design tokens + light/dark themes  (incl. header §20 + home §21 tokens)
 *   03 Typography & prose
 *   04 Layout: container, main, sections, rhythm
 *   05 Accessibility utilities (.ab-sr, .ab-skip-link, no-js)
 *   06 Site header base: brand, nav, icon buttons, burger, theme toggle
 *   07 Search form + type-ahead listbox
 *   08 Off-canvas panel
 *   09 Footer — "The Instrument Deck" (the signature dark plinth)
 *   10 Breadcrumbs
 *   11 Buttons
 *   12 Forms & fields
 *   13 Calculator card + basic keypad
 *   14 Result, steps, answer, formula, FAQ
 *   15 Cards, grids, chips
 *   16 Listing surfaces: calculator page, hub, page, TOC, tables, search, 404
 *   17 Ad wells
 *   18 Notices
 *   19 Safe areas, motion, forced colours, print, responsive tail
 *   20 Header — "The Command Bar" (the signature navy slab + double rule)
 *   21 Homepage — "Console & Tape"
 *
 *  §20 and §21 sit after §19 on purpose: they are variant layers that must win
 *  by source order over the §06/§07/§08 base they build on, and each carries its
 *  own motion / forced-colours / print rules that must follow §19's global ones.
 * ─────────────────────────────────────────────────────────────────────────────
 */

/* ==========================================================================
   01 · RESET & DOCUMENT DEFAULTS
   :where() keeps specificity at 0 so components always win.
   ========================================================================== */

*,
*::before,
*::after { box-sizing: border-box; }

:where(html) {
  -webkit-text-size-adjust: 100%;
  -moz-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--ab-header-h) + var(--ab-s-4));
  -webkit-tap-highlight-color: transparent;
}

:where(body) {
  margin: 0;
  min-height: 100%;
  background: var(--ab-bg);
  color: var(--ab-text);
  font-family: var(--ab-font-sans);
  font-size: var(--ab-fs-base);
  line-height: var(--ab-lh-base);
  overflow-wrap: break-word;
  /* No text-rendering / font-smoothing here on purpose: the stack is system UI
     fonts that already ship sensible defaults. optimizeLegibility delays first
     paint on long card grids, and antialiased thins the tabular figures on the
     dark palette — the one content type that must stay legible. */
}

:where(h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, pre) { margin: 0; }
:where(ul, ol) { margin: 0; padding: 0; }
:where(ul[class], ol[class]) { list-style: none; }

:where(img, picture, svg, video, canvas, iframe) { display: block; max-width: 100%; }
:where(img, video) { height: auto; }

/* Never override an authored fill= attribute: every theme icon is stroke-based
   and declares fill="none" on the root. A blanket fill:currentColor turned all
   of them into solid blobs. */
:where(svg:not([fill])) { fill: currentColor; }

:where(a) {
  color: var(--ab-link);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
:where(a:hover) { color: var(--ab-link-hover); }

:where(button, input, select, textarea, optgroup) {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}
:where(button) { background: none; border: 0; }
:where(textarea) { resize: vertical; }

:where(table) { border-collapse: collapse; border-spacing: 0; }

:where(hr) {
  border: 0;
  border-top: 1px solid var(--ab-border);
  margin: var(--ab-s-8) 0;
}

:where(code, kbd, samp, pre) { font-family: var(--ab-font-mono); font-size: 0.9375em; }
:where(abbr[title]) { text-decoration: underline dotted; cursor: help; }
:where(small) { font-size: 0.875em; }
:where(fieldset) { margin: 0; padding: 0; border: 0; min-width: 0; }
:where(legend) { padding: 0; }
:where(summary) { cursor: pointer; }
:where([hidden]) { display: none !important; }

::selection { background: var(--ab-selection-bg); color: var(--ab-selection-fg); }

/* Global focus. :focus-visible only, so mouse users never see a ring.
   No border-radius here — the outline already follows the element's own radius,
   and forcing 4px squared off pills and icon buttons mid-tab-through. */
:focus-visible {
  outline: 3px solid var(--ab-focus);
  outline-offset: 2px;
}
:focus:not(:focus-visible) { outline: none; }

/* Programmatic -1 targets: the scroll jump is the feedback, a ring is noise. */
.ab-main:focus,
.ab-header:focus { outline: none; }


/* ==========================================================================
   02 · DESIGN TOKENS
   :root  = palette + metrics (never change with theme)
   Theme  = three blocks only:
     a) :root, :root[data-theme="light"]                    → light payload
     b) @media dark → :root:not([data-theme="light"])       → dark payload
     c) :root[data-theme="dark"]                            → dark payload
   (b) and (c) come after (a) at equal specificity, so the manual toggle wins
   in BOTH directions. Every value that must flip is here and nowhere else.
   ========================================================================== */

:root {
  /* ---- Primary · Trust Blue ---- */
  --ab-p-50:  #F2F6FC;
  --ab-p-100: #E6ECFA;
  --ab-p-200: #C6D5F5;
  --ab-p-300: #8FAEEF;
  --ab-p-400: #5282EB;
  --ab-p-500: #1555E0;
  --ab-p-600: #1147BB;
  --ab-p-700: #0E3B9A;
  --ab-p-800: #0C307E;
  --ab-p-900: #09235D;
  --ab-p-950: #04122F;

  /* ---- Accent · Action Emerald ---- */
  --ab-a-400: #51ECB8;
  --ab-a-500: #14E19D;
  --ab-a-600: #10BC83;
  --ab-a-700: #0D9B6C;

  /* ---- Neutrals ---- */
  --ab-n-50:  #F8FAFC;
  --ab-n-100: #F1F5F9;
  --ab-n-200: #E2E8F0;
  --ab-n-300: #CBD5E1;
  --ab-n-500: #64748B;
  --ab-n-600: #475569;
  --ab-n-700: #334155;
  --ab-n-900: #0F172A;

  /* ---- Typography ---- */
  --ab-font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                  "Helvetica Neue", Arial, "Noto Sans", sans-serif,
                  "Apple Color Emoji", "Segoe UI Emoji";
  --ab-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
                  "Liberation Mono", monospace;

  --ab-fs-xs:    0.75rem;
  --ab-fs-sm:    0.8125rem;
  --ab-fs-md:    0.9375rem;
  --ab-fs-base:  17px;

  /* Fluid from here up. The answer figure is the hero of every one of the
     10,000 query pages, so --ab-fs-value must clear --ab-fs-h1 at EVERY
     viewport, not just the widest one: 44px vs 28px at 390px, 72px vs 40px
     at 1180px. The old fixed 30px h1 over a 28.6px value inverted that. */
  --ab-fs-lg:    clamp(1.0625rem, 1rem + 0.35vw, 1.1875rem);  /* 17 -> 19 */
  --ab-fs-h2:    clamp(1.375rem, 1.15rem + 1.05vw, 1.75rem);  /* 22 -> 28 */
  --ab-fs-h1:    clamp(1.75rem, 1.25rem + 2.2vw, 2.5rem);     /* 28 -> 40 */
  --ab-fs-value: clamp(2.75rem, 1.6rem + 5.6vw, 4.5rem);      /* 44 -> 72 */

  /* The live result well reserves 116px of height (see §14) and must not be
     re-typed at hero scale, or the first calculation shifts the page — the
     exact CLS the reservation exists to prevent. The pre-computed answer is
     the hero; the tool's own read-out is not. */
  --ab-fs-result: clamp(1.75rem, 1.2rem + 2.4vw, 2.25rem);    /* 28 -> 36 */

  --ab-lh-base:  1.6;
  --ab-lh-tight: 1.25;

  /* ---- Space ---- */
  --ab-s-1:  0.25rem;
  --ab-s-2:  0.5rem;
  --ab-s-3:  0.75rem;
  --ab-s-4:  1rem;
  --ab-s-5:  1.25rem;  /* The footer surfaces consume this. Without it those
                          declarations are invalid at computed-value time and
                          the padding silently collapses to zero. */
  --ab-s-6:  1.5rem;
  --ab-s-8:  2rem;
  --ab-s-12: 3rem;
  --ab-s-16: 4rem;   /* the one gap big enough to read as "the answer ended" */

  /* ---- Radii ---- */
  --ab-r-sm: 4px;
  --ab-r-md: 8px;
  --ab-r-lg: 12px;
  --ab-r-pill: 999px;

  /* ---- Layout ---- */
  --ab-measure:  720px;   /* reading column */
  --ab-wide:     840px;   /* tool + card grids */
  --ab-shell:    1180px;  /* header/footer bar */
  --ab-gutter:   var(--ab-s-4);
  --ab-rail:     300px;

  /* ---- Header "Command Bar" chrome metrics (§20) ----
     Static: none of these flip with the theme. --ab-header-h now means
     "bar row + double rule", so scroll-padding-top (§01), .ab-main's
     scroll-margin (§04), .ab-calc / .ab-page__content anchors (§13/§16) and the
     sticky ad rail (§17) all keep clearing the whole instrument. 50 + 5 = 55px
     of chrome on a phone, breadcrumb folded in. The >=900px overrides are the
     media query at the end of this section. */
  --ab-bar-h:    50px;                                     /* the row        */
  --ab-rule-h:   5px;                                      /* 2 + 2 + 1      */
  --ab-header-h: calc(var(--ab-bar-h) + var(--ab-rule-h)); /* 55px           */
  --ab-well-h:   44px;                                     /* every target   */
  --ab-bar-pad:  var(--ab-s-3);
  --ab-vv-top:   0px;   /* written by commandbar.js from visualViewport      */

  /* ---- Homepage tape reservation (§21) ----
     THE most important number on that surface: the tape is a FIXED height and
     its list scrolls inside it, so committing a calculation prepends a row
     without moving one pixel of the page below. Never write this from JS.
     Shorter at >=600px (rows collapse to one line), taller on tiny phones —
     both overrides live with the component in §21. */
  --ab-tape-h:   190px;

  /* ---- Motion ---- */
  --ab-ease: cubic-bezier(.2, .6, .3, 1);
  --ab-dur:  140ms;
  --ab-dur-panel: 200ms;
}

/* ---- LIGHT (default, and forced-light) ---------------------------------- */
:root,
:root[data-theme="light"] {
  --ab-bg:            #FFFFFF;
  --ab-bg-alt:        var(--ab-n-50);
  --ab-surface:       #FFFFFF;
  --ab-surface-2:     var(--ab-n-50);
  --ab-surface-tint:  var(--ab-p-50);

  --ab-text:          var(--ab-n-900);
  --ab-text-muted:    var(--ab-n-600);   /* 7.1:1 on white */
  --ab-text-subtle:   var(--ab-n-500);   /* 4.8:1 on white */

  --ab-border:        var(--ab-n-200);   /* decorative dividers only */
  --ab-border-strong: var(--ab-n-300);   /* decorative, heavier */
  --ab-border-input:  var(--ab-n-500);   /* 4.8:1 — every UI-control boundary */

  --ab-link:          var(--ab-p-600);
  --ab-link-hover:    var(--ab-p-700);
  --ab-focus:         var(--ab-p-600);
  --ab-nav-current:   var(--ab-p-700);

  --ab-danger:        #B91C1C;
  --ab-warn:          #D97706;
  --ab-tip:           var(--ab-a-700);

  --ab-selection-bg:  var(--ab-p-200);
  --ab-selection-fg:  var(--ab-p-950);
  --ab-backdrop:      rgba(4, 18, 47, .55);

  --ab-btn-primary-bg:       var(--ab-p-600);
  --ab-btn-primary-bg-hover: var(--ab-p-700);
  --ab-btn-primary-fg:       #FFFFFF;
  --ab-btn-calc-bg:          var(--ab-a-600);
  --ab-btn-calc-bg-hover:    var(--ab-a-700);
  --ab-btn-calc-fg:          var(--ab-p-950);  /* 7.5:1 — white would fail */

  /* Keypad */
  --ab-key-bg:          var(--ab-surface);
  --ab-key-bg-hover:    var(--ab-n-100);
  --ab-key-bd:          var(--ab-border-strong);
  --ab-key-op-bg:       var(--ab-p-50);
  --ab-key-op-bg-hover: var(--ab-p-100);
  --ab-key-op-fg:       var(--ab-p-700);
  --ab-key-op-bd:       var(--ab-p-200);
  --ab-key-fn-bg:       var(--ab-n-100);
  --ab-key-fn-bg-hover: var(--ab-n-200);
  --ab-key-fn-fg:       var(--ab-n-700);
  --ab-key-fn-bd:       var(--ab-n-200);

  /* Answer / result / steps */
  --ab-ans-bd:     var(--ab-p-200);
  --ab-ans-figure: var(--ab-p-700);
  --ab-step-bg:    var(--ab-p-100);
  --ab-step-fg:    var(--ab-p-800);

  /* Cards & chips */
  --ab-card-hover-bd: var(--ab-p-300);
  --ab-card-hover-bg: var(--ab-p-50);
  --ab-chip-hover-bg: var(--ab-p-50);
  --ab-chip-hover-bd: var(--ab-p-300);
  --ab-chip-hover-fg: var(--ab-p-700);
  --ab-chip-on-bg:    var(--ab-p-600);
  --ab-chip-on-fg:    #FFFFFF;

  --ab-404-code: var(--ab-p-100);

  --ab-shadow-sm: 0 1px 2px rgba(15, 23, 42, .06);
  --ab-shadow-md: 0 1px 3px rgba(15, 23, 42, .07), 0 8px 24px -12px rgba(15, 23, 42, .18);

  /* ---- Header "Command Bar" (§20) ----
     THE SLAB is navy in BOTH themes — compare this block with the two dark
     ones: it barely moves. That is the point. One <meta name="theme-color">,
     no white flash at the top of a dark-mode cold landing, no colour-flip bug
     surface in the most-viewed 55px of the site, and the only non-white header
     in a category of eight white ones. On-token: p-950, not a new colour. */
  --ab-bar-bg:      var(--ab-p-950);   /* #04122F                            */
  --ab-bar-rule:    var(--ab-p-800);   /* #0C307E — the double rule          */
  --ab-bar-fg:      #FFFFFF;           /* 18.6:1                             */
  --ab-bar-fg-mute: var(--ab-p-200);   /* #C6D5F5 · 12.9:1                   */
  --ab-bar-hover:   rgba(255, 255, 255, .10);
  --ab-bar-mark:    var(--ab-a-500);   /* #14E19D · 9.1:1 — the one hue      */
  --ab-bar-ring:    var(--ab-a-500);   /* focus on navy                      */

  /* The well punched into the slab — the visual hero of the header. */
  --ab-well-bg:   #FFFFFF;
  --ab-well-bd:   #1D3A72;
  --ab-well-fg:   var(--ab-n-900);     /* 17.9:1                             */
  --ab-well-ph:   var(--ab-n-600);     /* 7.5:1 — the placeholder carries the
                                          scale claim, so it must be genuinely
                                          readable, not a #94A3B8 whisper     */
  --ab-well-icon: var(--ab-n-600);
  --ab-well-ring: rgba(20, 225, 157, .28);

  --ab-scope-bg: var(--ab-p-100);
  --ab-scope-fg: var(--ab-p-800);      /* 9.6:1 on #E6ECFA                   */
  --ab-scope-bd: var(--ab-p-200);

  /* ---- Homepage "Console & Tape" (§21) ---- */
  --ab-stage-from:     var(--ab-p-50);
  --ab-stage-grid:     rgba(17, 71, 187, .07);
  --ab-stage-glow:     rgba(16, 188, 131, .13);
  --ab-stage-edge:     var(--ab-n-200);

  --ab-console-bg:     #FFFFFF;
  --ab-console-bd:     var(--ab-n-300);
  --ab-console-chrome: var(--ab-n-50);
  --ab-console-lip:    rgba(255, 255, 255, .9);
  --ab-console-glow:   rgba(17, 71, 187, .13);

  --ab-dot-a:          var(--ab-n-300);
  --ab-dot-b:          var(--ab-p-300);
  --ab-dot-c:          var(--ab-a-600);

  --ab-tape-bg:        var(--ab-n-50);
  --ab-tape-rule:      var(--ab-n-200);
  --ab-tape-tooth:     #FFFFFF;

  --ab-shelf-ord:      var(--ab-p-200);

  color-scheme: light;
  accent-color: var(--ab-p-600);
}

/* ---- DARK · system preference (unless the reader forced light) ----------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ab-bg:            #0B1220;
    --ab-bg-alt:        #0F1828;
    --ab-surface:       #131C2E;
    --ab-surface-2:     #182238;
    --ab-surface-tint:  #10203F;

    --ab-text:          #E8EEF7;
    --ab-text-muted:    #A9B8CE;
    --ab-text-subtle:   #8C9DB6;

    --ab-border:        #26324A;
    --ab-border-strong: #384863;
    --ab-border-input:  #8C9DB6;   /* 5.7:1 on --ab-surface-2 */

    --ab-link:          var(--ab-p-300);
    --ab-link-hover:    #B6CBF5;
    --ab-focus:         var(--ab-p-300);
    --ab-nav-current:   var(--ab-p-300);

    --ab-danger:        #FCA5A5;
    --ab-warn:          #F59E0B;
    --ab-tip:           var(--ab-a-500);

    --ab-selection-bg:  var(--ab-p-800);
    --ab-selection-fg:  #FFFFFF;
    --ab-backdrop:      rgba(2, 8, 20, .66);

    --ab-btn-primary-bg:       var(--ab-p-500);
    --ab-btn-primary-bg-hover: var(--ab-p-600);  /* 7.8:1 with white; p-400 was 3.67:1 */
    --ab-btn-primary-fg:       #FFFFFF;
    --ab-btn-calc-bg:          var(--ab-a-500);
    --ab-btn-calc-bg-hover:    var(--ab-a-400);
    --ab-btn-calc-fg:          var(--ab-p-950);

    --ab-key-bg:          var(--ab-surface);
    --ab-key-bg-hover:    #1C273E;
    --ab-key-bd:          var(--ab-border-strong);
    --ab-key-op-bg:       #172748;
    --ab-key-op-bg-hover: #1E3157;
    --ab-key-op-fg:       #C6D5F5;
    --ab-key-op-bd:       #2C3F66;
    --ab-key-fn-bg:       #1B263C;
    --ab-key-fn-bg-hover: #223049;
    --ab-key-fn-fg:       #C3D0E3;
    --ab-key-fn-bd:       #2B3A55;

    --ab-ans-bd:     #24406F;
    --ab-ans-figure: var(--ab-p-300);
    --ab-step-bg:    #17263F;
    --ab-step-fg:    var(--ab-p-200);

    --ab-card-hover-bd: var(--ab-p-500);
    --ab-card-hover-bg: var(--ab-surface-2);
    --ab-chip-hover-bg: var(--ab-surface-tint);
    --ab-chip-hover-bd: var(--ab-p-700);
    --ab-chip-hover-fg: var(--ab-p-300);
    --ab-chip-on-bg:    var(--ab-p-500);
    --ab-chip-on-fg:    #FFFFFF;

    --ab-404-code: var(--ab-p-900);

    --ab-shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --ab-shadow-md: 0 1px 3px rgba(0, 0, 0, .45), 0 8px 24px -12px rgba(0, 0, 0, .6);

    /* ---- Header "Command Bar" (§20) — still a navy slab, one shade deeper. */
    --ab-bar-bg:      #050D1F;
    --ab-bar-rule:    #1B2C4C;
    --ab-bar-fg:      #F2F6FC;
    --ab-bar-fg-mute: #A9B8CE;
    --ab-bar-hover:   rgba(255, 255, 255, .08);
    --ab-bar-mark:    var(--ab-a-500);
    --ab-bar-ring:    var(--ab-a-500);

    --ab-well-bg:   #16233C;
    --ab-well-bd:   #2E4470;
    --ab-well-fg:   #E8EEF7;           /* 12.9:1 on #16233C                  */
    --ab-well-ph:   #A9B8CE;           /* 7.2:1                              */
    --ab-well-icon: #A9B8CE;
    --ab-well-ring: rgba(20, 225, 157, .30);

    --ab-scope-bg: #16294D;
    --ab-scope-fg: #C6D5F5;            /* 8.9:1                              */
    --ab-scope-bd: #2C3F66;

    /* ---- Homepage "Console & Tape" (§21) ---- */
    --ab-stage-from:     #0E1A31;
    --ab-stage-grid:     rgba(146, 178, 240, .075);
    --ab-stage-glow:     rgba(20, 225, 157, .12);
    --ab-stage-edge:     var(--ab-border);

    --ab-console-bg:     var(--ab-surface);
    --ab-console-bd:     var(--ab-border-strong);
    --ab-console-chrome: #0F192B;
    --ab-console-lip:    rgba(255, 255, 255, .06);
    --ab-console-glow:   rgba(82, 130, 235, .22);

    --ab-dot-a:          #3A4A66;
    --ab-dot-b:          var(--ab-p-500);
    --ab-dot-c:          var(--ab-a-500);

    --ab-tape-bg:        #0F1828;
    --ab-tape-rule:      var(--ab-border);
    --ab-tape-tooth:     var(--ab-surface);

    --ab-shelf-ord:      #24406F;

    color-scheme: dark;
    accent-color: var(--ab-p-400);
  }
}

/* ---- DARK · forced by the toggle (wins on a light-preferring OS) --------- */
:root[data-theme="dark"] {
  --ab-bg:            #0B1220;
  --ab-bg-alt:        #0F1828;
  --ab-surface:       #131C2E;
  --ab-surface-2:     #182238;
  --ab-surface-tint:  #10203F;

  --ab-text:          #E8EEF7;
  --ab-text-muted:    #A9B8CE;
  --ab-text-subtle:   #8C9DB6;

  --ab-border:        #26324A;
  --ab-border-strong: #384863;
  --ab-border-input:  #8C9DB6;

  --ab-link:          var(--ab-p-300);
  --ab-link-hover:    #B6CBF5;
  --ab-focus:         var(--ab-p-300);
  --ab-nav-current:   var(--ab-p-300);

  --ab-danger:        #FCA5A5;
  --ab-warn:          #F59E0B;
  --ab-tip:           var(--ab-a-500);

  --ab-selection-bg:  var(--ab-p-800);
  --ab-selection-fg:  #FFFFFF;
  --ab-backdrop:      rgba(2, 8, 20, .66);

  --ab-btn-primary-bg:       var(--ab-p-500);
  --ab-btn-primary-bg-hover: var(--ab-p-600);
  --ab-btn-primary-fg:       #FFFFFF;
  --ab-btn-calc-bg:          var(--ab-a-500);
  --ab-btn-calc-bg-hover:    var(--ab-a-400);
  --ab-btn-calc-fg:          var(--ab-p-950);

  --ab-key-bg:          var(--ab-surface);
  --ab-key-bg-hover:    #1C273E;
  --ab-key-bd:          var(--ab-border-strong);
  --ab-key-op-bg:       #172748;
  --ab-key-op-bg-hover: #1E3157;
  --ab-key-op-fg:       #C6D5F5;
  --ab-key-op-bd:       #2C3F66;
  --ab-key-fn-bg:       #1B263C;
  --ab-key-fn-bg-hover: #223049;
  --ab-key-fn-fg:       #C3D0E3;
  --ab-key-fn-bd:       #2B3A55;

  --ab-ans-bd:     #24406F;
  --ab-ans-figure: var(--ab-p-300);
  --ab-step-bg:    #17263F;
  --ab-step-fg:    var(--ab-p-200);

  --ab-card-hover-bd: var(--ab-p-500);
  --ab-card-hover-bg: var(--ab-surface-2);
  --ab-chip-hover-bg: var(--ab-surface-tint);
  --ab-chip-hover-bd: var(--ab-p-700);
  --ab-chip-hover-fg: var(--ab-p-300);
  --ab-chip-on-bg:    var(--ab-p-500);
  --ab-chip-on-fg:    #FFFFFF;

  --ab-404-code: var(--ab-p-900);

  --ab-shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --ab-shadow-md: 0 1px 3px rgba(0, 0, 0, .45), 0 8px 24px -12px rgba(0, 0, 0, .6);

  /* ---- Header "Command Bar" (§20) ---- */
  --ab-bar-bg:      #050D1F;
  --ab-bar-rule:    #1B2C4C;
  --ab-bar-fg:      #F2F6FC;
  --ab-bar-fg-mute: #A9B8CE;
  --ab-bar-hover:   rgba(255, 255, 255, .08);
  --ab-bar-mark:    var(--ab-a-500);
  --ab-bar-ring:    var(--ab-a-500);

  --ab-well-bg:   #16233C;
  --ab-well-bd:   #2E4470;
  --ab-well-fg:   #E8EEF7;
  --ab-well-ph:   #A9B8CE;
  --ab-well-icon: #A9B8CE;
  --ab-well-ring: rgba(20, 225, 157, .30);

  --ab-scope-bg: #16294D;
  --ab-scope-fg: #C6D5F5;
  --ab-scope-bd: #2C3F66;

  /* ---- Homepage "Console & Tape" (§21) ---- */
  --ab-stage-from:     #0E1A31;
  --ab-stage-grid:     rgba(146, 178, 240, .075);
  --ab-stage-glow:     rgba(20, 225, 157, .12);
  --ab-stage-edge:     var(--ab-border);

  --ab-console-bg:     var(--ab-surface);
  --ab-console-bd:     var(--ab-border-strong);
  --ab-console-chrome: #0F192B;
  --ab-console-lip:    rgba(255, 255, 255, .06);
  --ab-console-glow:   rgba(82, 130, 235, .22);

  --ab-dot-a:          #3A4A66;
  --ab-dot-b:          var(--ab-p-500);
  --ab-dot-c:          var(--ab-a-500);

  --ab-tape-bg:        #0F1828;
  --ab-tape-rule:      var(--ab-border);
  --ab-tape-tooth:     var(--ab-surface);

  --ab-shelf-ord:      #24406F;

  color-scheme: dark;
  accent-color: var(--ab-p-400);
}

/* ---- Metric overrides at desktop width ----------------------------------
   Theme-independent, so they belong with the metrics and not in a theme block.
   The command bar grows its row at >=900px, which grows --ab-header-h to 63px
   and with it every scroll-padding / sticky offset derived from it. */
@media (min-width: 900px) {
  :root {
    --ab-bar-h:   58px;
    --ab-well-h:  46px;
    --ab-bar-pad: var(--ab-s-4);
  }
}


/* ==========================================================================
   03 · TYPOGRAPHY & PROSE
   Every page title carries .ab-h1 plus its surface class, so the scale is
   owned by one rule and surfaces only tweak spacing.
   ========================================================================== */

h1, .ab-h1 {
  font-size: var(--ab-fs-h1);
  line-height: var(--ab-lh-tight);
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--ab-text);
  text-wrap: balance;
}

h2, .ab-h2 {
  font-size: var(--ab-fs-h2);
  line-height: 1.3;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

/* Body is 17px, so h4 cannot be 16px and h5/h6 cannot be 15px — a subheading
   must never render smaller than the paragraph it introduces. h5/h6 buy their
   distinction with a case change instead of a size change, which is what lets
   a tight scale stay legible without collapsing. */
h3, .ab-h3 { font-size: clamp(1.25rem, 1.15rem + 0.5vw, 1.375rem); line-height: 1.3; font-weight: 700; }
h4, .ab-h4 { font-size: 1.125rem; line-height: 1.4; font-weight: 700; }   /* 18 > 17 body */
h5, h6     {
  font-size: 1rem;
  line-height: 1.4;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ab-text-muted);
}

.ab-lede { font-size: var(--ab-fs-lg); line-height: 1.55; color: var(--ab-text-muted); }
.ab-meta { font-size: var(--ab-fs-sm); color: var(--ab-text-subtle); }

.ab-prose { max-width: var(--ab-measure); }
.ab-prose > * + *  { margin-top: var(--ab-s-4); }
.ab-prose > * + h2 { margin-top: var(--ab-s-12); }
.ab-prose > * + h3 { margin-top: var(--ab-s-8); }
.ab-prose h2 + *,
.ab-prose h3 + *   { margin-top: var(--ab-s-3); }

.ab-prose ul,
.ab-prose ol { padding-left: 1.35em; }
.ab-prose ul { list-style: disc; }
.ab-prose ol { list-style: decimal; }
.ab-prose li + li { margin-top: var(--ab-s-2); }
.ab-prose li::marker { color: var(--ab-text-subtle); }

.ab-prose a { color: var(--ab-link); text-decoration: underline; }
.ab-prose a:hover { color: var(--ab-link-hover); }
.ab-prose strong { font-weight: 700; }

.ab-prose blockquote {
  border-left: 3px solid var(--ab-border-strong);
  padding: var(--ab-s-1) 0 var(--ab-s-1) var(--ab-s-4);
  color: var(--ab-text-muted);
}

.ab-prose code {
  background: var(--ab-surface-2);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-sm);
  padding: 0.1em 0.35em;
}

.ab-prose pre {
  background: var(--ab-surface-2);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
  padding: var(--ab-s-4);
  overflow-x: auto;
}
.ab-prose pre code { background: none; border: 0; padding: 0; }

.ab-prose img { border-radius: var(--ab-r-md); }

.ab-prose figcaption {
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-subtle);
  margin-top: var(--ab-s-2);
}

/* Tabular numerals wherever a figure can appear. .ab-table-wrap is the wrapper
   the theme actually emits around editor tables. */
.ab-tnum,
.ab-result__value,
.ab-answer__value,
.ab-calc,
.ab-table-wrap th,
.ab-table-wrap td,
input[type="number"],
input[inputmode="decimal"] { font-variant-numeric: tabular-nums; }

kbd {
  display: inline-block;
  min-width: 1.6em;
  padding: 0.1em 0.4em;
  color: var(--ab-text);
  background: var(--ab-surface-2);
  border: 1px solid var(--ab-border-strong);
  border-bottom-width: 2px;
  border-radius: var(--ab-r-sm);
  font-family: var(--ab-font-mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  text-align: center;
}


/* ==========================================================================
   04 · LAYOUT — container, main, sections, page rhythm
   .ab-container is THE layout wrapper (every template emits it).
   .ab-page is free to mean "the static page article".
   ========================================================================== */

.ab-container {
  width: 100%;
  max-width: var(--ab-measure);
  margin-inline: auto;
  padding-inline: var(--ab-gutter);
}
.ab-container--wide { max-width: var(--ab-wide); }
.ab-measure { max-width: var(--ab-measure); }

.ab-main {
  display: block;
  min-height: 50vh;               /* keeps the footer down on 404/empty search */
  padding-block: var(--ab-s-6) var(--ab-s-12);
  scroll-margin-top: calc(var(--ab-header-h) + var(--ab-s-4));
}

@media (min-width: 1024px) {
  .ab-main { padding-block: var(--ab-s-8) var(--ab-s-12); }
}

/* Page rhythm: ONE owl rule owns the spacing between top-level blocks, so
   every surface breathes the same regardless of what the blocks are called. */
.ab-container > * + * { margin-top: var(--ab-s-8); }
.ab-container > * + .ab-section { margin-top: var(--ab-s-12); }
.ab-container > .ab-breadcrumbs + * { margin-top: var(--ab-s-4); }

.ab-section { display: block; }

.ab-section__header {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ab-s-3);
  margin-bottom: var(--ab-s-4);
}

.ab-section__title { margin-bottom: var(--ab-s-3); }
.ab-section__header .ab-section__title { margin-bottom: 0; }

.ab-section__desc {
  margin-top: var(--ab-s-1);
  margin-bottom: var(--ab-s-4);
  color: var(--ab-text-muted);
  font-size: var(--ab-fs-md);
}
.ab-section__header .ab-section__desc { margin-bottom: 0; }

.ab-section--muted {
  border-top: 1px solid var(--ab-border);
  padding-top: var(--ab-s-6);
}

.ab-band {
  background: var(--ab-bg-alt);
  border-block: 1px solid var(--ab-border);
  padding-block: var(--ab-s-12);
}

/* The ad rail lives on .ab-calcpage__inner itself (§16) — one rail system, on
   the one template that has 300px of gutter to sell. The old generic
   .ab-layout--rail wrapper had zero call sites and has been removed. */


/* ==========================================================================
   05 · ACCESSIBILITY UTILITIES
   ========================================================================== */

.ab-sr,
.screen-reader-text {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Skip link — ONE name: .ab-skip-link (matches header.php). Transform-based so
   it stays in the layout box and works in RTL. z-index clears .ab-header (100)
   but deliberately sits BELOW .ab-mpanel (200): while the drawer is open the
   background is inert, so the skip link must not float over the modal. */
.ab-skip-link {
  position: absolute;
  inset-inline-start: var(--ab-s-2);
  top: var(--ab-s-2);
  z-index: 150;
  transform: translateY(-250%);
  padding: var(--ab-s-3) var(--ab-s-4);
  background: var(--ab-btn-primary-bg);
  color: var(--ab-btn-primary-fg);
  font-weight: 600;
  font-size: var(--ab-fs-md);
  border-radius: var(--ab-r-md);
  box-shadow: var(--ab-shadow-md);
  text-decoration: none;
  transition: transform var(--ab-dur) var(--ab-ease);
}
.ab-skip-link:focus,
.ab-skip-link:focus-visible {
  transform: translateY(0);
  color: var(--ab-btn-primary-fg);
}

/* No-JS: never ship a control that has a name and a role but no behaviour. */
html.no-js .ab-burger,
html.no-js .ab-header__searchbtn,
html.no-js .ab-theme-toggle,
/* Only the BUTTON form is dead without JS. Browse is now an <a href> to a real
   crawlable hub, so suppressing every [data-ab-menu-toggle] removed the entire
   primary navigation for no-JS and for crawlers that do not execute scripts. */
html.no-js button[data-ab-menu-toggle],
html.no-js [data-ab-theme-toggle],
html.no-js .ab-mpanel { display: none !important; }

html.no-js .ab-header__search { display: block; }

/* The burger is hidden without JS, so the bar itself must stay reachable at
   every width. The overflow handling that used to live here is now on .ab-nav
   unconditionally (§06) — only the un-hiding is no-js-specific. */
html.no-js .ab-nav { display: block; }


/* ==========================================================================
   06 · SITE HEADER
   ========================================================================== */

.ab-header {
  position: sticky;
  top: 0;
  z-index: 100;
  min-height: var(--ab-header-h);   /* reserved: the bar never reflows */
  background: var(--ab-bg);
  border-bottom: 1px solid var(--ab-border);
  transition: box-shadow var(--ab-dur) var(--ab-ease);
}

/* site.js adds .is-stuck once the page has scrolled. */
.ab-header.is-stuck { box-shadow: var(--ab-shadow-sm); }

.ab-header__inner {
  display: flex;
  align-items: center;
  gap: var(--ab-s-3);
  min-height: var(--ab-header-h);
  max-width: var(--ab-shell);
  margin-inline: auto;
  padding-inline: var(--ab-gutter);
}

.ab-header__tools {
  display: flex;
  align-items: center;
  gap: var(--ab-s-1);
  margin-inline-start: auto;
  flex: 0 0 auto;
}

/* ---- Brand -------------------------------------------------------------- */

.ab-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--ab-s-2);
  flex: 0 0 auto;
  padding-block: var(--ab-s-1);
  color: var(--ab-text);
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  border-radius: var(--ab-r-sm);
}
.ab-brand:hover { color: var(--ab-link); }

/* No fixed size: the printed width/height attributes (arbdar_logo 'size') win. */
.ab-brand__mark { flex: 0 0 auto; color: var(--ab-link); }

.ab-brand__name {
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.ab-brand__lead { font-weight: 700; }
.ab-brand__tail { font-weight: 400; color: var(--ab-text-muted); }

.ab-brand--footer { margin-bottom: var(--ab-s-2); }

/* Very narrow phones: keep "Arbdar", drop the tail. */
@media (max-width: 400px) {
  .ab-header .ab-brand__tail { display: none; }
}

/* ---- Primary navigation (one level, desktop only) ----------------------- */

/* The bar becomes visible at 900px, where six category names do not fit in the
   ~430px left over beside the brand, the search box and the tools. Without an
   overflow it paints over .ab-header__tools and out of the document — a
   horizontal scrollbar on the site's own chrome. Scroll it instead, with the
   scrollbar suppressed so the resting state still looks like a nav bar. */
.ab-nav {
  min-width: 0;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.ab-nav::-webkit-scrollbar { display: none; }

.ab-nav__list {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ab-nav__list > li > a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--ab-s-3);
  border-radius: var(--ab-r-md);
  color: var(--ab-text-muted);
  font-size: var(--ab-fs-md);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.ab-nav__list > li > a:hover {
  background: var(--ab-surface-2);
  color: var(--ab-text);
}
.ab-nav__list > .current-menu-item > a,
.ab-nav__list > .current-menu-parent > a,
.ab-nav__list > .current_page_item > a,
.ab-nav__list [aria-current="page"] { color: var(--ab-nav-current); }

/* ---- Icon buttons ------------------------------------------------------- */

.ab-icon { flex: 0 0 auto; }

.ab-iconbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--ab-r-md);
  background: transparent;
  color: var(--ab-text-muted);
  cursor: pointer;
  transition: background-color var(--ab-dur) var(--ab-ease),
              color var(--ab-dur) var(--ab-ease);
}
.ab-iconbtn:hover { background: var(--ab-surface-2); color: var(--ab-text); }

/* ---- Hamburger ---------------------------------------------------------- */

.ab-burger__bars {
  position: relative;
  display: block;
  width: 20px;
  height: 14px;
}
.ab-burger__bars > span {
  position: absolute;
  inset-inline: 0;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform var(--ab-dur) var(--ab-ease),
              opacity var(--ab-dur) var(--ab-ease);
}
.ab-burger__bars > span:nth-child(1) { top: 0; }
.ab-burger__bars > span:nth-child(2) { top: 6px; }
.ab-burger__bars > span:nth-child(3) { top: 12px; }

.ab-burger[aria-expanded="true"] .ab-burger__bars > span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.ab-burger[aria-expanded="true"] .ab-burger__bars > span:nth-child(2) { opacity: 0; }
.ab-burger[aria-expanded="true"] .ab-burger__bars > span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* ---- Colour-scheme toggle ----------------------------------------------
   Driven off aria-pressed, which site.js sets and maintains. One rule pair,
   no theme duplication: pressed === dark active === show the moon. */

.ab-theme-toggle { cursor: pointer; }
.ab-theme-toggle .ab-icon--sun  { display: block; }
.ab-theme-toggle .ab-icon--moon { display: none; }
.ab-theme-toggle[aria-pressed="true"] .ab-icon--sun  { display: none; }
.ab-theme-toggle[aria-pressed="true"] .ab-icon--moon { display: block; }

/* Full-width variant inside the off-canvas panel. */
.ab-theme-toggle--wide {
  display: flex;
  align-items: center;
  gap: var(--ab-s-2);
  width: 100%;
  min-height: 44px;
  padding-inline: var(--ab-s-3);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
  background: var(--ab-surface-2);
  color: var(--ab-text);
  font-size: var(--ab-fs-md);
  font-weight: 600;
  cursor: pointer;
}
.ab-theme-toggle--wide:hover { border-color: var(--ab-border-strong); }

/* ---- Responsive switches -------------------------------------------------
   Search appears at 760, the nav bar at 900. 900 is site.js's DESKTOP_QUERY —
   the two systems must agree or the burger and the nav both vanish. */

.ab-header__search { display: none; }

@media (min-width: 760px) {
  .ab-header__search    { display: block; }
  .ab-header__searchbtn { display: none; }
}
@media (max-width: 899px) {
  .ab-nav { display: none; }

  /* Three identical unlabelled 44px squares at 4px spacing in the hardest
     corner of the screen, the middle one permanently inverting the site with
     no undo — and a properly labelled duplicate already sits in the drawer.
     The header keeps search and the burger; the theme control keeps its
     .ab-theme-toggle--wide form inside the panel. */
  .ab-header__tools > .ab-theme-toggle:not(.ab-theme-toggle--wide) { display: none; }
  .ab-header__tools { gap: var(--ab-s-2); }
}
@media (min-width: 900px) {
  .ab-burger { display: none; }
}


/* ==========================================================================
   07 · SEARCH FORM (searchform.php — the args-aware form) + type-ahead
   ========================================================================== */

.ab-search {
  position: relative;
  flex: 0 1 auto;
  width: 100%;
}

.ab-search__wrap {
  position: relative;
  display: flex;
  align-items: center;
}

.ab-search__icon {
  position: absolute;
  inset-inline-start: var(--ab-s-3);
  display: flex;
  color: var(--ab-text-subtle);
  pointer-events: none;
}

.ab-search__input {
  width: 100%;
  min-height: 44px;
  padding-block: var(--ab-s-2);
  padding-inline-start: 2.375rem;
  padding-inline-end: 2.625rem;
  border: 1px solid var(--ab-border-input);
  border-radius: var(--ab-r-pill);
  background: var(--ab-surface-2);
  color: var(--ab-text);
  font-size: var(--ab-fs-base);   /* 17px — never triggers iOS zoom */
  appearance: none;
}
.ab-search__input::placeholder { color: var(--ab-text-subtle); opacity: 1; }
.ab-search__input:hover { border-color: var(--ab-text-subtle); }
.ab-search__input:focus {
  background: var(--ab-surface);
  border-color: var(--ab-focus);
}

.ab-search__submit {
  position: absolute;
  inset-inline-end: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--ab-r-pill);
  color: var(--ab-link);
  cursor: pointer;
}
.ab-search__submit:hover { background: var(--ab-surface-tint); }

/* Header variant: narrow, never wide enough to squeeze the nav. */
.ab-search--compact { width: clamp(180px, 26vw, 280px); }
.ab-search--compact .ab-search__input { min-height: 40px; font-size: var(--ab-fs-md); }

/* Block variant: 404 / no-results, where a visible submit reads better. */
.ab-search--block { display: flex; gap: var(--ab-s-2); width: 100%; max-width: 34rem; }
.ab-search--block .ab-search__wrap { flex: 1 1 auto; min-width: 0; }
.ab-search--block .ab-search__input { padding-inline-end: var(--ab-s-3); }
.ab-search--block .ab-search__submit {
  position: static;
  width: auto;
  height: auto;
  min-height: 44px;
  flex: 0 0 auto;
  gap: var(--ab-s-2);
  padding-inline: var(--ab-s-4);
  border-radius: var(--ab-r-md);
  background: var(--ab-btn-primary-bg);
  color: var(--ab-btn-primary-fg);
  font-size: var(--ab-fs-md);
  font-weight: 600;
}
.ab-search--block .ab-search__submit:hover { background: var(--ab-btn-primary-bg-hover); }

/* Type-ahead listbox — built by site.js, absent without JS. Absolutely
   positioned so it overlays instead of reflowing the page on every keystroke. */
.ab-search__results {
  position: absolute;
  inset-inline: 0;
  top: calc(100% + 6px);
  z-index: 120;
  /* svh, not vh: with the mobile keyboard up, 60vh is taller than the visible
     viewport and the last suggestions sit under the keyboard. The vh line is
     the fallback — without it, a browser that does not know svh drops the
     declaration entirely and the list becomes unbounded. */
  max-height: min(45vh, 320px);
  max-height: min(45svh, 320px);
  overflow-y: auto;
  overscroll-behavior: contain;
  margin: 0;
  padding: var(--ab-s-1);
  list-style: none;
  background: var(--ab-surface);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
  box-shadow: var(--ab-shadow-md);
}

/* 48px rows: suggestions were the one place the 44px target was dropped, and a
   mis-tap here sends the reader to the wrong calculator entirely. */
.ab-search__result {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: var(--ab-s-2) var(--ab-s-3);
  border-radius: var(--ab-r-sm);
  color: var(--ab-text);
  font-size: var(--ab-fs-md);
  line-height: 1.35;
  cursor: pointer;
}
.ab-search__result + .ab-search__result { margin-top: 2px; }
.ab-search__result.is-active,
.ab-search__result[aria-selected="true"],
.ab-search__result:hover {
  background: var(--ab-surface-tint);
  color: var(--ab-link);
}


/* ==========================================================================
   08 · OFF-CANVAS PANEL
   ========================================================================== */

/* site.js defers [hidden] by the length of the slide-out, so for 220ms after a
   close this full-viewport overlay is still in the document — and a backdrop
   at opacity:0 is still fully hit-testable. On mobile this panel IS the
   navigation, so close-then-tap is the dominant gesture and the site reads as
   "the first tap never works". pointer-events inherits, so the one pair below
   covers the backdrop and the drawer too, and the slide-out still animates. */
.ab-mpanel {
  position: fixed;
  inset: 0;
  z-index: 200;
  pointer-events: none;
}
.ab-mpanel.is-open { pointer-events: auto; }

.ab-mpanel__backdrop {
  position: absolute;
  inset: 0;
  background: var(--ab-backdrop);
  opacity: 0;
  touch-action: none;   /* a drag on the visible strip must not pan the page */
  transition: opacity var(--ab-dur-panel) var(--ab-ease);
}
.ab-mpanel.is-open .ab-mpanel__backdrop { opacity: 1; }

.ab-mpanel__drawer {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  display: flex;
  flex-direction: column;
  width: min(360px, 88vw);
  background: var(--ab-bg);
  border-inline-start: 1px solid var(--ab-border);
  box-shadow: var(--ab-shadow-md);
  transform: translateX(100%);
  transition: transform var(--ab-dur-panel) var(--ab-ease);
}
[dir="rtl"] .ab-mpanel__drawer { transform: translateX(-100%); }
.ab-mpanel.is-open .ab-mpanel__drawer { transform: translateX(0); }

.ab-mpanel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ab-s-2);
  flex: 0 0 auto;
  min-height: var(--ab-header-h);
  padding-inline: var(--ab-gutter);
  border-bottom: 1px solid var(--ab-border);
}

.ab-mpanel__title { margin: 0; font-size: var(--ab-fs-lg); font-weight: 700; }

.ab-mpanel__body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: var(--ab-s-6);
  padding: var(--ab-s-4) var(--ab-gutter) var(--ab-s-8);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.ab-mpanel__section { min-width: 0; }

.ab-mpanel__subtitle {
  margin: 0 0 var(--ab-s-2);
  font-size: var(--ab-fs-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ab-text-subtle);
}

/* Scroll lock. site.js writes the scrollbar width to --ab-sbw and it is paid
   back as padding, so the page never shifts sideways when the drawer opens.
   position:fixed rather than overflow:hidden alone — body{overflow:hidden} has
   never reliably held on iOS Safari. site.js owns the inline `top` (the saved
   scroll offset) and restores the scroll position on unlock. */
body.ab-scroll-lock {
  position: fixed;
  inset-inline: 0;
  width: 100%;
  overflow: hidden;
  padding-inline-end: var(--ab-sbw, 0px);
}

/* ---- Panel navigation --------------------------------------------------- */

.ab-mnav__list,
.ab-mnav .sub-menu { margin: 0; padding: 0; list-style: none; }

.ab-mnav__list > li + li { border-top: 1px solid var(--ab-border); }

.ab-mnav a {
  display: flex;
  align-items: center;
  min-height: 48px;
  color: var(--ab-text);
  font-size: var(--ab-fs-base);
  font-weight: 600;
  text-decoration: none;
}
.ab-mnav a:hover { color: var(--ab-link); }
.ab-mnav .current-menu-item > a,
.ab-mnav [aria-current="page"] { color: var(--ab-nav-current); }

.ab-mnav .sub-menu {
  margin-block-end: var(--ab-s-2);
  padding-inline-start: var(--ab-s-3);
  border-inline-start: 2px solid var(--ab-border);
}
.ab-mnav .sub-menu a {
  min-height: 44px;
  color: var(--ab-text-muted);
  font-size: var(--ab-fs-md);
  font-weight: 500;
}

/* ---- Link lists (panel categories) --------------------------------------
   .ab-footer__list is gone with the old footer: §09's Instrument Deck styles
   its own .ab-fx__list, and arbdar_footer_nav_fallback() is now called with an
   explicit menu_class, so it never falls back to the old name either. */

.ab-linklist {
  display: grid;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.ab-linklist a {
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  color: var(--ab-text-muted);
  text-decoration: none;
}
.ab-linklist a:hover { color: var(--ab-link); text-decoration: underline; }

.ab-linklist--panel {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--ab-s-2);
}
.ab-linklist--panel a {
  min-height: 44px;
  padding: var(--ab-s-2) var(--ab-s-3);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
  background: var(--ab-surface-2);
  color: var(--ab-text);
  font-size: var(--ab-fs-sm);
  font-weight: 600;
}
.ab-linklist--panel a:hover { border-color: var(--ab-card-hover-bd); text-decoration: none; }


/* ==========================================================================
   09 · FOOTER — "The Instrument Deck"

   Replaced WHOLESALE. The old .ab-footer__* family is gone: footer.php now
   emits .ab-fx* and .ab-postmain only, arbdar_footer_nav_fallback() is called
   with an explicit menu_class so it never emits .ab-footer__list either, and
   the §08 link-list rule and the §19 safe-area / print references to the old
   names have been removed with it. Nothing named .ab-footer* remains anywhere.

   THE CONCEPT: the footer is a DARK PLINTH in BOTH themes. The page lands on a
   slab. That is the point of view, and it is also the cheapest possible route
   to a complete dark mode — the surface is already dark, so only two local
   surface tokens shift and there is no half-finished dark footer to be caught
   out by. It also sidesteps the blue-on-navy contrast trap entirely: footer
   links are near-white, and emerald stays the single accent.

   TOKEN EXCEPTION, deliberate. Every other theme-flipping token in this file
   lives in §02. The footer's --ab-fx-* set does NOT, and must not: it is SCOPED
   to .ab-fx, and one of its values is a scoped --ab-focus override (the global
   --ab-focus is Trust Blue #1147BB, ~1.9:1 on navy — invisible). Hoisting these
   to :root would leak that focus colour to the whole document. They still flip
   correctly, in the same block pattern §02 uses:
     a) .ab-fx                                          -> light payload
     b) @media dark -> :root:not([data-theme="light"]) .ab-fx
     c) :root[data-theme="dark"]  .ab-fx
     d) :root[data-theme="light"] .ab-fx                -> light, declared last
   (b), (c) and (d) all compute to (0,3,0) — equal specificity — so source order
   decides, and (d) last means a reader who forces light gets light even on a
   dark-preferring OS.

   MEASURED CONTRAST (WCAG 2.1, computed not eyeballed)
     light-theme plinth  #04122F   L 0.0066
       #EAF1FD 15.8:1   #A8B8CF 8.9:1   #8FA2BC 6.9:1   #14E19D 10.4:1
     light-theme raised  #0A2050   L 0.0168
       #EAF1FD 13.9:1   #A8B8CF 7.8:1   #8FA2BC 6.0:1   #14E19D  9.2:1
     dark-theme plinth   #101B30   L 0.0111
       #EAF1FD 15.2:1   #A8B8CF 8.5:1   #8FA2BC 6.6:1
     dark-theme raised   #18243B   L 0.0177
       #EAF1FD 13.7:1   #A8B8CF 7.7:1   #8FA2BC 6.0:1
     focus ring #A8C8FF on #04122F  10.9:1
     emerald submit: #04122F text on #14E19D  10.4:1 (white would be 1.8:1)
   The dark-theme plinth sits ~1.8x above the page background (#0B1220) and is
   additionally separated by a real 1px hairline, so the boundary never depends
   on luminance alone.
   ========================================================================== */

/* ---- Footer tokens · LIGHT (the base) ----------------------------------- */
.ab-fx {
  --ab-fx-bg:       #04122F;   /* the plinth — = --ab-p-950 */
  --ab-fx-raised:   #0A2050;   /* status pill, console, notes */
  --ab-fx-raised-2: #0D2A63;   /* hover fill */
  --ab-fx-fg:       #EAF1FD;
  --ab-fx-muted:    #A8B8CF;
  --ab-fx-dim:      #8FA2BC;
  --ab-fx-line:     rgba(140, 175, 240, .18);
  --ab-fx-line-2:   rgba(140, 175, 240, .34);
  --ab-fx-accent:   var(--ab-a-500);
  --ab-fx-shadow:   0 20px 44px -22px rgba(4, 18, 47, .55);

  /* Scoped focus ring. One inherited override fixes every :focus-visible
     inside the footer without touching the global rule in §01. */
  --ab-focus: #A8C8FF;
}

/* ---- Footer tokens · DARK, system preference --------------------------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ab-fx {
    --ab-fx-bg:       #101B30;
    --ab-fx-raised:   #18243B;
    --ab-fx-raised-2: #1F2D49;
    --ab-fx-line:     rgba(140, 175, 240, .15);
    --ab-fx-line-2:   rgba(140, 175, 240, .30);
    --ab-fx-shadow:   0 20px 44px -22px rgba(0, 0, 0, .72);
  }
}

/* ---- Footer tokens · DARK, forced by the toggle ------------------------- */
:root[data-theme="dark"] .ab-fx {
  --ab-fx-bg:       #101B30;
  --ab-fx-raised:   #18243B;
  --ab-fx-raised-2: #1F2D49;
  --ab-fx-line:     rgba(140, 175, 240, .15);
  --ab-fx-line-2:   rgba(140, 175, 240, .30);
  --ab-fx-shadow:   0 20px 44px -22px rgba(0, 0, 0, .72);
}

/* ---- Footer tokens · LIGHT, forced by the toggle (declared last: wins) --- */
:root[data-theme="light"] .ab-fx {
  --ab-fx-bg:       #04122F;
  --ab-fx-raised:   #0A2050;
  --ab-fx-raised-2: #0D2A63;
  --ab-fx-line:     rgba(140, 175, 240, .18);
  --ab-fx-line-2:   rgba(140, 175, 240, .34);
  --ab-fx-shadow:   0 20px 44px -22px rgba(4, 18, 47, .55);
}


/* ---- 09.0 · THE AD WELL -------------------------------------------------
   After </main>, before <footer>, on the PAGE background.

   Below 100% of primary content, below the fold, outside the contentinfo
   landmark, and still in the scroll path. Nothing inside the plinth is for
   sale, so an ad can never be interleaved between the disclosure, the
   disclaimer and the copyright. Height is reserved by arbdar_ad_slot() as a
   FIXED height via §17 (--ab-ad-h / --ab-ad-m-h) — never a min-height. */

.ab-postmain {
  padding-inline: var(--ab-gutter);
  background: var(--ab-bg);
}
.ab-postmain .ab-ad { margin-block: var(--ab-s-8) 0; }


/* ---- 09.1 · THE PLINTH -------------------------------------------------- */

.ab-fx {
  position: relative;
  margin-top: var(--ab-s-12);   /* clearance for the docked status pill */
  padding-bottom: var(--ab-s-12);
  background: var(--ab-fx-bg);
  color: var(--ab-fx-fg);
  font-size: var(--ab-fs-md);
  /* isolation, NOT overflow:hidden — the status pill deliberately escapes the
     top edge, and clipping it would delete the signature. */
  isolation: isolate;
}

/* THE SEAM. A 1px emerald→blue hairline: a machined edge, not a border. It is
   the only ornament in the section. Decorative — the colour change itself
   carries the boundary, so nothing is lost in forced-colors or without it. */
.ab-fx::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--ab-a-600) 18%,
    var(--ab-p-400) 52%,
    transparent 100%
  );
  opacity: .6;
}

.ab-fx__inner {
  max-width: var(--ab-shell);
  margin-inline: auto;
  padding-inline: var(--ab-gutter);
}

.ab-fx svg { flex: none; }

/* Brand lockup on navy: §06 paints it with page tokens, which are invisible
   here. Three declarations, no duplication of the mark itself. */
.ab-fx .ab-brand        { color: var(--ab-fx-fg); }
.ab-fx .ab-brand:hover  { color: #FFFFFF; }
.ab-fx .ab-brand__mark  { color: var(--ab-fx-accent); }
.ab-fx .ab-brand__tail  { color: var(--ab-fx-muted); }


/* ---- 09.2 · STATUS BAR — the signature ---------------------------------
   Docked across the seam, pulled up out of the plinth so it floats half on the
   light page and casts a shadow onto it — the way a product docks a status
   widget over its shell. Every competitor puts a copyright line here. */

.ab-fx__status {
  max-width: var(--ab-shell);
  margin: -24px auto var(--ab-s-8);
  padding-inline: var(--ab-gutter);
}

.ab-fx__statusline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 var(--ab-s-4);
  margin: 0;
  padding: var(--ab-s-1) var(--ab-s-5);
  background: var(--ab-fx-raised);
  border: 1px solid var(--ab-fx-line-2);
  border-radius: var(--ab-r-lg);
  box-shadow: var(--ab-fx-shadow);
  color: var(--ab-fx-muted);
  font-family: var(--ab-font-mono);
  font-size: var(--ab-fs-sm);
  /* Tabular figures: the strip must not reflow when the count ticks over. */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: .01em;
}

@media (min-width: 900px) {
  .ab-fx__status { margin-top: -28px; }

  .ab-fx__statusline {
    flex-wrap: nowrap;
    padding-inline: var(--ab-s-8);
    border-radius: var(--ab-r-pill);
  }
}

/* The live dot. Ring scales and fades — transform and opacity only, so it
   never costs a layout and never repaints anything but itself. */
.ab-fx__dot {
  position: relative;
  flex: none;
  width: 8px;
  height: 8px;
  margin-inline-end: var(--ab-s-1);
  border-radius: 50%;
  background: var(--ab-fx-accent);
}
.ab-fx__dot::after {
  content: "";
  position: absolute;
  inset: -4px;
  border: 1px solid var(--ab-fx-accent);
  border-radius: 50%;
  opacity: 0;
  animation: ab-fx-pulse 2.6s var(--ab-ease) infinite;
}

@keyframes ab-fx-pulse {
  0%        { transform: scale(.55); opacity: .75; }
  70%, 100% { transform: scale(1.6); opacity: 0; }
}

.ab-fx__seg {
  display: inline-flex;
  align-items: center;
  min-height: 44px;      /* WCAG 2.5.5 — including the two links */
  padding-block: 2px;
}

.ab-fx__seg--state { color: var(--ab-fx-fg); font-weight: 700; }
.ab-fx__when       { color: var(--ab-fx-fg); }

/* Vertical rules, not pipe characters: a pipe is a glyph a screen reader can
   announce, and it never aligns. */
.ab-fx__seg + .ab-fx__seg {
  position: relative;
  padding-inline-start: var(--ab-s-4);
}
.ab-fx__seg + .ab-fx__seg::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 50%;
  width: 1px;
  height: 14px;
  transform: translateY(-50%);
  background: var(--ab-fx-line-2);
}

.ab-fx__seg--link {
  gap: var(--ab-s-1);
  color: var(--ab-fx-fg);
  text-decoration: none;
  white-space: nowrap;
}
.ab-fx__seg--link:hover { color: #FFFFFF; }
.ab-fx__seg--link .ab-icon { transition: transform var(--ab-dur) var(--ab-ease); }
.ab-fx__seg--link:hover .ab-icon { transform: translateX(2px); }

/* Push the two action links to the trailing edge on wide screens. */
@media (min-width: 900px) {
  .ab-fx__seg--link:nth-last-of-type(2) { margin-inline-start: auto; }
}


/* ---- 09.3 · THE UNDERLINE ----------------------------------------------
   One micro-interaction, used on every link in the footer. A 1px rule that
   grows from the leading edge. background-size only, so it is composited: no
   reflow, no repaint of the text. */

.ab-fx__u {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  padding-bottom: 1px;
  transition: background-size var(--ab-dur) var(--ab-ease);
}
[dir="rtl"] .ab-fx__u { background-position: 100% 100%; }

.ab-fx a:hover .ab-fx__u,
.ab-fx a:focus-visible .ab-fx__u { background-size: 100% 1px; }


/* ---- 09.4 · CONSOLE — the second-pageview engine ------------------------ */

.ab-fx__console {
  display: grid;
  gap: var(--ab-s-6);
  padding: var(--ab-s-6);
  background: var(--ab-fx-raised);
  border: 1px solid var(--ab-fx-line);
  border-radius: var(--ab-r-lg);
  box-shadow: var(--ab-fx-shadow);
}
.ab-fx__console:focus-within { border-color: var(--ab-fx-line-2); }

@media (min-width: 900px) {
  .ab-fx__console {
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
    gap: var(--ab-s-12);
    align-items: start;
    padding: var(--ab-s-8);
  }
}

.ab-fx__consoletitle {
  margin: 0 0 var(--ab-s-1);
  color: var(--ab-fx-fg);
  font-size: var(--ab-fs-h2);
  line-height: var(--ab-lh-tight);
  letter-spacing: -.01em;
}

.ab-fx__consolesub {
  margin: 0 0 var(--ab-s-4);
  max-width: 42ch;
  color: var(--ab-fx-muted);
  font-size: var(--ab-fs-md);
}

/* ---- The search form on navy.
   Targets the exact class API in searchform.php: __wrap / __input / __submit /
   __icon / __results. If that file is ever refactored these four rules must move
   with it, or the footer renders a light form on a dark card. ---------------- */

.ab-search--fx { max-width: none; }

.ab-search--fx .ab-search__input {
  background: var(--ab-fx-bg);
  border-color: var(--ab-fx-line-2);
  color: var(--ab-fx-fg);
  /* 16px floor: anything smaller and iOS zooms the whole page on focus, which
     at the very bottom of a long document is disorienting. --ab-fs-base is
     17px, so this is belt-and-braces against a future token change. */
  font-size: max(16px, var(--ab-fs-base));
}
.ab-search--fx .ab-search__input::placeholder { color: var(--ab-fx-dim); opacity: 1; }
.ab-search--fx .ab-search__input:hover { border-color: var(--ab-fx-muted); }
.ab-search--fx .ab-search__input:focus {
  background: var(--ab-fx-bg);
  border-color: var(--ab-fx-accent);
}
.ab-search--fx .ab-search__icon { color: var(--ab-fx-dim); }

/* Emerald, not Trust Blue: blue-on-navy is unreadable, and emerald is already
   the site's "do the thing" colour (--ab-btn-calc-*). #04122F on #14E19D is
   10.4:1; white on emerald would be 1.8:1. */
.ab-search--fx .ab-search__submit {
  background: var(--ab-fx-accent);
  color: var(--ab-p-950);
  font-weight: 700;
}
.ab-search--fx .ab-search__submit:hover { background: var(--ab-a-400); }

/* The type-ahead listbox must open UPWARD here — downward is off the document —
   and must be dark, because it renders on a dark card. Targeted through the
   form so it survives site.js renaming its internals. */
.ab-search--fx .ab-search__results {
  top: auto;
  bottom: calc(100% + 6px);
  background: var(--ab-fx-raised);
  border-color: var(--ab-fx-line-2);
  color: var(--ab-fx-fg);
}
.ab-search--fx .ab-search__result { color: var(--ab-fx-fg); }
.ab-search--fx .ab-search__result.is-active,
.ab-search--fx .ab-search__result[aria-selected="true"],
.ab-search--fx .ab-search__result:hover {
  background: var(--ab-fx-raised-2);
  color: #FFFFFF;
}

/* ---- Continuation strip.
   Node count is fixed at render: footer.js rewrites the text and href of these
   exact anchors and never adds or removes one, so the box cannot change size
   and CLS stays at zero with or without localStorage. --------------------- */

.ab-fx__recents { min-width: 0; }

.ab-fx__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ab-s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ab-fx__chip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--ab-s-4);
  border: 1px solid var(--ab-fx-line-2);
  border-radius: var(--ab-r-pill);
  color: var(--ab-fx-fg);
  font-size: var(--ab-fs-md);
  text-decoration: none;
  transition: background-color var(--ab-dur) var(--ab-ease),
              border-color     var(--ab-dur) var(--ab-ease),
              transform        var(--ab-dur) var(--ab-ease);
}
.ab-fx__chip:hover {
  background: var(--ab-fx-raised-2);
  border-color: var(--ab-fx-accent);
  color: #FFFFFF;
  transform: translateY(-1px);
}
/* The chip travels, like a key. */
.ab-fx__chip:active { transform: translateY(1px); }


/* ---- 09.5 · SHELF — three labelled <nav> landmarks ---------------------- */

.ab-fx__shelf {
  display: grid;
  gap: var(--ab-s-8);
  margin-top: var(--ab-s-12);
}

@media (min-width: 560px) {
  .ab-fx__shelf {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--ab-s-8) var(--ab-s-6);
  }
}

@media (min-width: 900px) {
  .ab-fx__shelf {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.15fr) minmax(0, .8fr);
    gap: var(--ab-s-12);
    margin-top: var(--ab-s-16);
  }
}

/* Column labels: overline + a 2px emerald tick. Besides the status dot and the
   submit button this is the only place the accent appears — it is what makes
   three columns read as one system instead of three lists. Source text is
   sentence case; the caps are CSS, so a screen reader is never handed an
   all-caps string to spell out. */
.ab-fx__label,
.ab-fx__eyebrow {
  margin: 0 0 var(--ab-s-3);
  padding-inline-start: var(--ab-s-3);
  border-inline-start: 2px solid var(--ab-fx-accent);
  color: var(--ab-fx-fg);
  font-size: var(--ab-fs-xs);
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: .09em;
  text-transform: uppercase;
}

.ab-fx__list { margin: 0; padding: 0; list-style: none; }

.ab-fx__link {
  display: grid;
  gap: 2px;
  align-content: center;
  min-height: 44px;
  padding-block: var(--ab-s-2);
  color: var(--ab-fx-fg);
  text-decoration: none;
}

.ab-fx__name {
  font-size: var(--ab-fs-md);
  font-weight: 600;
  line-height: 1.35;
}

/* Descriptions are rendered TEXT, never a title attribute. CalculatorSoup and
   RapidTables hide theirs there, where no phone and no screen reader ever
   surfaces them. */
.ab-fx__desc {
  max-width: 38ch;
  color: var(--ab-fx-dim);
  font-size: var(--ab-fs-sm);
  line-height: 1.45;
}
.ab-fx__link:hover .ab-fx__desc { color: var(--ab-fx-muted); }

/* Flat variant — the legal index. A legal index should read as an index, but
   every row is still a full 44px target: pipe-delimited inline text rows
   (RapidTables, CalculatorSoup, calculator.net) are an automatic fail here. */
.ab-fx__list--flat li a {
  display: flex;
  align-items: center;
  min-height: 44px;
  color: var(--ab-fx-muted);
  font-size: var(--ab-fs-md);
  text-decoration: none;
}
.ab-fx__list--flat li a:hover {
  color: var(--ab-fx-fg);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Exactly one escape hatch per discovery column, set off from the list. */
.ab-fx__more {
  display: inline-flex;
  align-items: center;
  gap: var(--ab-s-1);
  min-height: 44px;
  margin-top: var(--ab-s-2);
  padding-top: var(--ab-s-2);
  border-top: 1px solid var(--ab-fx-line);
  color: var(--ab-fx-accent);
  font-size: var(--ab-fs-md);
  font-weight: 700;
  text-decoration: none;
}
.ab-fx__more:hover { color: var(--ab-a-400); }
.ab-fx__more .ab-icon { transition: transform var(--ab-dur) var(--ab-ease); }
.ab-fx__more:hover .ab-icon { transform: translateX(3px); }

[dir="rtl"] .ab-fx__more .ab-icon,
[dir="rtl"] .ab-fx__seg--link .ab-icon { transform: scaleX(-1); }
[dir="rtl"] .ab-fx__more:hover .ab-icon { transform: scaleX(-1) translateX(3px); }
[dir="rtl"] .ab-fx__seg--link:hover .ab-icon { transform: scaleX(-1) translateX(2px); }


/* ---- 09.6 · DISCLOSURE STRATUM -----------------------------------------
   Readable body copy on a raised surface, on a reading measure. Not fine
   print, not an accordion, not 11px grey. */

.ab-fx__notes {
  display: grid;
  gap: var(--ab-s-4);
  margin-top: var(--ab-s-12);
}

@media (min-width: 760px) {
  .ab-fx__notes {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--ab-s-6);
  }
}

.ab-fx__note {
  padding: var(--ab-s-5);
  background: var(--ab-fx-raised);
  border: 1px solid var(--ab-fx-line);
  border-radius: var(--ab-r-md);
}

.ab-fx__note p {
  max-width: 62ch;
  margin: 0;
  color: var(--ab-fx-muted);
  font-size: var(--ab-fs-md);
  line-height: 1.65;
}

/* The persistent consent control. Only RapidTables has an equivalent in the
   entire competitive set. A real <button>, 44px, keyboard-operable — and
   [hidden] in the markup until footer.js confirms a handler exists, because a
   focusable control that does nothing is worse than no control. */
.ab-fx__btn {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: var(--ab-s-4);
  padding-inline: var(--ab-s-5);
  border: 1px solid var(--ab-fx-line-2);
  border-radius: var(--ab-r-md);
  background: transparent;
  color: var(--ab-fx-fg);
  font-size: var(--ab-fs-md);
  font-weight: 600;
  cursor: pointer;
  transition: background-color var(--ab-dur) var(--ab-ease),
              border-color     var(--ab-dur) var(--ab-ease);
}
.ab-fx__btn:hover {
  background: var(--ab-fx-raised-2);
  border-color: var(--ab-fx-accent);
}


/* ---- 09.7 · BASELINE ---------------------------------------------------- */

.ab-fx__base {
  display: grid;
  gap: var(--ab-s-6);
  margin-top: var(--ab-s-12);
  padding-top: var(--ab-s-8);
  border-top: 1px solid var(--ab-fx-line);
}

@media (min-width: 900px) {
  .ab-fx__base {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
    gap: var(--ab-s-12);
  }
}

.ab-fx__addr {
  display: grid;
  gap: var(--ab-s-1);
  color: var(--ab-fx-dim);
  font-size: var(--ab-fs-sm);
  font-style: normal;
  line-height: 1.6;
}

.ab-fx__entity { color: var(--ab-fx-muted); font-weight: 700; }
.ab-fx__sla    { color: var(--ab-fx-dim); }

.ab-fx__mail {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ab-fx-fg);
  text-decoration: none;
  overflow-wrap: anywhere;
}

.ab-fx__baseline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ab-s-4);
}

@media (min-width: 900px) {
  .ab-fx__baseline { justify-content: flex-end; text-align: end; }
  .ab-fx__copy     { flex: 1 1 100%; }
}

.ab-fx__copy {
  margin: 0;
  color: var(--ab-fx-dim);
  font-size: var(--ab-fs-sm);
  line-height: 1.7;
}

/* The credit, on the copyright baseline: same line, same size, one tone up so
   it reads as a signature rather than a footnote or an advertisement. */
.ab-fx__credit { color: var(--ab-fx-muted); }
.ab-fx__credit::before { content: " · "; color: var(--ab-fx-line-2); }
.ab-fx__credit a { color: var(--ab-fx-fg); text-decoration: none; }

@media (max-width: 559px) {
  .ab-fx__credit { display: block; }
  .ab-fx__credit::before { content: none; }
}

.ab-fx__top {
  display: inline-flex;
  align-items: center;
  gap: var(--ab-s-2);
  min-height: 44px;
  padding-inline: var(--ab-s-4);
  border: 1px solid var(--ab-fx-line-2);
  border-radius: var(--ab-r-pill);
  color: var(--ab-fx-fg);
  font-size: var(--ab-fs-sm);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--ab-dur) var(--ab-ease),
              border-color     var(--ab-dur) var(--ab-ease);
}
.ab-fx__top .ab-icon { transition: transform var(--ab-dur) var(--ab-ease); }
.ab-fx__top:hover { background: var(--ab-fx-raised); border-color: var(--ab-fx-accent); }
.ab-fx__top:hover .ab-icon { transform: translateY(-2px); }


/* ---- 09.8 · SAFE AREAS -------------------------------------------------
   §19's list no longer names anything in this section, so the three containers
   this surface actually uses declare their own. Notched phones in landscape put
   the first character of every line under the curve without it. */

@supports (padding: max(0px)) {
  .ab-fx__inner,
  .ab-fx__status,
  .ab-postmain {
    padding-inline-start: max(var(--ab-gutter), env(safe-area-inset-left));
    padding-inline-end:   max(var(--ab-gutter), env(safe-area-inset-right));
  }

  /* Clearance for the home indicator and, if one is ever added, the mobile
     anchor unit — the last thing in the document must not sit under it. */
  .ab-fx {
    padding-bottom: calc(var(--ab-s-12) + var(--ab-anchor-h, 0px) + env(safe-area-inset-bottom, 0px));
  }
}


/* ---- 09.9 · MOTION, FORCED COLOURS, PRINT ------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .ab-fx__dot::after { animation: none; opacity: 0; }

  .ab-fx__u,
  .ab-fx__chip,
  .ab-fx__btn,
  .ab-fx__top,
  .ab-fx__top .ab-icon,
  .ab-fx__more .ab-icon,
  .ab-fx__seg--link .ab-icon { transition: none; }

  .ab-fx__chip:hover,
  .ab-fx__chip:active { transform: none; }

  /* The underline becomes static, not absent: it is the only affordance on
     several of these links. */
  .ab-fx__u { background-size: 100% 1px; }
}

@media (forced-colors: active) {
  .ab-fx {
    background: Canvas;
    color: CanvasText;
    border-top: 1px solid CanvasText;
  }
  .ab-fx::before { background: CanvasText; opacity: 1; }

  .ab-fx__statusline,
  .ab-fx__console,
  .ab-fx__note,
  .ab-fx__chip,
  .ab-fx__btn,
  .ab-fx__top { border: 1px solid CanvasText; background: Canvas; }

  .ab-fx__dot { background: Highlight; }
  .ab-fx__u   { background-image: none; text-decoration: underline; }

  .ab-fx__label,
  .ab-fx__eyebrow { border-inline-start-color: CanvasText; }

  .ab-fx__base,
  .ab-fx__more,
  .ab-fx__seg + .ab-fx__seg::before { border-color: CanvasText; }
}

/* A printed amortisation schedule should carry its "not financial advice" line
   and the name of whoever published it. Everything navigational is furniture.
   Declared here rather than in §19 so it follows that section's global print
   block and wins where the two would otherwise disagree. */
@media print {
  .ab-postmain,
  .ab-fx::before,
  .ab-fx__status,
  .ab-fx__console,
  .ab-fx__shelf,
  .ab-fx__btn,
  .ab-fx__top { display: none !important; }

  .ab-fx {
    margin-top: var(--ab-s-8);
    padding: var(--ab-s-4) 0 0;
    background: none;
    color: #000;
    border-top: 1px solid #999;
  }

  .ab-fx__notes { display: block; margin-top: 0; }
  .ab-fx__note  { padding: 0; border: 0; background: none; }
  .ab-fx__note + .ab-fx__note { margin-top: var(--ab-s-4); }

  .ab-fx__note p,
  .ab-fx__copy,
  .ab-fx__addr,
  .ab-fx__entity,
  .ab-fx__eyebrow,
  .ab-fx__credit,
  .ab-fx__credit a { color: #000; }

  .ab-fx__eyebrow { border-inline-start-color: #000; }
  .ab-fx__base    { margin-top: var(--ab-s-4); padding-top: var(--ab-s-4); border-top-color: #999; }
}


/* ==========================================================================
   10 · BREADCRUMBS — one component, one prefix (.ab-breadcrumbs)
   ========================================================================== */

.ab-breadcrumbs {
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-muted);
}

.ab-breadcrumbs__list {
  display: flex;
  flex-wrap: wrap;          /* wraps rather than scrolls: no hidden crumbs */
  align-items: center;
  gap: var(--ab-s-1) var(--ab-s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.ab-breadcrumbs__item {
  display: inline-flex;
  align-items: center;
  gap: var(--ab-s-2);
  min-height: 24px;
  min-width: 0;
}

.ab-breadcrumbs__item + .ab-breadcrumbs__item::before {
  content: "/";
  color: var(--ab-border-strong);
}

.ab-breadcrumbs__link { color: var(--ab-text-muted); text-decoration: none; }
.ab-breadcrumbs__link:hover { color: var(--ab-link); text-decoration: underline; }

/* Long programmatic slugs must not blow out the line on a phone. */
.ab-breadcrumbs__current {
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ab-text);
  font-weight: 600;
}
@media (min-width: 640px) {
  .ab-breadcrumbs__current { max-width: min(48ch, 100%); }
}


/* ==========================================================================
   11 · BUTTONS — 44px tap target on the base; modifiers change colour only.
   ========================================================================== */

.ab-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ab-s-2);
  min-height: 44px;
  padding: 0.5rem 1.125rem;
  border: 1px solid transparent;
  border-radius: var(--ab-r-md);
  background: var(--ab-surface-2);
  color: var(--ab-text);
  font-size: var(--ab-fs-md);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  transition: background-color var(--ab-dur) var(--ab-ease),
              border-color var(--ab-dur) var(--ab-ease),
              color var(--ab-dur) var(--ab-ease),
              transform var(--ab-dur) var(--ab-ease);
}
.ab-btn:hover  { color: var(--ab-text); }
.ab-btn:active { transform: translateY(1px); }
.ab-btn svg    { width: 18px; height: 18px; flex: none; }

.ab-btn[disabled],
.ab-btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; transform: none; }
.ab-btn[aria-busy="true"] { pointer-events: none; opacity: .7; }

.ab-btn--primary {
  background: var(--ab-btn-primary-bg);
  color: var(--ab-btn-primary-fg);
}
.ab-btn--primary:hover {
  background: var(--ab-btn-primary-bg-hover);
  color: var(--ab-btn-primary-fg);
}

/* Calculate — the one loud element on the page. */
.ab-btn--calc {
  min-height: 48px;
  padding-inline: var(--ab-s-6);
  background: var(--ab-btn-calc-bg);
  color: var(--ab-btn-calc-fg);
  font-size: 1.0625rem;
  font-weight: 700;
}
.ab-btn--calc:hover {
  background: var(--ab-btn-calc-bg-hover);
  color: var(--ab-btn-calc-fg);
}

/* Ghost — reset / secondary. */
.ab-btn--ghost {
  background: transparent;
  border-color: var(--ab-border-input);
  color: var(--ab-text-muted);
}
.ab-btn--ghost:hover {
  background: var(--ab-surface-2);
  color: var(--ab-text);
}

.ab-btn--block { width: 100%; }

/* A button that has to read as a link: the consent control in the footer sits
   inside a sentence, so it must inherit that sentence's type and colour while
   keeping a real 44px target. */
.ab-linkbtn {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0;
  color: var(--ab-link);
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.ab-linkbtn:hover { color: var(--ab-link-hover); }


/* ==========================================================================
   12 · FORMS & FIELDS
   Control boundaries use --ab-border-input (>= 3:1 in both themes, SC 1.4.11).
   ========================================================================== */

.ab-field {
  display: flex;
  flex-direction: column;
  gap: var(--ab-s-1);
  min-width: 0;
}

.ab-field__label {
  font-size: var(--ab-fs-md);
  font-weight: 600;
  line-height: 1.3;
  color: var(--ab-text);
}

.ab-field__control {
  display: flex;
  align-items: stretch;
  gap: var(--ab-s-2);
  min-width: 0;
}
.ab-field__control > .ab-input { flex: 1 1 auto; min-width: 0; }

.ab-field__unit {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  padding-inline: var(--ab-s-3);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
  background: var(--ab-surface-2);
  color: var(--ab-text-muted);
  font-size: var(--ab-fs-md);
  font-weight: 600;
  white-space: nowrap;
}

.ab-field__help { font-size: var(--ab-fs-sm); color: var(--ab-text-subtle); line-height: 1.45; }
.ab-field__error { font-size: var(--ab-fs-sm); font-weight: 600; color: var(--ab-danger); }

.ab-input {
  width: 100%;
  min-height: 44px;
  padding: 0.5rem 0.75rem;
  background: var(--ab-surface);
  border: 1px solid var(--ab-border-input);
  border-radius: var(--ab-r-md);
  color: var(--ab-text);
  font-size: var(--ab-fs-base);   /* 17px — never triggers iOS zoom */
  line-height: 1.4;
  appearance: none;
  transition: border-color var(--ab-dur) var(--ab-ease);
}
.ab-input::placeholder { color: var(--ab-text-subtle); opacity: 1; }
.ab-input:hover { border-color: var(--ab-text-subtle); }
.ab-input:focus-visible { border-color: var(--ab-focus); outline-offset: 1px; }
.ab-input[disabled] { background: var(--ab-surface-2); color: var(--ab-text-subtle); cursor: not-allowed; }
.ab-input[aria-invalid="true"] { border-color: var(--ab-danger); }

/* Native spinners are a mis-tap magnet; keyboard entry is unaffected. */
.ab-input[type="number"] { -moz-appearance: textfield; }
.ab-input[type="number"]::-webkit-outer-spin-button,
.ab-input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* Select is .ab-input + .ab-select. Chevron is an inline data URI in both
   themes — no extra request, no theme-specific rule duplication elsewhere. */
.ab-select {
  padding-inline-end: 2.25rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23475569' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  background-size: 16px 16px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ab-select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23A9B8CE' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  }
}
:root[data-theme="dark"] .ab-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23A9B8CE' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
}
[dir="rtl"] .ab-select { background-position: left 0.75rem center; }


/* ==========================================================================
   13 · CALCULATOR CARD + BASIC KEYPAD
   Bound by the plugin's calc-core.js via .ab-calc[data-calc-type].
   ========================================================================== */

/* No shadow. One elevated object per screen, and on a query page that object
   is the answer (§14) — the tool is the secondary offer below it. The 1px
   border carries the card on its own. */
.ab-calc {
  background: var(--ab-surface);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-lg);
  box-shadow: none;
  padding: var(--ab-s-4);
  scroll-margin-top: calc(var(--ab-header-h) + var(--ab-s-4));
}
@media (min-width: 600px) {
  .ab-calc { padding: var(--ab-s-6); }
}

.ab-calc__title {
  font-size: var(--ab-fs-h2);
  font-weight: 700;
  line-height: var(--ab-lh-tight);
  margin-bottom: var(--ab-s-4);
}
.ab-calc__note { font-size: var(--ab-fs-sm); color: var(--ab-text-subtle); }

.ab-calc__fields {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ab-s-4);
}
@media (min-width: 34em) {
  .ab-calc__fields--multi { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.ab-calc__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ab-s-3);
  align-items: center;
  margin-top: var(--ab-s-6);
}
.ab-calc__actions .ab-btn { width: 100%; }
@media (min-width: 34em) {
  .ab-calc__actions .ab-btn { width: auto; min-width: 12rem; }
}

/* Runtime validation message for the tool as a whole (empty or unparseable
   input). Paired with .ab-field__error, which speaks for a single field. */
.ab-calc__error {
  margin-top: var(--ab-s-3);
  font-size: var(--ab-fs-md);
  font-weight: 600;
  color: var(--ab-danger);
}

/* Phones: the result must sit ABOVE the Calculate button.
   DOM order is fields → Calculate → result, so on a 390x844 screen with the
   keyboard up both the button and the 116px result well are below the fold.
   Reordering visually costs nothing and keeps the DOM/tab order sane, because
   the form's submit button stays the last control either way. */
@media (max-width: 33.999em) {
  .ab-calc { display: flex; flex-direction: column; }
  .ab-calc > h2,
  .ab-calc > .ab-calc__note { order: 0; }
  .ab-calc__fields  { order: 1; }
  .ab-result        { order: 2; margin-top: var(--ab-s-4); }
  .ab-calc__actions { order: 3; margin-top: var(--ab-s-4); }
  .ab-calc__error   { order: 4; }
}

/* ---- Basic keypad (.ab-basic) ------------------------------------------- */

.ab-basic {
  width: 100%;
  max-width: 380px;
  margin-inline: auto;
  padding: var(--ab-s-3);
  background: var(--ab-surface);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-lg);
  box-shadow: var(--ab-shadow-sm);
}

/* Fixed min-height: a long result never grows the card and shoves the page
   down. Digits shrink as they accumulate (calc-core writes data-len) rather
   than disappearing off an edge nobody knows they can scroll — the 76px
   reservation already gives the room. Start-aligned so the leading digits,
   which are the ones that carry magnitude, are the ones that stay visible. */
.ab-basic__screen {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  min-height: 76px;
  padding: var(--ab-s-2) var(--ab-s-3);
  margin-bottom: var(--ab-s-3);
  color: var(--ab-text);
  background: var(--ab-surface-2);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
  font-size: clamp(1.25rem, 1rem + 3.2vw, 2.25rem);
  font-weight: 700;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
}
[data-ab-screen][data-len="12"] { font-size: 1.5rem; }
[data-ab-screen][data-len="16"] { font-size: 1.125rem; }

.ab-basic__keys {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--ab-s-2);
}

.ab-key {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 58px;              /* thumb-sized, well past the 44px target */
  padding: 0;
  color: var(--ab-text);
  background: var(--ab-key-bg);
  border: 1px solid var(--ab-key-bd);
  border-radius: var(--ab-r-md);
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;    /* kills the 300ms double-tap-zoom delay */
  transition: background-color var(--ab-dur) var(--ab-ease),
              border-color var(--ab-dur) var(--ab-ease),
              transform var(--ab-dur) var(--ab-ease);
}
@media (min-width: 760px) {
  .ab-key { min-height: 52px; }
}
.ab-key:hover  { background: var(--ab-key-bg-hover); }
.ab-key:active { transform: translateY(1px); }

/* Operators in primary blue — the eye separates "what to do" from "the number". */
.ab-key--op {
  color: var(--ab-key-op-fg);
  background: var(--ab-key-op-bg);
  border-color: var(--ab-key-op-bd);
  font-size: 1.375rem;
}
.ab-key--op:hover { background: var(--ab-key-op-bg-hover); }

/* Functions (C, backspace, %) stay quiet — one of them is destructive. */
.ab-key--fn {
  color: var(--ab-key-fn-fg);
  background: var(--ab-key-fn-bg);
  border-color: var(--ab-key-fn-bd);
  font-size: 1.0625rem;
}
.ab-key--fn:hover { background: var(--ab-key-fn-bg-hover); }

/* Equals is the single Action-Emerald element on the page. */
.ab-key--eq {
  color: var(--ab-btn-calc-fg);
  background: var(--ab-btn-calc-bg);
  border-color: transparent;
  font-weight: 700;
}
.ab-key--eq:hover { background: var(--ab-btn-calc-bg-hover); }

.ab-key--wide { grid-column: span 2; }


/* ==========================================================================
   14 · RESULT · STEPS · ANSWER · FORMULA · FAQ
   ========================================================================== */

/* Reserved height BEFORE the first calculation, so injecting the answer never
   shifts the page. */
.ab-result {
  min-height: 116px;
  margin-top: var(--ab-s-6);
  padding: var(--ab-s-4) var(--ab-s-6);
  background: var(--ab-surface-tint);
  border: 1px solid var(--ab-ans-bd);
  border-radius: var(--ab-r-md);
}

.ab-result__label,
.ab-result__stepslabel,
.ab-answer__label {
  font-size: var(--ab-fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ab-text-subtle);
}
.ab-result__stepslabel { margin-top: var(--ab-s-4); }

.ab-result__value,
.ab-answer__value {
  margin-top: var(--ab-s-1);
  font-size: var(--ab-fs-result);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--ab-ans-figure);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* Placeholder copy must not shout at the size of a result figure. */
.ab-result__placeholder {
  font-size: var(--ab-fs-md);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--ab-text-subtle);
}

/* The working area is hidden by an attribute, not a selector: the wrapper
   ships as <div class="ab-result__steps" hidden data-ab-steps-wrap> and
   calc-core removes `hidden` when it has steps to show. The old :has() rule
   plus its @supports fallback hid two different elements — the fallback hid
   the <ol>, leaving a permanent WORKING label over nothing — and put a :has()
   dependency on a critical surface. [hidden] works everywhere (§01). */

/* ---- Steps (static "How this is calculated" and JS-filled working) ------- */

.ab-steps {
  list-style: none;
  counter-reset: ab-step;
  display: flex;
  flex-direction: column;
  gap: var(--ab-s-3);
  max-width: var(--ab-measure);
}
.ab-steps > li,
.ab-steps > div,
.ab-steps > p {
  position: relative;
  counter-increment: ab-step;
  padding-inline-start: 2.5rem;
  min-height: 1.75rem;
  color: var(--ab-text);
  font-variant-numeric: tabular-nums;
}
.ab-steps > li::before,
.ab-steps > div::before,
.ab-steps > p::before {
  content: counter(ab-step);
  position: absolute;
  inset-inline-start: 0;
  top: 0.1em;
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--ab-r-pill);
  background: var(--ab-step-bg);
  color: var(--ab-step-fg);
  font-size: var(--ab-fs-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.ab-steps--live { margin-top: var(--ab-s-2); }

/* ---- The answer block (the hero of every query page) --------------------
   This is the whole product: 10,000 pages exist to deliver this figure to
   somebody who arrived cold from Google. It used to be the quietest object on
   the screen — a ~2% luminance tint that is invisible outdoors, no elevation
   while the secondary tool below it carried a shadow, and a figure that
   resolved SMALLER than the H1 restating the question. Now: a plain surface
   card, the page's only shadow, an accent cap that names it without a label,
   and a figure at --ab-fs-value that clears the H1 at every viewport. */

.ab-answer {
  position: relative;
  padding: var(--ab-s-8) var(--ab-s-6);
  background: var(--ab-surface);
  border: 1px solid var(--ab-ans-bd);
  border-radius: var(--ab-r-lg);
  box-shadow: var(--ab-shadow-md);
  overflow: hidden;
}
.ab-answer::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 4px;
  background: var(--ab-btn-calc-bg);
}
.ab-answer__label { margin-bottom: var(--ab-s-2); }
.ab-answer__value {
  font-size: var(--ab-fs-value);
  line-height: 1.05;
  letter-spacing: -0.035em;
  font-variant-numeric: tabular-nums lining;
  color: var(--ab-text);
}
.ab-answer__summary {
  margin-top: var(--ab-s-3);
  max-width: var(--ab-measure);
  color: var(--ab-text-muted);
}

/* One line of provenance directly under the figure — the only trust signal on
   the page cold search traffic actually lands on. */
.ab-answer__trust {
  margin-top: var(--ab-s-3);
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-subtle);
}

/* Reviewer / review-date credit. These are E-E-A-T signals, so they read as
   deliberate attribution rather than fine print — muted, but not hidden. */
.ab-calcpage__reviewer {
  color: var(--ab-text-muted);
  font-weight: 600;
  text-decoration: none;
}
a.ab-calcpage__reviewer:hover { color: var(--ab-link); text-decoration: underline; }

.ab-calcpage__review {
  display: inline-flex;
  align-items: center;
  gap: var(--ab-s-1);
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-subtle);
}

/* ---- Formula ------------------------------------------------------------ */

.ab-formula__box {
  padding: var(--ab-s-4) var(--ab-s-6);
  background: var(--ab-surface-2);
  border: 1px solid var(--ab-border);
  border-inline-start: 3px solid var(--ab-link);
  border-radius: var(--ab-r-md);
  font-family: var(--ab-font-mono);
  font-size: var(--ab-fs-md);
  line-height: 1.7;
  overflow-x: auto;
}
.ab-formula__box p + p { margin-top: var(--ab-s-3); }

/* ---- Sources ("Where this formula comes from") -------------------------- */

.ab-sources {
  display: grid;
  gap: var(--ab-s-3);
  max-width: var(--ab-measure);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ab-sources__item {
  padding-inline-start: var(--ab-s-4);
  border-inline-start: 2px solid var(--ab-border);
  font-size: var(--ab-fs-md);
  line-height: 1.5;
}
.ab-sources__pub { color: var(--ab-text-subtle); }

/* ---- FAQ (native details/summary, no JS) -------------------------------- */

.ab-faq {
  display: flex;
  flex-direction: column;
  max-width: var(--ab-measure);
  border-top: 1px solid var(--ab-border);
}

.ab-faq__item { border-bottom: 1px solid var(--ab-border); }

.ab-faq__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ab-s-4);
  min-height: 56px;
  padding: var(--ab-s-3) 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--ab-text);
  list-style: none;
  cursor: pointer;
}
.ab-faq__q::-webkit-details-marker { display: none; }
.ab-faq__q::marker { content: ""; }
.ab-faq__q:hover { color: var(--ab-link); }
.ab-faq__q:focus-visible { outline-offset: -3px; }

/* Chevron drawn with borders — no icon font, no extra markup. */
.ab-faq__q::after {
  content: "";
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  margin-inline-end: 0.35rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  color: var(--ab-text-subtle);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--ab-dur) var(--ab-ease);
}
.ab-faq__item[open] > .ab-faq__q::after { transform: rotate(-135deg) translate(-2px, -2px); }
.ab-faq__item[open] > .ab-faq__q { color: var(--ab-text); }

.ab-faq__a {
  padding: 0 0 var(--ab-s-4);
  color: var(--ab-text-muted);
  max-width: var(--ab-measure);
}


/* ==========================================================================
   15 · CARDS, GRIDS, CHIPS
   ONE card component: article.ab-card > h?.ab-card__title > a.ab-card__link.
   The link is stretched over the whole card — one tab stop, full-card target.
   ========================================================================== */

.ab-cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ab-s-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 34rem) {
  .ab-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 56rem) {
  .ab-cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Dense variant for the programmatic internal-link surfaces. */
.ab-cards--compact { gap: var(--ab-s-3); }
@media (min-width: 34rem) {
  .ab-cards--compact { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 52rem) {
  .ab-cards--compact { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* An in-grid ad cell: a real unit at card position 6, where the reader is
   still choosing, instead of a leaderboard stranded above the first card. */
.ab-cards__adcell { grid-column: 1 / -1; }
.ab-cards__adcell .ab-ad { margin-block: var(--ab-s-2); }

.ab-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--ab-s-2);
  min-height: 44px;
  padding: var(--ab-s-4);
  background: var(--ab-surface);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-lg);
  color: var(--ab-text);
  transition: border-color var(--ab-dur) var(--ab-ease),
              background-color var(--ab-dur) var(--ab-ease);
}
@media (hover: hover) {
  .ab-card:hover {
    border-color: var(--ab-card-hover-bd);
    background: var(--ab-card-hover-bg);
  }
}

.ab-card__title {
  margin: 0;
  font-size: var(--ab-fs-lg);
  font-weight: 700;
  line-height: var(--ab-lh-tight);
}

.ab-card__link { color: var(--ab-text); text-decoration: none; }
.ab-card__link::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.ab-card__link:hover { color: var(--ab-link); text-decoration: underline; }

/* Ring the card, not just the text, when the stretched link takes focus.
   Where :has() is available that outer ring is the ONLY ring — the inner one
   on the link itself is redundant and draws a second box through the middle
   of the card. Where :has() is not, the inner ring is the whole fallback, so
   it is only ever suppressed inside the @supports. */
.ab-card:has(.ab-card__link:focus-visible) {
  outline: 3px solid var(--ab-focus);
  outline-offset: 2px;
}
@supports selector(:has(*)) {
  .ab-card__link:focus-visible { outline: none; }
}

.ab-card__excerpt { margin: 0; font-size: var(--ab-fs-md); color: var(--ab-text-muted); }

/* The result belongs on the card as a value, not buried in an excerpt: a link
   grid that shows its answers is the second pageview. */
.ab-card__answer {
  margin: 0;
  font-size: var(--ab-fs-lg);
  font-weight: 700;
  color: var(--ab-ans-figure);
}

.ab-card__meta {
  margin: 0;
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-subtle);
  font-variant-numeric: tabular-nums;
}

/* Example answer-page chips sit above the stretched overlay so they stay
   individually clickable. */
.ab-card__examples {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ab-s-2);
  margin: auto 0 0;              /* pins chips to the bottom of an uneven row */
  padding: var(--ab-s-1) 0 0;
  list-style: none;
  position: relative;
  z-index: 1;
}
.ab-card__example { display: inline-flex; }

/* Compact variants: category tiles and the programmatic link grid. */
.ab-card--cat,
.ab-card--compact {
  gap: 2px;
  padding: var(--ab-s-3) var(--ab-s-4);
  border-radius: var(--ab-r-md);
  justify-content: center;
}
.ab-card--cat .ab-card__title,
.ab-card--compact .ab-card__title {
  font-size: var(--ab-fs-md);
  font-weight: 600;
}
.ab-card--compact .ab-card__link { color: var(--ab-link); }

/* ---- Chips -------------------------------------------------------------- */

.ab-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ab-s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.ab-chips--center { justify-content: center; }

.ab-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--ab-s-2);
  min-height: 36px;
  padding: 0.25rem 0.75rem;
  background: var(--ab-surface-2);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-pill);
  font-size: var(--ab-fs-sm);
  font-weight: 600;
  color: var(--ab-text-muted);
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--ab-dur) var(--ab-ease),
              border-color var(--ab-dur) var(--ab-ease),
              color var(--ab-dur) var(--ab-ease);
}
.ab-chip:hover {
  background: var(--ab-chip-hover-bg);
  border-color: var(--ab-chip-hover-bd);
  color: var(--ab-chip-hover-fg);
}
.ab-chip[aria-current="page"],
.ab-chip--active {
  background: var(--ab-chip-on-bg);
  border-color: var(--ab-chip-on-bg);
  color: var(--ab-chip-on-fg);
}
@media (pointer: coarse) {
  .ab-chip { min-height: 44px; }
}

/* Primary-navigation chip row (homepage quick links). */
.ab-chip--lg { min-height: 44px; padding-inline: var(--ab-s-4); }
@media (min-width: 760px) {
  .ab-chip--lg { min-height: 38px; padding-inline: var(--ab-s-3); }
}


/* ==========================================================================
   16 · SURFACES — calculator page, hub, page, search, 404, pagination

   THE HOMEPAGE IS NO LONGER HERE. front-page.php is now "Console & Tape" and
   emits .ab-stage / .ab-console / .ab-tape / .ab-board / .ab-shelf /
   .ab-colophon / .ab-dateline / .ab-home-hero / .ab-rubric — all of which live
   in §21. The old .ab-hero / .ab-tool__* / .ab-keyhelp* / .ab-trust* family had
   exactly one call site, that template, and has been deleted with it. The new
   surface reuses .ab-container, .ab-section, .ab-cards, .ab-card, .ab-chip and
   .ab-chips from §15/§16 unchanged, per the v1 class contract.
   ========================================================================== */

/* ---- Calculator page ---------------------------------------------------- */

.ab-calcpage__inner {
  width: 100%;
  max-width: var(--ab-wide);
  margin-inline: auto;
  padding-inline: var(--ab-gutter);
  display: flex;
  flex-direction: column;
  gap: var(--ab-s-6);
}

/* Rhythm around the answer: bind it to the H1 that asks the question, keep the
   supporting sections close, then break HARD before the tool. The size of that
   last gap is the whole "your answer is above, a calculator is below" story. */
.ab-calcpage__inner > .ab-answer { margin-top: var(--ab-s-2); }
.ab-calcpage__inner > .ab-answer + .ab-section { margin-top: var(--ab-s-4); }
.ab-calcpage__inner > .ab-calc { margin-top: var(--ab-s-16); }

/* ---- Desktop ad rail ---------------------------------------------------
   At 1440px the 840px column left ~300px of dead gutter on each side while a
   fully styled, sticky, priced 300x600 well (.ab-ad--rail, §17) had no call
   site at all. Below 1180 the aside is not rendered at all — the reservation
   would be 600px of nothing on a phone. The max-width is the sum of what is
   actually inside it (840 + 300 + a 2rem column gap + both gutters), so the
   main column resolves to exactly --ab-wide once there is room for it. */
.ab-calcpage__rail { display: none; }

@media (min-width: 1180px) {
  .ab-calcpage__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--ab-rail);
    column-gap: var(--ab-s-8);
    row-gap: var(--ab-s-6);
    max-width: calc(var(--ab-wide) + var(--ab-rail) + var(--ab-s-8) + 2 * var(--ab-gutter));
    align-items: start;
  }
  /* The grid switches this container off flex, so the margin-top overrides
     above keep doing the answer/tool spacing — row-gap only replaces `gap`. */
  .ab-calcpage__inner > *:not(.ab-calcpage__rail) { grid-column: 1; }

  .ab-calcpage__rail {
    display: block;
    grid-column: 2;
    /* Every row on this page is implicit, and a negative grid line counts back
       from the end of the EXPLICIT grid — so `1 / -1` collapses to one row and
       inflates the breadcrumb row instead of spanning the page. Span a large
       count so the track covers however many rows the template generates. */
    grid-row: 1 / span 999;
    align-self: stretch;   /* full-height track, or the sticky ad cannot travel */
  }
}

.ab-calcpage__header { display: flex; flex-direction: column; gap: var(--ab-s-2); }
.ab-calcpage__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ab-s-2);
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-subtle);
}
.ab-calcpage__sep { color: var(--ab-border-strong); }
.ab-calcpage__hublink { color: var(--ab-link); }

.ab-calc404 { max-width: var(--ab-measure); }
.ab-calc404__lead { color: var(--ab-text-muted); }
.ab-calc404__cta { margin-top: var(--ab-s-4); }

.ab-links__more { margin-top: var(--ab-s-4); font-size: var(--ab-fs-md); }

/* ---- Category hub ------------------------------------------------------- */

.ab-hub__header { margin-bottom: var(--ab-s-6); }
.ab-hub__title { margin-bottom: var(--ab-s-3); }
.ab-hub__intro { color: var(--ab-text-muted); }
.ab-hub__count {
  margin-top: var(--ab-s-2);
  font-size: var(--ab-fs-md);
  color: var(--ab-text-subtle);
  font-variant-numeric: tabular-nums;
}

/* ---- Static page (About, Contact, legal) -------------------------------- */

.ab-page__header {
  margin-bottom: var(--ab-s-6);
  padding-bottom: var(--ab-s-4);
  border-bottom: 1px solid var(--ab-border);
}
.ab-page__meta {
  margin-top: var(--ab-s-2);
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-subtle);
  font-variant-numeric: tabular-nums;
}
.ab-page__thumb {
  margin-block: var(--ab-s-6);
  border-radius: var(--ab-r-lg);
  overflow: hidden;
}
.ab-page__thumb img { width: 100%; }

/* Clear the sticky header when a TOC link jumps to a section. */
.ab-page__content h2 { scroll-margin-top: calc(var(--ab-header-h) + var(--ab-s-4)); }

.ab-toc {
  margin-block: var(--ab-s-6);
  padding: var(--ab-s-4);
  background: var(--ab-surface-2);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
}
.ab-toc__title {
  margin-bottom: var(--ab-s-2);
  font-size: var(--ab-fs-sm);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ab-text-subtle);
}
.ab-toc__list { counter-reset: ab-toc; display: grid; gap: var(--ab-s-1); list-style: none; margin: 0; padding: 0; }
.ab-toc__item { counter-increment: ab-toc; }
.ab-toc__link {
  display: inline-block;
  padding-block: var(--ab-s-1);
  font-size: var(--ab-fs-md);
  text-decoration: none;
}
.ab-toc__link::before {
  content: counter(ab-toc) ".";
  margin-inline-end: var(--ab-s-2);
  color: var(--ab-text-subtle);
  font-variant-numeric: tabular-nums;
}
.ab-toc__link:hover { text-decoration: underline; }

/* ---- Tables — the page never scrolls sideways --------------------------- */

.ab-table-wrap {
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  margin-block: var(--ab-s-6);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
}
.ab-table-wrap:focus-visible { outline: 3px solid var(--ab-focus); }

.ab-table-wrap table {
  min-width: 100%;
  border-collapse: collapse;
  font-size: var(--ab-fs-md);
  background: var(--ab-surface);
}
.ab-table-wrap caption {
  caption-side: top;
  text-align: start;
  padding: var(--ab-s-3) var(--ab-s-4);
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-subtle);
}
.ab-table-wrap th,
.ab-table-wrap td {
  padding: 0.625rem 0.875rem;
  text-align: start;
  border-bottom: 1px solid var(--ab-border);
  vertical-align: top;
}
.ab-table-wrap thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--ab-surface-2);
  font-size: var(--ab-fs-sm);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ab-text-muted);
  white-space: nowrap;
}
.ab-table-wrap tbody tr:last-child td { border-bottom: 0; }
.ab-table-wrap tbody tr:nth-child(even) td { background: var(--ab-bg-alt); }

/* ---- Search results ----------------------------------------------------- */

.ab-results__header { margin-bottom: var(--ab-s-6); }
.ab-results__title { overflow-wrap: anywhere; }
.ab-results__query { color: var(--ab-link); }
.ab-results__count {
  margin-top: var(--ab-s-2);
  color: var(--ab-text-muted);
  font-size: var(--ab-fs-md);
  font-variant-numeric: tabular-nums;
}
.ab-results__form { margin-top: var(--ab-s-4); max-width: 34rem; }

/* ---- Empty states & 404 ------------------------------------------------- */

.ab-empty {
  padding: var(--ab-s-6) var(--ab-s-4);
  background: var(--ab-surface-2);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-lg);
}
.ab-empty__lead { margin-bottom: var(--ab-s-4); font-size: var(--ab-fs-lg); color: var(--ab-text); }

.ab-404__header { margin-bottom: var(--ab-s-8); }
.ab-404__code {
  font-size: clamp(3rem, 2rem + 6vw, 5rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ab-404-code);
  font-variant-numeric: tabular-nums;
}
.ab-404__title { margin-block: var(--ab-s-2) var(--ab-s-3); }
.ab-404__lead { color: var(--ab-text-muted); max-width: var(--ab-measure); }
.ab-404__search { margin-top: var(--ab-s-6); max-width: 34rem; }
.ab-404__home { margin-top: var(--ab-s-8); }

/* ---- Pagination --------------------------------------------------------- */

.ab-pagination { margin-top: var(--ab-s-6); }

.ab-pagination ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--ab-s-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.ab-pagination .page-numbers {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding-inline: var(--ab-s-3);
  font-size: var(--ab-fs-md);
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  color: var(--ab-link);
  background: var(--ab-surface);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
}
.ab-pagination .page-numbers:hover { border-color: var(--ab-border-strong); }

.ab-pagination .page-numbers.current,
.ab-pagination [aria-current="page"] {
  background: var(--ab-chip-on-bg);
  border-color: var(--ab-chip-on-bg);
  color: var(--ab-chip-on-fg);
  font-weight: 700;
}
.ab-pagination .page-numbers.dots {
  background: none;
  border-color: transparent;
  color: var(--ab-text-subtle);
  min-width: 1.5rem;
}

.ab-pagination--inline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ab-s-2);
  margin-top: var(--ab-s-6);
}
.ab-pagination__label { font-size: var(--ab-fs-sm); color: var(--ab-text-subtle); }
.ab-pagination__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding-inline: var(--ab-s-2);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
}


/* ---- Tap feedback on touch devices -------------------------------------
   -webkit-tap-highlight-color is transparent document-wide (§01) and every
   card hover is correctly gated behind (hover: hover), which between them
   left cards, chips, nav items and pagination with literally no response to a
   tap until the next page painted. Card grids ARE the internal-linking
   mechanic for 10,000 pages, so this is the site's primary navigation.
   Deliberately placed after every component it names, so it wins on source
   order, and deliberately OUTSIDE prefers-reduced-motion: these are colour
   changes, not motion, and must survive it. */
@media (hover: none) {
  /* Split, not one selector list: an engine without :has() would otherwise
     drop the plain :active half with it and the cards would stay dead. */
  .ab-card:active {
    background: var(--ab-card-hover-bg);
    border-color: var(--ab-card-hover-bd);
  }
  .ab-card:has(.ab-card__link:active) {
    background: var(--ab-card-hover-bg);
    border-color: var(--ab-card-hover-bd);
  }
  .ab-chip:active,
  .ab-linklist--panel a:active,
  .ab-pagination .page-numbers:active {
    background: var(--ab-chip-hover-bg);
    border-color: var(--ab-chip-hover-bd);
    color: var(--ab-chip-hover-fg);
  }
  .ab-mnav a:active,
  .ab-search__result:active,
  .ab-nav__list > li > a:active { background: var(--ab-surface-tint); }
}


/* ==========================================================================
   17 · AD WELLS
   Rules of the house: never above the ANSWER or the calculator, whichever the
   template delivers first; every slot reserves its height from the inline
   --ab-ad-h / --ab-ad-m-h pair written by arbdar_ad_slot(); the modifier
   supplies a fallback so a slot that forgets the inline values still reserves
   space.

   These are FIXED heights, not min-heights. A min-height is a floor, not a
   size: a unit that comes back taller than the floor still shifts everything
   below it, and `contain: layout` does not contain size either — so the old
   spec could not prevent CLS at all, whatever the comment claimed. The
   corollary is that the reservation is now a promise: the ad unit must be
   locked to the same size (data-ad-format="rectangle" with fixed dimensions,
   never "auto"), because anything taller is clipped and anything shorter
   leaves grey.

   The 2.5rem added to the inner height is the chrome: 2 x 8px padding, the
   12px/1.25 label and the 4px gap = 35px. Undersize it and `contain: strict`
   clips the bottom of the creative.

   Markup: <div class="ab-ad ab-ad--rect" style="--ab-ad-h:280px;--ab-ad-m-h:250px">
             <span class="ab-ad__label">Advertisement</span>
             <div class="ab-ad__inner" id="…" data-ab-ad="slot-id"></div>
           </div>
   ========================================================================== */

.ab-ad {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ab-s-1);
  margin-block: var(--ab-s-8);
  margin-inline: auto;
  padding: var(--ab-s-2);
  width: 100%;
  max-width: 100%;
  text-align: center;
  background: var(--ab-surface-2);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
  contain: strict;
  height: calc(var(--ab-ad-m-h, 250px) + 2.5rem);
}

.ab-ad__label {
  font-size: var(--ab-fs-xs);
  line-height: 1.25;
  font-weight: 500;   /* ad chrome sits visibly below everything editorial */
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ab-text-subtle);
}

.ab-ad__inner {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  width: 100%;
  height: var(--ab-ad-m-h, 250px);
}
.ab-ad__inner > * { max-width: 100%; margin-inline: auto; }

@media (min-width: 768px) {
  .ab-ad        { height: calc(var(--ab-ad-h, 250px) + 2.5rem); }
  .ab-ad__inner { height: var(--ab-ad-h, 250px); }
}

/* Fallback reservations per placement (inline style wins when present).
   The leaderboard reserved 100px on mobile while a responsive unit routinely
   serves 300x250 — a ~150px shift of the entire hub, on the head terms. */
.ab-ad--leaderboard { --ab-ad-m-h: 250px; --ab-ad-h: 90px; }
.ab-ad--rect,
.ab-ad--inline,
.ab-ad--footer,
.ab-ad--home        { --ab-ad-m-h: 250px; --ab-ad-h: 280px; }
.ab-ad--rail        { --ab-ad-m-h: 600px; --ab-ad-h: 600px; }

/* Half-page rail: sidebar only, never injected into the flow. */
.ab-ad--rail {
  margin-block: 0;
  position: sticky;
  top: calc(var(--ab-header-h) + var(--ab-s-4));
}
@media (max-width: 1179px) {
  .ab-ad--rail { display: none; }
}
@media (min-width: 1180px) {
  .ab-ad--rail .ab-ad__inner { width: 300px; }
}

/* Safety net: an ad must never crowd the tool. */
.ab-calc + .ab-ad { margin-top: var(--ab-s-8); }


/* ==========================================================================
   18 · NOTICES
   ========================================================================== */

.ab-notice {
  display: grid;
  gap: var(--ab-s-1);
  padding: var(--ab-s-3) var(--ab-s-4);
  border: 1px solid var(--ab-border);
  border-inline-start: 3px solid var(--ab-link);
  border-radius: var(--ab-r-md);
  background: var(--ab-surface-2);
  font-size: var(--ab-fs-md);
  color: var(--ab-text-muted);
}
.ab-notice__title { font-weight: 700; color: var(--ab-text); }
.ab-notice--info { border-inline-start-color: var(--ab-link); }
.ab-notice--tip  { border-inline-start-color: var(--ab-tip); }
.ab-notice--warn { border-inline-start-color: var(--ab-warn); }


/* ==========================================================================
   19 · SAFE AREAS, MOTION, FORCED COLOURS, PRINT, RESPONSIVE TAIL
   (The generic utility layer that used to sit here — .ab-stack/.ab-flex/
   .ab-mt-*/.ab-hide-sm and friends — had zero call sites in any template, in
   site.js, or in the plugin, and has been removed.)
   ========================================================================== */

/* ---- Safe areas ---------------------------------------------------------
   Notched and rounded-display phones in landscape, where the gutter alone
   puts the first character of every line under the curve. Declared last on
   purpose: each of these containers sets `padding-inline` earlier, and a
   shorthand would otherwise overwrite these longhands. */
@supports (padding: max(0px)) {
  .ab-header__inner,
  .ab-container,
  .ab-calcpage__inner {
    padding-inline-start: max(var(--ab-gutter), env(safe-area-inset-left));
    padding-inline-end:   max(var(--ab-gutter), env(safe-area-inset-right));
  }
  .ab-mpanel__body { padding-bottom: calc(var(--ab-s-8) + env(safe-area-inset-bottom)); }
}
/* The footer's own containers (.ab-fx__inner / .ab-fx__status / .ab-postmain)
   declare theirs in §09, and the homepage stage declares its own in §21. */

/* ---- Small phones ------------------------------------------------------- */

/* Spacing only. The type scale is fluid now (§02), so re-declaring h1/h2 here
   would just re-introduce the fixed sizes the clamps replaced. */
@media (max-width: 380px) {
  :root { --ab-gutter: 0.875rem; }
}

/* The old .ab-hero__sub line-clamp went with the old homepage; §21's hero has
   its own fold budget. The tighter block rhythm still applies everywhere. */
@media (max-width: 599px) {
  .ab-container > * + * { margin-top: var(--ab-s-6); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .ab-key:active { transform: none; }
  .ab-btn:active { transform: none; }
}

@media (forced-colors: active) {
  .ab-btn,
  .ab-input,
  .ab-card,
  .ab-calc,
  .ab-key,
  .ab-chip,
  .ab-result,
  .ab-answer,
  .ab-search__input { border: 1px solid CanvasText; }
  .ab-faq__q::after { border-color: CanvasText; }
  .ab-mpanel__drawer { border: 1px solid CanvasText; }
}

@media print {
  .ab-header,
  .ab-mpanel,
  .ab-skip-link,
  .ab-skip-link:focus,
  .ab-ad,
  .ab-calcpage__rail,
  .ab-search,
  .ab-breadcrumbs,
  .ab-pagination,
  .ab-toc,
  .ab-links,
  .ab-calc__actions,
  .ab-chips { display: none !important; }

  html, body { background: #FFF; color: #000; font-size: 12pt; }

  .ab-container,
  .ab-calcpage__inner { max-width: none; padding: 0; }

  /* The footer (§09) and the header/homepage (§20/§21) each print their own
     rules, after this block, because each keeps a different thing on paper. */

  .ab-calc,
  .ab-result,
  .ab-answer,
  .ab-card { box-shadow: none; border: 1px solid #999; background: none; }

  .ab-faq__item { display: block; }
  .ab-faq__a { display: block !important; }

  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; color: #444; }
}

/* ==========================================================================
   ARBDAR SIGNATURE SURFACE :: HEADER — Command Bar
   Authoritative stylesheet for this surface. Appended last so it wins over
   any partial copy merged earlier in the file.
   ========================================================================== */

/* =============================================================================
 * ARBDAR · HEADER & PRIMARY NAVIGATION — "THE COMMAND BAR"
 *
 * HOW TO INSTALL: append this block to the END of assets/css/main.css.
 * It is written to WIN BY SOURCE ORDER over the existing §06 (site header),
 * §07 (search) and §08 (off-canvas panel) without requiring those sections to
 * be deleted first — deliberately, because other agents are editing that file
 * concurrently and a surgical replacement is a merge conflict waiting to
 * happen. Every property those sections set that this surface needs to change
 * is explicitly re-declared below. When the dust settles, §06 and the header
 * half of §07 can be deleted and nothing here will change.
 *
 * System fonts only · no external request · no image · no icon font.
 * Every value that must flip with the theme lives in EXACTLY three blocks (C1),
 * which is what makes dark-mode coverage provable rather than hopeful, and why
 * the manual toggle wins in both directions.
 *
 *  C1  Tokens + metrics (light / OS-dark / forced-dark)
 *  C2  The bar, and the double rule
 *  C3  Brand
 *  C4  The well: chip, input, key hint, clear, submit
 *  C5  Instant results
 *  C6  Browse control · theme toggle · the reclaimed breadcrumb band
 *  C7  The Browse panel: catgrid, chips, links, colophon
 *  C8  Desktop
 *  C9  Motion, forced colours, print — and the ad policy for this surface
 * ========================================================================== */


/* =============================================================================
 * C1 · TOKENS
 *
 * --ab-header-h is the theme's existing global name: main.css uses it for
 * scroll-padding-top and any sticky rail, so it is REDEFINED here rather than
 * replaced, and it now means "bar row + rule" so anchors still land clear.
 *
 * 50px of bar + 5px of rule = 55px of total chrome on a phone, breadcrumb
 * included, against the field's ~56px bar + ~28px crumb band = ~84px with no
 * search in it.
 * ========================================================================== */

:root {
  --ab-bar-h:     50px;                                        /* the row       */
  --ab-rule-h:    5px;                                         /* 2 + 2 + 1     */
  --ab-header-h:  calc(var(--ab-bar-h) + var(--ab-rule-h));    /* 55px          */
  --ab-well-h:    44px;                                        /* every target  */
  --ab-bar-pad:   var(--ab-s-3);
  --ab-vv-top:    0px;   /* written by commandbar.js from visualViewport        */
}

@media (min-width: 900px) {
  :root { --ab-bar-h: 58px; --ab-well-h: 46px; --ab-bar-pad: var(--ab-s-4); }
}

/* ---- (a) LIGHT, and forced-light ---------------------------------------- */
:root,
:root[data-theme="light"] {
  /*
   * THE SLAB. Navy in BOTH themes — look at the dark block: it barely moves.
   * That is the whole point. It means ONE <meta name="theme-color">, no white
   * flash at the top of the page on a dark-mode cold landing, and no
   * colour-flip bug surface in the most-viewed 55px of the site. It is also the
   * single cheapest way to be the only non-white header in a category of eight
   * white headers, and it is on-token: p-950, not a new colour.
   */
  --ab-bar-bg:      var(--ab-p-950);      /* #04122F                           */
  --ab-bar-rule:    var(--ab-p-800);      /* #0C307E — the double rule         */
  --ab-bar-fg:      #FFFFFF;              /* 18.6:1                            */
  --ab-bar-fg-mute: var(--ab-p-200);      /* #C6D5F5 · 12.9:1                  */
  --ab-bar-hover:   rgba(255, 255, 255, .10);
  --ab-bar-mark:    var(--ab-a-500);      /* #14E19D · 9.1:1 — the one hue     */
  --ab-bar-ring:    var(--ab-a-500);      /* focus on navy                     */

  /* The well punched into the slab. This is the visual hero of the header. */
  --ab-well-bg:   #FFFFFF;
  --ab-well-bd:   #1D3A72;
  --ab-well-fg:   var(--ab-n-900);        /* 17.9:1                            */
  --ab-well-ph:   var(--ab-n-600);        /* 7.5:1 — the placeholder carries the
                                             scale claim, so it must be genuinely
                                             readable, not a #94A3B8 whisper    */
  --ab-well-icon: var(--ab-n-600);
  --ab-well-ring: rgba(20, 225, 157, .28);

  --ab-scope-bg: var(--ab-p-100);
  --ab-scope-fg: var(--ab-p-800);         /* 9.6:1 on #E6ECFA                  */
  --ab-scope-bd: var(--ab-p-200);
}

/* ---- (b) DARK by OS preference, unless the reader forced light ----------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ab-bar-bg:      #050D1F;
    --ab-bar-rule:    #1B2C4C;
    --ab-bar-fg:      #F2F6FC;
    --ab-bar-fg-mute: #A9B8CE;
    --ab-bar-hover:   rgba(255, 255, 255, .08);
    --ab-bar-mark:    var(--ab-a-500);
    --ab-bar-ring:    var(--ab-a-500);

    --ab-well-bg:   #16233C;
    --ab-well-bd:   #2E4470;
    --ab-well-fg:   #E8EEF7;              /* 12.9:1 on #16233C                 */
    --ab-well-ph:   #A9B8CE;              /* 7.2:1                             */
    --ab-well-icon: #A9B8CE;
    --ab-well-ring: rgba(20, 225, 157, .30);

    --ab-scope-bg: #16294D;
    --ab-scope-fg: #C6D5F5;               /* 8.9:1                             */
    --ab-scope-bd: #2C3F66;
  }
}

/* ---- (c) DARK forced by the toggle, wins on a light-preferring OS -------- */
:root[data-theme="dark"] {
  --ab-bar-bg:      #050D1F;
  --ab-bar-rule:    #1B2C4C;
  --ab-bar-fg:      #F2F6FC;
  --ab-bar-fg-mute: #A9B8CE;
  --ab-bar-hover:   rgba(255, 255, 255, .08);
  --ab-bar-mark:    var(--ab-a-500);
  --ab-bar-ring:    var(--ab-a-500);

  --ab-well-bg:   #16233C;
  --ab-well-bd:   #2E4470;
  --ab-well-fg:   #E8EEF7;
  --ab-well-ph:   #A9B8CE;
  --ab-well-icon: #A9B8CE;
  --ab-well-ring: rgba(20, 225, 157, .30);

  --ab-scope-bg: #16294D;
  --ab-scope-fg: #C6D5F5;
  --ab-scope-bd: #2C3F66;
}


/* =============================================================================
 * C2 · THE BAR — and the double rule
 *
 * Still `position: sticky`. A sticky bar needs no spacer element, so
 * header-attributable CLS is zero by construction — on load, on scroll and on
 * theme change — and site.js's existing .is-stuck / inert handling keeps
 * working. The iOS on-screen-keyboard case is solved by TRANSLATING this
 * element (see .ab-vv below), never by swapping it to fixed, which would pull
 * it out of flow and jump the document by its own height.
 *
 * THE DOUBLE RULE is painted as three background layers inside the header's own
 * box: a 2px rule, a 2px gap of the slab's own ground, a 1px rule. In print,
 * that is the compositor's and the accountant's mark for a total that is final
 * and has been checked. It costs 5px, zero bytes, zero elements, and it is what
 * turns a dark rectangle into an instrument. It reappears in the panel (C7) as
 * the section divider, so the device is used more than once.
 * ========================================================================== */

.ab-header--cmd {
  position: sticky;
  top: 0;
  z-index: 100;
  height: var(--ab-header-h);
  min-height: 0;                       /* overrides §06's min-height          */
  padding-top: env(safe-area-inset-top, 0px);
  box-sizing: content-box;             /* the notch inset is extra, not a squeeze */
  border-bottom: 0;                    /* the rule below replaces it          */
  color: var(--ab-bar-fg);
  background:
    linear-gradient(var(--ab-bar-rule), var(--ab-bar-rule)) bottom 3px left / 100% 2px no-repeat,
    linear-gradient(var(--ab-bar-rule), var(--ab-bar-rule)) bottom       left / 100% 1px no-repeat,
    var(--ab-bar-bg);
  transform: translate3d(0, var(--ab-vv-top, 0px), 0);
}

/* No elevation shadow. A 55px slab closed with a double rule reads as a printed
   masthead; a drop shadow reads as a 2013 navbar. Explicitly neutralised so
   §06's .is-stuck rule cannot reintroduce one. */
.ab-header--cmd.is-stuck { box-shadow: none; }

.ab-header--cmd .ab-header__inner {
  display: flex;
  align-items: center;
  gap: var(--ab-s-2);
  height: var(--ab-bar-h);
  min-height: 0;
  max-width: var(--ab-shell);
  margin-inline: auto;
  padding-inline: var(--ab-bar-pad);
}

/* Focus inside the bar is emerald on navy: 9.1:1, unmistakable, and it is the
   only ring the bar ever draws. */
.ab-header--cmd :focus-visible {
  outline: 3px solid var(--ab-bar-ring);
  outline-offset: 2px;
}

/* ...but the search WELL is a light surface punched into that navy slab, and
   emerald on white is 1.7:1 — an SC 1.4.11 failure on the site's primary
   control, in the default theme. Only the colour changes; the 3px/2px
   geometry above is untouched. */
.ab-header--cmd .ab-search__wrap :focus-visible,
.ab-header--cmd .ab-search--cmd :focus-visible {
  outline-color: var(--ab-p-600);
}

/* THE READING LINE. Rides ON the thick bar of the double rule, so it costs no
   extra chrome height. Transform only — it can neither reflow nor contribute to
   CLS — and it is gated behind @supports so a browser without scroll timelines
   renders nothing rather than a permanently full emerald bar. */
.ab-header__progress { display: none; }

@supports (animation-timeline: scroll()) {
  .ab-ctx-deep .ab-header__progress {
    display: block;
    position: absolute;
    inset-inline: 0;
    bottom: 3px;
    height: 2px;
    transform: scaleX(0);
    transform-origin: 0 50%;
    background: linear-gradient(90deg, var(--ab-p-400), var(--ab-a-500));
    animation: ab-cmd-read linear;
    animation-timeline: scroll(root block);
  }
  @keyframes ab-cmd-read { to { transform: scaleX(1); } }
}

/* Pin the bar to the VISUAL viewport while the field is focused — the one
   moment a header search box is actually in use, and the case not one of the
   eight competitors has tested. commandbar.js writes --ab-vv-top only while the
   field has focus, so scrolling an ordinary page never touches the compositor. */
html.ab-vv { scroll-behavior: auto; }


/* =============================================================================
 * C3 · BRAND
 *
 * The theme's ONE lockup, unchanged: arbdar_logo() is what the footer prints
 * too, and forking the mark for the header is the first inconsistency a
 * discerning eye finds. Only the colours change — the mark becomes the single
 * hit of emerald in the bar.
 * ========================================================================== */

.ab-header--cmd .ab-brand {
  flex: 0 0 auto;
  min-height: var(--ab-well-h);
  align-items: center;
  padding-block: 0;
  padding-inline: 2px;
  color: var(--ab-bar-fg);
  border-radius: var(--ab-r-md);
}
.ab-header--cmd .ab-brand:hover { color: var(--ab-bar-fg); }
.ab-header--cmd .ab-brand__mark { color: var(--ab-bar-mark); }
.ab-header--cmd .ab-brand__name { font-size: 1rem; letter-spacing: -0.012em; }
.ab-header--cmd .ab-brand__tail { color: var(--ab-bar-fg-mute); }

/*
 * Below 720px the wordmark never renders — on ANY template, at ANY scroll
 * position. Its visibility depends only on viewport width, which is precisely
 * what guarantees no state change in this header can move a single pixel of the
 * document. The mark alone is the brand at 26px; a small mark welded to a
 * dominant search well reads intentional, where a small mark beside empty space
 * reads unfinished.
 */
@media (max-width: 719px) {
  .ab-header--cmd .ab-brand__name { display: none; }
}


/* =============================================================================
 * C4 · THE WELL — the header's hero
 *
 * §07 styles .ab-search__wrap as a relative box with an absolutely-positioned
 * icon and submit. The command variant re-specifies it as a single flex row:
 * chip / input / key hint / clear / submit, all static, inside one bordered
 * well punched into the slab.
 * ========================================================================== */

.ab-search--cmd {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;                      /* overrides §07's width:100%            */
  margin: 0;
}

.ab-search--cmd .ab-search__wrap {
  display: flex;
  align-items: center;
  gap: 4px;
  height: var(--ab-well-h);
  max-width: 620px;
  padding-inline: 6px 4px;
  background: var(--ab-well-bg);
  border: 1px solid var(--ab-well-bd);
  border-radius: var(--ab-r-md);
  /* An inset highlight, not a drop shadow: the field reads as punched INTO the
     slab rather than floating on it. One declaration, no paint cost. */
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .06);
  transition: box-shadow var(--ab-dur) var(--ab-ease),
              border-color var(--ab-dur) var(--ab-ease);
}

.ab-search--cmd .ab-search__wrap:focus-within {
  border-color: var(--ab-bar-ring);
  box-shadow: 0 0 0 3px var(--ab-well-ring);
}

/* §07 prints a leading magnifier; the command variant does not use one — the
   chip owns that edge and the placeholder already says what the field is. */
.ab-search--cmd .ab-search__icon { display: none; }

.ab-search--cmd .ab-search__input {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
  height: 100%;
  min-height: 0;
  padding: 0 2px;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--ab-well-fg);
  font-size: 16px;      /* load-bearing: below 16px iOS Safari zooms the page,
                           which on a sticky bar looks exactly like detaching */
  line-height: 1;
}
.ab-search--cmd .ab-search__input::placeholder { color: var(--ab-well-ph); opacity: 1; }
.ab-search--cmd .ab-search__input:hover  { border-color: transparent; }
/* The well carries the focus ring, so the input draws none of its own: the
   affordance is the whole instrument, not a rectangle inside a rectangle. */
.ab-search--cmd .ab-search__input:focus,
.ab-search--cmd .ab-search__input:focus-visible { outline: none; background: none; border-color: transparent; }
.ab-search--cmd .ab-search__input::-webkit-search-cancel-button,
.ab-search--cmd .ab-search__input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }

/* ---- The context chip — the signature move ------------------------------
   The breadcrumb parent, the up-link and the search scope, in one element, at
   zero extra header height. A 26px chip that is a 44px target: ::after stretches
   the hit area to the full well height, which is how WCAG 2.5.5 is met without
   making the chip visually heavy. */

.ab-cmd__chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex: 0 1 auto;
  min-width: 0;
  max-width: 42%;
  height: 26px;
  padding-inline: 8px;
  border: 1px solid var(--ab-scope-bd);
  border-radius: var(--ab-r-sm);
  background: var(--ab-scope-bg);
  color: var(--ab-scope-fg);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
}
.ab-cmd__chip::after {            /* the real 44px target */
  content: "";
  position: absolute;
  inset-block: -9px;
  inset-inline: -3px;
}
.ab-cmd__chip:hover { border-color: var(--ab-p-400); color: var(--ab-scope-fg); }
.ab-cmd__chip-label { overflow: hidden; text-overflow: ellipsis; }
.ab-cmd__chip-chev  { flex: 0 0 auto; opacity: .6; font-size: 14px; }

/* Under 340px the chip is squeezing a field that has nothing left to give. */
@media (max-width: 339px) { .ab-cmd__chip { display: none; } }

/* ---- Key hint, clear, submit -------------------------------------------- */

.ab-cmd__key {
  display: none;
  flex: 0 0 auto;
  min-width: 20px;
  padding: 2px 5px;
  border: 1px solid var(--ab-well-bd);
  border-radius: var(--ab-r-sm);
  color: var(--ab-well-ph);
  font-family: var(--ab-font-mono);
  font-size: 11px;
  line-height: 1.3;
  text-align: center;
}
/* Fine pointers only, and only once JS has bound the shortcut, so the hint is
   never a promise the page cannot keep. */
html.js.ab-fine .ab-search--cmd .ab-search__wrap:not(:focus-within) .ab-cmd__key { display: block; }

.ab-cmd__clear,
.ab-search--cmd .ab-search__submit {
  position: static;                 /* overrides §07's absolute placement    */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  border-radius: var(--ab-r-sm);
  cursor: pointer;
}
/* 34px painted, 44px tappable: the target grows without the bar growing. */
.ab-cmd__clear::after,
.ab-search--cmd .ab-search__submit::after {
  content: "";
  position: absolute;
  width: 44px;
  height: 44px;
  transform: translate(-5px, -5px);
}
/* `relative` is required to anchor the 44px ::after target — but it also
   re-activates §07's `inset-inline-end: 4px`, which `position: static` above
   had been neutralising. Clear the inset explicitly or the button drifts. */
.ab-cmd__clear,
.ab-search--cmd .ab-search__submit { position: relative; inset-inline: auto; }

.ab-cmd__clear { color: var(--ab-well-icon); }
.ab-search--cmd .ab-search__submit { color: var(--ab-p-600); background: none; }
.ab-cmd__clear:hover,
.ab-search--cmd .ab-search__submit:hover { background: var(--ab-n-100); }

:root[data-theme="dark"] .ab-search--cmd .ab-search__submit { color: var(--ab-p-300); }
:root[data-theme="dark"] .ab-cmd__clear:hover,
:root[data-theme="dark"] .ab-search--cmd .ab-search__submit:hover { background: rgba(255, 255, 255, .07); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .ab-search--cmd .ab-search__submit { color: var(--ab-p-300); }
  :root:not([data-theme="light"]) .ab-cmd__clear:hover,
  :root:not([data-theme="light"]) .ab-search--cmd .ab-search__submit:hover { background: rgba(255, 255, 255, .07); }
}


/* =============================================================================
 * C5 · INSTANT RESULTS
 *
 * Rendered by commandbar.js into the same .ab-search__results / role="listbox"
 * contract §07 already defines and site.js already knows, so the two search
 * implementations are interchangeable and only one is ever bound.
 *
 * On a phone the list is full-bleed under the bar and its height is clamped to
 * the VISUAL viewport, so it stops above the on-screen keyboard instead of
 * rendering behind it.
 * ========================================================================== */

.ab-search--cmd .ab-search__results {
  position: absolute;
  inset-inline: calc(-1 * var(--ab-bar-pad));
  top: calc(100% + 7px);
  z-index: 120;
  max-width: none;
  max-height: min(58vh, calc(var(--ab-vv-h, 100vh) - var(--ab-header-h) - 24px));
  padding: var(--ab-s-1);
  border-radius: 0 0 var(--ab-r-lg) var(--ab-r-lg);
  border-color: var(--ab-border);
}

/* Group overline: "IN MATH" / "EVERYWHERE". Not an option — presentational. */
.ab-search__group {
  padding: var(--ab-s-3) var(--ab-s-3) var(--ab-s-1);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ab-text-muted);
}
.ab-search__group:first-child { padding-top: var(--ab-s-2); }

.ab-search--cmd .ab-search__result {
  gap: var(--ab-s-3);
  min-height: 52px;
  justify-content: space-between;
}
.ab-search__result-name { flex: 1 1 auto; font-weight: 600; line-height: 1.3; }
.ab-search__result-cat {
  flex: 0 0 auto;
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-muted);
  white-space: nowrap;
}

/* Keyboard selection is a background AND a 3px leading rule: never colour
   alone, so it survives a monochrome display and forced colours. */
.ab-search--cmd .ab-search__result.is-active,
.ab-search--cmd .ab-search__result[aria-selected="true"] {
  box-shadow: inset 3px 0 0 var(--ab-a-600);
}
.ab-search--cmd .ab-search__result.is-active .ab-search__result-cat,
.ab-search--cmd .ab-search__result[aria-selected="true"] .ab-search__result-cat { color: var(--ab-link); }

/* The submit path stays visible at the bottom of the list: instant search never
   becomes the only route to the full results page. */
.ab-search__all {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ab-s-3);
  min-height: 48px;
  margin-top: var(--ab-s-1);
  padding: var(--ab-s-2) var(--ab-s-3);
  border-top: 1px solid var(--ab-border);
  color: var(--ab-link);
  font-size: var(--ab-fs-md);
  cursor: pointer;
}
.ab-search__all:hover { background: var(--ab-surface-tint); }


/* =============================================================================
 * C6 · BROWSE · THEME · AND THE RECLAIMED BREADCRUMB BAND
 * ========================================================================== */

.ab-header__nav { display: flex; flex: 0 0 auto; }

/* Four dots, not three bars. A hamburger promises "the menu I hid"; a grid
   promises "the catalogue", which is what the panel actually is. */
.ab-browse {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ab-s-2);
  min-width: 44px;
  min-height: 44px;
  padding-inline: 10px;
  border-radius: var(--ab-r-md);
  color: var(--ab-bar-fg);
  font-size: var(--ab-fs-md);
  font-weight: 600;
  text-decoration: none;
}
.ab-browse:hover { background: var(--ab-bar-hover); color: var(--ab-bar-fg); }

.ab-browse__grid { display: grid; grid-template-columns: repeat(2, 5px); gap: 3px; }
.ab-browse__grid i {
  width: 5px;
  height: 5px;
  border-radius: 1.5px;
  background: currentColor;
  transition: transform var(--ab-dur) var(--ab-ease);
}
.ab-browse:hover .ab-browse__grid i:nth-child(1) { transform: translate(-1px, -1px); }
.ab-browse:hover .ab-browse__grid i:nth-child(2) { transform: translate(1px, -1px); }
.ab-browse:hover .ab-browse__grid i:nth-child(3) { transform: translate(-1px, 1px); }
.ab-browse:hover .ab-browse__grid i:nth-child(4) { transform: translate(1px, 1px); }

.ab-browse__text { display: none; }
@media (min-width: 420px) { .ab-browse__text { display: inline; } }

/* Desktop-only theme toggle — see header.php for why it is not on the phone.
   900px matches site.js's DESKTOP_QUERY; the two systems must agree. */
.ab-header__theme { display: none; color: var(--ab-bar-fg-mute); }
.ab-header--cmd .ab-header__theme:hover { background: var(--ab-bar-hover); color: var(--ab-bar-fg); }
@media (min-width: 900px) { html.js .ab-header__theme { display: inline-flex; } }
html.no-js .ab-header__theme { display: none; }   /* never ship a dead button */

/*
 * ── THE 28px, RECOVERED ─────────────────────────────────────────────────────
 * Every template in this theme still calls arbdar_listing_breadcrumbs() exactly
 * as it does today; nothing about their contract changes and no other file has
 * to move. Below 900px the trail is made visually hidden — the scope chip in
 * the bar has already delivered the parent link and it did so for zero vertical
 * pixels — and it is restored on desktop, where there is room for the full
 * trail and it is genuinely useful.
 *
 * The <ol> is in the DOM on EVERY viewport, so assistive tech, text-extraction
 * crawlers and the plugin's BreadcrumbList JSON-LD are all untouched. This is
 * ~28px recovered above the fold on ~10,000 of the site's most valuable pages,
 * bought with one media query and no cross-template coordination.
 *
 * Opt a template out with <body class="ab-keep-crumbs">.
 * ──────────────────────────────────────────────────────────────────────────── */
@media (max-width: 899px) {
  body:not(.ab-keep-crumbs) .ab-breadcrumbs {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
}


/* =============================================================================
 * C7 · THE BROWSE PANEL
 *
 * Same element, same classes, same JS module the theme already ships — so the
 * focus trap, the `inert` outside landmarks, the iOS-proof scroll lock and
 * Escape all keep working, untouched. Only the geometry and the contents change:
 * full-bleed on a phone, because this is a catalogue, not a drawer.
 * ========================================================================== */

.ab-mpanel--browse .ab-mpanel__drawer {
  width: min(440px, 100%);
  border-inline-start: 0;
}
@media (min-width: 560px) {
  .ab-mpanel--browse .ab-mpanel__drawer { border-inline-start: 1px solid var(--ab-border); }
}
.ab-mpanel--browse .ab-mpanel__head { min-height: var(--ab-header-h); }

/* The double rule again, as the divider. Same device, second use. */
.ab-mpanel__rule {
  height: var(--ab-rule-h);
  background:
    linear-gradient(var(--ab-border-strong), var(--ab-border-strong)) top    left / 100% 2px no-repeat,
    linear-gradient(var(--ab-border-strong), var(--ab-border-strong)) bottom left / 100% 1px no-repeat;
}

/* ---- Category grid: icon-led, folio-numbered, counted -------------------- */

.ab-catgrid {
  counter-reset: ab-folio;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--ab-s-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 560px) { .ab-catgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.ab-cat {
  counter-increment: ab-folio;
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--ab-s-2);
  min-height: 60px;
  padding: var(--ab-s-2) var(--ab-s-3);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-md);
  background: var(--ab-surface-2);
  color: var(--ab-text);
  text-decoration: none;
}
.ab-cat:hover {
  border-color: var(--ab-card-hover-bd);
  background: var(--ab-card-hover-bg);
  color: var(--ab-text);
}

/* THE FOLIO NUMBER. A CSS counter, decimal-leading-zero: zero bytes of markup
   and the single strongest reference-work signal available for free. */
.ab-cat::before {
  content: counter(ab-folio, decimal-leading-zero);
  position: absolute;
  top: 6px;
  inset-inline-end: 8px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
  color: var(--ab-text-subtle);
}
.ab-cat--all::before { content: none; }

.ab-cat__icon {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--ab-r-md);
  background: var(--ab-surface-tint);
  color: var(--ab-link);
}
.ab-cat__body { display: flex; flex-direction: column; min-width: 0; }
.ab-cat__name {
  font-size: var(--ab-fs-md);
  font-weight: 600;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ab-cat__n {
  font-size: var(--ab-fs-xs);
  color: var(--ab-text-muted);
  font-variant-numeric: tabular-nums;   /* the site's own subject matter, applied */
}

/* ---- Recently-used chips ------------------------------------------------ */

.ab-chips--recent { display: flex; flex-wrap: wrap; gap: var(--ab-s-2); margin: 0; padding: 0; list-style: none; }
.ab-chips--recent a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  padding-inline: var(--ab-s-3);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-pill);
  background: var(--ab-surface-2);
  color: var(--ab-text);
  font-size: var(--ab-fs-sm);
  font-weight: 600;
  text-decoration: none;
}
.ab-chips--recent a:hover { border-color: var(--ab-chip-hover-bd); color: var(--ab-chip-hover-fg); }

/* ---- Site links + colophon ---------------------------------------------- */

.ab-panel-links { display: flex; flex-wrap: wrap; gap: var(--ab-s-1) var(--ab-s-4); margin: 0; padding: 0; list-style: none; }
.ab-panel-links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--ab-text-muted);
  font-size: var(--ab-fs-md);
  text-decoration: none;
}
.ab-panel-links a:hover { color: var(--ab-link); text-decoration: underline; }

.ab-mpanel__colophon {
  margin: 0;
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-muted);
}


/* =============================================================================
 * C8 · DESKTOP
 *
 * No horizontal category row, at any width. Deliberate: Omni's 14-link inline
 * row fails 44px targets, cannot survive 375px and does not scale past ~14
 * items, and a hover mega-menu on desktop plus a drawer on mobile is two mental
 * models for one task. One model everywhere is the point of this concept.
 * ========================================================================== */

@media (min-width: 900px) {
  .ab-header--cmd .ab-header__inner { gap: var(--ab-s-4); }

  /* The well is centred and capped, so it reads as the instrument the bar is
     built around rather than as filler between two clusters of controls. */
  .ab-search--cmd { max-width: 620px; margin-inline: auto; }
  .ab-search--cmd .ab-search__results { inset-inline: 0; border-radius: var(--ab-r-lg); }
}


/* =============================================================================
 * C9 · MOTION · FORCED COLOURS · PRINT · AD POLICY
 * ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .ab-header--cmd,
  .ab-header--cmd *,
  .ab-header__progress { transition-duration: 1ms !important; animation: none !important; }
  .ab-header__progress { display: none !important; }
}

@media (forced-colors: active) {
  .ab-header--cmd { border-bottom: 1px solid CanvasText; background: Canvas; }
  .ab-search--cmd .ab-search__wrap,
  .ab-cmd__chip,
  .ab-cat,
  .ab-chips--recent a { border: 1px solid CanvasText; }
  .ab-search--cmd .ab-search__result.is-active,
  .ab-search--cmd .ab-search__result[aria-selected="true"] { outline: 2px solid Highlight; outline-offset: -2px; }
  .ab-mpanel__rule { forced-color-adjust: none; background: CanvasText; }
  .ab-header__progress { display: none; }
}

@media print {
  .ab-header--cmd,
  .ab-header__progress,
  .ab-mpanel--browse { display: none !important; }
  /* The trail is the only wayfinding a printed page can carry, so it comes back
     regardless of what C6 did to it on screen. */
  .ab-breadcrumbs {
    position: static !important;
    width: auto; height: auto;
    margin: 0; padding: 0;
    clip: auto; clip-path: none;
    display: block !important;
  }
}

/* ─────────────────────────────────────────────────────────────────────────────
 * AD POLICY FOR THIS SURFACE — normative, not advisory.
 *
 * This surface ships NO ad slot and must never gain one. An ad directly beneath
 * the bar is an ad above the primary content on every one of ~10,000 pages,
 * which is the single placement the revenue model forbids and the one that
 * earns a Better Ads / Core Web Vitals penalty. The first unit belongs after the
 * answer, in the content template, with a FIXED height (see main.css §17 and
 * arbdar_tool_rendered()). The Browse panel never contains advertising: a
 * navigation surface that monetises is the calculator-online.net trap, and that
 * site's #1 documented user complaint is over-monetisation.
 * ────────────────────────────────────────────────────────────────────────────*/

/* ==========================================================================
   ARBDAR SIGNATURE SURFACE :: HOMEPAGE — Console & Tape
   Authoritative stylesheet for this surface. Appended last so it wins over
   any partial copy merged earlier in the file.
   ========================================================================== */

/* ==========================================================================
   20 · HOMEPAGE — "Console & Tape"
   Appended to assets/css/main.css.

   Obeys the file's rule that every value which flips with the theme lives in
   exactly three blocks and nowhere else:
     a) :root, :root[data-theme="light"]
     b) @media (prefers-color-scheme: dark) -> :root:not([data-theme="light"])
     c) :root[data-theme="dark"]
   (b) and (c) follow (a) at equal specificity, so the manual toggle wins in
   BOTH directions.

   No web fonts, no images, no external requests. The stage decoration is two
   CSS gradients behind a mask; the tape's torn edge is a repeating conic
   gradient; the copy icon is two bordered boxes. Every hover/active effect is
   transform, opacity, colour or box-shadow only — never a layout property —
   so nothing on this page can move the page.

   20.1 tokens · 20.2 stage · 20.3 dateline + hero · 20.4 console
   20.5 tape · 20.6 chips · 20.7 rubrics · 20.8 board · 20.9 colophon
   20.10 responsive · 20.11 motion, forced colours, print
   ========================================================================== */

/* ---- 20.1a · Theme payload · LIGHT (and forced-light) -------------------- */
:root,
:root[data-theme="light"] {
  --ab-stage-from:     var(--ab-p-50);
  --ab-stage-grid:     rgba(17, 71, 187, .07);
  --ab-stage-glow:     rgba(16, 188, 131, .13);
  --ab-stage-edge:     var(--ab-n-200);

  --ab-console-bg:     #FFFFFF;
  --ab-console-bd:     var(--ab-n-300);
  --ab-console-chrome: var(--ab-n-50);
  --ab-console-lip:    rgba(255, 255, 255, .9);
  --ab-console-glow:   rgba(17, 71, 187, .13);

  --ab-dot-a:          var(--ab-n-300);
  --ab-dot-b:          var(--ab-p-300);
  --ab-dot-c:          var(--ab-a-600);

  --ab-tape-bg:        var(--ab-n-50);
  --ab-tape-rule:      var(--ab-n-200);
  --ab-tape-tooth:     #FFFFFF;

  --ab-shelf-ord:      var(--ab-p-200);
}

/* ---- 20.1b · Theme payload · DARK by system preference ------------------- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ab-stage-from:     #0E1A31;
    --ab-stage-grid:     rgba(146, 178, 240, .075);
    --ab-stage-glow:     rgba(20, 225, 157, .12);
    --ab-stage-edge:     var(--ab-border);

    --ab-console-bg:     var(--ab-surface);
    --ab-console-bd:     var(--ab-border-strong);
    --ab-console-chrome: #0F192B;
    --ab-console-lip:    rgba(255, 255, 255, .06);
    --ab-console-glow:   rgba(82, 130, 235, .22);

    --ab-dot-a:          #3A4A66;
    --ab-dot-b:          var(--ab-p-500);
    --ab-dot-c:          var(--ab-a-500);

    --ab-tape-bg:        #0F1828;
    --ab-tape-rule:      var(--ab-border);
    --ab-tape-tooth:     var(--ab-surface);

    --ab-shelf-ord:      #24406F;
  }
}

/* ---- 20.1c · Theme payload · DARK forced by the toggle ------------------- */
:root[data-theme="dark"] {
  --ab-stage-from:     #0E1A31;
  --ab-stage-grid:     rgba(146, 178, 240, .075);
  --ab-stage-glow:     rgba(20, 225, 157, .12);
  --ab-stage-edge:     var(--ab-border);

  --ab-console-bg:     var(--ab-surface);
  --ab-console-bd:     var(--ab-border-strong);
  --ab-console-chrome: #0F192B;
  --ab-console-lip:    rgba(255, 255, 255, .06);
  --ab-console-glow:   rgba(82, 130, 235, .22);

  --ab-dot-a:          #3A4A66;
  --ab-dot-b:          var(--ab-p-500);
  --ab-dot-c:          var(--ab-a-500);

  --ab-tape-bg:        #0F1828;
  --ab-tape-rule:      var(--ab-border);
  --ab-tape-tooth:     var(--ab-surface);

  --ab-shelf-ord:      #24406F;
}

/* ---- 20.1d · Metrics (never flip with theme) ---------------------------- */
:root {
  /* THE most important number on this surface. The tape is a FIXED height and
     its list scrolls inside it, so committing a calculation prepends a row
     without moving one pixel of the page below. Never write this from JS. */
  --ab-tape-h: 190px;
}


/* ==========================================================================
   20.2 · THE STAGE
   A full-bleed band, a direct child of <main> and OUTSIDE .ab-container, so
   there is no 100vw arithmetic and therefore no way to introduce horizontal
   overflow when a scrollbar is present.
   ========================================================================== */

.ab-stage {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  /* Cancel .ab-main's top padding so the band starts flush under the header. */
  margin-top: calc(var(--ab-s-6) * -1);
  padding: var(--ab-s-6) var(--ab-gutter) var(--ab-s-10, var(--ab-s-8));
  background: linear-gradient(180deg, var(--ab-stage-from) 0%, var(--ab-bg) 78%);
  border-bottom: 1px solid var(--ab-stage-edge);
  contain: paint;
}

@media (min-width: 1024px) {
  .ab-stage { margin-top: calc(var(--ab-s-8) * -1); }
}

/* Graph paper. Only drawn where the mask is supported — an unmasked grid runs
   hard into the page edge and reads as a mistake rather than a texture. */
@supports (mask-image: radial-gradient(#000, transparent)) or (-webkit-mask-image: radial-gradient(#000, transparent)) {
  .ab-stage::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -2;
    background-image:
      linear-gradient(to right, var(--ab-stage-grid) 1px, transparent 1px),
      linear-gradient(to bottom, var(--ab-stage-grid) 1px, transparent 1px);
    background-size: 28px 28px;
    -webkit-mask-image: radial-gradient(115% 85% at 50% 0%, #000 26%, transparent 76%);
            mask-image: radial-gradient(115% 85% at 50% 0%, #000 26%, transparent 76%);
    pointer-events: none;
  }
}

/* The one warm note on the page: a soft emerald bloom under the console, which
   is the only place the accent colour appears at size. */
.ab-stage::after {
  content: "";
  position: absolute;
  z-index: -1;
  left: 50%;
  top: 34%;
  width: min(680px, 120%);
  aspect-ratio: 2 / 1;
  transform: translate(-50%, -50%);
  background: radial-gradient(closest-side, var(--ab-stage-glow), transparent 70%);
  pointer-events: none;
}

.ab-stage__inner {
  max-width: var(--ab-wide);
  margin-inline: auto;
  display: grid;
  gap: var(--ab-s-4);
}

@supports (padding: max(0px)) {
  .ab-stage {
    padding-inline-start: max(var(--ab-gutter), env(safe-area-inset-left));
    padding-inline-end:   max(var(--ab-gutter), env(safe-area-inset-right));
  }
}


/* ==========================================================================
   20.3 · DATELINE + HERO
   The dateline is 24px of letterspaced small caps carrying a data-derived
   revision date — a checkable fact above the fold, which no competitor prints
   at all. It is also the typographic key for every rubric further down.
   ========================================================================== */

.ab-dateline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--ab-s-1);
  margin: 0;
  font-size: 0.6875rem;
  line-height: 1.6;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ab-text-subtle);
  font-variant-numeric: tabular-nums;
}
.ab-dateline__mark { color: var(--ab-text-muted); }
.ab-dateline__sep  { opacity: .6; }
.ab-dateline time  { color: inherit; }

.ab-home-hero { text-align: start; }

.ab-home-hero__title {
  margin: 0 0 var(--ab-s-2);
  font-size: var(--ab-fs-h1);
  line-height: var(--ab-lh-tight);
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.ab-home-hero__sub {
  margin: 0;
  max-width: 42ch;
  color: var(--ab-text-muted);
  font-size: var(--ab-fs-md);
  text-wrap: pretty;
}


/* ==========================================================================
   20.4 · THE CONSOLE
   A product shell, not a card: a chrome strip, a body, and a tape drawer.
   ========================================================================== */

.ab-console {
  position: relative;
  display: block;
  max-width: 100%;
  background: var(--ab-console-bg);
  border: 1px solid var(--ab-console-bd);
  border-radius: var(--ab-r-lg);
  box-shadow:
    inset 0 1px 0 var(--ab-console-lip),
    var(--ab-shadow-md);
  overflow: hidden;
  transition: box-shadow var(--ab-dur) var(--ab-ease);
}

/* The focus choreography that makes it feel like software: the whole shell
   lifts when the field inside it is being used. box-shadow only — the ring
   cannot change the box. */
.ab-console:focus-within {
  box-shadow:
    inset 0 1px 0 var(--ab-console-lip),
    0 0 0 4px var(--ab-console-glow),
    var(--ab-shadow-md);
}

/* ---- chrome strip -------------------------------------------------------- */

.ab-console__chrome {
  display: flex;
  align-items: center;
  gap: var(--ab-s-3);
  min-height: 34px;
  padding: 0 var(--ab-s-3);
  background: var(--ab-console-chrome);
  border-bottom: 1px solid var(--ab-console-bd);
}

.ab-console__dots { display: inline-flex; gap: 5px; flex: none; }
.ab-console__dots i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ab-dot-a);
}
.ab-console__dots i:nth-child(2) { background: var(--ab-dot-b); }
.ab-console__dots i:nth-child(3) { background: var(--ab-dot-c); }

.ab-console__name {
  font-size: var(--ab-fs-xs);
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--ab-text-subtle);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The live classification. This is the product telling you what it thinks you
   meant, before it tells you the answer. */
.ab-console__kind {
  margin-inline-start: auto;
  flex: none;
  padding: 3px 10px;
  border-radius: var(--ab-r-pill);
  background: var(--ab-surface-tint);
  border: 1px solid var(--ab-console-bd);
  color: var(--ab-link);
  font-family: var(--ab-font-mono);
  font-size: var(--ab-fs-xs);
  line-height: 1.5;
  white-space: nowrap;
}

/* ---- body ---------------------------------------------------------------- */

.ab-console__body { display: grid; margin: 0; }
.ab-console__main { padding: var(--ab-s-3) var(--ab-s-3) var(--ab-s-2); }

/* ---- the field ----------------------------------------------------------- */

/* .ab-search__wrap is styled in §07 for the header form; inside the console it
   is a different component, so every inherited value is reset here rather than
   left to a cascade accident. position:relative is load-bearing — the
   type-ahead listbox is appended into this element. */
.ab-console .ab-search__wrap.ab-console__field {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--ab-s-2);
  min-height: 54px;
  padding: 0 var(--ab-s-2) 0 var(--ab-s-3);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--ab-border);
  border-radius: 0;
  box-shadow: none;
}

.ab-console__prompt {
  display: flex;
  flex: none;
  color: var(--ab-text-subtle);
}

.ab-console__input {
  flex: 1 1 auto;
  min-width: 0;
  height: 52px;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  color: var(--ab-text);
  font-family: var(--ab-font-mono);
  font-size: 17px;                 /* never below 16px: iOS zooms on focus */
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  appearance: none;
  -webkit-appearance: none;
}
.ab-console__input::placeholder { color: var(--ab-text-subtle); opacity: 1; }

/* The shell carries the focus indicator (see :focus-within above), so the
   inner outline would draw a second box through the middle of the control. */
.ab-console__input:focus,
.ab-console__input:focus-visible { outline: none; }

.ab-console__go {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;                    /* WCAG 2.5.5 */
  border-radius: var(--ab-r-md);
  color: var(--ab-btn-primary-fg);
  background: var(--ab-btn-primary-bg);
  cursor: pointer;
  transition: background-color var(--ab-dur) var(--ab-ease),
              transform var(--ab-dur) var(--ab-ease);
}
.ab-console__go:hover  { background: var(--ab-btn-primary-bg-hover); }
.ab-console__go:active { transform: scale(.94); }

/* ---- the parse ribbon: the visible machinery ----------------------------- */

.ab-console__ribbon {
  display: flex;
  align-items: baseline;
  gap: var(--ab-s-2);
  min-height: 28px;
  margin: 0;
  padding: var(--ab-s-2) var(--ab-s-3) 0;
  font-size: var(--ab-fs-sm);
  color: var(--ab-text-muted);
}

.ab-console__reads {
  flex: none;
  font-size: var(--ab-fs-xs);
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ab-text-subtle);
}

.ab-console__expr {
  min-width: 0;
  font-family: var(--ab-font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--ab-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- the answer ---------------------------------------------------------- */

.ab-console__answer {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--ab-s-3);
  min-height: 64px;
  padding: 0 var(--ab-s-3);
}

.ab-console__eq {
  flex: none;
  font-family: var(--ab-font-mono);
  font-size: var(--ab-fs-lg);
  /* Deliberately NOT the emerald: #0D9B6C on white is 3.3:1 and fails AA for
     text. The accent survives on this page only as a non-text mark, where the
     3:1 threshold applies. */
  color: var(--ab-text-subtle);
}

.ab-console__value {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--ab-font-mono);
  font-size: var(--ab-fs-result);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ab-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The commit sweep — 3.3:1 against both surfaces, and the only place the
   emerald appears inside the console. */
.ab-console__answer::after {
  content: "";
  position: absolute;
  left: var(--ab-s-3);
  bottom: 6px;
  width: 56px;
  height: 2px;
  border-radius: 2px;
  background: var(--ab-btn-calc-bg);
  transform: scaleX(0);
  transform-origin: left center;
  opacity: 0;
}
.ab-console[data-ab-commit] .ab-console__answer::after {
  animation: ab-sweep 520ms var(--ab-ease) 1;
}

@keyframes ab-sweep {
  0%   { transform: scaleX(0); opacity: 0; }
  22%  { transform: scaleX(1); opacity: 1; }
  100% { transform: scaleX(1); opacity: 0; }
}

.ab-console__copy {
  flex: none;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--ab-r-md);
  border: 1px solid var(--ab-border);
  color: var(--ab-text-muted);
  cursor: pointer;
  transition: color var(--ab-dur) var(--ab-ease),
              border-color var(--ab-dur) var(--ab-ease),
              background-color var(--ab-dur) var(--ab-ease);
}
.ab-console__copy:hover {
  color: var(--ab-text);
  border-color: var(--ab-border-strong);
  background: var(--ab-surface-2);
}
.ab-console__copy[data-ab-copied] { color: var(--ab-tip); border-color: var(--ab-tip); }

/* Two offset squares. An icon drawn in CSS costs nothing to ship and needs no
   icon font — which the CSP forbids anyway. */
.ab-console__copyicon {
  position: relative;
  width: 13px;
  height: 13px;
  border: 1.6px solid currentColor;
  border-radius: 3px;
}
.ab-console__copyicon::before {
  content: "";
  position: absolute;
  left: -5px;
  top: -5px;
  width: 13px;
  height: 13px;
  border: 1.6px solid currentColor;
  border-right: 0;
  border-bottom: 0;
  border-radius: 3px 0 0 0;
}

/* ---- the bridge ---------------------------------------------------------- */

.ab-console__bridge {
  display: flex;
  align-items: center;
  gap: var(--ab-s-2);
  min-height: 48px;
  margin: 0;
  padding: 0 var(--ab-s-3);
  border-top: 1px solid var(--ab-border);
  font-size: var(--ab-fs-md);
}

.ab-console__arrow { flex: none; color: var(--ab-text-subtle); }

.ab-console__bridgelink {
  min-width: 0;
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ab-console__bridgelink:hover { border-bottom-color: currentColor; }

/* ---- the keypad ---------------------------------------------------------- */

.ab-console__pad {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  padding: var(--ab-s-3);
  border-top: 1px solid var(--ab-border);
  background: var(--ab-surface-2);
}

.ab-ckey {
  min-height: 44px;                /* WCAG 2.5.5, with the 6px gap as spacing */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--ab-key-bd);
  border-radius: var(--ab-r-md);
  background: var(--ab-key-bg);
  color: var(--ab-text);
  font-family: var(--ab-font-mono);
  font-size: var(--ab-fs-lg);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;      /* kills the 300ms double-tap-zoom delay */
  transition: background-color var(--ab-dur) var(--ab-ease),
              transform 90ms var(--ab-ease),
              border-color var(--ab-dur) var(--ab-ease);
}
.ab-ckey:hover  { background: var(--ab-key-bg-hover); }
.ab-ckey:active { transform: scale(.95); }

.ab-ckey--op {
  background: var(--ab-key-op-bg);
  border-color: var(--ab-key-op-bd);
  color: var(--ab-key-op-fg);
}
.ab-ckey--op:hover { background: var(--ab-key-op-bg-hover); }

.ab-ckey--fn {
  background: var(--ab-key-fn-bg);
  border-color: var(--ab-key-fn-bd);
  color: var(--ab-key-fn-fg);
  font-size: var(--ab-fs-md);
}
.ab-ckey--fn:hover { background: var(--ab-key-fn-bg-hover); }

.ab-ckey--wide { grid-column: span 2; }

.ab-ckey--eq {
  background: var(--ab-btn-calc-bg);
  border-color: transparent;
  color: var(--ab-btn-calc-fg);    /* #04122F on emerald = 7.5:1 */
  font-weight: 700;
}
.ab-ckey--eq:hover { background: var(--ab-btn-calc-bg-hover); }


/* ==========================================================================
   20.5 · THE TAPE — the signature
   A paper strip in the bottom of the console. Ruled, monospaced, tabular, with
   a torn top edge. Every row is a link.

   FIXED height + internal scroll + `contain: layout size`. Prepending a
   committed calculation therefore cannot move one pixel of the page below it,
   which is both the CLS guarantee and the reason it behaves like real
   adding-machine paper feeding past a window.
   ========================================================================== */

.ab-tape {
  position: relative;
  display: flex;
  flex-direction: column;
  height: calc(var(--ab-tape-h) + 34px);   /* 34px = the head row */
  background: var(--ab-tape-bg);
  border-top: 1px solid var(--ab-console-bd);
  padding: var(--ab-s-4) var(--ab-s-3) 0;
  contain: layout size;
  overflow: hidden;
}

/* The tear. 10px teeth, drawn, not drawn on. */
.ab-tape::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 6px;
  background:
    repeating-conic-gradient(from 135deg at 50% 0,
      var(--ab-tape-tooth) 0 25%, transparent 0 50%) 0 0 / 10px 12px;
  pointer-events: none;
  z-index: 1;
}

.ab-tape__head {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: var(--ab-s-2);
  flex-wrap: wrap;
  margin-bottom: var(--ab-s-2);
}

.ab-tape__title {
  margin: 0;
  font-size: var(--ab-fs-xs);
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ab-text-subtle);
}

.ab-tape__hint {
  margin: 0;
  flex: 1 1 180px;
  font-size: var(--ab-fs-xs);
  line-height: 1.4;
  color: var(--ab-text-subtle);
}

.ab-tape__clear {
  flex: none;
  min-height: 32px;
  padding: 0 var(--ab-s-3);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-pill);
  background: var(--ab-surface);
  color: var(--ab-text-muted);
  font-size: var(--ab-fs-xs);
  cursor: pointer;
}
.ab-tape__clear:hover { color: var(--ab-text); border-color: var(--ab-border-strong); }

.ab-tape__list {
  flex: 1 1 auto;
  min-height: 0;
  list-style: none;
  margin: 0;
  padding: 0 0 var(--ab-s-2);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.ab-tape__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas:
    "expr eq val"
    "to   to  tag";
  align-items: baseline;
  column-gap: var(--ab-s-2);
  row-gap: 2px;
  padding: var(--ab-s-2) 0;
  border-bottom: 1px dashed var(--ab-tape-rule);
  font-family: var(--ab-font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--ab-fs-sm);
}
.ab-tape__row:last-child { border-bottom: 0; }

.ab-tape__expr {
  grid-area: expr;
  color: var(--ab-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ab-tape__eq  { grid-area: eq;  color: var(--ab-text-subtle); }
.ab-tape__val { grid-area: val; color: var(--ab-text); font-weight: 700; }

.ab-tape__to {
  grid-area: to;
  justify-self: start;
  max-width: 100%;
  font-family: var(--ab-font-sans);
  font-size: var(--ab-fs-sm);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ab-tape__to::before { content: "\2192\00a0"; color: var(--ab-text-subtle); }
.ab-tape__to:hover { border-bottom-color: currentColor; }

.ab-tape__tag {
  grid-area: tag;
  align-self: center;
  padding: 1px 6px;
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-sm);
  font-size: 10px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ab-text-subtle);
}

/* A freshly printed row. translateY + opacity: no layout property animates,
   and the row is inside a size-contained scroller either way. */
.ab-tape__row--new { animation: ab-print 260ms var(--ab-ease) 1; }

@keyframes ab-print {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}


/* ==========================================================================
   20.6 · QUERY-VOICE CHIP ROW
   Reuses .ab-chip / .ab-chips from §15; this only positions the row.
   ========================================================================== */

.ab-askrow { text-align: center; }
.ab-askrow .ab-chips { justify-content: center; }

/* Board tool chips: smaller, but never below the coarse-pointer floor §15
   already sets. */
.ab-chip--sm { font-size: var(--ab-fs-xs); padding-inline: var(--ab-s-3); }


/* ==========================================================================
   20.7 · RUBRICS
   The section device for this surface: letterspaced small caps with a hairline
   rule running out to the edge. It replaces the plain <h2> + description pair
   and is what makes the console, the board and the colophon read as parts of
   one document rather than three stacked widgets.
   ========================================================================== */

.ab-rubric {
  display: flex;
  align-items: center;
  gap: var(--ab-s-3);
  margin: 0 0 var(--ab-s-4);
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ab-text-muted);
  text-wrap: nowrap;
}

.ab-rubric::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--ab-border);
}

/* The qualifier that keeps a ranking claim honest ("deepest first"). */
.ab-rubric__note {
  flex: none;
  order: 3;
  font-weight: 500;
  letter-spacing: .1em;
  color: var(--ab-text-subtle);
}

.ab-rubric--major {
  color: var(--ab-text);
  padding-top: var(--ab-s-4);
  border-top: 3px double var(--ab-border-strong);
}


/* ==========================================================================
   20.8 · THE BOARD
   A ranked status board, not a taxonomy. The ordinal is the visible argument
   that the list has an order; the rubric note says which one.
   ========================================================================== */

.ab-board__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--ab-s-3);
  counter-reset: none;
}

.ab-shelf {
  position: relative;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    "ord title"
    "ord meta"
    ".   desc"
    ".   tools";
  column-gap: var(--ab-s-3);
  row-gap: 2px;
  height: 100%;
  padding: var(--ab-s-4);
  background: var(--ab-surface);
  border: 1px solid var(--ab-border);
  border-radius: var(--ab-r-lg);
  transition: border-color var(--ab-dur) var(--ab-ease),
              box-shadow var(--ab-dur) var(--ab-ease),
              transform var(--ab-dur) var(--ab-ease);
}
@media (hover: hover) {
  .ab-shelf:hover {
    border-color: var(--ab-card-hover-bd);
    box-shadow: var(--ab-shadow-md);
    transform: translateY(-2px);
  }
}

.ab-shelf__ord {
  grid-area: ord;
  align-self: start;
  font-family: var(--ab-font-mono);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.04em;
  color: var(--ab-shelf-ord);
  font-variant-numeric: tabular-nums;
}

.ab-shelf__title {
  grid-area: title;
  margin: 0;
  font-size: var(--ab-fs-lg);
  line-height: 1.25;
}
.ab-shelf__link { color: var(--ab-text); text-decoration: none; }
.ab-shelf__link:hover { color: var(--ab-link); text-decoration: underline; }

.ab-shelf__meta {
  grid-area: meta;
  margin: 0;
  font-family: var(--ab-font-mono);
  font-size: var(--ab-fs-xs);
  letter-spacing: .04em;
  color: var(--ab-text-subtle);
  font-variant-numeric: tabular-nums;
}

.ab-shelf__desc {
  grid-area: desc;
  margin: var(--ab-s-2) 0 0;
  font-size: var(--ab-fs-sm);
  line-height: 1.5;
  color: var(--ab-text-muted);
}

.ab-shelf__tools {
  grid-area: tools;
  list-style: none;
  margin: var(--ab-s-3) 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--ab-s-2);
}

.ab-board__more { margin: var(--ab-s-6) 0 0; text-align: center; }

.ab-board__morelink {
  display: inline-flex;
  align-items: center;
  gap: var(--ab-s-2);
  min-height: 44px;
  padding: 0 var(--ab-s-4);
  border: 1px solid var(--ab-border-strong);
  border-radius: var(--ab-r-pill);
  text-decoration: none;
  font-weight: 600;
  font-size: var(--ab-fs-md);
  transition: background-color var(--ab-dur) var(--ab-ease),
              border-color var(--ab-dur) var(--ab-ease);
}
.ab-board__morelink:hover { background: var(--ab-surface-2); border-color: var(--ab-link); }
.ab-board__morelink svg { transition: transform var(--ab-dur) var(--ab-ease); }
.ab-board__morelink:hover svg { transform: translateX(3px); }


/* ==========================================================================
   20.9 · THE COLOPHON
   Four checkable artefacts, each gated in PHP on its evidence existing.
   ========================================================================== */

.ab-colophon {
  margin-top: var(--ab-s-16);
}

.ab-colophon__grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--ab-s-6);
}

.ab-colophon__item { position: relative; padding-inline-start: var(--ab-s-4); }
.ab-colophon__item::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: .55em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--ab-btn-calc-bg);   /* non-text mark: the 3:1 threshold */
}

.ab-colophon__h {
  margin: 0 0 var(--ab-s-1);
  font-size: var(--ab-fs-base);
  font-weight: 700;
  line-height: 1.35;
}

.ab-colophon__item p {
  margin: 0;
  font-size: var(--ab-fs-sm);
  line-height: 1.55;
  color: var(--ab-text-muted);
}
.ab-colophon__item p + p { margin-top: var(--ab-s-2); }

.ab-colophon__addr { font-style: normal; font-variant-numeric: tabular-nums; }

.ab-colophon__foot {
  margin: var(--ab-s-6) 0 0;
  padding-top: var(--ab-s-4);
  border-top: 1px solid var(--ab-border);
  font-size: var(--ab-fs-sm);
  line-height: 2;
  color: var(--ab-text-muted);
}
.ab-colophon__foot a { display: inline-block; padding-block: 4px; }
.ab-colophon__sep { padding: 0 var(--ab-s-2); color: var(--ab-text-subtle); }


/* ==========================================================================
   20.10 · RESPONSIVE
   Mobile-first: everything above is the phone layout.

   Phone fold budget, 390x844 with a 60px sticky header (~700px usable):
     dateline 26 + hero title 74 + gap 8 + sub 22 + gap 16
     + console chrome 34 + field 54 + ribbon 28 + answer 64 + bridge 48
     + keypad (5 x 44 + 4 x 6 + 24) 268
     = 642px
   What clears the fold: the proposition, a working input that ALREADY shows an
   answer, the machinery that produced it, a named link into the catalogue, and
   the keypad. The tape's head row peeks in at ~660px, which is what invites
   the scroll.
   ========================================================================== */

@media (min-width: 600px) {
  .ab-board__list  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ab-colophon__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Rows collapse to one line, so the reservation gets shorter, not taller. */
  :root { --ab-tape-h: 158px; }

  .ab-tape__row {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 9ch) minmax(0, 22ch) auto;
    grid-template-areas: "expr eq val to tag";
    column-gap: var(--ab-s-4);
  }
  .ab-tape__val { text-align: end; }
  .ab-tape__to  { justify-self: end; }
}

@media (min-width: 760px) {
  .ab-stage { padding-block: var(--ab-s-8) var(--ab-s-12); }
  .ab-stage__inner { gap: var(--ab-s-6); }

  .ab-dateline,
  .ab-home-hero { text-align: center; justify-content: center; }
  .ab-home-hero__sub { margin-inline: auto; }
}

@media (min-width: 860px) {
  /* The keypad moves beside the field instead of under it; the tape spans the
     whole shell, because it is the receipt for both halves. */
  .ab-console__body { grid-template-columns: minmax(0, 1fr) 264px; }
  .ab-console__main { padding: var(--ab-s-4) var(--ab-s-4) var(--ab-s-3); }
  .ab-console__pad  { border-top: 0; border-inline-start: 1px solid var(--ab-border); }

  .ab-console .ab-search__wrap.ab-console__field { min-height: 62px; }
  .ab-console__input { height: 60px; font-size: 20px; }
  .ab-console__answer { min-height: 80px; }
  .ab-ckey { min-height: 46px; }

  .ab-tape { padding-inline: var(--ab-s-4); }

  .ab-board__list    { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .ab-colophon__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--ab-s-8); }
}

/* Very small phones: buy back the fold from the keypad, not from the tool. */
@media (max-width: 380px) {
  :root { --ab-tape-h: 176px; }
  .ab-console__pad { gap: 5px; padding: var(--ab-s-2); }
  .ab-console__value { font-size: 1.625rem; }
}


/* ==========================================================================
   20.11 · MOTION, FORCED COLOURS, PRINT
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .ab-console,
  .ab-ckey,
  .ab-shelf,
  .ab-board__morelink,
  .ab-board__morelink svg,
  .ab-console__go,
  .ab-console__copy { transition: none; }

  .ab-tape__row--new,
  .ab-console[data-ab-commit] .ab-console__answer::after { animation: none; }

  .ab-shelf:hover { transform: none; }
  .ab-ckey:active,
  .ab-console__go:active { transform: none; }
}

@media (forced-colors: active) {
  .ab-stage::before,
  .ab-stage::after,
  .ab-tape::before,
  .ab-console__answer::after,
  .ab-colophon__item::before { display: none; }

  .ab-console,
  .ab-shelf,
  .ab-ckey,
  .ab-tape__row,
  .ab-console__copy { border-color: CanvasText; }

  .ab-rubric::after { background: CanvasText; }

  .ab-console__kind,
  .ab-tape__tag { forced-color-adjust: none; }
}

@media print {
  /* The tape is a receipt. It prints. The decoration, the keypad and the ad do
     not. */
  .ab-stage::before,
  .ab-stage::after,
  .ab-console__pad,
  .ab-console__go,
  .ab-console__copy,
  .ab-askrow,
  .ab-board,
  .ab-search__results { display: none !important; }

  .ab-stage   { background: none; border: 0; padding: 0; margin-top: 0; }
  .ab-console { border: 1px solid #000; box-shadow: none; }
  .ab-tape    { background: none; height: auto; contain: none; overflow: visible; }
  .ab-tape__list { overflow: visible; }
  .ab-tape__to::after { content: " (" attr(href) ")"; font-size: 9pt; }
}
