/* ==========================================================================
   RTOCalc design system
   --------------------------------------------------------------------------
   Loaded on every page, immediately after the Bootstrap 5.3.3 CDN link.

   Strategy: remap Bootstrap's OWN --bs-* custom properties rather than fight
   its selectors. Bootstrap 5.3 resolves nearly every component through those
   variables, so a token swap here reaches .btn, .card, .form-control, .nav,
   .dropdown, .modal and the utility classes at once. Only the handful of
   things Bootstrap hard-codes need a real rule further down.

   Design rules this file enforces (see the reference system):
     - The page ground is warm parchment, never pure #fff.
     - Monochrome ground and type; ONE accent (sienna) means "interactive".
     - Accent marks links, buttons and focus. Never headings or section fills.
     - Nothing gets a radius larger than 6px except pills.
     - No alternating dark/light section bands. One continuous canvas.
     - Shadows are ink-tinted. Never pure black.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Fonts

   Self-hosted for the same reason the report font is (see
   assets/fonts/README.md): no third-party request on a page whose first
   paint is the headline. Latin subset only.
   -------------------------------------------------------------------------- */

/* Display face. A light italic serif against crisp sans is the pairing the
   whole system rests on — Cormorant is one of the few free serifs that ships
   genuinely light optical weights.

   400, not 300: at 300 the hairlines were thinner than the design needed on
   screen. 400 keeps the delicacy and the contrast against the 600-weight sans
   labels while giving the headline actual presence. 500 was tried and rejected
   — it sits close enough to the bold sans that the deliberate weight
   asymmetry stops reading. */
@font-face {
    font-family: 'Cormorant Garamond';
    font-style: italic;
    font-weight: 400;
    font-display: swap;
    src: url('../assets/fonts/cormorant-garamond-400-italic.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* One variable file covers the 400 body and 600 label weights. */
@font-face {
    font-family: 'IBM Plex Sans';
    font-style: normal;
    font-weight: 400 600;
    font-display: swap;
    src: url('../assets/fonts/ibm-plex-sans-var.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Figures only. Tabular numerals so columns of money line up. */
@font-face {
    font-family: 'IBM Plex Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../assets/fonts/ibm-plex-mono-400.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
   2. Tokens
   -------------------------------------------------------------------------- */

:root {
    /* Surface. --paper is the page; --paper-raised is the only pure white on
       the site and is reserved for cards that must lift off the canvas. */
    --paper:        #fdfcfa;
    --paper-raised: #ffffff;
    /* One step between --paper and --paper-sunken, for tonal panels that need
       to separate from the page without reading as a different section. */
    --paper-tint:   #faf8f3;
    --paper-sunken: #f5f2ec;

    /* Ink. Near-black with a blue undertone, carried down through four steps.
       --ink-4 is placeholder-only and does not meet 4.5:1 by design; labels
       on this site are always visible, never replaced by a placeholder. */
    --ink:   #14243b;
    --ink-2: #3e4a5c;
    /* Darkened from #6b7280, which cleared 4.5:1 on --paper (4.72) but not on
       the sunken footer ground (4.33). This clears it on all three surfaces:
       paper 4.93, sunken 4.53, raised 5.06. */
    --ink-3: #686f7c;
    --ink-4: #9aa0a6;

    /* Accent — dark sienna.

       The system began deliberately near-monochrome, on the reference's rule
       that a CTA is never filled with a brand colour. That is now overridden
       on purpose: one accent carries INTERACTION, and nothing else. If it is
       sienna it can be clicked — links, buttons, focus. It is never
       decoration, never a heading, never a section fill.

       #96502b is dark enough to carry white text (6.04:1) and light enough to
       read as copper rather than brown-black, and it clears 4.5:1 on all four
       grounds — paper 5.89, tint 5.69, sunken 5.40, soft 5.18. */
    --accent:        #96502b;
    --accent-strong: #7a3f20;   /* hover / active; 8.00 on paper */
    --accent-soft:   #f6ece4;   /* tinted ground for accent surfaces */

    /* State — "you are here", as distinct from "click me".

       Sienna was doing both jobs: buttons and links, AND active tabs, active
       nav, the snapshot toggle, step dots. Two different meanings in one
       colour, so a filled toggle and a filled button shouted equally.

       The fix is subtraction, not a new hue. Every candidate second colour
       collided with something already in the palette — teals landed ΔE 7.3-8.2
       from --status-pos, plums and slate ΔE 4.7-7.1 from --series-1 (for scale,
       the accent/negative-red collision that made a threshold bar look like a
       button was ΔE 5.9). Ink sits ΔE 29.1 from the accent and means nothing
       else. It was already in the palette.

       Rule: if it is sienna you can click it. If it is ink it is where you are. */
    --state-current: var(--ink);          /* active tab, active nav, selected */
    --state-track:   var(--paper-sunken); /* the groove a segmented control sits in */
    --state-raised:  var(--paper-raised); /* the selected segment itself */

    /* Structure */
    --rule:        #e5e1d8;
    --rule-strong: #cfc9bc;
    --focus-ring:  0 0 0 4px rgba(150, 80, 43, .22);

    /* Data visualisation. NEVER on UI chrome — the accent owns interaction,
       these carry meaning the monochrome UI cannot.

       SERIES and STATUS are deliberately separate sets. Green previously did
       both jobs — the Cash Flow series AND the positive-earnings status — which
       is how a status colour ends up in a legend and a series colour ends up
       implying "good". Keep them apart.

       Series: a validated categorical pair (dataviz six checks, light surface)
       — ΔE 30.1 normal, 27.9 protan, 28.4 deutan, 23.9 tritan; both clear the
       chroma floor and 3:1 against the card. The previous #2c5282 navy FAILED
       the chroma floor: desaturated enough to read as grey rather than blue.
       Marks only — --series-2 is 3.64:1, fine for a fill, never for text. */
    --series-1: #3b4fa0;   /* indigo — net sale proceeds */
    --series-2: #a8801f;   /* ochre  — cash flow          */
    /* A deeper ochre for a fill that must carry WHITE TEXT inside it, which
       --series-2 cannot: white on #a8801f is 3.64:1 and ink on it is 4.29:1 —
       neither clears 4.5, because the chart ochre is tuned as a MARK colour,
       not a label ground. #8f6d1a gives white 4.81:1 in the same hue family.
       Chart marks keep --series-2; only text-bearing fills use this. */
    --series-2-deep: #8f6d1a;

    --series-3: #7a8ca3;   /* reserved, unvalidated as a trio */
    --series-4: #c4b79a;

    /* Status: reserved. Never reused as a series colour.
       --status-neg replaced #b4472f, which sat ΔE 5.9 from --accent — close
       enough that the below-threshold bar read as a sienna button. #8f1f3a is
       ΔE 11.3 away and 8.66:1 as text. */
    --status-pos: #1f7a4d;
    --status-neg: #8f1f3a;
    /* Caution: possible, but not for every buyer — a US down payment between the
       3% program-only minimum and the 5% standard. Every amber sits near the
       sienna accent or the ochre series; #a85d00 was the furthest of 14 tried:
       ΔE2000 10.7 from --accent, 14.9 from --series-2, 12.2 from
       --series-2-deep, 32.5 from --status-neg. Text 4.96:1 on white, 4.68:1 on
       paper. */
    --status-caution: #a85d00;

    /* Radii */
    --r-sm:   3px;   /* buttons, nav items, tabs */
    --r-md:   6px;   /* cards, inputs, modals */
    --r-pill: 9999px;

    /* Spacing, 6px base */
    --s1: 6px;  --s2: 12px; --s3: 18px; --s4: 24px; --s5: 36px;
    --s6: 48px; --s7: 72px; --s8: 96px; --s9: 120px;

    /* Elevation. Three layers, ink-tinted: a hairline outline, a close
       shadow, and a wide ambient one. */
    --lift: 0 0 0 1px rgba(20, 36, 59, .10),
            0 18px 40px rgba(20, 36, 59, .07),
            0 72px 72px rgba(20, 36, 59, .05);
    --lift-sm: 0 0 0 1px rgba(20, 36, 59, .09),
               0 6px 16px rgba(20, 36, 59, .05);

    /* Type */
    --font-display: 'Cormorant Garamond', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
    --font-sans: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

    --t-caption: 0.6875rem;  /*  11px — eyebrows, chart axes  */
    --t-small:   0.875rem;   /*  14px  */
    --t-body:    1.0625rem;  /*  17px  */
    --t-lead:    1.25rem;    /*  20px  */
    --t-h3:      1.5rem;     /*  24px  */
    --t-h2:      2rem;       /*  32px  */
    --t-section: clamp(1.875rem, 4vw, 2.75rem);  /* 30 → 44px */
    /* Capped at 64px rather than the reference's 78px: this hero also has to
       fit two labelled fields and a CTA inside one viewport, and 78px pushed
       the CTA below the fold on a 900px-tall window. */
    --t-display: clamp(2.25rem, 5.5vw, 4rem);    /* 36 → 64px */
}

/* --------------------------------------------------------------------------
   3. Bootstrap variable remap

   One block, and every Bootstrap component inherits the system.
   -------------------------------------------------------------------------- */

:root {
    --bs-font-sans-serif: var(--font-sans);
    --bs-font-monospace: var(--font-mono);
    --bs-body-font-family: var(--font-sans);
    --bs-body-font-size: var(--t-body);
    --bs-body-line-height: 1.55;

    --bs-body-bg: var(--paper);
    --bs-body-bg-rgb: 253, 252, 250;
    /* Ink is the default text colour, as in the reference; --ink-2 and --ink-3
       are opt-in for secondary and muted copy. Headings deliberately inherit
       rather than being pinned to --ink: pages that still carry a dark band
       (about.php, pathway.php and others awaiting the rollout) set a light
       colour on the band, and a pinned heading colour turned those headings
       ink-on-navy. */
    --bs-body-color: var(--ink);
    --bs-body-color-rgb: 20, 36, 59;
    --bs-emphasis-color: var(--ink);
    --bs-heading-color: inherit;
    --bs-secondary-color: var(--ink-3);
    --bs-tertiary-color: var(--ink-4);
    --bs-secondary-bg: var(--paper-sunken);
    --bs-tertiary-bg: var(--paper-sunken);

    --bs-border-color: var(--rule);
    --bs-border-color-translucent: rgba(20, 36, 59, .10);
    --bs-border-radius: var(--r-md);
    --bs-border-radius-sm: var(--r-sm);
    --bs-border-radius-lg: var(--r-md);
    --bs-border-radius-xl: var(--r-md);
    --bs-border-radius-2xl: var(--r-md);
    --bs-border-radius-pill: var(--r-pill);

    /* Links are accent + underline. The underline stays: colour alone must
       never be the only thing marking a link. */
    --bs-link-color: var(--accent);
    --bs-link-color-rgb: 150, 80, 43;
    --bs-link-hover-color: var(--accent-strong);
    --bs-link-hover-color-rgb: 122, 63, 32;

    /* "Primary" IS the accent. This is what makes .btn-primary, .text-primary
       and .border-primary carry sienna across all 40 pages without touching
       one of them. */
    --bs-primary: var(--accent);
    --bs-primary-rgb: 150, 80, 43;
    --bs-primary-text-emphasis: var(--accent-strong);
    --bs-primary-bg-subtle: var(--accent-soft);
    --bs-primary-border-subtle: var(--accent);

    --bs-secondary: var(--ink-3);
    --bs-secondary-rgb: 104, 111, 124;

    /* Semantic colours survive, retuned for the cream ground. */
    --bs-success: var(--status-pos);
    --bs-success-rgb: 31, 122, 77;
    /* Their SUBTLE variants were never retuned, so every .alert-success on the
       site still painted Bootstrap's pastel mint. Tone and a hairline instead,
       per .panel-tint: the meaning rides on the text colour, not on a fill. */
    --bs-success-text-emphasis: var(--status-pos);
    --bs-success-bg-subtle: var(--paper-tint);
    --bs-success-border-subtle: var(--rule);
    /* Info had no retune at all: cyan badges, icons and pale-cyan notices. It
       carries no status meaning here, so it takes the secondary ink. */
    --bs-info: var(--ink-3);
    --bs-info-rgb: 104, 111, 124;
    --bs-info-text-emphasis: var(--ink-2);
    --bs-info-bg-subtle: var(--paper-tint);
    --bs-info-border-subtle: var(--rule);
    --bs-danger: var(--status-neg);
    --bs-danger-rgb: 143, 31, 58;
    --bs-form-invalid-color: var(--status-neg);
    --bs-form-invalid-border-color: var(--status-neg);

    --bs-box-shadow: var(--lift);
    --bs-box-shadow-sm: var(--lift-sm);
}

/* --------------------------------------------------------------------------
   4. Base
   -------------------------------------------------------------------------- */

/* Paper texture.

   Painted as background layers on <body> itself rather than as a fixed
   ::before overlay. The overlay version needed `body > * { z-index: 1 }` to
   sit above it, which gave the navbar and <main> the same stacking level —
   and <main> comes later in the DOM, so it covered every open nav dropdown.
   Background layers need no stacking games at all.

   --texture-* are set by the texture variants below; a page can override them
   without touching this rule. */
body {
    background-color: var(--paper);
    background-image: var(--texture-layers, none);
    background-size: var(--texture-size, auto);
    background-repeat: repeat;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

main {
    flex: 1;
}

/* --------------------------------------------------------------------------
   4b. Texture

   Atoms first, then the page-level variants. All are inline SVG or gradients
   — no image request, crisp at every DPI, and free to swap.

   The atoms are the vocabulary: a surface composes one or two of them into
   `background-image` and lists the matching sizes in `background-size`. The
   opacities are baked into each atom rather than parameterised, because they
   are perceptually tuned against one another — a 1px line on a 32px lattice
   lays down roughly three times the ink of a 1px dot on a 24px one, so
   matching their nominal alpha would NOT match their apparent weight.
   -------------------------------------------------------------------------- */

:root {
    /* Dot lattices. --tex-dot is the standard 24px field; --tex-dot-wide is a
       sparser 32px one for surfaces where the structure should recede further. */
    --tex-dot:      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Ccircle cx='1' cy='1' r='1' fill='%2314243b' fill-opacity='.07'/%3E%3C/svg%3E");
    --tex-dot-size: 24px 24px;

    --tex-dot-wide:      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32'%3E%3Ccircle cx='1' cy='1' r='1' fill='%2314243b' fill-opacity='.06'/%3E%3C/svg%3E");
    --tex-dot-wide-size: 32px 32px;

    /* Line grid, as two crossing gradients. Kept lighter than the dots for the
       coverage reason above. */
    --tex-grid-x:    linear-gradient(to right, rgba(20, 36, 59, .032) 1px, transparent 1px);
    --tex-grid-y:    linear-gradient(to bottom, rgba(20, 36, 59, .032) 1px, transparent 1px);
    --tex-grid-size: 32px 32px, 32px 32px;

    /* Fractal grain. baseFrequency .8 gives a coarser tooth than .9, which at
       these opacities actually registers instead of averaging to flat cream. */
    --tex-grain:      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)' opacity='.15'/%3E%3C/svg%3E");
    --tex-grain-size: 160px 160px;

    /* Diagonal hatch, 45 degrees on an 8px tile. Directional where the dot and
       line lattices are static, so it reads as a different material rather
       than a denser version of the same one. Used outside the content column
       and on tonal panels, never behind running text. */
    --tex-diag:      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M-1 1 L1 -1 M0 8 L8 0 M7 9 L9 7' stroke='%2314243b' stroke-opacity='.045' stroke-width='1'/%3E%3C/svg%3E");
    --tex-diag-size: 8px 8px;

    /* Wider, fainter hatch for large empty areas, where the 8px tile reads as
       a solid tint rather than as lines. */
    --tex-diag-wide:      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14'%3E%3Cpath d='M-1 1 L1 -1 M0 14 L14 0 M13 15 L15 13' stroke='%2314243b' stroke-opacity='.035' stroke-width='1'/%3E%3C/svg%3E");
    --tex-diag-wide-size: 14px 14px;

    /* A touch stronger, for a surface carrying grain alone with no lattice
       beneath it to supply structure. */
    --tex-grain-solo:      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)' opacity='.19'/%3E%3C/svg%3E");
    --tex-grain-solo-size: 160px 160px;
}

/* Page-level variants. One class on <html> sets the base texture for the whole
   document; individual surfaces may override it (see 4c and home.css). */

/* A - Dot grid alone. Reads as ruled paper rather than as a pattern. */
:root.tex-dots {
    --texture-layers: var(--tex-dot);
    --texture-size: var(--tex-dot-size);
}

/* B - Line grid alone. Most architectural; reads as graph paper. */
:root.tex-grid {
    --texture-layers: var(--tex-grid-x), var(--tex-grid-y);
    --texture-size: var(--tex-grid-size);
}

/* C - Grain alone. The paper-fibre reading; no lattice at all. */
:root.tex-speckle {
    --texture-layers: var(--tex-grain-solo);
    --texture-size: var(--tex-grain-solo-size);
}

/* D - Grain over a dot grid. Structure plus tooth: the lattice gives the page
       an underlying order, the noise keeps it from looking digital. Active. */
:root.tex-both {
    --texture-layers: var(--tex-dot), var(--tex-grain);
    --texture-size: var(--tex-dot-size), var(--tex-grain-size);
}

/* --------------------------------------------------------------------------
   4c. Per-surface texture

   A surface that wants its own texture paints it as its own background, over
   an opaque --paper fill so the document texture beneath does not show through
   and double up.

   Deliberately NOT a pseudo-element: an absolutely-positioned ::before needs a
   negative z-index to sit behind its own content, and that is precisely the
   stacking trap that put <main> over the nav dropdowns. A background paints
   behind content by definition and needs no stacking at all.

   Changes land on section boundaries, which already carry a 1px rule and a
   large pad either side — the seam has a reason to be where it is, so the
   texture can change hard rather than needing a crossfade.
   -------------------------------------------------------------------------- */

.tex-surface {
    background-color: var(--paper);
    background-repeat: repeat;
}
.tex-surface-dots {
    background-image: var(--tex-dot), var(--tex-grain);
    background-size: var(--tex-dot-size), var(--tex-grain-size);
}
.tex-surface-dots-wide {
    background-image: var(--tex-dot-wide), var(--tex-grain);
    background-size: var(--tex-dot-wide-size), var(--tex-grain-size);
}
.tex-surface-grid {
    background-image: var(--tex-grid-x), var(--tex-grid-y), var(--tex-grain);
    background-size: var(--tex-grid-size), var(--tex-grain-size);
}
.tex-surface-grain {
    background-image: var(--tex-grain-solo);
    background-size: var(--tex-grain-solo-size);
}
.tex-surface-flat {
    background-image: none;
}
.tex-surface-diag {
    background-image: var(--tex-diag-wide), var(--tex-grain);
    background-size: var(--tex-diag-wide-size), var(--tex-grain-size);
}

/* --------------------------------------------------------------------------
   4d. The column frame

   A narrow measure centred in a wide viewport has nothing holding it, so it
   reads as floating. Two hairlines running the full height of the content
   give it edges, and the ground either side of them carries a different
   texture and a half-step darker tone — so the column reads as a sheet laid
   on a surface rather than as text adrift on one.

   Everything here is a border and a background. No overlays, no z-index.
   -------------------------------------------------------------------------- */

/* Width tracks Bootstrap's .container at every breakpoint, less the container's
   own 0.75rem gutter each side — so the frame's rules land exactly on the logo's
   left edge and the account menu's right edge rather than 12px outside them. */
.framed {
    margin: 0 auto;
    border-left: 1px solid var(--rule);
    border-right: 1px solid var(--rule);
    background-color: var(--paper);
    background-image: var(--tex-grain);
    background-size: var(--tex-grain-size);
}
@media (min-width: 576px)  { .framed { max-width: calc(540px  - 1.5rem); } }
@media (min-width: 768px)  { .framed { max-width: calc(720px  - 1.5rem); } }
@media (min-width: 992px)  { .framed { max-width: calc(960px  - 1.5rem); } }
@media (min-width: 1200px) { .framed { max-width: calc(1140px - 1.5rem); } }
@media (min-width: 1400px) { .framed { max-width: calc(1320px - 1.5rem); } }

/* The ground the frame sits on: a half-step down in tone, with the hatch to
   mark it as a different material from the column's plain grain. */
.frame-ground {
    background-color: var(--paper-tint);
    background-image: var(--tex-diag-wide), var(--tex-grain);
    background-size: var(--tex-diag-wide-size), var(--tex-grain-size);
    background-repeat: repeat;
}

/* Below this the margins are too narrow to read as ground, and the frame
   becomes two rules pressed against the text. Drop it and let the column run
   edge to edge. */
@media (max-width: 767.98px) {
    .framed {
        border-left: 0;
        border-right: 0;
        background-color: transparent;
        background-image: none;
    }
    .frame-ground {
        background-color: var(--paper);
        background-image: var(--tex-grain);
        background-size: var(--tex-grain-size);
    }
}

/* A tonal panel inside the column — callouts, contents lists, anything that
   should punctuate running prose. Tone and a hairline, never a fill colour. */
.panel-tint {
    background-color: var(--paper-tint);
    background-image: var(--tex-diag), var(--tex-grain);
    background-size: var(--tex-diag-size), var(--tex-grain-size);
    border: 1px solid var(--rule);
    border-radius: var(--r-md);
}

h1, h2, h3, h4, h5, h6 {
    font-weight: 600;
    letter-spacing: -0.011em;
}

/* Links carry an underline so they are never distinguished by colour alone —
   which matters more here than usual, because there is no accent colour. */
main a:not(.btn):not(.nav-link):not(.dropdown-item):not(.navbar-brand):not(.btn-outline):not(.btn-ghost) {
    text-decoration: underline;
    text-underline-offset: 0.18em;
    text-decoration-thickness: 1px;
    text-decoration-color: var(--rule-strong);
    transition: text-decoration-color .15s ease;
}
main a:not(.btn):not(.nav-link):not(.dropdown-item):not(.navbar-brand):not(.btn-outline):not(.btn-ghost):hover {
    text-decoration-color: currentColor;
}

::selection {
    background: rgba(20, 36, 59, .12);
}

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Bootstrap's .g-5 sets --bs-gutter-x to 3rem, and .row turns that into a
   -1.5rem margin either side. The container only pads 0.75rem, so on a phone
   the row hangs 0.75rem past the viewport and the page scrolls sideways —
   12px at 390px wide. Columns are full-width at this size, so the horizontal
   gutter buys nothing and is pure overflow.

   Halving only --bs-gutter-x makes the negative margin exactly match the
   container padding. --bs-gutter-y is left at 3rem: stacked columns still
   need the vertical breathing room, and it costs no horizontal space. */
@media (max-width: 575.98px) {
    .row.g-5 {
        --bs-gutter-x: 1.5rem;
    }
}

/* --------------------------------------------------------------------------
   5. Type primitives
   -------------------------------------------------------------------------- */

/* Hairline serif display. Opt-in, not applied to every heading: a dashboard
   table full of italic serif would be unreadable. Used deliberately on
   marketing surfaces. */
.display-serif {
    font-family: var(--font-display);
    font-style: italic;
    font-weight: 400;
    color: var(--ink);
    letter-spacing: -0.02em;
    line-height: 1.12;
    text-wrap: balance;
}
.display-serif em {
    font-style: italic;
}

.t-display { font-size: var(--t-display); }
.t-section { font-size: var(--t-section); line-height: 1.16; }

.eyebrow {
    font-family: var(--font-sans);
    font-size: var(--t-caption);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-3);
    margin: 0 0 var(--s3);
}

.lead-quiet {
    font-size: var(--t-lead);
    line-height: 1.5;
    color: var(--ink-2);
    text-wrap: pretty;
}

/* Money and other figures. Tabular so a column of them aligns. */
.figure-mono {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.01em;
}

/* Keeps a compound term whole. "rent-to-own" otherwise broke as "rent-to-" /
   "own" at narrow widths. The plain hyphen is retained for search and
   copy-paste. */
.nowrap {
    white-space: nowrap;
}

/* Forces a line break, but only from md up — narrow viewports wrap on their
   own and an extra hard break there strands a short line. */
.br-md {
    display: none;
}
@media (min-width: 768px) {
    .br-md {
        display: inline;
    }
}

.rule-h {
    border: 0;
    border-top: 1px solid var(--rule);
    margin: var(--s6) 0;
    opacity: 1;
}

/* --------------------------------------------------------------------------
   6. Buttons

   Three levels, one accent, one size:

     .btn-outline / .btn-primary   the action  — sienna
     .btn-ghost / .btn-outline-secondary  the alternative — neutral outline
     everything else inherits from those.

   Sizing is set once on .btn so a Bootstrap button and a system button stand
   the same height side by side. The old rule padded .btn-primary alone to
   0.75/1.5rem, which is why the results-page Calculate towered over the
   "Edit Terms" next to it.
   -------------------------------------------------------------------------- */

.btn {
    --bs-btn-padding-y: 0.5rem;
    --bs-btn-padding-x: 1.1rem;
    font-weight: 500;
    border-radius: var(--r-sm);
}

/* The system CTA. Outlined at rest so a page is not shouted at, filled on
   hover so the action is unmistakable once you are on it. */
.btn-outline {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    font-family: var(--font-sans);
    font-size: var(--t-small);
    font-weight: 500;
    line-height: 1.2;
    color: var(--accent);
    background: var(--paper-raised);
    border: 1px solid var(--accent);
    border-radius: var(--r-sm);
    padding: 0.625rem 1.25rem;
    text-decoration: none;
    cursor: pointer;
    transition: background .15s ease, color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.btn-outline:hover {
    color: var(--paper-raised);
    background: var(--accent);
    border-color: var(--accent);
}
.btn-outline:active {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
}
.btn-outline:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}
.btn-outline:disabled,
.btn-outline[disabled] {
    color: var(--ink-4);
    border-color: var(--rule-strong);
    background: var(--paper-sunken);
    cursor: not-allowed;
}

.btn-outline.btn-outline-lg {
    font-size: var(--t-body);
    padding: 0.875rem 1.5rem;
}

/* The quiet alternative. Ink, not accent — it is not the action. */
.btn-ghost {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-family: var(--font-sans);
    font-size: var(--t-small);
    font-weight: 500;
    color: var(--ink-2);
    background: none;
    border: 0;
    padding: 0.625rem 0.5rem;
    text-decoration: none;
    cursor: pointer;
}
.btn-ghost:hover {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 0.18em;
}
.btn-ghost:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: var(--r-sm);
}

/* Bootstrap buttons still in the app UI resolve through --bs-primary, which
   is the accent — so .btn-primary is a filled sienna button everywhere. */
.btn-primary {
    --bs-btn-bg: var(--accent);
    --bs-btn-border-color: var(--accent);
    --bs-btn-hover-bg: var(--accent-strong);
    --bs-btn-hover-border-color: var(--accent-strong);
    --bs-btn-active-bg: var(--accent-strong);
    --bs-btn-active-border-color: var(--accent-strong);
    --bs-btn-disabled-bg: var(--ink-4);
    --bs-btn-disabled-border-color: var(--ink-4);
}
.btn-outline-primary {
    --bs-btn-color: var(--accent);
    --bs-btn-border-color: var(--accent);
    --bs-btn-hover-bg: var(--accent);
    --bs-btn-hover-border-color: var(--accent);
    --bs-btn-active-bg: var(--accent-strong);
    --bs-btn-active-border-color: var(--accent-strong);
}

/* .btn-secondary was mid-grey and read as disabled wherever it carried a real
   action (the results-page Calculate). It is now the accent too — the pages
   using it mean it as the primary action. */
.btn-secondary {
    --bs-btn-bg: var(--accent);
    --bs-btn-border-color: var(--accent);
    --bs-btn-color: #fff;
    --bs-btn-hover-bg: var(--accent-strong);
    --bs-btn-hover-border-color: var(--accent-strong);
    --bs-btn-hover-color: #fff;
    --bs-btn-active-bg: var(--accent-strong);
    --bs-btn-active-border-color: var(--accent-strong);
    --bs-btn-active-color: #fff;
}

/* The neutral outline beside it — "Edit Terms", "Back to Listing". Ink so it
   reads as the way out rather than a second action competing with the first. */
.btn-outline-secondary {
    --bs-btn-color: var(--ink-2);
    --bs-btn-border-color: var(--rule-strong);
    --bs-btn-hover-bg: var(--paper-sunken);
    --bs-btn-hover-border-color: var(--ink-3);
    --bs-btn-hover-color: var(--ink);
    --bs-btn-active-bg: var(--paper-sunken);
    --bs-btn-active-border-color: var(--ink-3);
    --bs-btn-active-color: var(--ink);
}

/* --------------------------------------------------------------------------
   7. Forms
   -------------------------------------------------------------------------- */

.form-label {
    font-size: var(--t-small);
    font-weight: 600;
    color: var(--ink);
    margin-bottom: 0.4rem;
}

.form-control,
.form-select {
    background-color: var(--paper-raised);
    border: 1px solid var(--rule-strong);
    border-radius: var(--r-md);
    color: var(--ink);
    font-size: var(--t-body);
    padding: 0.6rem 0.75rem;
    transition: border-color .15s ease, box-shadow .15s ease;
}

/* A select has to reserve room on the right for its own arrow. Bootstrap does
   that with padding-right: 2.25rem, which the symmetric padding above wiped
   out — on a .w-auto select (which shrinks to its content) the arrow then sat
   directly on the last digit. Setting the indicator explicitly rather than
   restoring Bootstrap's default, because --bs-body-color changed and the
   stock arrow is drawn in the old one. */
.form-select {
    padding-right: 2.25rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23686f7c' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    background-size: 14px 11px;
}
.form-control::placeholder {
    color: var(--ink-4);
}
.form-control:focus,
.form-select:focus {
    border-color: var(--accent);
    box-shadow: var(--focus-ring);
}

.input-group-text {
    background: var(--paper-raised);
    border: 1px solid var(--rule-strong);
    border-radius: var(--r-md);
    color: var(--ink-3);
    font-size: var(--t-body);
}

.form-text,
.form-check-label {
    color: var(--ink-3);
}

/* --------------------------------------------------------------------------
   8. Surfaces
   -------------------------------------------------------------------------- */

.card {
    --bs-card-bg: var(--paper-raised);
    --bs-card-border-color: var(--rule);
    --bs-card-border-radius: var(--r-md);
    --bs-card-cap-bg: var(--paper-raised);
}

/* The one elevation in the system. */
.card-lift,
.card.shadow-sm,
.card.shadow {
    background: var(--paper-raised);
    border: 0;
    border-radius: var(--r-md);
    box-shadow: var(--lift-sm) !important;
}

.modal-content {
    background: var(--paper);
    border: 0;
    border-radius: var(--r-md);
    box-shadow: var(--lift);
}
.modal-header,
.modal-footer {
    border-color: var(--rule);
}

.dropdown-menu {
    --bs-dropdown-bg: var(--paper-raised);
    --bs-dropdown-border-color: var(--rule);
    --bs-dropdown-border-radius: var(--r-md);
    --bs-dropdown-link-color: var(--ink-2);
    --bs-dropdown-link-hover-bg: var(--paper-sunken);
    --bs-dropdown-link-hover-color: var(--ink);
    --bs-dropdown-link-active-bg: var(--paper-sunken);
    --bs-dropdown-link-active-color: var(--ink);
    --bs-dropdown-font-size: var(--t-small);
    box-shadow: var(--lift-sm);
    border: 1px solid var(--rule);
    padding: 0.35rem;
}
.dropdown-item {
    border-radius: var(--r-sm);
}

.alert {
    border-radius: var(--r-md);
    border-color: var(--rule);
}

.badge {
    font-weight: 600;
    letter-spacing: 0.02em;
}

/* --------------------------------------------------------------------------
   9. Site chrome
   -------------------------------------------------------------------------- */

/* Flat hairline above the canvas — no shadow, no blur, no separate fill.
   The navbar is part of the page, not a bar sitting on top of it. */
.navbar {
    background-color: transparent !important;
    border-bottom: 1px solid var(--rule);
    box-shadow: none;
    padding-top: 0.875rem;
    padding-bottom: 0.875rem;
}
.navbar .nav-link {
    font-size: var(--t-small);
    font-weight: 500;
    color: var(--ink-2);
}
/* Nav links are navigation, not actions — they carry state, so they are ink.
   This also leaves "Sign Up for Free" as the only sienna thing in the navbar,
   which is the point of having an accent at all. */
.navbar .nav-link:hover,
.navbar .nav-link:focus {
    color: var(--state-current);
}
.navbar .nav-link.active {
    color: var(--state-current) !important;
    font-weight: 600;
}
.dropdown-item.active-page {
    color: var(--state-current);
    font-weight: 600;
}
.navbar-brand {
    font-weight: 600;
    color: var(--ink) !important;
}
.navbar-toggler {
    border-color: var(--rule);
    border-radius: var(--r-sm);
}
.navbar-toggler:focus {
    box-shadow: var(--focus-ring);
}

/* Hover-open for the desktop dropdowns. Mobile users tap the parent link,
   which navigates directly to the section page. */
@media (min-width: 992px) {
    .navbar .nav-item.dropdown:hover > .dropdown-menu {
        display: block;
        margin-top: 0;
    }
}

@media (max-width: 991px) {
    .navbar-collapse .nav-item {
        text-align: left;
    }
    .navbar-collapse .btn,
    .navbar-collapse .btn-outline {
        margin-left: 0 !important;
        text-align: left;
        justify-content: flex-start;
    }
}

/* Footer. The old bg-dark band broke the continuous-canvas rule; it is now a
   sunken step on the same ground, separated by a hairline. */
.site-footer {
    background: var(--paper-sunken);
    border-top: 1px solid var(--rule);
    color: var(--ink-2);
    padding: var(--s7) 0 var(--s5);
    margin-top: var(--s8);
}
.site-footer h6 {
    font-size: var(--t-caption);
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink-3);
    margin-bottom: var(--s2);
}
/* The Services heading is itself a link. Without this it inherits the link
   colour and reads as the one emphasised column heading of the seven. */
.site-footer h6 a {
    color: inherit;
    font-size: inherit;
    letter-spacing: inherit;
}
.site-footer li {
    margin-bottom: 0.35rem;
}
.site-footer a {
    color: var(--ink-2);
    text-decoration: none;
    font-size: var(--t-small);
}
.site-footer a:hover {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 0.18em;
}
.site-footer .footer-tagline {
    color: var(--ink-3);
    font-size: var(--t-small);
    max-width: 30ch;
}
.site-footer hr {
    border-top: 1px solid var(--rule);
    opacity: 1;
}
.site-footer .footer-legal {
    font-size: var(--t-small);
    color: var(--ink-3);
}

/* --------------------------------------------------------------------------
   10. Shared page furniture
   -------------------------------------------------------------------------- */

.auth-container {
    max-width: 450px;
    margin: var(--s8) auto;
    padding: var(--s6);
    background: var(--paper-raised);
    border: 1px solid var(--rule);
    border-radius: var(--r-md);
    box-shadow: var(--lift-sm);
}
.auth-container h1 {
    font-size: var(--t-h2);
    font-weight: 600;
    margin-bottom: var(--s5);
    text-align: center;
}

/* Tooltip trigger. Initialised site-wide by js/tooltips.js. */
.tip {
    border-bottom: 1px dashed var(--ink-4);
    cursor: help;
}
.tip:hover {
    border-bottom-color: var(--accent);
    color: var(--accent);
}
/* A .tip is a <span>, so it carries tabindex="0" to be reachable at all — which
   makes a visible focus state mandatory rather than optional. Without this the
   only indication was the UA outline, which the rest of the system replaces. */
.tip:focus-visible {
    outline: none;
    border-bottom-color: var(--accent);
    color: var(--accent);
    box-shadow: var(--focus-ring);
    border-radius: var(--r-sm);
}

/* Touch. There is no hover to reveal a tip and no cursor to change shape, so
   the dashed underline is doing all the work alone. A small mark restores the
   affordance — it is a trigger, so it takes the accent legitimately. */
@media (hover: none) {
    .tip {
        cursor: default;
    }
    .tip::after {
        content: "\F431"; /* bi-info-circle */
        font-family: "bootstrap-icons";
        font-size: 0.72em;
        color: var(--accent);
        margin-left: 0.25em;
        vertical-align: 0.15em;
        line-height: 1;
    }
}

.tooltip-inner {
    background: var(--ink);
    border-radius: var(--r-sm);
    font-size: var(--t-small);
    padding: 0.5rem 0.7rem;
}
.tooltip .tooltip-arrow::before {
    border-top-color: var(--ink);
    border-bottom-color: var(--ink);
    border-left-color: var(--ink);
    border-right-color: var(--ink);
}

/* --------------------------------------------------------------------------
   10b. Ink portrait

   Shared by the homepage event block and the About page header, which is why
   it lives here rather than in either page's own stylesheet. Each page sets
   its own width; everything else is identical in both places.

   No frame at all — no card, no shadow, no radius. Framing an engraving in a
   white box is the one thing that would make it look pasted on.

   The source PNG has an opaque pure-white background and no alpha channel, so
   it cannot simply be made transparent. multiply blends each pixel with the
   page behind it: white becomes the ground and disappears, while the ink is
   unaffected. Same technique as the ebook cover below, and the reason the
   black-and-white file is used rather than the cream one — the cream version
   carries its own ground, which multiply would darken into a visible square.
   -------------------------------------------------------------------------- */

.portrait {
    margin: 0;
    max-width: 100%;
}
/* multiply is on the image alone, not the figure: applied to the whole figure
   it would blend the caption text into the ground as well. */
.portrait img {
    display: block;
    width: 100%;
    height: auto;
    mix-blend-mode: multiply;
}

/* Names the face. Mono and small so it reads as a caption rather than a
   heading — both pages that use it already have one of those. */
.portrait-byline {
    font-family: var(--font-mono);
    font-size: var(--t-caption);
    line-height: 1.5;
    color: var(--ink-3);
    margin: var(--s2) 0 0;
}
.portrait-byline strong {
    display: block;
    font-weight: 400;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink);
}

/* --------------------------------------------------------------------------
   10c. Report fan

   Exported report pages as overlapping sheets, each one lower and offset.
   Shared by the homepage Deal Advisory section and deal-advisory.php, which is
   why it lives here rather than in home.css.

   Cropped to their chart region and allowed to bleed off the bottom of the
   frame rather than shown whole: a full letter page at this width is
   unreadable, and the charts are the part worth seeing.

   The sheets carry --lift, the system's three-layer ink-tinted shadow, so they
   read as paper on a surface — the same metaphor as .framed on the content
   pages and the paper textures behind them.
   -------------------------------------------------------------------------- */

.report-fan {
    position: relative;
    /* Sized to contain the absolutely-positioned sheets. The front one starts
       10rem down and runs 14rem of image plus its ~2.2rem caption = 26.2rem.
       Too short and the stack bleeds into the next section. */
    min-height: 27rem;
}

.report-sheet {
    margin: 0;
    background: var(--paper-raised);
    border-radius: var(--r-md);
    box-shadow: var(--lift);
    overflow: hidden;
}
.report-sheet img {
    display: block;
    width: 100%;
    height: auto;
}

/* The caption tag, borrowed from the reference: a small monospace label that
   names the artefact so the reader knows what they are looking at. */
.report-tag {
    font-family: var(--font-mono);
    font-size: var(--t-caption);
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-3);
    background: var(--paper);
    border-bottom: 1px solid var(--rule);
    padding: 0.5rem 0.75rem;
}

@media (min-width: 992px) {
    .report-sheet {
        position: absolute;
    }
    /* A cascade, not a zigzag: each sheet steps the same direction so the
       group reads as one fanned stack of documents. The three are the three
       analysis reports in the RTOCalc Deal Package.

       The 5rem vertical step is doing specific work — the caption band is
       ~2.2rem, so a step that size leaves every label legible above the sheet
       in front of it, plus ~2.8rem of chart. An earlier 3rem step showed the
       labels but almost none of the charts, which made the back two look like
       filler rather than deliverables.

       12% horizontal step with 76% width: adjacent sheets overlap by 64% of
       their width, but the left 12% strip of each is never covered, so no
       sheet is reduced to a caption alone. */
    .report-sheet-back {
        top: 0;
        left: 0;
        width: 76%;
        z-index: 1;
    }
    .report-sheet-mid {
        top: 5rem;
        left: 12%;
        width: 76%;
        z-index: 2;
    }
    .report-sheet-front {
        top: 10rem;
        left: 24%;
        width: 76%;
        z-index: 3;
    }
    /* Clipped so each bleeds off the bottom rather than ending in a hard edge
       mid-chart. The back two only ever show ~2.8rem of image, so they are
       capped short; the front one carries the readable chart. */
    .report-sheet-back img,
    .report-sheet-mid img {
        max-height: 10rem;
        object-fit: cover;
        object-position: top;
    }
    .report-sheet-front img {
        max-height: 14rem;
        object-fit: cover;
        object-position: top;
    }
}

/* Below lg the overlap stops working — two stacked sheets at full width read
   more clearly than a cramped fan. */
@media (max-width: 991.98px) {
    .report-fan {
        min-height: 0;
        display: grid;
        gap: var(--s4);
    }
    .report-sheet img {
        max-height: 15rem;
        object-fit: cover;
        object-position: top;
    }
}

/* --------------------------------------------------------------------------
   11. Ebook lead capture (includes/ebook_cta.php)

   Shared by several pages, so it lives here rather than in home.css.
   -------------------------------------------------------------------------- */

.ebook-cta {
    position: relative;
    padding: var(--s8) 0;
    border-top: 1px solid var(--rule);
}
/* The cover art is an RGB PNG with no alpha channel — its "background" is
   opaque white pixels, so it cannot simply be made transparent. multiply
   blends each pixel with the page behind it: pure white becomes the ground
   (invisible) while the artwork barely shifts, because the ground is
   near-white. Same technique the old hero illustration used.

   No box-shadow: --lift-sm leads with a 1px outline ring, which was the
   "stroke" boxing the cover in. */
.ebook-cta .ebook-cover {
    width: 190px;
    max-width: 100%;
    border-radius: 0;
    box-shadow: none;
    mix-blend-mode: multiply;
}
.ebook-cta .ebook-sub {
    color: var(--ink-3);
    max-width: 46ch;
    margin: var(--s2) 0 var(--s4);
}
.ebook-cta .ebook-privacy {
    font-size: 0.8125rem;
    color: var(--ink-3);
    margin: var(--s2) 0 0;
}
.ebook-cta-dismiss {
    position: absolute;
    top: var(--s4);
    right: var(--s4);
    background: none;
    border: 0;
    color: var(--ink-4);
    font-size: 0.9rem;
    line-height: 1;
    padding: 6px;
    cursor: pointer;
    border-radius: var(--r-sm);
}
.ebook-cta-dismiss:hover {
    color: var(--ink);
}
.ebook-cta-dismiss:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

/* --------------------------------------------------------------------------
   12. Chat widget clearance

   The widget is fixed at bottom-right and injects its own <style> at runtime,
   after these stylesheets — hence !important, which is the only way to reach
   it from here.

   On a short phone the hero CTA now sits low enough that the bubble clipped
   its bottom-right corner (measured 47x24px at 375x600; no overlap at 390
   wide). Lifting it clears the button without moving it anywhere it would
   cover something else.
   -------------------------------------------------------------------------- */

@media (max-width: 767.98px) and (max-height: 740px) {
    #rtocalc-chat-toggle {
        bottom: 12px !important;
        right: 12px !important;
        transform: scale(0.85);
        transform-origin: bottom right;
    }
}

/* --------------------------------------------------------------------------
   13. Motion
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}
