/* CashMAX - Apex X identity
   Midnight/Navy surfaces · Electric Blue 600 → Signal Cyan 400 gradient
   (always ascending, 45°) · Poppins */

/* ==================== THE CONTROL VOCABULARY ====================
   One form per role, enforced by tests/vocabulary.test.mjs - a new
   control conforms or the suite fails.

   PRIMARY ACTION   solid button, .btn-primary - THE ONLY SOLID BLUE
                    fill on any page. radius --r-control.
                    (Go to provider, diagnosis CTA, form submits.)
   MODE             segmented control, .seg - connected group, hairline
                    border; the selected segment fills NAVY with white
                    text. radius --r-control.
                    NAVY MEANS "YOU ARE HERE", BLUE MEANS "DO THIS."
   LINK             plain text link, chevron ok. No border, no fill.
                    (presets, how-we-rank, details, show-all, the
                    filter-sentence terms.)
   INPUT            native styled inputs. radius --r-control.
   CONDITION TAG    the ONE capsule, .pill - a condition attached to
                    this product, max one per row, radius --r-pill.
                    Nothing else may be capsule-shaped.

   Applied filters render as the FILTER SENTENCE (.filter-sentence),
   plain removing links - never tokens. Feature lists render as the
   FEATURE LINE (.feat-line), muted middot text - never capsules.
   ================================================================= */
:root {
  --midnight: #060D1F;      /* Midnight 950 - primary dark surface */
  /* LOOKFIRST BRAND (kit v1.0, 16 Sep 2026): the five colours, named as
     the kit names them. The working tokens below alias onto them, so
     every existing rule reads the brand without a rewrite. */
  --ink-980: #07090D;       /* Ink 980 - dark surfaces */
  --platinum: #E6E9EF;      /* Platinum - "look" and eye rings on dark */
  --navy-900: #0B1E3F;      /* Navy Ink 900 - "look" and rings on light */
  --electric-blue: #2563EB; /* Electric Blue - "first" on light */
  --signal-cyan: #22D3EE;   /* Signal Cyan - "first" on dark, pupils */
  --navy: var(--navy-900);          /* text on light */
  --cobalt: #1D4ED8;        /* Cobalt 700 - hover states */
  --blue: var(--electric-blue);     /* primary brand blue */
  --cyan-mid: #06B6D4;      /* Cyan 500 */
  --cyan: var(--signal-cyan);       /* accent */
  --frost: #F4F7FC;         /* Frost White - text on dark */
  --grad: linear-gradient(45deg, var(--blue) 0%, var(--cyan) 100%);
  /* THE grain tile - hero sky and twilight band share it, so the two
     surfaces can never drift. Defined once, referenced twice. */
  --grain-tile: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.88' numOctaves='3'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23n)' opacity='.38'/%3E%3C/svg%3E");

  --bg: #F4F7FC;
  --card: #ffffff;
  --text: var(--navy);
  --muted: #5B6B85;
  --border: #E7EEF9;
  /* RADIUS - four names, one decision each. A radius that expresses a
     GEOMETRIC relationship is deliberately NOT tokenised and stays a
     literal: 50% on a circle means "a circle". Naming it would hide
     what it is. Three rules: data is square; a control nested in a
     surface shrinks to --r-control rather than echoing its parent;
     siblings in a row match exactly. */
  --r-data: 0;           /* rates, table cells, data badges - the pSEO
                            pages inherit this instead of re-deciding */
  --r-control: 6px;      /* buttons, inputs, selects, chips */
  --r-surface: 12px;     /* cards, panels, modals, drawers */
  --r-card: 24px;        /* the home-page savings card (Card A, 26 Sep 2026) */
  --r-pill: 999px;       /* metadata pills, tabs, toggles - deliberately
                            NOT buttons; a pill is a different species */
  --shadow: 0 1px 3px rgba(11, 30, 63, .08), 0 4px 14px rgba(11, 30, 63, .06);

  /* TYPE, in two roles.
     LOOKFIRST (16 Sep 2026): one family, Poppins, at the kit's weights -
    Regular for text, Medium for the wordmark's own cut, SemiBold and Bold
    for headlines so nothing is faux-bolded by the browser. Data columns
    keep tabular figures via font-variant-numeric; no second family. The
    display token now simply aliases the sans token, kept so every rule
    that reads it keeps reading it.
*/
  --font-sans: "Poppins", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-display: var(--font-sans);
}
* { box-sizing: border-box; }
body {
  margin: 0;
  font: 16px/1.55 var(--font-sans);
  background: var(--bg); color: var(--text);
}
/* THE container: every section aligns to these edges - header,
   categories, founder, footer (the hero's sky spans the page and
   composes its own 1200px interior). One definition: 1280px with
   24px sides at every width (founder's layout brief, 30 Sep 2026 - the
   logo, the breadcrumb, the H1 and the filter sidebar share one edge). */
.wrap { max-width: 1280px; margin: 0 auto; padding: 0 24px; }
a { color: var(--blue); text-decoration: none; }
a:hover { text-decoration: underline; color: var(--cobalt); }

/* numeric rigor: every data number renders tabular, one rule on the
   number-bearing components - decimals stack down a list of tiles. */
.metric strong, .metric span, .result-count,
.compare-table td, .compare-table th, .rank-status,
.rw-btn, .rw-chip, .rw-manage, .cap-note, .history-strip, .fab-badge,
.opt-count, .cat-count, .cat-figure {
  font-variant-numeric: tabular-nums;
}



/* header - dark surface, on-dark lockup */
.site-header { background: var(--midnight); border-bottom: 1px solid rgba(34, 211, 238, .14); }
.site-header .wrap { display: flex; align-items: center; gap: 32px; height: 64px; }
.logo { display: flex; align-items: center; text-decoration: none; }
/* LookFirst wordmark (16 Sep): the kit asks one eye-diameter of clear
   space around the lockup and a 110px minimum width. In the 64px bar the
   eye ring is ~0.6 of the logo height, so 34px tall gives ~20px rings
   and 15px of air above and below - and 146px of width. */
.logo .logo-mark { height: 34px; width: auto; display: block; }
/* The mark's drawing starts 145 of its 980 units in from the left of its
   box: pull the box back by that much so the "l" itself sits on the
   container edge with the breadcrumb, the H1 and the sidebar. */
.site-header .logo { margin-left: -5px; }
.logo:hover { text-decoration: none; }


/* hero - headline-led, LEFT-composed on the brand's dark surface: the
   content column holds the left, the right carries the instrument.
   isolation lets the grain overlay sit at z -1: above the background
   ramp, beneath every child. The Southern Cross was removed from here
   2026-08-21 - the markup and its CSS are in git if it returns. */
/* ================================================================== */
/* THE TWILIGHT HERO                                                   */
/*                                                                     */
/* Australian civil twilight - the sky AFTER the sun has gone, which is */
/* the sky a constellation appears in. Near-black at the zenith,        */
/* falling through indigo and violet to a thin dusty-rose band at the   */
/* horizon.                                                            */
/*                                                                     */
/* SCOPED, NOT GLOBAL. Every colour below is a literal or a hero-local  */
/* custom property. Nothing here redefines --midnight, --cyan or        */
/* --blue: those paint the nav, the footer, the navy Home loans tile    */
/* and twenty other things, and redefining one to suit the hero would   */
/* silently repaint half the site.                                     */
/* ================================================================== */
.hero-dark {
  /* The gradient is written by lib/sky.mjs and injected as a custom
     property, so the stops exist in exactly one place - the module that
     also computes contrast against them. Two copies would drift, and a
     contrast report against a stale copy is worse than none. */
  background: var(--sky);
  /* Full width because layout() renders it outside .wrap, not through
     width:100vw, which counts the scrollbar (see render.mjs). */
  text-align: left;
  /* Enough sky above the headline to read as depth, not so much that
     the top becomes a void. The Cross no longer lives up here - it sits
     in the pocket below the headline and left of the card - so this is
     breathing room only. */
  /* Asymmetric deliberately. The TOP space is compositional - it holds
     the black band, the upper starfield and the constellation. The
     BOTTOM was just padding, and it pushed the rose horizon band away
     from the content it is meant to sit under. */
  /* HERO HEIGHT LAW (founder, 31 Aug - fourth and final round of the
     see-saw): the hero's height is CONTENT plus padding, never a fixed
     floor. Anchor: top padding puts the eyebrow 60-80px below the nav
     (align-items:start on .hero-inner keeps that independent of the
     card's weight); bottom: 56px below the lower column. NO min-height
     - the last one was sized for a taller left column and left ~350px
     of dead gradient; the sky's stops are percentages, so the gradient
     compresses with the hero and the rose horizon always fits. A
     structural test pins this block: no min-height may return here. */
  /* 8 Sep (founder: "the sky is too deep and pushes the product links
     down"): both anchors to the law's minimums - eyebrow 60px below the
     nav, 44px below the lower column. The disclosure slab that made
     the card's column the tallest thing in the hero is gone: since
     21 Sep the hero carries one short line under the card. */
  padding: 60px 0 44px;
  position: relative; overflow: hidden; isolation: isolate;
}

/* GRAIN. Non-negotiable: a gradient this smooth reads as machine-
   rendered without it, and large dark gradients band badly on 8-bit
   displays. feTurbulence at .88 with 3 octaves is fine enough to
   dissolve the ramp without reading as texture. overlay keeps it
   neutral - it lightens the darks and darkens the lights rather than
   greying the whole field. */
.hero-dark::after {
  content: ""; position: absolute; inset: 0; z-index: 4;
  pointer-events: none; opacity: .42; mix-blend-mode: overlay;
  background-image: var(--grain-tile);
}

/* HAZE. Bottom 30%, darkening toward the base, so the warm band sits
   behind atmosphere rather than glowing. It is also what carries the
   disclosure line's contrast: on bare sky that text measures 3.00:1 and
   fails - the haze is why it passes. Do not lighten it without
   re-running tests/herocontrast.test.mjs. */
.sky-haze {
  position: absolute; left: 0; right: 0; bottom: 0; height: 30%;
  z-index: 1; pointer-events: none;
  background: linear-gradient(180deg,
    rgba(7, 12, 30, 0) 0%, rgba(7, 12, 30, .28) 48%, rgba(7, 12, 30, .72) 100%);
}

/* STARS. Server-rendered from a fixed seed; position and opacity ride
   in inline styles because each one is unique data, not a rule. */
.sky-stars { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.sky-stars i {
  position: absolute; display: block; border-radius: 50%;
  background: #FFFFFF;
  box-shadow: 0 0 3px rgba(255, 255, 255, .55);
}

/* THE SOUTHERN CROSS - position only.
   Absolutely placed in the clear band between the headline's right edge
   and the card's top edge. It used to be a flex child of a right-column
   cell that no longer exists, so the positioning CONTEXT changed; the
   component did not. Clearances are asserted in tests/crux.test.mjs at
   1280, 1440 and 1920. */
.hero-dark .crux {
  position: absolute; z-index: 2; pointer-events: none;
  /* THE POCKET: below the headline, left of the card.
     Everything in the left column below the headline stops by x=537 -
     the stats strip is the widest of them - so the clear band there is
     227px across and 307px deep, far roomier than the strip above the
     card where this first sat. Anchored from the RIGHT so it tracks the
     card's left edge as the viewport widens rather than drifting away
     from it: the content column is 1136px centred and the card 430px of
     that, so 50% - 84px puts the Cross's right edge 40px clear of the
     card at 1280 and further clear above it. Verified by measurement at
     three widths in tests/hero.test.mjs. */
  right: calc(50% - 90px); top: 248px;
  /* 147px until 8 Sep. The hero lost its dead height that day, and a
     147px Cross (196px tall at the 120x160 viewBox) left only 9px to
     give before its 40px bottom clearance broke; at 110px it stands
     147px tall from the same top, so the hero can be content-tall and
     the Cross still clears everything - remeasured in hero.test.mjs.
     104px since 21 Sep: the hero lost its three-line disclosure slab and
     stands 429px, which left a 110px Cross 34px of bottom clearance
     against the 40px rule; at 104px it stands 139px tall and keeps 42px. */
  width: 104px; height: auto;
}

/* THE NAV, continuous with the sky.
   --midnight (#060D1F) against the sky's top band (#02030A) left a seam
   - close enough to read as a mistake rather than a join. Floating the
   nav transparently OVER the hero removes the seam too, but costs the
   hero 72px of its own height, and that height turned out to be exactly
   what the Southern Cross needed to clear the card. So the nav stays a
   solid bar and simply takes the sky's own top colour: seamless by
   being the same colour, not by being absent. Home page only - every
   other page keeps the standard header. */
body.home .site-header {
  background: #02030A; border-bottom: none;
}

/* ANCHORED, NEVER CENTRED (standing rule, reapplied 31 Aug): with
   align-items:center the copy's top position was a FUNCTION of the
   card's height - the moment the legal wording grew the card, ~230px
   of empty sky opened above the eyebrow. That is what reset the
   60-80px nav-to-headline rule; start-alignment makes the anchor
   independent of whatever the card weighs. */
.hero-inner {
  max-width: 1200px; margin: 0 auto; padding: 0 32px;
  /* the card column's floor is the width at which the card's lead and
     foot row still sit on one line each (374px, the 34vw of 1100), so
     the card keeps its depth down to the stacking breakpoint. */
  display: grid; grid-template-columns: minmax(0, 1fr) clamp(374px, 34vw, 430px);
  /* column gap only: the caption row below the columns spaces itself
     with its own margin - a 56px row gap put 56px of sky above it. */
  column-gap: 56px; row-gap: 0; align-items: start; position: relative; z-index: 3;
}
@media (max-width: 900px) { .hero-inner { padding: 0 24px; } }
.hero-copy { min-width: 0; }

/* THE KICKER (deck: docs/copy/homepage.md). Stats-strip register -
   14px regular sans, BOLD WHITE category phrase and figures, muted
   labels and middots. The mono-caps letterspaced treatment is deleted
   from homepage use (reserved for the card plate and foot); the
   register test enforces it. */
.hero-kicker {
  font-size: 14px; color: #9EACC7; margin: 0 0 14px; font-weight: 400;
}
.hero-kicker strong { color: #FFFFFF; font-weight: 700; }
.hero-kicker i { font-style: normal; color: rgba(158, 172, 199, .4); padding: 0 4px; }

/* THE CARD.
   Cool grey with NO violet in it - a lavender tint reads as contaminated
   by the sky behind it. No white inner rim: that hairline of pure white
   was the glare. Two stacked shadows, one broad and one tight, so the
   card sits BELOW the light rather than glowing - depth doing the work
   that dimming would otherwise do. */
.hero-right {
  display: flex; flex-direction: column; align-items: stretch; gap: 14px;
  min-width: 0; align-self: stretch;
}
/* CARD A (founder's brief, 26 Sep 2026): a white card, the card radius,
   one deep soft shadow so it sits on the dark hero. THE DEPTH OF THE
   LEFT COLUMN (founder, the same day: "on full screen it should be the
   same depth as the text and components on the left hand side"): the
   right column stretches to the grid row, which the hero copy sets
   (kicker, H1, subhead, buttons: 283px at 1440), and the card fills the
   column, so its bottom edge is the copy's bottom edge at every
   desktop width. The copy's top stays anchored (the grid still
   start-aligns; only this column stretches). The card's own content
   is cut to fit that depth on a same-day read: 416px wide, 22px of
   padding, 10px between blocks (space-between spreads any spare
   height evenly), the figure at 48px, the boxes 26x34, the strip and
   foot tightened, no "Lowest today" line. */
.hero-right .lt {
  background: #FFFFFF; border: none; border-radius: var(--r-card);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  box-shadow: 0 30px 80px rgba(0, 0, 0, .35);
  padding: 22px; max-width: 416px; margin-inline: auto; width: 100%;
  position: relative; display: flex; flex-direction: column; gap: 10px;
  flex: 1 1 auto; justify-content: space-between;
}
.hero-right .lt::before, .hero-right .lt::after { content: none; }
/* the constellation is the right panel's centerpiece, floating on a
   soft glow field - a designed zone, not leftover margin. The star
   geometry itself is untouched. */
/* The right column is a stack: sky, then the instrument, then the
   actions it is about. The Southern Cross keeps its own space above the
   housing rather than sitting behind it - a constellation read through
   frosted glass is a texture, not a motif. */
.hero-right {
  display: flex; flex-direction: column; align-items: stretch; gap: 18px;
  min-width: 0;
}
/* the note belongs to the housing above it, not to the button below */
.hero-right .disclosure-note + .hero-actions { margin-top: 4px; }
.hero-right .hero-actions { margin-top: -6px; }
/* The button matches the housing's width so object and action read as
   one unit rather than a widget with a button near it. */
.hero-right .hero-actions { margin-top: 0; align-items: stretch; }
.hero-right .hero-actions .btn { width: 100%; }
/* TWO CLUSTERS, not an even ladder. Headline + subhead read as one
   message (18px apart); stats + disclosure read as one credibility
   block (12px apart); a single 40px gap separates the two. Even
   spacing is what made every item float independently - grouping
   reads as structure and costs less height. */
/* The Southern Cross keeps its own airspace above the housing. Placing
   it absolutely in the left column's empty area was tried and measured:
   it removed the dead space but put the constellation directly behind
   the tagline, stats and disclosure text. Clear air costs height here -
   see the layout note in the build report. Its opacities are never
   touched to make it fit. */
@media (max-width: 899px) {
  .hero-inner { grid-template-columns: 1fr; }
  /* Stacked, the card sits under the buttons: its own space above it
     (the grid keeps row-gap 0 - see .hero-inner), clear of the CTA's
     glow (founder, 30 Sep: "the loan example is too close to the
     buttons"). */
  .hero-right { margin-top: 40px; }
  /* Below the stacking breakpoint the columns collapse and the clear
     band the Cross occupies stops existing - every position would put
     stars behind the headline or the card. It is hidden rather than
     shrunk: a constellation squeezed into a gap it does not fit reads
     as debris, and it is decoration, not information. */
  .hero-dark .crux { display: none; }
  .hero-dark { padding: 96px 0 72px; }
}
@media (prefers-reduced-motion: reduce) {
  }
/* the content column: left 60%, natural headline break against 640px -
   "Compare every bank and energy plan" / "in Australia." */
/* Text colours are MEASURED against the sky at the depth each element
   sits, not chosen and hoped for - see tests/herocontrast.test.mjs,
   which recomputes every ratio from the same gradient the sky renders
   and fails below WCAG AA. */
.hero-dark h1 {
  font-family: var(--font-display);
  color: #FFFFFF; font-size: clamp(30px, 4.4vw, 52px); font-weight: 700;
  /* Poppins is geometric and already wide-set; -.02em keeps the mass of
     the headline without the round letters touching. */
  letter-spacing: -.02em; margin: 0 0 18px; max-width: 660px; line-height: 1.05;
}
/* THE TAGLINE (founder, 30 Sep 2026): "Always LookFirst." is a sign-off,
   not a sentence - semibold white at about half the headline's size,
   with "First" in the wordmark's signal cyan (--signal-cyan is "first"
   on dark, as in the logo above it). The grey 16.5px sentence style it
   inherited "looks weak" (founder). Both colours are in the contrast
   table (tests/hero.test.mjs). */
.hero-dark .tagline {
  color: #FFFFFF; max-width: 560px; margin: 0;
  font-family: var(--font-display); font-size: clamp(20px, 2.1vw, 26px);
  font-weight: 600; line-height: 1.25; letter-spacing: -.01em;
}
.hero-dark .tagline .brand-first { color: var(--signal-cyan); }
/* the secondary route, a text link beside the CTA rather than a second
   button - one primary action, one way past it */
.hero-browse {
  color: #C3CDE4; font-size: 15px; font-weight: 600; text-decoration: none;
  white-space: nowrap;
}
.hero-browse:hover { color: #FFFFFF; text-decoration: underline; }
/* CTA carries a soft glow so it reads as the lit object in a dark field */
.hero-copy .hero-actions {
  display: flex; align-items: center; gap: 22px; flex-wrap: wrap;
  margin: 30px 0 0;
}
.hero-copy .hero-actions .btn-primary {
  box-shadow: 0 6px 26px rgba(37, 99, 235, .45), 0 2px 6px rgba(2, 4, 12, .4);
}
/* Page-level proof: one register above tile meta, one below headings
   (deck rationale). Ink-2, not mute - and the freshness phrase in bold
   near-black, the kicker's bold-figures emphasis pattern. */
.section-provenance {
  font-size: 13.5px; color: #41567A; margin: -6px 0 22px;
}
.section-provenance strong { color: #0B1B33; font-weight: 700; }

/* category grid - the tiles' own rules sit at the end of this file
   (HOME TILES); this marker closes the hero block for hero.test.mjs */


/* browse */
.page-head { padding: 30px 0 6px; }
.page-head h1 {
  font-family: var(--font-display); font-weight: 700;
  margin: 0 0 4px; font-size: 27px; letter-spacing: -.3px;
}
.page-head p { margin: 0; color: var(--muted); }
.filters { display: flex; gap: 10px; margin: 16px 0 20px; flex-wrap: wrap; }
.filters select {
  padding: 9px 12px; border: 1px solid var(--border); border-radius: var(--r-control);
  background: var(--card); font-size: 14px; color: var(--text); font-family: inherit;
}
.prod-list { display: flex; flex-direction: column; gap: 14px; padding-bottom: 90px; }
/* ===================================================================
   THE PRODUCT ROW
   identity | pills | figures | actions

   BLUE IS RESERVED FOR ACTIONS. From here on, the only blue in a row is
   the Go to provider button. Rates used to render in a cobalt-to-cyan
   gradient, which made the most saturated thing on the page a number
   nobody can click - so the eye was pulled to the figure and the action
   had to compete with it. Figures are mono near-black; blue means "you
   can press this".
   =================================================================== */
.prod-card {
  position: relative;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-surface);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .06); padding: 16px 20px;
  /* The design file sizes these for a full-width page; the browse list
     sits beside a 260px filter sidebar and gets 854px, so fixed columns
     overflowed it by 87px. Fluid identity and figures, fixed actions -
     the button must not shrink below its label. */
  display: grid;
  /* Identity keeps a floor: below ~178px the name box drops under 130px
     and shows ~20 characters, which is not a name any more. The figures
     column holds three cells on home loans, so its floor is the
     measured width of three mono figures. The pill line takes what
     remains; featureBudget is sized to the column this yields at
     1280px, so the line still does not wrap. */
  /* Every track floored: the tags column's old zero floor let the other
     floors starve it to ~50px around 1000px layouts - the capsule
     ellipsed to two letters and the feature line stacked one character
     per line. Floors total 688px, and below the width that can hold
     them the row RESTACKS (the 1000px media rule) instead of crushing
     a column. */
  grid-template-columns: minmax(150px, .95fr) minmax(140px, 1.2fr) minmax(234px, 1.1fr) 150px;
  gap: 14px; align-items: center;
  transition: transform 140ms ease, box-shadow 140ms ease,
              background-color 140ms ease, border-color 140ms ease;
}
/* Tile hover/focus feedback: lift + faint cool tint + sharpened
   hairline. Transform and shadow only - nothing that reflows. The tint
   is a neutral half-step, never a colour statement. Inner controls keep
   their own hover states on top; the tile body stays a default-arrow
   surface (only real controls are pointers). Applies to product tiles
   only - statement blocks and toolbar controls are not tiles. */
.prod-card:hover, .prod-card:focus-within {
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .10);
  background: #FAFCFE;
  border-color: #D8E3F3;             /* var(--border) one step stronger */
}
@media (prefers-reduced-motion: reduce) {
  .prod-card:hover, .prod-card:focus-within { transform: none; }
}
/* THE RESTACK POINT is 1220px, not 1080 (30 Sep 2026): the sidebar grew
   to 300px with a 32px gap, and at 1081-1150 the four tracks' floors no
   longer fit - every row overran its card by up to 53px and the page
   scrolled sideways. Below 1220 even the widest row template (cards'
   three-column missions, 730px of floors) is short of room, so every
   row restacks there, headers and label rules with it. */
@media (max-width: 1220px) { .prod-card { grid-template-columns: 1fr; } }
/* doubled logo chips can't share a 220px column with the product name -
   the brand cell stacks: chip above, name below at full cell width */
.prod-brand { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.prod-brand h3 { margin: 0; font-size: 16px; line-height: 1.35; font-weight: 600; }
.brand-name { color: var(--muted); font-size: 13px; }
/* ---------------------------------------------------- provider logo chip
   One white chip per provider, identical within any grid: same size,
   radius and padding, so the grid reads as one designed system. Logos
   never touch chip edges; safe areas scale with the logo's measured
   optical class. Text fallback renders the provider name in the same
   chip - no initials, no glyphs, no broken images. */
.logo-chip {
  /* 62px, not 88: measured across 106 tiles, the tallest logo rendered
     58px and most wordmarks ~13px - the rest was empty box. The
     max-height caps below rise to match, so every logo keeps its
     rendered size; only the air around it goes. */
  width: 128px; height: 62px; flex: none; border-radius: var(--r-control);
  background: #FFFFFF; box-shadow: 0 1px 2px rgba(0, 0, 0, .08);
  display: inline-flex; align-items: center; justify-content: center;
  overflow: hidden; position: relative;
}
.logo-chip.lg { width: 192px; height: 128px; }
.logo-chip img {
  display: block; width: auto; height: auto;
  max-width: 70%; max-height: 95%; object-fit: contain; /* in-between */
}
.logo-chip.logo-wide img { max-width: 80%; max-height: 88%; }   /* wordmarks */
.logo-chip.logo-square img { max-width: 65%; max-height: 80%; } /* square marks */
/* text fallback: shown for providers with no usable asset, or when the
   image fails to load (onerror adds .logo-failed) */
.logo-chip .logo-chip-name { display: none; }
.logo-chip.logo-text .logo-chip-name,
.logo-chip.logo-failed .logo-chip-name {
  display: flex; align-items: center; justify-content: center;
  position: absolute; inset: 0; padding: 3px 6px; text-align: center;
}
.logo-chip.logo-failed img { display: none; }
.logo-chip-name span {
  font-weight: 600; color: #333; font-size: 14px; line-height: 1.2;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.logo-chip.lg .logo-chip-name span { font-size: 17px; }

/* Provider logos render at FULL COLOUR in every state - the old
   grid-wide grayscale flag and its hover restore are gone. */
/* metric values never wrap or crop - "36 mo" fits at any width; a
   squeezed cell wraps whole metrics to the next row instead */
/* IDENTITY: one brand mention, not two. The row carried a 150px logo
   box AND the brand name under the product title; the chip is 40px and
   the name appears once, above the product. */
.prod-id { display: flex; align-items: center; gap: 11px; min-width: 0; }
.prod-id .logo-chip {
  /* 56px by the founder's pick at Gate 1: wordmark legibility over the
     16px of name box it costs (height cost is zero - the action stack
     governs the row). */
  flex: none; width: 56px; height: 56px; padding: 6px; border-radius: 9px;
}
.prod-id .logo-chip img { max-width: 100%; max-height: 100%; }
.prod-id-t { min-width: 0; }
/* Brand identifies or it fails ("BOQ Sp..." names nobody - founder,
   29 Aug): the line wraps instead of ellipsizing. Two lines of muted
   11.5px is quieter than a wrong guess at who the lender is. */
.prod-brandline {
  font-size: 11.5px; color: var(--muted); margin: 0 0 2px;
  overflow-wrap: anywhere;
}
/* THE GUARANTEE LEGEND - the sidebar module the row shields point to.
   House style: hairline rule, sidebar type scale, no illustration.
   Its shield is the rows' shield, at legend size - that identity is
   the point. Position gating (.gg-side/.gg-flow) is shared with the
   callout component. */
.provider-roster { margin: 10px 0; }
.provider-roster summary { cursor: pointer; font-weight: 600; font-size: 14px; }
.roster-list { columns: 2; font-size: 13px; margin: 8px 0 0; padding-left: 18px; }
.roster-when { color: var(--muted); font-size: 11px; }
.coverage-link { color: inherit; }
@media (max-width: 640px) { .roster-list { columns: 1; } }

/* THE DEPOSIT-PROTECTION CALLOUT (founder, 5 Sep: "a clear callout
   box, tier-1"). Was a borderless text block under a hairline that
   read as cut-off copy, especially once the sidebar started scrolling.
   Now a contained card: a faint protection-green wash, a full hairline
   frame with a heavier green rule down the leading edge, and the
   shield promoted into a filled chip so the eye lands on the mark
   before the words. --fcs-green is the one source for the family.
   No dark variant: the browse chrome renders light at every OS
   setting (the facet sidebar around this box does), so a
   prefers-color-scheme swap on the callout alone would strand dark
   navy body text on a dark ground - it stays light with its page. */
:root {
  --fcs-green: #0E7A4E;
  --fcs-green-ground: #EEF7F1;
  --fcs-green-hair: #CFE6DA;
}
.fcs-legend {
  margin: 14px 0 0; padding: 14px 16px 15px;
  background: var(--fcs-green-ground);
  border: 1px solid var(--fcs-green-hair);
  border-left: 3px solid var(--fcs-green);
  border-radius: var(--r-surface);
}
.fcs-legend-head {
  display: flex; align-items: center; gap: 9px;
  font-size: 14px; font-weight: 700; color: var(--navy); margin: 0 0 8px;
  line-height: 1.3;
}
/* the shield rides in a filled green chip - the callout's one strong
   mark, matching the row badge's colour so page and sidebar agree */
.fcs-legend-shield {
  color: #fff; flex: none;
  width: 26px; height: 26px; padding: 4px; box-sizing: border-box;
  background: var(--fcs-green); border-radius: 50%;
}
/* white shield in the green chip, the tick punched back through in
   green so the mark stays legible (white-on-white would vanish) */
.fcs-legend-shield .fcs-badge-check { stroke: var(--fcs-green); }
.fcs-legend-body {
  font-size: 12.5px; color: var(--text); line-height: 1.55; margin: 0 0 8px;
  opacity: .82;
}
.fcs-legend-more { margin: 0; }
.fcs-legend .gg-learn { color: var(--fcs-green); font-weight: 600; }
.fcs-legend .gg-learn:hover, .fcs-legend .gg-learn:focus-visible {
  color: var(--fcs-green);
}

/* the guarantee badge on its own line in the identity block */
.prod-id-t > .fcs-badge { margin: 0 0 3px; }
/* At grid widths the identity column (~160px) cannot hold the full
   badge beside the logo without painting over the tags column - so
   the badge compacts to its shield. The claim survives in full: the
   title and the hidden label keep it readable and findable, clicking
   opens the FCS explanation, and the deposits sidebar carries the
   complete statement. Every narrower layout shows the full text. */
@media (min-width: 1221px) {
  .prod-card .fcs-badge .fcs-badge-label {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap;
  }
  .prod-card .fcs-badge { padding: 4px 5px; }
}
/* INK, NOT LINK-BLUE. It is still the link to Details - a product name
   that looks like a heading and behaves like a link is fine; a row of
   blue titles competing with a blue button is not.
   TWO LINES, clamped - ruled at Gate 1 on the measured numbers: one
   line completed 3% of live names and hid 46% of the average one; two
   lines complete half at zero height cost, because the action stack
   still governs the row. CDR puts the distinguishing part at the END
   ("... - Owner Occupied - Variable - P&I"); title carries the full
   name for the rest. */
.prod-name {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; text-decoration: none;
  font-size: 15px; font-weight: 700; color: var(--text); line-height: 1.25;
}
.prod-name:hover { text-decoration: underline; }

/* FIGURES: mono ink, muted label beneath. auto-fit tracks the cell
   count: two for most categories, three on home loans (rate,
   comparison, repayment). */
.prod-metrics { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 12px; }
.metric { min-width: 0; }
/* A FIGURE MAY WRAP, NEVER OVERPAINT. nowrap + zero-floored tracks let
   the BT mission's "0% for 24 months" paint across its revert-rate and
   fee neighbours - the same defect the condition capsule had. Short
   figures ($3,771, 5.74%) never wrap, so column alignment under the
   headers is untouched; long ones break honestly instead of lying on
   top of the next number. */
.metric strong {
  display: block; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 20px; font-weight: 600; letter-spacing: -.02em; line-height: 1.12;
  /* Figures are ATOMIC - "$3,771" must never break a digit onto the
     next line, so no sub-word wrapping here. The guarantees live one
     level up: the metrics track is floored wide enough for the widest
     aligned figure, and the missions' long strings ("0% for 24
     months") span the full metrics width on their own row. */
  color: #0B1220; white-space: nowrap;
}
/* The missions have no header columns to align with, and their
   headline figure ("0% for 24 months") is three times the width of a
   rate - so the headline takes the full metrics width and the two
   secondary figures share the line beneath it. Squeezing three cells
   into one row was how the window painted over the revert rate. */
.results-mission .prod-metrics {
  grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 12px;
}
/* BUY NOW PAY LATER (25 Sep): four figures - the on-time cost, the
   late fee, the instalment count, the purchase sizes - in a column
   floored for three. Two by two, headline top-left: a row of four
   squeezed each cell to 50px at 1150 and "$1 to $1,999.99" painted
   over its neighbour. The cards' 18px, for the cards' reason; the
   "none published" state wraps rather than overpaint. */
.results-bnpl .prod-metrics {
  grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 12px;
}
.results-bnpl .prod-card .metric strong { font-size: 18px; }
.results-bnpl .prod-card .metric strong.na {
  font-size: 15px; white-space: normal; line-height: 1.2;
}
.results-mission .metric-est { grid-column: 1 / -1; }
/* Energy rows carry FOUR figures (est cost, DMO comparison, usage,
   supply) where banking rows carry three, and have no header columns
   to align with - four 20px mono cells in one row was the est-cost
   painting over "38% below" painting over the tariffs. Two rows of
   two: the money story on top, the tariff mechanics beneath. */
.results-energy .prod-metrics {
  grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px 12px;
}
/* ...but only when there ARE four. Gas rows never carry a reference
   comparison (no benchmark exists), and an electricity row whose
   estimate is partial carries none either - three cells in a 2x2 left
   a hole under the money story on every such row (founder, 8 Sep).
   Three cells take the banking shape: one row of three. The count is
   stamped on the wrapper by the renderer. */
.results-energy .prod-metrics.metrics-3 {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* A missing figure is a fact, uniformly stated, and it never collapses
   the column - a row that loses a slot stops aligning with the rows
   above it, and the eye reads the misalignment as a different product
   shape rather than as an absent number. */
/* "not stated" is TEXT, not a figure - it wraps (the 390px rewards
   program column set it in a 75px cell and nowrap bled 4px). */
.metric strong.na { color: #B9C4D6; font-size: 15px; font-weight: 500;
  padding-top: 3px; white-space: normal; line-height: 1.15; }
.metric span { display: block; font-size: 10.5px; color: var(--muted); margin-top: 5px; line-height: 1.3; }
/* The partial-estimate asterisk lives INSIDE the figure's <strong> and
   is a mark, not a label: the label rule above made it a block, so "*"
   sat on its own line under "$626" (founder, 8 Sep). The same override
   shape as .rate-from - the figure's inline children stay inline. */
.metric strong .est-flag {
  display: inline; font-size: 12px; margin-left: 2px; vertical-align: top;
  color: var(--muted); cursor: help;
}


/* J.1 - the diagnosis entry strip: a plain band, not a card. */


/* The rewards mission's honesty note, and its text-valued column. */
.mission-note {
  margin: 2px 0 8px; font-size: 12.5px; color: var(--muted);
  max-width: 66ch;
}
.mission-note a { color: var(--blue); }
/* A tiered fee RANGE is compound - two figures and a unit - and may
   break at its hyphens (the <wbr> the renderer plants), never inside a
   number. */
.metric strong.fee-range { white-space: normal; line-height: 1.15; }

/* A TEXT-valued column: wraps like language, outranking the figure
   rule's nowrap on specificity. */
.metric strong.metric-text {
  font-family: var(--font-sans);
  font-size: 13.5px; font-weight: 600; color: var(--navy);
  white-space: normal; line-height: 1.3;
}

/* The one question above the cards list - on the signature sky. */
.mission-band { padding: 12px 16px 12px; margin: 8px 0 6px; }
.mission-q { margin: 0 0 8px; font-size: 13px; font-weight: 600; }
.sky-surface .mission-q { color: #fff; }
/* The active mission's definition, one line under the control (founder,
   6 Sep). Same measured label palette as the band's other secondary
   text; wraps freely, never truncates - a definition cut short is no
   definition. */
.mission-desc { margin: 8px 0 0; font-size: 12.5px; line-height: 1.45; max-width: 68ch; }
.sky-surface .mission-desc { color: #C6D0E6; }
/* THE 60-SECOND GUIDE (founder, 6 Sep): a native <details> whose
   summary sits beside the mission question - top-right of the band -
   while its body, when opened, flows beneath the control at full width.
   Collapsed by default, JS-optional; the same copy as /guides/{slug}. */
.mission-band.sky-surface { position: relative; }
/* The band lifts every child above its ::after sheen by positioning it
   (.sky-surface > *), which made the <details> the summary's anchor -
   closed, a light-on-light line just under the band; open, the top of
   its own body. The details is un-positioned at higher specificity so
   the summary anchors to the band's question row, and the summary and
   body carry their own lift above the sheen instead. */
.mission-band .mission-guide { position: static; }
.mission-band .mission-guide > summary {
  position: absolute; top: 12px; right: 16px; z-index: 3; cursor: pointer;
  font-size: 12.5px; font-weight: 600; color: #C6D0E6; list-style: none;
}
.mission-band .guide-body { position: relative; z-index: 3; }
.mission-guide > summary::-webkit-details-marker { display: none; }
.mission-guide > summary:hover, .mission-guide[open] > summary { color: #fff; text-decoration: underline; }
.guide-body {
  margin: 12px 0 2px; padding-top: 12px; max-width: 72ch;
  border-top: 1px solid rgba(255, 255, 255, .15);
}
.guide-body .guide-title { margin: 0 0 8px; font-size: 15px; font-weight: 700; color: #fff; }
.guide-body p { margin: 0 0 8px; font-size: 13px; line-height: 1.55; color: #C6D0E6; }
.guide-body a { color: #fff; text-decoration: underline; text-underline-offset: 2px; }
.guide-body .guide-closing { color: #ACBCD6; font-size: 11.5px; }
.guide-body .guide-more { margin: 4px 0 0; font-size: 12.5px; }
@media (max-width: 600px) {
  /* same specificity as the desktop rule, or it loses and the summary
     lands on top of the question at phone widths */
  .mission-band .mission-guide > summary {
    position: static; display: inline-block; margin-top: 8px;
  }
}
/* The standalone page: prose scale, the guide's own closing line plus
   the standard contextual note, one CTA into the live list. */
.guide-page .guide-title { font-size: 26px; line-height: 1.2; margin: 0 0 14px; }
/* FOUNDER CREDIBILITY SURFACES (brief, 8 Sep). One source (about.mjs),
   muted register everywhere but /about's own credential paragraph. The
   author line sits directly under the guide title; the byline sits
   inside the trust pages' methodology flow, never at page top. */
/* Both live inside .prose articles, whose `.prose p` color rule
   out-specifies a bare class - so the muted register is declared at
   the same specificity it must beat. */
.prose p.guide-author { color: var(--muted); font-size: 13px; margin: -6px 0 16px; }
.prose p.maintainer-byline {
  color: var(--muted); font-size: 13px; max-width: 68ch;
  border-top: 1px solid rgba(11, 27, 51, .12);
  padding-top: 10px; margin: 18px 0 22px;
}
.about-page .about-photo {
  display: block; width: 168px; height: auto;
  border-radius: var(--r-surface); margin: 4px 0 14px;
}
.about-page .about-cred { max-width: 68ch; }
.about-page .about-cred strong { font-size: 1.05em; }
.guide-page p { max-width: 68ch; }
.guide-page .guide-cta { margin: 18px 0 10px; }
/* THE GUIDE TABLES (structure pass, 7 Sep): the situation map and the
   moving-debt numbers. Compact, on the global table card. The site's
   table idiom is a scroll wrapper (.compare-scroll) built for a wide
   compare grid; three short columns degrade better by STACKING - under
   600px each row becomes a small card, every cell captioned by its
   column name from data-label, so nothing scrolls sideways at 390px. */
.guide-table { font-size: 13px; margin: 12px 0 14px; max-width: 68ch; }
.guide-table th, .guide-table td { padding: 8px 12px; vertical-align: top; }
.guide-table thead th {
  font-size: 11px; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); background: var(--bg);
}
.guide-table td:first-child { font-weight: 600; white-space: nowrap; }
.guide-h { display: block; margin: 16px 0 6px; }
.guide-page h2.guide-h { font-size: 19px; margin: 26px 0 8px; }
.guide-page .guide-table { font-size: 14px; }
/* inside the dark band: a white card table reads as the one light
   surface, like the selected segment; type steps down with the body */
.guide-body .guide-table { font-size: 12.5px; margin: 10px 0 12px; }
.guide-body .guide-table th, .guide-body .guide-table td { padding: 7px 10px; }
/* the band paints its text white; on the white table card the cells
   take the page's ink and links the page's blue, or they vanish */
.guide-body .guide-table td { color: var(--text); }
.guide-body .guide-table a { color: var(--blue); }
.guide-body strong.guide-h { color: #fff; font-size: 13.5px; }
@media (max-width: 600px) {
  .guide-table, .guide-table tbody, .guide-table tr, .guide-table td { display: block; }
  .guide-table thead { display: none; }
  .guide-table tr { border-bottom: 1px solid var(--border); padding: 6px 0; }
  .guide-table tbody tr:last-child { border-bottom: none; }
  .guide-table td { border: none; padding: 3px 12px; white-space: normal; }
  .guide-table td::before {
    content: attr(data-label); display: block; font-size: 10.5px;
    text-transform: uppercase; letter-spacing: .4px; color: var(--muted);
    font-weight: 600; margin-bottom: 1px;
  }
  .guide-table td:first-child { font-weight: 700; padding-top: 6px; }
}
/* SEG, DARK VARIANT. Same you-are-here semantics inverted for the sky:
   navy fill would vanish on its own colour, so the selected segment is
   WHITE with navy text - the one legible statement of "here" a navy
   ground allows. Sanctioned alongside the base rule the check pins. */
.sky-surface .seg { border-color: rgba(255, 255, 255, .22); background: rgba(255, 255, 255, .07); margin: 0; }
.sky-surface .seg-item { color: #C6D0E6; }
.sky-surface .seg-item + .seg-item { border-left-color: rgba(255, 255, 255, .15); }
.sky-surface .seg-item:hover { background: rgba(255, 255, 255, .10); color: #fff; }
.sky-surface .seg-item.on { background: #fff; color: var(--navy); }

/* MODE: the segmented control. Connected group, hairline border,
   selected segment NAVY - never blue; blue is for actions. */
.seg {
  display: inline-flex; border: 1px solid var(--border);
  border-radius: var(--r-control); background: var(--card);
  overflow: hidden; margin: 0 0 12px;
}
.seg-item {
  padding: 7px 14px; font-size: 12.5px; font-weight: 600;
  color: var(--muted); text-decoration: none; white-space: nowrap;
}
/* Narrow phones: the two personal-loans tabs ("Personal loans · 213",
   "Car & vehicle loans · 248") ran 7px past the strip at 375px, and the
   strip clips. Tighter padding first; a strip that still cannot fit
   wraps rather than cuts a label. */
@media (max-width: 400px) {
  .seg { flex-wrap: wrap; }
  .seg-item { padding: 7px 10px; }
}
.seg-item + .seg-item { border-left: 1px solid var(--border); }
.seg-item:hover { color: var(--text); background: var(--bg);
  text-decoration: none; /* a mode is not a link, even under the cursor */ }
.seg-item.on { background: var(--navy); color: #fff; }
@media (max-width: 860px) {
  .seg { max-width: 100%; overflow-x: auto; scrollbar-width: none; }
  .seg::-webkit-scrollbar { display: none; }
}

/* The FEATURE LINE - what feature capsules became. Muted text, wraps. */
.feat-line { margin: 4px 0 0; font-size: 12px; color: var(--muted); }
/* The loan's stated traits (secured, balloon, vehicles, documentation,
   credit history, a rate range) - the feature line's register, its own
   line, since it is not a feature list and the feature line is capped. */
.trait-line { margin-top: 2px; }
/* the non-bank lender badge: muted text on the brand line, never a capsule */
.lender-badge { color: var(--muted); font-weight: 400; white-space: nowrap; }
/* The "^" beside every comparison rate: a link to the footnote, sized
   for a thumb (a 24px target) without pushing the figure around. */
.cr-mark {
  display: inline-block; min-width: 24px; min-height: 24px; line-height: 24px;
  margin: -6px -4px -6px 0; text-align: center; vertical-align: top;
  font-family: var(--font-sans); font-size: 13px; font-weight: 700;
  color: #2563EB; text-decoration: none;
}
.cr-mark:hover, .cr-mark:focus { text-decoration: underline; }
.cat-figure .cr-mark { font-size: 14px; position: relative; z-index: 2; }
.metric strong .cr-mark, th .cr-mark { font-size: 12px; }
.compare-notes .important-link { text-decoration: underline; margin-left: 6px; white-space: nowrap; }
/* who is not on the BNPL page, by name, under the count (25 Sep) */
.coverage-gap { margin: 2px 0 8px; font-size: 12.5px; color: var(--muted); line-height: 1.45; }
/* the visible text behind every card marker, one line each */
.footnotes .fn { display: block; margin-top: 3px; }

/* Four numeric columns need more track than three, and the WINDOW cell
   needs more than its equal quarter: "0% · 24 mo" is ~120px of mono,
   and four equal 1fr columns hand it ~80 - the text painted over the
   then-rate at common laptop widths. The row template widens the
   metrics slot AND the metrics grid goes unequal, window first; the
   header strip mirrors both, as everywhere else. */
/* Four numeric columns fit the shared restack budget (1220px since 30 Sep 2026) by ceding
   identity/tags width and dropping the mission's mono to 18px - the
   row stays horizontal at every desktop width; stacking happens only
   where every grid stacks (below 1220), never above. */
/* The wrap caps content near 1152px, so THAT is the budget four
   columns must fit - there is no wider desktop. Tags hold a capsule
   plus a two-line feature line (~148px); the metrics slot at 240px
   carries the 18px four-column grid with room. Floors sum to the
   restack budget exactly. */
@media (min-width: 1221px) {
  /* HOME LOANS: a tiered rate reads "from 5.99%" - 84px against the 70px
     third of a 234px slot, and the prefix ran into the comparison rate
     beside it ("from 5.99%5.86%", 30 Sep 2026). The slot floors at 270px
     and its columns take 1.2 : 1.1 : 1 shares (--metric-template) - the
     "from" figure and the comparison rate's 24px "^" target both clear.
     Header strip mirrors it. */
  .results-hl:not(.results-cards) .prod-card,
  .results-hl:not(.results-cards) .col-headers {
    grid-template-columns: minmax(150px, .95fr) minmax(140px, 1.2fr) minmax(270px, 1.1fr) 150px;
  }
  .results-bt .prod-card,
  .results-bt .col-headers {
    grid-template-columns: minmax(140px, .85fr) minmax(148px, .8fr) minmax(240px, 1.6fr) 150px;
  }
  /* Three-column card missions (rewards, carry-a-balance) need a wider
     metrics slot than the two-column plain template provides - "55
     days" at 20px mono is 74px, and a 60px share bled it over its
     neighbour. Tags cede; floors fit the restack budget. */
  .results-c3 .prod-card,
  .results-c3 .col-headers {
    grid-template-columns: minmax(150px, .9fr) minmax(120px, .7fr) minmax(300px, 1.5fr) 150px;
  }
  /* CARDS identity column, all missions (founder, 29 Aug): the 86px
     card face left the name a 74px box - 87 of 120 payfull rows still
     clipped past their two lines, and "BOQ Sp..." named nobody. The
     tags column cedes (its capsule wraps now, so a narrower track
     costs height, never words); metrics and actions floors are
     untouched, so the overpaint law is unmoved. The width shift rides
     the fr shares, not the floors - floor sums (671 / 730 / 662) stay
     at or under the pre-existing templates', so the tight 1081-1150
     band is no worse than before. The mission rules re-state their
     metric floors at higher specificity so this generic cards rule
     cannot starve a four-column BT row. */
  .results-cards .prod-card,
  .results-cards .col-headers {
    grid-template-columns: minmax(175px, 1.35fr) minmax(112px, .65fr) minmax(234px, 1.1fr) 150px;
  }
  .results-cards.results-c3 .prod-card,
  .results-cards.results-c3 .col-headers {
    grid-template-columns: minmax(175px, 1.25fr) minmax(105px, .4fr) minmax(300px, 1.5fr) 150px;
  }
  .results-cards.results-bt .prod-card,
  .results-cards.results-bt .col-headers {
    grid-template-columns: minmax(160px, 1.1fr) minmax(112px, .5fr) minmax(240px, 1.6fr) 150px;
  }
  /* ENERGY identity column (founder, 8 Sep): plan names run long and
     END in the part that tells them apart - "Origin Affinity Variable -
     Comparable - July'26 (Residential Demand with Dedicated Circuit)" -
     so three Origin rows at 1280 read as one plan listed thrice, while
     the tags column beside them holds at most one capsule. The tags
     column cedes to the name (its capsule wraps, costing height, never
     words); metrics and actions floors are untouched, so the overpaint
     law is unmoved. Floors sum to 726px, inside the restack budget. Energy
     has no column headers to mirror. */
  .results-energy .prod-card {
    grid-template-columns: minmax(230px, 1.7fr) minmax(112px, .5fr) minmax(234px, 1fr) 150px;
  }
}
/* Energy names get a third line at every width: the distinguishing
   clause is the tail ("... (Residential Demand with Dedicated
   Circuit)"), two lines of a 91-character name never reach it, and an
   energy row has the height to spare - its tags column holds at most
   one capsule. Title still carries the whole name. */
.results-energy .prod-name { -webkit-line-clamp: 3; }
.results-bt .prod-card .metric strong { font-size: 18px; }
/* Every cards mission runs three or four slots since the purchase rate
   joined (29 Aug second pass) - so ALL cards rows take the 18px the BT
   columns earned, for the reason BT earned it: "55 days" at 20px mono
   is 81px against a 78px three-column track, and a uniform cards type
   size beats a mission-by-mission mix. The two TEXT variants keep
   their own sizes - this selector outguns theirs on specificity, so
   they are re-stated here rather than silently inflated to 18px mono
   ("ShopBack" at 18px overpainted its 390px track). */
.results-cards .prod-card .metric strong { font-size: 18px; }
.results-cards .prod-card .metric strong.metric-text { font-size: 13.5px; }
.results-cards .prod-card .metric strong.na { font-size: 15px; }
.metric .na { color: var(--muted); font-weight: 500; }

/* "from" on a tiered headline: quiet, unmissable, never bold - the
   figure stays the figure, the prefix is the qualification. */
/* Outranks the .metric span label styling on specificity - the
   qualifier is part of the FIGURE, never a label. */
.metric strong .rate-from, .rate-from {
  display: inline; font-size: 12px; font-weight: 500; color: var(--muted);
}
.band-best .rate-from { color: #C6D0E6; }

/* THE CAPSULE CANNOT PAINT OVER ITS NEIGHBOURS - AND CANNOT TRUNCATE.
   At 960px the pills column compresses and a nowrap capsule used to
   overflow straight across the fee figure ("Fo$0siness customers19.99%").
   The first fix was an ellipsis; the founder overruled it (29 Aug): a
   cut-off eligibility warning is a caveat that no longer caveats -
   "For business cu..." fails its legal job. So the capsule WRAPS: the
   column still shrinks (minmax(0,...)) and a wrapping box grows DOWN,
   never across, so the overpaint law holds without amputating the
   condition. The row gets taller; that is the honest trade. */
.prod-pills { min-width: 0; }
.prod-pills .pill {
  max-width: 100%; white-space: normal; overflow-wrap: anywhere;
  display: inline-block; vertical-align: bottom;
  height: auto; line-height: 1.35; text-align: left;
}

/* The card face in a cards row: the product's own imagery at the card's
   real aspect (1.586). Slightly larger than the logo chip - on this
   category the face IS the identity - and the name beside it gets a
   second line for the same reason ("CommBank Qantas..." truncated to
   nothing defeats the row). */
.prod-id .logo-chip.card-face { width: 86px; height: 54px; padding: 3px; }
.prod-id .logo-chip.card-face img { max-width: 100%; max-height: 100%; }
.card-face + .prod-id-t .prod-name {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  white-space: normal;
}

/* THE AMOUNT STRIP: one field that personalises every figure below it,
   so it sits full-width above the list rather than inside the control
   bar. GET form; the URL stays the only state. */
/* ============ THE TWILIGHT BAND (Part 4b) ============
   The hero's sky turned horizontal - the site's own signature at the
   moment of conversion. Surface gradient arrives from lib/sky.mjs via
   --band-sky (same SKY_STOPS as the hero, rose band omitted); grain is
   the shared --grain-tile. SANCTIONED SURFACE: this is the hero-sky
   module colour, not a new tint and not action blue - the one blue in
   the band is the primary button. */
/* .sky-surface is the SHARED skin: any top instrument that carries the
   site's signature (the home-loans band, the energy postcode question,
   the cards mission question) takes this class plus the --band-sky
   custom property from lib/sky.mjs. Surface here, layout in each
   instrument's own class - so the skin cannot fork. */
.sky-surface {
  position: relative; overflow: hidden; isolation: isolate;
  border-radius: var(--r-surface); color: #fff;
  background: var(--band-sky);
}
.sky-surface::after {
  content: ""; position: absolute; inset: 0; z-index: 1;
  pointer-events: none; opacity: .4; mix-blend-mode: overlay;
  background-image: var(--grain-tile);
}
.sky-surface > * { position: relative; z-index: 2; }
/* ONE ROW OF CONTROLS at desktop (founder's declutter brief, 30 Sep
   2026) - amount, rate, the stars' spacer, the diagnosis button - and the
   scoped market figure as the card's one caption line under them. */
.twilight-band {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 12px 20px 10px; margin: 0 0 14px;
}
.band-seg { display: flex; align-items: center; gap: 10px; }
.twilight-band label {
  font-size: 13.5px; font-weight: 600; color: #C6D0E6; white-space: nowrap;
}
.band-in {
  display: inline-flex; align-items: baseline; gap: 5px;
  background: rgba(255, 255, 255, .09);
  border: 1px solid rgba(255, 255, 255, .18);
  border-radius: var(--r-control); padding: 8px 12px;
}
.band-in:focus-within {
  border-color: rgba(255, 255, 255, .55);
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .12);
}
.band-sym, .band-unit {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; color: #ACBCD6;
}
.band-in input {
  border: 0; outline: 0; background: none; color: #fff; width: 108px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-variant-numeric: tabular-nums; font-size: 17px; font-weight: 600;
}
.band-in input::placeholder { color: rgba(255, 255, 255, .35); }
.band-in-sm input { width: 58px; }
.band-div { width: 1px; height: 34px; background: rgba(255, 255, 255, .15); }
.band-best b { color: #fff; }
/* The empty stretch the stars live in: positions are percentages OF
   THIS SPACER, so a star can never sit behind text or a control - the
   geometry is the guarantee, not luck. */
.band-space { flex: 1 1 40px; align-self: stretch; position: relative; min-width: 0; }
.band-star {
  position: absolute; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle, rgba(255, 255, 255, .9), rgba(200, 220, 255, 0) 70%);
}
.band-go { white-space: nowrap; }
/* The quiet second submit: applies the amount to THIS list. A ghost on
   the dark ground - the blue stays on the diagnosis button. */
.band-apply {
  background: none; border: 1px solid rgba(255, 255, 255, .25);
  color: #C6D0E6; font-size: 12.5px; font-weight: 600;
  padding: 7px 12px; border-radius: var(--r-control); cursor: pointer;
}
.band-apply:hover { border-color: rgba(255, 255, 255, .5); color: #fff; }
.band-cap {
  flex-basis: 100%; margin: -4px 0 0; font-size: 12px; line-height: 1.5;
  /* #ACBCD6, not the mock's #93A5C4: that one measured 3.88:1 at the
     violet end of the gradient - under AA for this size. This one holds
     4.5+ at the same worst position; the band test pins it. */
  color: #ACBCD6;
}
@media (max-width: 860px) {
  .band-div, .band-space { display: none; }
  .band-go { width: 100%; }
}

/* COLUMN HEADERS (home loans): the number columns named once, sticky,
   and the two rate headers ARE the sort control. The grid mirrors the
   row template exactly - same columns, same padding - so each label
   sits over its cells at every width the row can take. */
.col-headers {
  display: grid;
  grid-template-columns: minmax(150px, .95fr) minmax(140px, 1.2fr) minmax(234px, 1.1fr) 150px;
  gap: 14px; align-items: center;
  position: sticky; top: 0; z-index: 6;
  background: var(--bg);
  padding: 5px 21px;                    /* row padding + its 1px border */
  border-bottom: 1px solid var(--border);
  margin: 4px 0 2px;
}
.col-h-nums { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 12px; }
/* A label column's explainer (interest-free days): the facet idiom's
   "What's this?" summary sits under the label; the paragraph opens as
   a band spanning the whole sticky strip (the strip is the containing
   block), over the first row - transient, JS-optional. Type resets
   because the strip's labels are small-caps register. */
.col-help { display: block; }
.col-help > summary {
  display: inline; cursor: pointer; font-size: 10.5px; color: var(--muted);
  list-style: none; text-transform: none; letter-spacing: 0;
}
.col-help > summary::-webkit-details-marker { display: none; }
.col-help > summary:hover, .col-help[open] > summary { color: var(--text); }
.col-help > .facet-hint {
  position: absolute; left: 21px; right: 21px; top: 100%; z-index: 7;
  margin: 2px 0 0; padding: 10px 14px;
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-control); box-shadow: var(--shadow);
  font-size: 12.5px; line-height: 1.5; font-weight: 400; color: var(--text);
  text-transform: none; letter-spacing: 0;
}
/* Under a header strip the number area is an N-COLUMN grid driven by
   the mission's column list (--metric-template, set on the section) -
   two for pay-in-full, three for carry, four for moving debt - so a
   four-number mission renders horizontally at desktop instead of
   falling anywhere near a stack. */
.results-hl .prod-metrics,
.results-hl .col-h-nums {
  grid-auto-flow: row;
  grid-template-columns: var(--metric-template, repeat(3, minmax(0, 1fr)));
}
.col-sort, .col-h-label {
  font-size: 10.5px; color: var(--muted); text-decoration: none;
  letter-spacing: .02em; line-height: 1.3;
}
.col-sort:hover { color: var(--text); text-decoration: underline; }
.col-sort.on { color: var(--text); font-weight: 600; }
.col-sort .dir { font-size: 8px; margin-left: 3px; vertical-align: 1px; }
@media (max-width: 1220px) { .col-headers { display: none; } }

/* Under the headers, per-row labels are redundant on desktop - except
   one that carries data ("Fixed 3 yr rate"), which keeps rendering. */
/* Matched to the header strip's own cutoff: between the old 861 and
   the row-restack point the headers are gone, so the labels must be
   back - a naked figure column is the alternative. */
@media (min-width: 1221px) {
  .results-hl .metric span { display: none; }
  .results-hl .metric span.keep { display: block; }
  /* the "from" qualifier lives INSIDE the figure - never a label */
  .results-hl .metric strong .rate-from { display: inline; }
}

/* PILLS: canonical order comes from lib/pillorder.mjs. ONE LINE - the
   wrap is what re-inflated the rows. The renderer guarantees the line
   fits by capping features (two when a condition or caution pill shares
   the line, three otherwise); wrap stays as the safety valve for the
   rare row carrying several cautions, which must all remain visible. */
/* Flex items default to min-width:auto, which lets a long feature line
   shove itself over the fee column instead of wrapping - every child
   gets an explicit zero floor. */
.prod-pills { display: flex; flex-direction: column; gap: 4px;
  justify-content: center; min-width: 0; }
.prod-pills > * { min-width: 0; }

.pill {
  background: #EEF1F6; border-radius: var(--r-pill); padding: 5px 10px;
  font-size: 12px; color: #475569; white-space: nowrap;
}
.pill-cond { background: #EAF2FF; color: #12305f; }
.pill-caution { background: #FFF8E7; color: #6b4a12; }

/* ACTIONS: one solid button, everything else quiet. */
/* The Compare label floats at the row's top-right corner, so the action
   stack starts BELOW it - without the offset the button sat underneath
   the checkbox and both were unclickable in the overlap. */
/* Bottom-aligned, so the floating Compare label above it can never be
   underneath the button. Anchoring the stack to the TOP instead forced
   26px of clearance onto every row and cost 60px of height; hanging it
   from the bottom costs nothing, because the row is already as tall as
   its tallest column. */
/* Flex column, not block: the button is inline-flex, and inside a block
   container it sits on a text baseline that donates ~16px of line-box
   slop under it - measured, the stack claimed 102px for 86px of
   content. Flex kills the line box; the margins between children are
   the only spacing. */
.prod-actions { text-align: right; display: flex; flex-direction: column;
  align-items: stretch; align-self: end; }
.prod-actions .btn { width: 100%; justify-content: center; }
.prod-details {
  display: block; font-size: 12.5px; color: #475569;
  text-decoration: none; margin: 8px 0 0;
}
.prod-details:hover { text-decoration: underline; }
/* Stated on EVERY row, both ways. A line that only appears when there is
   something to disclose teaches the reader that its absence means
   nothing. */
.prod-comm {
  font-size: 9.5px; color: var(--muted); margin: 7px 0 0;
  text-decoration: none; display: block; line-height: 1.45;
}
a.prod-comm:hover { text-decoration: underline; color: var(--blue); }

/* COMPARE: quiet, beside Details. A real checkbox, so it still submits
   with JS off - which is also why it cannot be the mock's 13px <i>. */
.prod-sub {
  display: flex; align-items: center; justify-content: flex-end; gap: 12px;
  margin: 8px 0 0;
}
.prod-sub .prod-details { margin: 0; }
.prod-card .compare-label {
  display: inline-flex; align-items: center; gap: 5px; line-height: 1;
  font-size: 11px; color: var(--muted); border: none; background: none;
  padding: 0; height: auto; cursor: pointer; white-space: nowrap;
}
.prod-card .compare-check { width: 13px; height: 13px; margin: 0; }
.prod-tags { display: flex; gap: 6px; flex-wrap: wrap; }

/* ------------------------------------------------------------------ tags
   ONE tag component, site-wide. Every tile tag/badge is `.tag` plus at
   most one tonal variant - no per-feature colours, no gold.

   Tonal formula (one hue per variant, set once in --tag-hue):
     text  = the full tone            background = same hue @ 10%
     border = same hue @ 28%
   Every variant carries the same 1px border, the same padding and the
   same 1.7 line-height, so tag height is IDENTICAL everywhere (26.7px)
   regardless of variant, font fallback or wrapping context. Only the
   hue changes with the semantic role.

   Variants: (default) NEUTRAL - slate, every feature/informational tag.
             .tag-caution      - amber, eligibility + restriction badges.
   The government-guarantee marker OUTGREW this system: it is now the
   solid .fcs-badge below, not a tag variant. */
.tag {
  --tag-hue: 71, 85, 105;                 /* slate 600 - #475569 */
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: var(--r-data);
  font-size: 11px; font-weight: 550; letter-spacing: .015em; line-height: 1.7;
  color: rgb(var(--tag-hue));
  background: rgba(var(--tag-hue), .10);
  border: 1px solid rgba(var(--tag-hue), .28);
  text-transform: capitalize; white-space: nowrap;
}
.tag-caution { --tag-hue: 138, 90, 0; }     /* amber - #8A5A00 */

/* The GUARANTEE badge - the site's one solid-fill marker. Deliberately
   a different species from the tags: fill vs tonal, 6px corners vs
   999px pill, icon-first. Sits beside the provider name on qualifying
   tiles and after the statement-block headline. Compliance: a plain
   green rounded rectangle only - no navy, no gold, no crest, no
   starburst, no circular seal geometry. */
.fcs-badge {
  display: inline-flex; align-items: center; gap: 5px;
  height: 22px; padding: 3px 8px; box-sizing: border-box;
  border-radius: var(--r-control);
  background: rgb(6, 95, 70); color: #fff;   /* guarantee green #065F46 */
  font-size: 10.5px; font-weight: 600; letter-spacing: .01em;
  line-height: 1; white-space: nowrap; cursor: pointer;
  /* a hint of physical badge: 1px darker bottom edge - no gradient */
  box-shadow: inset 0 -1px 0 rgb(4, 68, 50);
  transition: background 140ms ease;
}
.fcs-badge:hover { background: rgb(9, 116, 86); }
.fcs-badge:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.fcs-badge-shield { flex: none; display: block; }
/* the check knocks out of the white shield in the badge green */
.fcs-badge-check { stroke: rgb(6, 95, 70); }
/* In the provider-name slot. A 171px badge beside a long Australian
   institution name cannot fit one line in the brand cell, and letting
   it wrap made 112 of 120 tiles differ from the other 8 - so it takes
   its own line DIRECTLY UNDER the name on every tile: one deterministic
   position, big four and small banks identical. */
.brand-name { display: block; }
.brand-name .fcs-badge { display: flex; width: fit-content; margin: 5px 0 0; }
/* transitions belong to tags that respond to the pointer, and nowhere
   else: a static label has no state to animate between. */
a.tag, button.tag, .tag[role="button"] {
  cursor: pointer; text-decoration: none;
  transition: background 140ms ease, border-color 140ms ease;
}
a.tag:hover, button.tag:hover, .tag[role="button"]:hover {
  background: rgba(var(--tag-hue), .18);
  border-color: rgba(var(--tag-hue), .45);
  text-decoration: none;
}
a.tag:focus-visible, button.tag:focus-visible, .tag[role="button"]:focus-visible {
  outline: 2px solid var(--cyan); outline-offset: 2px;
}
/* Scoped to the diagnosis result card: the browse row's action stack
   spaces itself with margins (styles above), and an unscoped gap here
   was silently adding 16px to every browse row on top of them. */
.switch-result-card .prod-actions { gap: 8px; }
/* Tile "Compare" is a real checkbox (the tray reads its change events),
   but it shares a row with Details and Go-to, so it carries the MEDIUM
   box: same height, radius and icon gap as the buttons beside it. The
   neutral treatment keeps it a selection control, not a third action.
   Quiet INLINE control, not the old 38px chip - the chip height is what
   forced the action stack past the button and re-inflated every row. */
.compare-label:hover { color: var(--text); }
.compare-label:has(input:checked) { color: var(--blue); }
.compare-label:focus-within { outline: 2px solid var(--cyan); outline-offset: 2px; }
/* One-line caption under the results bar naming the compare mechanism -
   the tray only appears AFTER a first tick, so without this line nothing
   on the page says side-by-side exists. */
/* ------------------------------------------------------------- buttons
   THREE sizes × THREE variants, one radius, one source of truth. There
   are no per-page button styles: if a button needs to look different,
   it needs a different variant, not an override.

   Height is FIXED and box-sized, so a border can never change it and no
   button can be a different height from its neighbours in a row.
   Horizontal padding holds a constant ~0.41 ratio to height at all
   three sizes. Radius is --r-control (6px), shared with every other
   control on the site - inputs, selects, chips - so a button and the
   field beside it agree.

     LARGE   44px / 15px   - hero primary CTA only
     MEDIUM  38px / 14px   - in-page actions, tile actions, form and
                             modal submits (the default; no class)
     SMALL   30px / 12.5px - inline + utility actions

     .btn-primary    filled accent - one per surface, the main action
     .btn-secondary  outline - standard actions, incl. every external
                     "Go to provider" link (identical treatment + glyph)
     .btn-ghost      text-only - tertiary actions and toggles           */
.btn {
  --btn-h: 38px; --btn-px: 16px; --btn-fs: 14px;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 6px;                                    /* icon spacing */
  height: var(--btn-h); padding: 0 var(--btn-px);
  border-radius: var(--r-control); border: 1.5px solid transparent;
  font-family: inherit; font-size: var(--btn-fs); font-weight: 600; line-height: 1;
  white-space: nowrap; text-align: center; cursor: pointer;
  transition: background 140ms ease, border-color 140ms ease, filter 140ms ease;
}
.btn-lg { --btn-h: 44px; --btn-px: 18px; --btn-fs: 15px; }
.btn-sm { --btn-h: 30px; --btn-px: 12px; --btn-fs: 12.5px; }

.btn-primary { background: var(--blue); color: #fff !important; }
.btn-primary:hover { text-decoration: none; background: var(--cobalt); }
.btn-secondary { background: transparent; color: var(--blue) !important; border-color: var(--blue); }
.btn-secondary:hover { text-decoration: none; background: rgba(37, 99, 235, .07); }
.btn-ghost { background: transparent; color: var(--muted) !important; }
.btn-ghost:hover { text-decoration: none; background: rgba(37, 99, 235, .07); color: var(--cobalt) !important; }
.btn:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.btn.disabled { opacity: .45; pointer-events: none; }
/* external-link glyph: same 6px gap as any other button icon */
.btn-ext { font-size: .9em; line-height: 1; }

/* Touch pointers: SMALL keeps its 30px box but gets a 44px tap target.
   The overlay is painted inside the button and changes no layout, so
   rows stay level and nothing reflows between pointer types. */
@media (pointer: coarse) {
  .btn-sm, .filter-fab { position: relative; }
  .btn-sm::after, .filter-fab::after {
    content: ""; position: absolute; left: 0; right: 0;
    top: 50%; height: 44px; transform: translateY(-50%);
  }
}

/* Selection controls - filter chips, view tabs, segmented toggles - are
   a separate family from buttons: pill radius, but the SMALL height, so
   every control on a browse page lines up. ONE pill token (--r-pill);
   the last 99px straggler (.fab-badge) was folded into it in the
   radius-token pass. */
/* Selection controls share the small 30px height so a browse page's
   controls line up - but they are CONTROLS, so they take the control
   radius. The capsule radius belongs to the condition tag alone. */
.customer-toggle .opt, .facet-chips .opt-label {
  display: inline-flex; align-items: center; height: 30px;
  border-radius: var(--r-control); white-space: nowrap;
}
.fineprint { color: var(--muted); font-size: 12.5px; }

/* DISCLOSURE. Quiet but not hidden: it must not compete with the figure
   it qualifies, and it must not be so faint it reads as concealed. Uses
   the muted token, one step below fineprint, with a hairline above so it
   reads as attached to the figures rather than floating after them. */
.disclosure-note {
  margin: 14px 0 0; padding-top: 10px;
  border-top: 1px solid var(--border);
  color: var(--muted); font-size: 12.5px; line-height: 1.55;
  max-width: 70ch;
}
/* On the dark hero the border and text invert with the surface. */
.hero-dark .disclosure-note {
  border-top-color: rgba(244, 247, 252, .14); color: #8CA0C4;
}
/* THE HERO'S ONE QUALIFICATION moved inside the card on 25 Sep 2026
   (founder): see .hero-right .hero-basis in the card block below. */
/* Inside the diagnosis verdict, which is a dark panel of its own. */
.switch-verdict .disclosure-note {
  border-top-color: rgba(244, 247, 252, .14); color: #94A3B8;
}
.disclosure-footer {
  margin: 12px 0 0; color: #8CA0C4; font-size: 12.5px; line-height: 1.6;
}
.disclosure-footer a { color: #B9C7DE; text-decoration: underline; }
.disclosure-footer a:hover { color: var(--frost); }

/* THE TWO NOTES THAT QUALIFY A COMPARISON (founder, 21 Sep 2026), set
   as one quiet run of 12.5px text directly above the table: the coverage
   statement, then the standing contextual line. No hairline and no
   measure cap here - they belong to the table beneath them, and at the
   results column's width they take two lines. */
.compare-notes { margin: 10px 0 12px; font-size: 12.5px; line-height: 1.5; color: var(--muted); }
.compare-notes .fineprint, .compare-notes .disclosure-note {
  display: inline; margin: 0; padding: 0; border: 0; max-width: none;
  font-size: inherit; line-height: inherit; color: inherit;
}
.compare-notes .coverage-link { text-decoration: underline; }

/* THE FOOTER'S DISCLOSURES BLOCK (founder, 21 Sep 2026): licensing,
   market coverage, how we are paid, the method - four labelled items in
   the footer's own register, two columns where there is room. */
/* IMPORTANT INFORMATION - the one footer block (25 Sep 2026): 13px on
   1.5, two text colours, a thin divider, no box. #8CA0C4 on the
   midnight footer is 7.3:1, #B9C7DE higher; the light-surface twin
   (.cr-footnote in .prose) uses --muted, 5.4:1 on white. */
.site-footer .important-info-row { padding-top: 0; }
.important-info {
  width: 100%; max-width: 900px; margin-top: 0; padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, .14);
  font-size: 13px; line-height: 1.5; color: #8CA0C4;
}
.important-info h2 { margin: 0 0 8px; font-size: 15px; font-weight: 600; color: #B9C7DE; }
.important-info p { margin: 0 0 8px; }
.important-info p:last-child { margin-bottom: 0; }
.important-info a { color: #B9C7DE; text-decoration: none; }
.important-info a:hover { color: var(--frost); text-decoration: underline; }
.important-info .footer-entity { margin-top: 12px; }
.important-info strong { font-weight: 700; color: #B9C7DE; }
.important-info .cr-footnote { color: #8CA0C4; }

/* The full statement. Placeholders are meant to look unfilled - an
   invented ABN in a legal statement is a defect nobody notices until it
   matters, so an obvious gap is the safer failure. */
.disclosure-full h2 {
  font-size: 17px; margin: 30px 0 8px; letter-spacing: -.01em;
}
.disclosure-full p { margin: 0 0 12px; line-height: 1.65; }
.disclosure-todo {
  background: rgba(var(--tag-hue, 138, 90, 0), .14);
  color: #8A5A00; border: 1px dashed rgba(138, 90, 0, .45);
  border-radius: var(--r-control); padding: 1px 6px;
  font-size: .95em; font-weight: 600;
}

/* A scroll cue, deliberately almost subliminal: muted slate, no
   motion, no bounce. It fades out once the hero is behind the reader -
   a cue that outlives its usefulness becomes furniture. The fade is a
   200ms opacity transition, dropped entirely under reduced motion. */
.hero-scroll {
  position: absolute; left: 32px; bottom: 18px; z-index: 1;
  display: inline-flex; align-items: center; gap: 7px;
  color: #6F82A6; text-decoration: none;
  font-size: 11px; letter-spacing: .14em; text-transform: uppercase;
  font-weight: 600;
  opacity: .75; transition: opacity 200ms ease, color 140ms ease;
}
.hero-scroll:hover { color: #8CA0C4; opacity: 1; }
.hero-scroll:focus-visible { outline: 2px solid var(--cyan); outline-offset: 4px; }
.hero-scroll.gone { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .hero-scroll { transition: none; } }
@media (max-width: 899px) { .hero-scroll { display: none; } }

/* ------------------------------------------- Loyalty Cost Meter
   A physical instrument in dark glass. Every surface is CSS - no
   images, no canvas - so it costs one paint and scales to any width.
   The housing is lit from above: the top border is brighter than the
   bottom and an inset highlight sits under it, which is what stops a
   translucent panel reading as a flat rectangle. */
.lt {
  position: relative; margin: 0; padding: 20px 20px 17px;
  border-radius: var(--r-surface);
  background: linear-gradient(180deg,
    rgba(255, 255, 255, .085) 0%, rgba(255, 255, 255, .028) 55%,
    rgba(255, 255, 255, .015) 100%);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border: 1px solid rgba(255, 255, 255, .10);
  border-top-color: rgba(255, 255, 255, .22);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16),
              0 18px 40px rgba(2, 6, 16, .45);
}
/* Without backdrop-filter the low-opacity fill has nothing to blur and
   the panel turns to mud, so it becomes solid navy instead. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .lt { background: #0C1730; }
}
/* screw heads - lit from upper-left, same as the panel */
.lt-plate {
  margin: 0 0 10px; padding: 0 6px;
  font-size: 10.5px; letter-spacing: .16em; text-transform: uppercase;
  font-weight: 700; color: #8CA0C4;
}
/* the register sits in a recess, not on the surface */
.lt-bezel {
  border-radius: var(--r-control); padding: 12px 14px;
  background: linear-gradient(180deg, #050B18, #08101F);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, .65),
              inset 0 -1px 0 rgba(255, 255, 255, .05);
}
/* THE ARGUMENT: static type, because a number that does not move reads
   as a fact rather than an animation. */
.lt-annual {
  display: flex; align-items: baseline; gap: 8px; margin: 0;
  font-variant-numeric: tabular-nums;
}
.lt-amt {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 46px; font-weight: 600; letter-spacing: -.035em;
  line-height: 1; color: #EAF2FF;
}
.lt-per {
  font-size: 12px; letter-spacing: .12em; text-transform: uppercase;
  color: #8CA0C4; font-weight: 600;
}
/* THE EVIDENCE: the same wheels, subordinate. Below a hairline so it
   reads as a footnote to the figure, not a second headline. */
.lt-live {
  display: flex; align-items: center; gap: 9px;
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid rgba(255, 255, 255, .07);
}
.lt-reg {
  display: flex; align-items: flex-end; gap: 3px;
  font-variant-numeric: tabular-nums; --d: 20px;
}
.lt-sym { font-size: 13px; color: rgba(255, 255, 255, .45); padding-bottom: .3em; }
.lt-dot { font-size: 15px; padding: 0 1px .12em; color: rgba(255, 255, 255, .7); }
.lt-cap {
  font-size: 9.5px; letter-spacing: .13em; text-transform: uppercase;
  color: #6F82A6; font-weight: 600;
}
/* Drum faces are DARKER than the panel - a wheel sits inside the
   machine, it does not float on it. The falloff top and bottom is the
   curve of the drum; the centre seam is where the halves meet. */
.lt-drum {
  position: relative; overflow: hidden; display: block;
  width: calc(var(--d) * .68); height: calc(var(--d) * 1.24);
  border-radius: 2px; border: 1px solid rgba(255, 255, 255, .08);
  background: linear-gradient(180deg, rgba(0, 0, 0, .6) 0%,
    rgba(0, 0, 0, .1) 24%, rgba(255, 255, 255, .03) 50%,
    rgba(0, 0, 0, .1) 76%, rgba(0, 0, 0, .6) 100%), #050A12;
}
.lt-drum::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; background: rgba(255, 255, 255, .07);
}
.lt-strip { display: block; will-change: transform; }
.lt-strip i {
  display: block; height: calc(var(--d) * 1.24);
  line-height: calc(var(--d) * 1.24); font-size: var(--d);
  font-style: normal; font-weight: 500; text-align: center;
  color: #DCE8FA; letter-spacing: -.03em;
}
/* THE one saturated element: the fractional register, in the logo cyan
   - never the CTA blue, which would make the meter compete with the
   button beneath it. */
.lt-fine { display: flex; align-items: flex-end; gap: 2px; margin-left: 5px; }
.lt-fine .lt-drum {
  --d: 14px; border-color: rgba(56, 189, 248, .5);
  background: linear-gradient(180deg, rgba(0, 0, 0, .45),
    rgba(255, 255, 255, .10) 50%, rgba(0, 0, 0, .45)), #0B4E6B;
  box-shadow: 0 0 12px rgba(56, 189, 248, .32);
}
.lt-fine .lt-strip i { color: var(--cyan); }
.lt-foot {
  display: flex; justify-content: space-between; gap: 10px;
  margin: 13px 0 0; font-size: 9.5px; letter-spacing: .13em;
  text-transform: uppercase; color: #6F82A6; font-weight: 600;
}
/* Mobile: the instrument leads, so the drums grow rather than shrink. */
@media (max-width: 900px) {
  .lt-reg { --d: 24px; }
  .lt-fine .lt-drum { --d: 17px; }
  .lt-amt { font-size: 50px; }
}
/* Under 400px the fine wheels go; the read-time clause stays (it no
   longer describes the wheels). */
@media (max-width: 400px) {
  .lt-fine { display: none; }
}

/* compare tray */
.compare-tray {
  position: fixed; left: 50%; bottom: -80px; transform: translateX(-50%);
  z-index: 60; /* above the mobile filter FAB (40) */
  background: var(--midnight); color: var(--frost); border: 1px solid rgba(34,211,238,.3);
  border-radius: var(--r-surface); padding: 12px 20px;
  display: flex; gap: 16px; align-items: center; box-shadow: 0 8px 30px rgba(2,6,16,.5);
  transition: bottom .2s ease;
  max-width: calc(100vw - 24px);
}
.compare-tray.visible { bottom: 22px; }

/* detail */
.detail-head { display: flex; gap: 16px; align-items: center; }
.detail-head .btn { margin-left: auto; }
.prod-desc { color: var(--muted); max-width: 760px; }
.detail-section { margin: 26px 0; }
.detail-section h2 { font-size: 18px; font-weight: 600; margin: 0 0 10px; }
/* Published CDR detail rendered as prose rather than badges: the
   feature type is the label, the provider's own sentence is the
   content. */
.detail-list { margin: 0; padding: 0; list-style: none; }
.detail-list li {
  padding: 9px 0; border-bottom: 1px solid var(--border);
  font-size: 14px; line-height: 1.5; color: var(--text);
}
.detail-list li:last-child { border-bottom: 0; }
.detail-list strong { font-weight: 600; text-transform: capitalize; }
.detail-list span { display: block; color: var(--muted); font-size: 13px; margin-top: 2px; }
.detail-docs li { border-bottom: 0; padding: 5px 0; }
.detail-docs a { color: var(--blue); font-weight: 600; }
/* A rate's own explanation, attached to the rate rather than crammed
   into another column. */
.rate-note td {
  padding-top: 0; color: var(--muted); font-size: 12.5px; line-height: 1.5;
}
/* Provider card imagery. Fixed height so a row of cards from different
   banks lines up whatever aspect ratio each publishes. */
/* Card art. The height set here is a CEILING, not a size: every image
   also carries an inline max-height equal to its own pixel height, read
   from the file at download time. Provider images run from 82px to
   1240px tall, and stretching a small one to fill the row makes a real
   bank's card look like a bad scan - so a small source simply renders
   small. width:auto plus the width/height attributes keeps the ratio. */
.card-art { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; }
.card-art figure { margin: 0; }
.card-art img {
  display: block; height: 132px; width: auto; border-radius: var(--r-control);
  box-shadow: 0 6px 18px rgba(11, 30, 63, .16);
}
.card-art figcaption {
  margin-top: 7px; font-size: 12.5px; color: var(--muted);
}
/* LEAD POSITION. The card sits directly under the product name, where a
   reader confirms they are on the right page before reading a single
   rate. Most products publish one image; a handful publish five design
   variants of the same account, so the row shrinks as it fills rather
   than pushing the rates off the screen. 4+ variants are decoration,
   not information, so they get the smallest size. */
.card-art-lead { margin: 14px 0 18px; }
.card-art-lead[data-count="1"] img { height: 148px; }
.card-art-lead[data-count="2"] img { height: 132px; }
.card-art-lead[data-count="3"] img { height: 116px; }
.card-art-lead[data-count="4"] img,
.card-art-lead[data-count="5"] img { height: 96px; }
@media (max-width: 640px) {
  .card-art-lead { gap: 12px; }
  .card-art-lead img { height: 104px !important; }
}
table { width: 100%; border-collapse: collapse; background: var(--card);
  border: 1px solid var(--border); border-radius: var(--r-surface); overflow: hidden;
  box-shadow: var(--shadow); font-size: 14px; }
th, td { padding: 11px 14px; text-align: left; border-bottom: 1px solid var(--border); }
thead th { background: #EDF2FB; font-size: 12px; text-transform: uppercase;
  letter-spacing: .5px; color: var(--muted); font-weight: 600; }
tbody tr:last-child td { border-bottom: none; }

/* ---------------------------------------------------------- compare table
   The table's job is not to display two products, it is to show which
   one wins each line. So the winning cell is marked, the gap between
   best and worst is stated in the row label, and features render as a
   tick matrix - a bag of tags in each cell hid the one thing a reader
   came for. */
.compare-scroll { overflow-x: auto; padding-bottom: 40px; }
.compare-table th { vertical-align: top; }
.compare-table thead th { background: var(--card); text-transform: none; font-size: 15px; }
/* chip on its own line, name under it: inline, the name wrapped around
   the chip and read as "MyStateBank Basic / Variable Loan" */
.compare-table thead .logo-chip { display: flex; margin: 0 0 8px; }
.compare-table thead th > a {
  display: block; font-weight: 600; line-height: 1.35;
}
.compare-table tbody th { width: 190px; background: #EDF2FB; font-size: 13px; }
.compare-table td { vertical-align: top; font-size: 15px; }

/* the product's category, under its name in the header */
.cmp-cat {
  display: block; margin-top: 3px; font-size: 11.5px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .4px; font-weight: 600;
}
/* the note under a row label - what the figure actually means */
.compare-table tbody th small {
  display: block; margin-top: 3px; font-size: 11.5px; font-weight: 400;
  color: var(--muted); text-transform: none; letter-spacing: 0; line-height: 1.4;
}
/* the gap between best and worst, stated so the reader does not have to
   do the subtraction that the table exists to save them */
.cmp-spread {
  display: block; margin-top: 5px; font-size: 11.5px; font-weight: 600;
  color: var(--cobalt); font-variant-numeric: tabular-nums;
}

/* THE WINNING CELL. Tinted, not just labelled: the eye finds the colour
   before it reads the word, which is the entire point of scanning a
   comparison. Never marked on a tie or against an unpublished figure -
   winning against nothing is not winning. */
.cmp-best { background: #ECFDF5; box-shadow: inset 3px 0 0 #059669; }
.cmp-flag {
  display: block; margin-top: 4px; font-size: 10.5px; font-weight: 700;
  letter-spacing: .5px; text-transform: uppercase; color: #047857;
}
.cmp-best + td, .compare-table td { transition: none; }

/* a figure the provider does not publish - stated, never shown as zero */
.cmp-none { color: var(--muted); font-size: 13px; font-style: italic; }
.compare-table td small { display: block; font-size: 12px; color: var(--muted); }

/* feature matrix */
.cmp-group th {
  background: var(--navy) !important; color: var(--frost);
  font-size: 11.5px; text-transform: uppercase; letter-spacing: .6px;
  width: auto !important;
}
.cmp-yes { color: #047857; font-size: 17px; font-weight: 700; }
.cmp-no { color: #C6D2E4; font-size: 17px; }
.cmp-apply td { padding-top: 16px; }

/* when the comparison itself is limited - one product, mixed types, or
   nothing in common. Said plainly rather than rendering empty rows. */
.cmp-warn {
  background: #FFF8E7; border: 1px solid #E8CD9A; border-radius: var(--r-surface);
  padding: 12px 16px; margin: 14px 0; font-size: 13.5px; color: var(--text);
  line-height: 1.5; max-width: 760px;
}
.cmp-shared { font-size: 13.5px; color: var(--muted); max-width: 760px; margin: 14px 0 0; }
.cmp-shared strong { color: var(--text); }

@media (max-width: 640px) {
  .compare-table tbody th { width: 130px; font-size: 12.5px; }
  .compare-table td { font-size: 14px; }
}

/* ------------------------------------------------------------------ */
/* Faceted filtering - sidebar, chips, mobile drawer                   */
/* ------------------------------------------------------------------ */

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* category page header with image banner */
/* Browse page head, compressed (J.2): the list is the page - the head
   earns one line of identity, not a hero band. Product pages keep the
   default .page-head sizing. */
/* Breadcrumb, title row, intro - stacked flush on the container edge;
   the category tile sits inline with the H1 (founder's layout brief,
   30 Sep 2026) instead of floating left of the whole block. */
.cat-head { display: block; padding: 22px 0 20px; }
.cat-head .cat-crumb { margin: 0 0 8px; }
.cat-title { display: flex; align-items: center; gap: 12px; }
.cat-head h1 { font-size: 28px; line-height: 1.2; margin: 0; }
.cat-head .cat-thumb { height: 36px; width: 36px; border-radius: 10px; }
.cat-thumb {
  height: 44px; width: 78px; object-fit: cover; border-radius: var(--r-surface);
  border: 1px solid var(--border); box-shadow: var(--shadow); flex: none;
}

/* two-pane layout: the sidebar's top and the results header's first row
   start on the same line (align-items: start, nothing above either) */
.browse-layout {
  display: grid; grid-template-columns: 300px minmax(0, 1fr);
  gap: 32px; align-items: start;
}
@media (max-width: 860px) {
  /* minmax(0,1fr), not 1fr: a bare 1fr honours the widest descendant's
     min-content (the sort select alone is 290px), so one wide control
     was silently widening the whole page - 47px of horizontal scroll on
     a phone. Zero floor lets children scroll inside their own boxes. */
  .browse-layout { grid-template-columns: minmax(0, 1fr); }
  /* the sidebar's row is empty on a phone (the panel is the drawer), so
     the desktop gap would only push the results down */
  .browse-layout { row-gap: 0; }
  .results-bar { flex-wrap: wrap; gap: 4px 10px; }
}

/* facet sidebar */
/* No nested scrollbar (J.4): the panel takes its natural height and the
   wrapper below owns stickiness. The one interior scroll that remains
   is the brand list's own (.facet-scroll) - a 105-provider list at
   natural height would BE the page. */
.facet-panel {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-surface); box-shadow: var(--shadow);
}
.facet-panel form { padding: 4px 0 14px; }
.facet-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px 6px;
}
.facet-panel-head h2 { margin: 0; font-size: 15px; font-weight: 600; }
.clear-all { --btn-px: 8px; margin-right: -8px; }

/* min-inline-size: fieldsets default to min-content and refuse to
   shrink below their widest descendant - the legend's circular 100%
   sizing blew the brands fieldset to 470px inside the 260px track,
   which pushed the type-to-filter input (width: 100% of that body)
   right across the results column (founder defect report, 1 Sep). */
fieldset.facet { border: none; border-top: 1px solid var(--border); margin: 0; padding: 0 16px 12px; min-inline-size: 0; }
fieldset.facet legend { padding: 0; width: calc(100% + 0px); }
.facet-toggle {
  display: flex; width: 100%; align-items: center; justify-content: space-between;
  font-family: inherit; padding: 0; margin: 9px 0 3px;
  text-transform: uppercase; letter-spacing: .5px;
}
.facet-toggle::after {
  content: ""; width: 8px; height: 8px; flex: none; margin-left: 8px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform .15s; margin-top: -4px;
}
.facet.collapsed .facet-toggle::after { transform: rotate(-45deg); margin-top: 2px; }
.facet.collapsed .facet-body { display: none; }
/* The explainer sits behind a native <details> (J.3): same idiom as
   switch-help, sized for the sidebar. */
.facet-help { margin: 0 0 6px; }
.facet-help summary {
  font-size: 11px; color: var(--muted); cursor: pointer;
  list-style-position: inside;
}
.facet-help summary:hover { color: var(--text); }
.facet-help[open] summary { color: var(--text); }
.facet-hint { margin: 4px 0 4px; font-size: 12px; color: var(--muted); line-height: 1.45; }

/* PRODUCTS SHOWN (founder's declutter brief, 30 Sep 2026): the default
   filters as one switch. The heading wears the facet toggles' register
   without being a toggle - the group never collapses. The switch is a
   native checkbox (role="switch") under a drawn track: off is slate
   #7C8BA1 (3.5:1 on the card, so the control is visible at rest), on is
   navy - a state, like the selected segment; action blue is for the
   primary action only (vocabulary test). Classes are .toggle-*: the
   refinance flow owns .switch-*. */
.facet-legend {
  display: flex; align-items: center; height: 30px; margin: 9px 0 3px;
  font-size: 12.5px; font-weight: 600; line-height: 1; color: var(--muted);
  text-transform: uppercase; letter-spacing: .5px;
}
.toggle {
  position: relative; display: flex; align-items: center; gap: 10px;
  padding: 5px 4px; font-size: 13.5px; cursor: pointer;
}
.toggle-input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.toggle-track {
  position: relative; flex: none; width: 34px; height: 20px;
  border-radius: 10px; background: #7C8BA1; transition: background 140ms ease;
}
.toggle-track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(11, 30, 63, .3);
  transition: transform 140ms ease;
}
.toggle-input:checked + .toggle-track { background: var(--navy); }
.toggle-input:checked + .toggle-track::after { transform: translateX(14px); }
.toggle-input:focus-visible + .toggle-track { outline: 2px solid var(--cyan); outline-offset: 2px; }
.toggle:hover .toggle-label { color: var(--cobalt); }
.toggle-hint { margin: 2px 0 2px 48px; font-size: 12px; line-height: 1.45; color: var(--muted); }
@media (prefers-reduced-motion: reduce) {
  .toggle-track, .toggle-track::after { transition: none; }
}

/* option rows - native inputs, styled */
.facet-opts { display: flex; flex-direction: column; gap: 2px; }
.facet-scroll { max-height: 208px; overflow-y: auto; padding-right: 4px; }
.opt {
  display: flex; align-items: center; gap: 9px; font-size: 13.5px;
  padding: 5px 4px; border-radius: var(--r-control); cursor: pointer;
}
.opt:hover { background: rgba(37, 99, 235, .06); }
.opt input[type="checkbox"], .opt input[type="radio"] {
  width: 16px; height: 16px; margin: 0; accent-color: var(--blue); flex: none;
}
.opt-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.opt-count { color: var(--muted); font-size: 12px; }
.opt-disabled { opacity: .42; }
.opt-disabled, .opt-disabled input { cursor: not-allowed; }

/* chip-style options (terms, fixed years) */
.facet-chips { flex-direction: row; flex-wrap: wrap; gap: 7px; }
.facet-chips .opt { padding: 0; border-radius: var(--r-control); }
.facet-chips .opt:hover { background: none; }
.facet-chips .opt input { position: absolute; opacity: 0; pointer-events: none; }
.facet-chips .opt-label {
  padding: 0 13px; border: 1.5px solid var(--border);
  font-size: 12.5px; font-weight: 500; overflow: visible;
}
.facet-chips .opt:hover .opt-label { border-color: var(--cobalt); color: var(--cobalt); }
.facet-chips .opt:has(input:checked) .opt-label {
  background: var(--navy); color: #fff; border-color: transparent; font-weight: 600;
}
.facet-chips .opt:has(input:focus-visible) .opt-label {
  outline: 2px solid var(--cyan); outline-offset: 2px;
}
.facet-chips .opt-count { display: none; }

/* free-text search facet */
.facet-body input[type="search"] {
  width: 100%; padding: 8px 11px; border: 1px solid var(--border);
  border-radius: var(--r-control); font: inherit; font-size: 13.5px;
}

/* apply button - hidden when JS auto-submits on desktop */
.apply-btn { width: calc(100% - 32px); margin: 12px 16px 0; }
/* full-width bar toggles: the GHOST variant's colours, a layout of
   their own (the button classes can't carry space-between + 100%). */
.facet-toggle {
  height: 30px; font-size: 12.5px; font-weight: 600; line-height: 1;
  color: var(--muted); background: none; border: none; cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.facet-toggle:hover { color: var(--cobalt); }
/* Confirm STAYS VISIBLE with JS on. It used to be hidden above 861px
   because JS applied every change as you made it; now that changes are
   collected, hiding the button would leave no way to submit them. */
.apply-btn.has-pending { box-shadow: 0 0 0 3px rgba(37, 99, 235, .25); }

/* Enhancement-only chrome: present in the markup, shown only once the
   script has claimed the document. Without JS the provider list is the
   complete experience and a dead search box would be a lie. */
.js-only { display: none; }
.js .js-only { display: block; }

/* type-to-filter over the provider list */
.brand-search { margin: 0 0 10px; }
.brand-search input {
  width: 100%; padding: 7px 10px; font-family: inherit; font-size: 13.5px;
  border: 1px solid var(--border); border-radius: var(--r-control);
  background: var(--card); color: var(--text);
}
.brand-search-empty { margin: 6px 0 0; font-size: 12.5px; color: var(--muted); }
/* A provider you ticked, then typed past. It stays checked and stays on
   screen - losing a selection you cannot see is how the result set stops
   matching the boxes. */
.opt-pinned { order: -1; }
.facet-body .opt[hidden] { display: none; }

/* THE TOOLBAR (founder's declutter brief, 30 Sep 2026): ONE row above
   the results, then straight into the list. Left: the count, with "Rates
   checked" under it. Right: the alert bell, the sort, How we rank. It
   wraps on narrow screens. The hairline under it is the only rule
   between the view controls and the first result. */
.results-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px 16px; flex-wrap: wrap;
  margin: 0 0 16px; padding: 0 0 14px; border-bottom: 1px solid var(--border);
}
.results-bar-left { min-width: 0; }
.results-checked { margin: 3px 0 0; font-size: 13px; line-height: 1.5; color: var(--muted); }
.rank-link {
  font-size: 13px; color: var(--muted); white-space: nowrap;
  text-decoration: underline; text-underline-offset: 2px;
}
.rank-link:hover { color: var(--blue); }
.result-count { margin: 0; font-weight: 700; font-size: 22px; line-height: 1.25; }
/* A wrapping row in the bar, so on a phone "from 81 providers" drops to
   its own line whole and flush left instead of splitting after "81"
   (the gap only spaces a shared line). */
.results-bar .result-count {
  display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 8px;
}
/* the provider count is context, not headline - lighter and quieter so
   the figure carries the row on its own */
.count-from {
  font-weight: 400; font-size: 16px; color: var(--muted); white-space: nowrap;
}
.results-bar-right { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.sort-label { font-size: 13px; color: var(--muted); display: flex; gap: 8px; align-items: center; }
.sort-label select {
  height: 40px; padding: 0 11px; border: 1px solid var(--border); border-radius: var(--r-control);
  background: var(--card); font-size: 13.5px; color: var(--text); font-family: inherit;
}
/* The view controls sit ABOVE the toolbar - they decide what it counts -
   and the first of them starts level with the sidebar's top. */
.results > :first-child { margin-top: 0; }
.results > .seg, .results > .mission-band, .results > .postcode-bar { margin-bottom: 16px; }
/* The fixed compare tray's clearance, at the end of everything the
   results column holds (it used to pad the list, when nothing followed). */
.results { padding-bottom: 90px; }
.results .prod-list { padding-bottom: 0; }
.muted-val { color: var(--muted); font-size: 15px; font-weight: 500; }
/* .bt-tabs died with the vocabulary pass: every mode strip is .seg. */

/* THE LIST'S LAST ROW (founder's declutter brief, 30 Sep 2026): what the
   specialty switch would add and the way to add it - a dashed card after
   the last result, only while something is hidden. Grey text, blue only
   on the link: blue means "link" and nothing else. */
.list-end-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 6px 16px; flex-wrap: wrap; padding: 14px 20px;
  border: 1.5px dashed #C9D5E6; border-radius: var(--r-surface);
  font-size: 14px; color: var(--muted);
}
.list-end-row p { margin: 0; }
.list-end-row a { font-weight: 600; color: var(--blue); }
/* the other counts of what a filter left out (deposit, fee band) */
.list-end-note { margin: 0; font-size: 13px; color: var(--muted); }
.list-end-note a { font-weight: 600; color: var(--blue); }

.cap-note { text-align: center; color: var(--muted); font-size: 13px; margin: 6px 0 0; }

/* The coverage sentence and Important information, directly under the
   list (DISCLAIMER_POSITION "below"): 12px, muted, links underlined. */
.results-disclaimer { margin: 16px 0 0; font-size: 12px; line-height: 1.5; color: var(--muted); }
.results-disclaimer a, .about-results > summary {
  color: inherit; text-decoration: underline; text-underline-offset: 2px;
}
.results-disclaimer a:hover, .about-results > summary:hover { color: var(--blue); }
/* ...or behind "About these results" on the Rates checked line (the
   "top" variant): a native <details>, its sentence in a popover. */
.about-results { display: inline-block; position: relative; }
.about-results > summary { display: inline; cursor: pointer; list-style: none; }
.about-results > summary::-webkit-details-marker { display: none; }
.about-results-pop {
  position: absolute; left: 0; top: calc(100% + 6px); z-index: 30;
  width: min(320px, calc(100vw - 32px)); margin: 0; padding: 10px 12px;
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-control); box-shadow: var(--shadow);
  font-size: 12.5px; line-height: 1.5; color: var(--text);
}
.about-results-pop a { color: var(--blue); }

/* postcode-first energy browsing */
.postcode-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 12px 16px; margin: 0 0 14px;
}
/* Dark-adapted internals: same measured palette as the home-loans band
   (labels #C6D0E6 hold 13.3:1 at the dark end and 6+:1 at the violet
   end; fields take the translucent-white treatment). */
.postcode-bar label { font-size: 13.5px; font-weight: 600; color: #C6D0E6; }
.postcode-bar input {
  /* 118px holds the "e.g. 2000" placeholder and the spaced 4-digit
     entry without clipping (founder, 5 Sep: the old 92px cut the
     placeholder). Tracking eased 2px -> 1px so the example reads as
     words, not spaced-out letters; the entered digits stay tabular. */
  width: 118px; padding: 8px 11px;
  background: rgba(255, 255, 255, .09); color: #fff;
  border: 1px solid rgba(255, 255, 255, .18);
  border-radius: var(--r-control); font: inherit; font-size: 15px; letter-spacing: 1px;
  font-variant-numeric: tabular-nums;
}
.postcode-bar input::placeholder { color: rgba(255, 255, 255, .4); }
.postcode-bar input:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
.zone-note { font-size: 12px; color: var(--muted); }
.sky-surface .zone-note { color: #C6D0E6; }
/* a saved postcode says so beside its own field, with the way to forget
   it (30 Sep 2026 - it used to lead the filter sentence) */
.pc-saved { font-size: 12.5px; color: #C6D0E6; }
.pc-saved a { color: #fff; text-decoration: underline; text-underline-offset: 2px; }
/* the customer toggle on the sky: same control, dark clothes */
.sky-surface .customer-toggle {
  border-color: rgba(255, 255, 255, .22); background: rgba(255, 255, 255, .07);
}
.sky-surface .customer-toggle .opt { color: #C6D0E6; }
.sky-surface .customer-toggle .opt:has(input:checked) { background: #fff; color: var(--navy); }
/* A mode control in form clothing: the radios must stay radios (they
   submit with the postcode form), but the SHAPE follows the segmented
   vocabulary - control radius, hairline, navy selection. */
.customer-toggle {
  display: inline-flex; gap: 2px; border: 1px solid var(--border);
  border-radius: var(--r-control); padding: 2px; background: var(--card);
}
.customer-toggle .opt {
  cursor: pointer; padding: 0 12px; font-size: 12.5px; font-weight: 600;
  color: var(--muted);
}
.postcode-bar .customer-toggle input[type="radio"] {
  position: absolute; width: 1px; height: 1px; opacity: 0; /* label is the control */
}
.customer-toggle .opt:has(input:checked) { background: var(--navy); color: #fff; }
.customer-toggle .opt:has(input:focus-visible) {
  outline: 2px solid var(--cyan); outline-offset: 1px;
}
.postcode-prompt h3 { font-size: 18px; }

/* /energy/{postcode} landing pages */
.history-strip {
  background: var(--navy); color: #fff; font-weight: 600;
  padding: 12px 18px; border-radius: var(--r-surface); margin: 0 0 14px;
}
.ref-context {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-surface); box-shadow: var(--shadow);
  padding: 14px 18px; margin: 0 0 18px;
}
.ref-context p { margin: 0; }
.pc-fuel { margin: 22px 0; }
.pc-fuel h2 { margin-bottom: 4px; }
.pc-fuel .result-count { margin: 0 0 10px; }
.prod-card.slim { padding: 14px 16px; }
.empty-note { color: var(--muted); }
.share-row { display: flex; gap: 12px; align-items: center; margin: 20px 0; flex-wrap: wrap; }

.avail-divider {
  margin: 10px 0 2px; padding: 8px 4px; color: #8A5A00; font-size: 13px;
  font-weight: 600; border-bottom: 2px dashed #E8CD9A;
}
.metric-ref strong { -webkit-background-clip: initial; background-clip: initial; background: none; }

/* Accessible reference-price tooltip: a native <details> disclosure -
   real button semantics, keyboard-operable, never hover-only. */
.metric-ref { position: relative; }
.ref-tip { display: inline-block; }
.ref-tip summary {
  list-style: none; cursor: pointer; display: inline-flex;
  align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%;
  border: 1.5px solid currentColor; font-size: 11px; font-weight: 700;
  color: var(--muted); user-select: none;
}
.ref-tip summary::-webkit-details-marker { display: none; }
.ref-tip summary:hover { color: var(--cobalt); border-color: var(--cobalt); }
.ref-tip summary:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
.ref-tip[open] summary { color: var(--cobalt); border-color: var(--cobalt); }
.ref-tip-body {
  position: absolute; z-index: 30; left: 0; top: calc(100% + 6px);
  width: 260px; max-width: 78vw; padding: 10px 12px;
  background: var(--card); color: var(--ink, #1a2233);
  border: 1px solid var(--border); border-radius: var(--r-surface);
  box-shadow: 0 8px 24px rgba(6, 13, 31, .18);
  font-size: 12.5px; font-weight: 400; line-height: 1.45; text-align: left;
}
.ref-tip-body a { display: inline-block; margin-top: 4px; font-weight: 600; }
td .ref-tip { position: relative; }
td .ref-tip .ref-tip-body { left: auto; right: 0; }
.metric-ref.ref-below strong { color: #0E7A46; }
.metric-ref.ref-above strong { color: #A8471B; }


/* empty state */
.empty-state {
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-surface);
  box-shadow: var(--shadow); padding: 34px 28px; text-align: center;
}
.empty-state h3 { margin: 0 0 8px; font-size: 17px; }
.empty-state p { color: var(--muted); margin: 0 0 6px; }
.empty-state ul { list-style: none; padding: 0; margin: 10px 0 0; display: inline-block; text-align: left; }
.empty-state li { padding: 4px 0; font-size: 14px; color: var(--muted); }

/* focus visibility for interactive filter elements */
.facet-toggle:focus-visible, .chip:focus-visible, .bt-tab:focus-visible,
.opt input:focus-visible {
  outline: 2px solid var(--cyan); outline-offset: 2px;
}

/* mobile: bottom-sheet drawer (classes applied by filters.js) */
/* The FAB is a filter control, not a conversion action, so it takes the
   SECONDARY variant - the page's one PRIMARY stays with the watch
   signup. It floats over scrolling content, so the chrome rule gives it
   an opaque backdrop; that is a surface property, not a button size. */
.filter-fab {
  display: none; position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 40; box-shadow: 0 8px 30px rgba(2, 6, 16, .4); background: var(--card);
}
.filter-fab .fab-badge { background: rgba(37, 99, 235, .12); color: var(--blue); }
.fab-badge {
  /* 50% is geometry (a round count bubble), deliberately literal. */
  background: rgba(6, 13, 31, .35); border-radius: 50%; padding: 1px 8px;
  font-size: 12px;
}
@media (max-width: 860px) {
  .js .filter-fab { display: inline-flex; }
  .js .facet-panel {
    position: fixed; left: 0; right: 0; bottom: 0; top: 8vh; z-index: 60;
    max-height: none; border-radius: var(--r-surface) var(--r-surface) 0 0; border-bottom: none;
    transform: translateY(102%); transition: transform .22s ease; margin: 0;
    /* The drawer scrolls its own content. This lived on the base rule
       until J.4 removed the desktop nested scrollbar - the DRAWER is a
       fixed box and still needs it, or everything below the fold is
       unreachable on a phone. */
    overflow-y: auto; overscroll-behavior: contain;
  }
  .js body.drawer-open .facet-panel { transform: translateY(0); }
  .js body.drawer-open { overflow: hidden; }
  .js .facet-panel .apply-btn {
    display: block; position: sticky; bottom: 10px; z-index: 2;
    box-shadow: 0 4px 18px rgba(6, 13, 31, .3);
  }
  .drawer-close { --btn-px: 8px; font-size: 20px; font-weight: 400; }
  .drawer-backdrop {
    position: fixed; inset: 0; background: rgba(6, 13, 31, .55); z-index: 50;
    opacity: 0; pointer-events: none; transition: opacity .2s;
  }
  body.drawer-open .drawer-backdrop { opacity: 1; pointer-events: auto; }
}

/* footer - dark surface with mono-white mark */
/* ONE FOOTER (founder's brief, 26 Sep 2026). Row 1: the mark and one
   tagline left, one 14px link row right, underline on hover only. Row
   2: Important information, below. Every colour clears 4.5:1 on the
   midnight: #8CA0C4 7.3:1, #C9CFE0 12.7:1, white higher. */
.site-footer { background: var(--midnight); color: #8CA0C4; margin-top: 40px; font-size: 13px; }
.site-footer .wrap { padding-top: 28px; padding-bottom: 28px; }
.site-footer p { margin: 0; }
.footer-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px 24px; flex-wrap: wrap;
}
.footer-brand { display: flex; align-items: center; gap: 12px; }
.footer-brand img { height: 30px; flex: none; opacity: .9; }
.footer-tagline { font-size: 15px; font-weight: 600; color: #fff; letter-spacing: .01em; }
.footer-links { display: flex; gap: 4px 22px; flex-wrap: wrap; margin: 0; }
.footer-links a {
  display: inline-flex; align-items: center; min-height: 44px;
  color: #C9CFE0; font-size: 14px; font-weight: 500; text-decoration: none;
}
.footer-links a:hover { color: var(--frost); text-decoration: underline; }

/* hero CTA row - primary and secondary inline, left-aligned */
.hero-actions {
  display: flex; gap: 12px; justify-content: flex-start; align-items: center;
  flex-wrap: wrap; margin-top: 26px;
}
/* The hero's second action is a LINK, not a button. Two boxed controls
   side by side read as co-equal primaries and the row loses its
   hierarchy - there should be one thing to commit to and one thing to
   browse. Slate/frost treatment, quiet text-link register
   below it, so the hero's plain links agree with each other. The arrow
   carries the distinction: the primary commits (→), this navigates (›).
   Widths are deliberately NOT equalised - the labels differ. */
/* Quiet by design: at semibold it read as a subheading and the eye
   stopped on it as if it were structure. Body colour, body weight, the
   chevron carrying the only colour. */
.hero-link {
  color: #8CA0C4; font-size: 15.5px; font-weight: 400;
  text-decoration: none; white-space: nowrap;
  transition: color 140ms ease;
}
.hero-link .hero-link-arrow { color: var(--cyan); margin-left: 3px; }
.hero-link:hover { color: var(--frost); text-decoration: underline; }
.hero-link:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }
@media (max-width: 600px) {
  .hero-actions { flex-direction: column; align-items: stretch; }
  .hero-actions .btn { width: 100%; }
  /* the link sits CENTRED under the button - stretching it full-width
     would make it read as a second button again */
  .hero-actions .hero-link { align-self: center; margin-top: 2px; }
}

/* category intro + freshness lines */
/* One line on desktop (J.2): the full sentence rides the title
   attribute; the ellipsis only ever costs the tail of a sentence whose
   job is done by its first clause. On mobile nowrap would STRETCH THE
   PAGE to the sentence's full width (measured: 853px on a 375px
   viewport), so it wraps there like any paragraph. */
.cat-intro { max-width: 620px; margin: 4px 0 0; }
@media (min-width: 861px) {
  .cat-intro {
    max-width: none; white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis;
  }
}
.freshness-line { margin: 0 0 10px; color: var(--muted); font-size: 12.5px; }
.detail-head .freshness-line { margin: 2px 0 0; }

/* statutory comparison-rate warning - keep prominent, don't restyle away */
/* The comparison-rate footnote on a light surface (the statement page):
   plain text, no box - the boxed callout retired 25 Sep 2026. */
.cr-footnote { font-size: 13px; line-height: 1.5; color: var(--muted); }
.prose .cr-footnote { font-size: 13px; line-height: 1.5; color: var(--muted); }

/* product detail apply block */
.apply-block { margin-left: auto; display: flex; flex-direction: column; gap: 6px; align-items: flex-end; }
.apply-block .apply-note { font-size: 11.5px; color: var(--muted); max-width: 220px; text-align: right; }

/* implausible published value: flagged dash, plain colour (the metric
   gradient would make it look like a real figure) */
.metric strong .rate-flag {
  -webkit-background-clip: initial; background-clip: initial; background: none;
  color: var(--muted); cursor: help; border-bottom: 1px dotted var(--muted);
}

/* Rate-watch toolbar control - a SMALL (30px) neutral tonal button in
   the results toolbar that becomes a green tonal chip once a watch is
   live. Both follow the tag system's tonal-fill pattern (text at full
   tone, ~10% fill, ~28% border); green derives from the guarantee
   chip's hue - the --watch-green token below. The popover anchors under
   the control (bottom sheet under 600px). No-JS: button hides and the
   popover renders as a plain always-visible card. */
:root {
  --watch-neutral: 71, 85, 105;   /* slate - the tag system's neutral */
  --watch-green: 6, 95, 70;       /* derived from the guarantee-badge green */
}
.rate-watch { position: relative; display: inline-block; margin-left: auto; }
/* the [hidden] attribute must beat the author display values below */
.rate-watch [hidden] { display: none !important; }
.rw-btn, .rw-chip {
  display: inline-flex; align-items: center; gap: 6px;
  cursor: pointer; font-family: inherit; line-height: 1; white-space: nowrap;
}
/* The trigger is a GHOST button, "Alert me above 5.55%" (founder's
   layout brief, 30 Sep 2026): hairline border, no fill, blue text - it
   sits beside the sort select, the same height, and reads as the lesser
   of the two. */
.rw-btn {
  height: 38px; padding: 0 12px; border: 1px solid var(--border);
  border-radius: var(--r-control); background: transparent;
  font-size: 13.5px; font-weight: 600; color: var(--blue);
  transition: color 140ms ease, border-color 140ms ease, background 140ms ease;
}
.rw-btn:hover {
  color: var(--cobalt); border-color: rgba(37, 99, 235, .45);
  background: rgba(37, 99, 235, .05); text-decoration: none;
}
.rw-btn:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; }
/* THE TOOLBAR'S BELL (founder's declutter brief, 30 Sep 2026): a 40px
   square, the same ghost as the labelled button. Its tooltip is its
   accessible name, drawn from data-tip on hover and on keyboard focus;
   display:none otherwise, so a hidden tip can never widen the page. */
.rw-icon { position: relative; width: 40px; height: 40px; padding: 0; justify-content: center; }
.rw-icon::after {
  content: attr(data-tip); display: none;
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 41;
  width: max-content; max-width: 240px; padding: 6px 10px;
  border-radius: var(--r-control); background: var(--navy); color: #fff;
  font-size: 12px; font-weight: 500; line-height: 1.4; white-space: normal;
  text-align: left; pointer-events: none;
}
.rw-icon:hover::after, .rw-icon:focus-visible::after { display: block; }
.rate-watch.open .rw-icon::after { display: none; }
/* the "watching" state stays a chip - it is a status, not an action */
.rw-chip {
  height: 30px; padding: 0 12px; border-radius: var(--r-control);
  font-size: 12.5px; font-weight: 600;
  transition: background 140ms ease, border-color 140ms ease;
}
.rw-chip {
  color: rgb(var(--watch-green));
  background: rgba(var(--watch-green), .10);
  border: 1px solid rgba(var(--watch-green), .28);
}
.rw-chip:hover {
  background: rgba(var(--watch-green), .18);
  border-color: rgba(var(--watch-green), .45);
}
.rw-btn:focus-visible, .rw-chip:focus-visible {
  outline: 2px solid var(--cyan); outline-offset: 2px;
}
.rw-bell { flex: none; }
.rw-label b { font-weight: 700; }
.rw-caret { font-size: 10px; opacity: .8; }
/* the 160ms transformation - armed only after a state swap, so initial
   render never flashes */
.rate-watch.swapped .rw-btn:not([hidden]),
.rate-watch.swapped .rw-chip:not([hidden]) { animation: rwFade 160ms ease; }
@keyframes rwFade { from { opacity: 0; } to { opacity: 1; } }

.rw-pop {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  width: min(360px, calc(100vw - 32px));
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-surface); box-shadow: var(--shadow);
  padding: 14px; display: none; text-align: left;
}
.rate-watch.js.open .rw-pop { display: block; animation: rwFade 160ms ease; }
.rw-x {
  position: absolute; top: 8px; right: 8px; width: 26px; height: 26px;
  background: none; border: 0; border-radius: var(--r-control); cursor: pointer;
  font-size: 13px; line-height: 1; color: var(--muted);
}
.rw-x:hover { background: rgba(37, 99, 235, .07); color: var(--text); }
.rw-x:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }
/* Where the prefilled figure comes from, in the scoped words the toolbar
   button no longer carries (30 Sep). Muted, above the inputs, clear of
   the close button. */
.rw-hint {
  margin: 0 26px 10px 0;
  font-size: 12.5px; line-height: 1.45; color: var(--muted);
}
.rw-hint b { color: var(--text); font-weight: 600; }
.rw-form {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding-right: 22px;                     /* clears the ✕ corner */
}
.rw-rate { position: relative; flex: none; width: 100px; }
.rw-rate input {
  width: 100%; height: 38px; box-sizing: border-box; padding: 0 30px 0 10px;
  border: 1px solid var(--border); border-radius: var(--r-control); font: inherit;
  font-size: 14px; font-variant-numeric: tabular-nums;
}
.rw-suffix {
  position: absolute; top: 50%; right: 9px; transform: translateY(-50%);
  font-size: 12px; color: var(--muted); pointer-events: none;
}
.rate-watch[data-kind="dollars"] .rw-rate { width: 116px; }
.rate-watch[data-kind="dollars"] .rw-rate input { padding-right: 42px; }
.rw-email {
  flex: 1 1 140px; height: 38px; box-sizing: border-box; padding: 0 10px;
  border: 1px solid var(--border); border-radius: var(--r-control); font: inherit;
  font-size: 14px;
}
.rw-rate input:focus-visible, .rw-email:focus-visible {
  outline: 2px solid var(--cyan); outline-offset: 1px;
}
.rw-form .btn { flex: 1 1 100%; }          /* PRIMARY/MEDIUM, full row */
/* The reassurance beneath the email field (13 Sep): one muted line that
   opens in place. A full row, so it sits under the inputs and above the
   button; the caret is its only affordance, so it never competes with
   the primary control for attention. */
.rw-privacy {
  flex-basis: 100%; margin: -2px 0 0;
  color: var(--muted); font-size: 12px; line-height: 1.45;
}
.rw-privacy summary {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 5px;
}
.rw-privacy summary::-webkit-details-marker { display: none; }
.rw-privacy summary::after {
  content: "▸"; font-size: 10px; transition: transform 140ms ease;
}
.rw-privacy[open] summary::after { transform: rotate(90deg); }
.rw-privacy summary:hover { color: var(--cobalt); }
.rw-privacy p { margin: 4px 0 0; }
.rw-privacy a { color: inherit; text-decoration: underline; }
/* errors stay in the neutral family - no red, no orange, anywhere */
.rw-err { flex-basis: 100%; font-size: 12.5px; font-weight: 600; color: var(--text); }
.rw-err:empty { display: none; }
.rw-manage { margin: 0; padding-right: 22px; font-size: 13.5px; line-height: 1.55; }
.rw-stop {
  background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
  font-size: 13px; font-weight: 600; color: var(--blue);
}
.rw-stop:hover { color: var(--cobalt); }

/* no-JS: the popover is just a card in the flow, form always visible */
.rate-watch:not(.js) { flex-basis: 100%; }
.rate-watch:not(.js) .rw-btn { display: none; }
.rate-watch:not(.js) .rw-pop { position: static; display: block; width: auto; box-shadow: none; }
.rate-watch:not(.js) .rw-x { display: none; }

/* energy landing: the electricity heading row is the toolbar */
.pc-fuel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 4px;
}
.pc-fuel-head h2 { margin: 0; }

/* On a phone the popover becomes a bottom sheet, and the toolbar wraps:
   the controls take the full width under the count, from the left edge,
   so the bell's tooltip opens rightward. */
@media (max-width: 600px) {
  .results-bar-right { width: 100%; }
  .results-bar-right .rate-watch { margin-left: 0; }
  .rw-icon::after { right: auto; left: 0; }
  .rate-watch.js.open .rw-pop {            /* bottom sheet */
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: auto; z-index: 60;
    border-radius: var(--r-surface) var(--r-surface) 0 0; border-bottom: 0;
    box-shadow: 0 -8px 30px rgba(11, 30, 63, .18);
    padding: 16px; padding-bottom: max(16px, env(safe-area-inset-bottom));
  }
}

/* honeypot: absolutely removed from human view, still in the DOM for bots */
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
.manage-actions form { display: flex; gap: 6px; }

/* data staleness warning (footer, site-wide) */
.stale-warning {
  background: #FBEFD9; color: #8A5A00; border: 1px solid #E8CD9A;
  border-radius: var(--r-surface); padding: 10px 16px; margin: 18px 0 0;
  font-size: 13px; font-weight: 500;
}

/* ------------------------------------- rate history (detail pages) */
/* Step chart over the daily capture (lib/ratehistory.mjs). Series
   colours are DATA ink, deliberately not the action blue - a blue fill
   belongs to .btn-primary alone (vocabulary test), and a rate painted
   like a button reads as tappable, the same confusion the square-data
   radius rule exists to prevent. Rate: #0891B2, the Signal Cyan family
   darkened for contrast; comparison: #7C3AED, dashed. The pair is
   machine-validated on the white surface (lightness band, chroma
   floor, CVD separation, normal-vision floor, contrast - dataviz pass,
   26 Sep 2026), and both lines carry worded end labels, so identity is
   never colour alone. Text wears text tokens; grid and axes stay
   recessive. */
.rh-chart { display: block; width: 100%; max-width: 660px; height: auto; margin: 2px 0 0; }
.rh-grid { stroke: #E4EAF3; stroke-width: 1; }
.rh-tick { fill: var(--muted); font-size: 11px; }
.rh-rate { stroke: #0891B2; fill: none; stroke-width: 2; stroke-linejoin: round; }
.rh-comp { stroke: #7C3AED; fill: none; stroke-width: 2; stroke-dasharray: 5 4; stroke-linejoin: round; }
.rh-dot { fill: #0891B2; stroke: #FFFFFF; stroke-width: 1.5; }
.rh-chip-rate { fill: #0891B2; background: #0891B2; }
.rh-chip-comp { fill: #7C3AED; background: #7C3AED; }
.rh-endlab { fill: var(--text); font-size: 12px; font-weight: 600; }
.rh-legend { display: flex; align-items: center; gap: 6px; margin: 0 0 2px;
  color: var(--muted); font-size: 12.5px; }
.rh-legend .rh-key { display: inline-block; width: 10px; height: 10px;
  border-radius: var(--r-data); }
.rh-legend .rh-key + .rh-key,
.rh-legend .rh-key:not(:first-child) { margin-left: 10px; }
.rh-details { margin: 8px 0 0; }
.rh-details summary { cursor: pointer; color: var(--muted); font-size: 12.5px; }
.rh-details table { margin-top: 8px; }

/* delisted / ended product notice (detail pages) - the site's one amber,
   directly under the page head so it reads before any figure does */
.delisted-note {
  background: #FBEFD9; color: #8A5A00; border: 1px solid #E8CD9A;
  border-radius: var(--r-surface); padding: 10px 16px; margin: 14px 0 0;
  font-size: 13.5px; font-weight: 500;
}

/* static prose pages */
.prose { max-width: 680px; padding: 36px 0 40px; }
.prose h1 { font-size: 27px; font-weight: 600; letter-spacing: -.3px; margin: 0 0 18px; }
.prose p { margin: 0 0 14px; line-height: 1.65; color: var(--text); }
.prose strong { font-weight: 600; }

/* ------------------------------------------- refinance diagnosis flow */
.switch-flow { max-width: 640px; margin: 0 auto; padding: 24px 0 48px; }
.switch-warmth { font-size: 13px; color: var(--muted); margin: 2px 0 10px; }
.switch-kicker { color: var(--muted, #64748b); font-size: 0.9rem;
  text-transform: uppercase; letter-spacing: 0.06em; margin: 8px 0; }
.switch-progress { display: flex; gap: 8px; margin: 4px 0 20px; }
.switch-progress .dot { width: 10px; height: 10px; border-radius: 50%;
  background: #e2e8f0; }
.switch-progress .dot.done { background: var(--navy); }
.switch-progress .dot.now { background: linear-gradient(45deg, var(--electric-blue), var(--signal-cyan));
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.18); }
.switch-card { background: #fff; border: 1px solid #e2e8f0; border-radius: var(--r-surface);
  padding: 28px; box-shadow: 0 2px 8px rgba(6, 13, 31, 0.05); }
.switch-card h1 { font-size: 1.45rem; margin: 0 0 6px; }
.switch-hint { color: #64748b; margin: 0 0 18px; }
.switch-error { color: #b91c1c; background: #fef2f2; border: 1px solid #fecaca;
  border-radius: var(--r-surface); padding: 10px 14px; }
.switch-input { display: flex; align-items: center; gap: 8px;
  font-size: 1.4rem; margin: 12px 0 4px; }
.switch-input .affix { color: #64748b; font-weight: 600; }
.switch-input input { font-size: 1.4rem; padding: 12px 14px; width: 100%;
  max-width: 280px; border: 2px solid #cbd5e1; border-radius: var(--r-control); }
.switch-input input:focus { border-color: var(--electric-blue); outline: none; }
.switch-options { display: grid; gap: 10px; margin: 14px 0 4px; }
.switch-option { display: flex; align-items: flex-start; gap: 12px;
  border: 2px solid #e2e8f0; border-radius: var(--r-surface); padding: 14px 16px;
  cursor: pointer; transition: border-color 0.12s, background 0.12s; }
.switch-option:hover { border-color: #93c5fd; }
.switch-option:has(input:checked) { border-color: var(--electric-blue); background: #eff6ff; }
.switch-option input { margin-top: 4px; accent-color: var(--electric-blue); }
.switch-option strong { display: block; }
.switch-option em { display: block; font-style: normal; color: #64748b;
  font-size: 0.9rem; margin-top: 2px; }
.switch-help { margin: 14px 0 4px; font-size: 0.95rem; }
.switch-help summary { color: var(--electric-blue); cursor: pointer; }
.switch-help p { color: #475569; margin: 8px 0 0; }
.switch-next { margin-top: 18px; width: 100%; }
.switch-back { margin-top: 12px; }
.switch-payback { background: #ecfdf5; border: 1px solid #a7f3d0;
  border-radius: var(--r-surface); padding: 12px 16px; color: #065f46; }

.switch-flow-result { max-width: 860px; }
.switch-verdict { background: linear-gradient(135deg, #060D1F, #12305f);
  color: #fff; border-radius: var(--r-surface); padding: 32px; margin-bottom: 16px; }
.switch-verdict h1 { font-size: 1.7rem; margin: 4px 0 12px; color: #fff; }
.switch-verdict h1 strong { background: linear-gradient(45deg, #4f8cf7, var(--signal-cyan));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  font-size: 2.1rem; }
.verdict-kicker { text-transform: uppercase; letter-spacing: 0.08em;
  font-size: 0.8rem; color: var(--signal-cyan); margin: 0; }
.verdict-sub { color: #cbd5e1; margin: 0 0 10px; }
.verdict-sub strong { color: #fff; }
.verdict-confidence { color: #94a3b8; font-size: 0.9rem; margin: 0; }
.verdict-confidence a { color: var(--signal-cyan); }
.switch-verdict.verdict-caution { background: linear-gradient(135deg, #1f1506, #4a3305); }
.switch-note { color: #475569; font-size: 0.92rem; margin: 6px 0; }

/* categories section header */
.cat-section { padding-top: 8px; }
.section-head {
  font-family: var(--font-display); font-weight: 700;
  text-align: left; font-size: 26px; margin: 24px 0 2px;
  letter-spacing: -.02em;
}

/* CIVIC STATEMENT - the page's one centered pause (the standing
   exception to the left-aligned container). Pull-quote register:
   large and light-ish at 500, deliberately not the 700-800 headline
   voice, with the three-word payoff set apart on its own line at 700.
   The air around it IS the design - the founder read the old version
   as "too much space under cards" because a 21.6px line could not
   earn 96px of surrounding air. The cure is a statement scaled to own
   it, framed symmetrically (96px both sides, replacing 96 above / 48
   below), not less space. No accent colour: the guarantee green is
   load-bearing elsewhere on this site, so a decorative rule or green
   payoff here would dilute a factual signal - the 500 -> 700 weight
   step carries the emphasis instead. */
.civic {
  max-width: 860px; margin: 0 auto; padding: 96px 0; text-align: center;
}
.civic p {
  margin: 0; font-size: clamp(24px, 2.5vw, 32px); line-height: 1.35;
  font-weight: 500; color: var(--text); letter-spacing: -.01em;
  font-optical-sizing: auto;
}
/* one sentence per line at desktop; below 700px the two sentences run
   together and wrap naturally, but the payoff always keeps its beat */
.civic p > span { display: block; }
.civic-payoff { display: block; font-weight: 700; }
/* the statement's own padding is the whole separator - the founder
   section's top margin would otherwise stack onto it (96 above, 144
   below) and tip the frame off balance */
.civic + .founder { margin-top: 0; }
@media (max-width: 700px) {
  .civic { padding: 64px 0; }
  .civic p > span { display: inline; }
}

/* founder statement - the page's closing beat, generous air around it */
/* The photo column (founder, 21 Sep: "feels very big"): it was 40% of
   the section, 434px at the 1136px container. Now a third - 375px, a
   seventh smaller - with a 260px floor so the side-by-side layout keeps
   a real photo down to its 700px breakpoint, and a 400px ceiling. The
   statement takes what the photo gives up. */
.founder {
  display: grid; grid-template-columns: clamp(260px, 33%, 400px) 1fr; gap: 52px;
  align-items: center; margin: 48px 0 96px;
}
.founder-photo img {
  display: block; width: 100%; height: auto; border-radius: var(--r-surface);
  box-shadow: 0 12px 32px rgba(6, 13, 31, .12);
}
/* until the photo file lands, show a quiet placeholder, never a broken img */
.founder-photo.missing img { display: none; }
.founder-photo.missing::after {
  content: "Photo - /img/william-cole.jpg"; display: flex; align-items: center;
  justify-content: center; aspect-ratio: 1 / 1; border-radius: var(--r-surface);
  border: 1.5px dashed var(--border); color: var(--muted); font-size: 13px;
}
.founder-quote { margin: 0; }
.founder-quote blockquote { margin: 0; position: relative; }
.founder-quote blockquote::before {
  content: "\201C"; position: absolute; left: -8px; top: -34px;
  font-size: 84px; line-height: 1; color: var(--border); font-family: Georgia, serif;
}
.founder-quote blockquote p {
  margin: 0; font-size: 1.2rem; line-height: 1.65; max-width: 65ch;
}
/* The brand word inside the statement (founder, 21 Sep: "people who
   don't lookfirst") takes the closing line's weight - one bold register
   in the quote, not two. Lowercase, as the wordmark sets it. */
.founder-close, .founder-brand { font-weight: 600; }
.founder-quote figcaption { margin-top: 20px; }
.founder-name { font-weight: 500; }
.founder-role { display: block; font-size: .9em; color: var(--muted); margin-top: 2px; }
/* The credential as the signature's third line (founder, 8 Sep) - the
   same string /about renders, from about.mjs, in the role's register. */
.founder-cred { display: block; font-size: .9em; color: var(--muted); margin-top: 2px; max-width: 60ch; }
/* Side-by-side survives down to ~700px - a 40% photo column is still a
   good size there. Below that, stack, and cap the photo so a full-width
   square can never dwarf the statement. */
@media (max-width: 700px) {
  .founder { grid-template-columns: 1fr; gap: 28px; margin: 36px auto 56px; }
  .founder-photo { max-width: 380px; margin: 0 auto; }
  .founder-quote blockquote::before { top: -22px; font-size: 64px; }
}


/* /our-data lede */
.prose .lede { font-size: 1.12rem; line-height: 1.6; }

/* "How this list is ranked" - collapsible disclosure above every table */
/* Financial Claims Scheme modal (opened by the GUARANTEE tag) */
.fcs-modal {
  max-width: 460px; width: calc(100% - 32px); padding: 24px 26px;
  border: 1px solid var(--border); border-radius: var(--r-surface);
  background: var(--card); color: var(--text);
  box-shadow: 0 18px 48px rgba(6, 13, 31, .28);
}
.fcs-modal::backdrop { background: rgba(6, 13, 31, .55); }
.fcs-modal h2 { margin: 0 0 12px; font-size: 18px; font-weight: 600; line-height: 1.3; }
.fcs-modal p { margin: 0 0 12px; font-size: 13.5px; line-height: 1.6; color: var(--text); }
.fcs-modal form { margin: 4px 0 0; display: flex; justify-content: flex-end; }

/* /our-data disclosure blocks */
.disclosure-statement { max-width: 620px; margin: 26px 0 30px; }
.disclosure-statement blockquote p { font-size: 1.2rem; line-height: 1.6; }
.disclosure-para {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--r-surface); box-shadow: var(--shadow);
  padding: 16px 20px; font-size: 1.05rem; line-height: 1.6;
}

/* government guarantee statement - term deposits, between filters and
   the first tile, styled as an ANNOTATION: the site's own thought in
   the margin, visibly not a product tile. Sage rule + barely-there wash
   + broken grid alignment carry that; the copy is untouched.

   Two green families, deliberately separate: --annotation-green-* below
   is the annotation's sage; the .gg-chip keeps its own --tag-hue
   guarantee green, so either can be tuned without moving the other. */
/* The pop-art gap bubble is retired (founder, 1 Sep) - its artwork,
   safe-zone geometry, entrance animation, Bangers headline and green
   marker highlight left with it. The .gg-side/.gg-flow position gate
   and .gg-learn below survive: the deposits' guarantee legend renders
   through the same two instances. */
.gg-side { display: none; }
/* top bulges + trail need clearance from the panel above / whatever
   sits below */
.gg-side { margin-top: 12px; }
/* The flow instance caps at a readable column width, centered. */
.gg-flow-wrap { max-width: 420px; margin: 0 auto; }
.gg-flow { margin: 16px 0; }
@media (min-width: 861px) {
  .gg-side { display: block; }
  .gg-flow { display: none; }
}
/* sidebar column: panel + card share the 300px track; the WRAPPER
   carries the panel's previous sticky (card sticks with it - no new
   sticky behaviour, same top offset). Its scrollbar, when it has one,
   is a thin rounded hairline, not the chunky native bar (founder's
   layout brief, 30 Sep 2026). */
.side-col {
  display: flex; flex-direction: column; gap: 14px; min-width: 0;
  scrollbar-width: thin; scrollbar-color: var(--border) transparent;
}
.side-col::-webkit-scrollbar { width: 6px; }
.side-col::-webkit-scrollbar-track { background: transparent; }
.side-col::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
@media (min-width: 861px) {
  /* the drawer's close button, injected on a phone, has no job while the
     panel sits in the page beside the results */
  .drawer-close { display: none; }
  /* The max-height + overflow pair, returned exactly as the J.4 note
     predicted: the panel outgrew the viewport (brands open + the
     guarantee card measures 1,100px against common 700-900px
     viewports), which made the bottom facets unreachable while pinned
     (founder defect report, 1 Sep). overflow-y is AUTO, so a panel
     that fits keeps J.4's natural flow with no inner scrollbar - the
     scrollbar exists only when the alternative is unreachable
     content. */
  .side-col {
    position: sticky; top: 16px;
    max-height: calc(100vh - 32px);
    overflow-y: auto; overscroll-behavior: contain;
  }
}
.gg-side .gg-learn { font-size: 12.5px; }
.gg-learn {
  background: none; border: 0; padding: 0; margin-left: 4px; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--text);
  text-decoration: underline; text-underline-offset: 2px;
}
.gg-learn:hover, .gg-learn:focus-visible { color: var(--blue); }

/* ================================================================== */
/* THE CARD FACE, on the twilight hero                                 */
/*                                                                     */
/* The meter was built as a dark instrument in a dark hero. On a cool  */
/* grey card every one of those values inverts: pale drums with        */
/* near-black digits, a hairline that darkens rather than lightens,    */
/* and no screws or recessed bezel - a lit surface has no machined     */
/* housing. Scoped to .hero-right so the component keeps its dark      */
/* treatment anywhere else it is used.                                 */
/* ================================================================== */

/* ================================================================== */
/* CARD A (founder's brief, 26 Sep 2026): the blocks of the savings     */
/* card, top to bottom. Every colour clears 4.5:1 on white; the brief's */
/* eyebrow cyan #0B8FAE sits at 3.8:1 there, so the eyebrow wears the   */
/* nearest dark cyan that clears it (#0A7A94, 5.0:1). Scoped to        */
/* .hero-right so the component keeps its dark treatment elsewhere.   */
/* ================================================================== */
.hero-right .lt-top { display: flex; flex-direction: column; gap: 4px; }
.hero-right .lt-eyebrow {
  margin: 0; font-size: 12px; font-weight: 600; letter-spacing: 1px;
  line-height: 1.2; text-transform: uppercase; color: #0A7A94;
}
.hero-right .lt-lead {
  margin: 0; font-size: 16px; font-weight: 600; line-height: 1.25; color: #0B1020;
}
.hero-right .lt-annual {
  display: flex; align-items: baseline; gap: 10px; margin: 0;
  font-variant-numeric: tabular-nums;
}
.hero-right .lt-amt {
  font-family: var(--font-sans); font-size: 48px; font-weight: 800;
  line-height: 1; letter-spacing: -1.5px; color: #0B1020;
}
.hero-right .lt-per {
  font-family: var(--font-sans); font-size: 16px; font-weight: 500;
  letter-spacing: normal; text-transform: none; color: #4E556B;
}
/* THE TICKER: no panel. The pulse dot, the "$", the flip boxes for
   dollars and cents, the sub-cent boxes in cyan, the caption. The
   wheels stay (founder, 25 Sep): the scrolling accrual is the tension. */
.hero-right .lt-live {
  margin: 0; padding: 0; border-top: none;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.hero-right .lt-live-dot {
  flex: none; width: 8px; height: 8px; border-radius: 50%;
  background: #22C3E6; box-shadow: 0 0 10px rgba(34, 195, 230, .9);
}
@media (prefers-reduced-motion: no-preference) {
  .hero-right .lt-live-dot { animation: lt-pulse 2.4s ease-out infinite; }
  @keyframes lt-pulse {
    0% { box-shadow: 0 0 10px rgba(34, 195, 230, .9); }
    70% { box-shadow: 0 0 10px rgba(34, 195, 230, .9), 0 0 0 7px rgba(34, 195, 230, 0); }
    100% { box-shadow: 0 0 10px rgba(34, 195, 230, .9); }
  }
}
.hero-right .lt-reg { display: flex; align-items: flex-end; gap: 4px; }
.hero-right .lt-sym {
  font-size: 16px; font-weight: 500; color: #4E556B; padding: 0 2px 6px 0;
}
.hero-right .lt-dot { font-size: 22px; font-weight: 600; line-height: 1; color: #0B1020; padding: 0 1px 6px; }
/* the dollar and cent flip boxes: flat, 30x42, the pale fill on a
   hairline, a whisper of shade on the lower half, 28/600 ink digits.
   The face is the box's inner height exactly, so no glyph clips. */
.hero-right .lt-reg > .lt-drum {
  width: 26px; height: 34px; border-radius: var(--r-control);
  background: #F4F6FB; border: 1px solid #D5DAE6;
  box-shadow: inset 0 -17px 0 rgba(11, 16, 32, .04);
}
.hero-right .lt-reg > .lt-drum::after { display: none; }
.hero-right .lt-reg > .lt-drum .lt-strip i {
  height: 32px; line-height: 32px; font-size: 22px; font-weight: 600;
  letter-spacing: 0; color: #0B1020;
}
/* the sub-cent boxes: cyan, glowing, bottom-aligned, dark digits (white
   on this cyan is 2.1:1; the near-black is 9.7:1) */
.hero-right .lt-fine { display: flex; align-items: flex-end; gap: 4px; margin-left: 2px; }
.hero-right .lt-fine .lt-drum {
  width: 20px; height: 26px; border-radius: var(--r-control);
  background: #22C3E6; border: 0; box-shadow: 0 0 12px rgba(34, 195, 230, .6);
}
.hero-right .lt-fine .lt-drum::after { display: none; }
.hero-right .lt-fine .lt-strip i {
  height: 26px; line-height: 26px; font-size: 16px; font-weight: 600;
  letter-spacing: 0; color: #05060F;
}
.hero-right .lt-cap {
  font-family: var(--font-sans); font-size: 12.5px; font-weight: 400;
  line-height: 1.25; letter-spacing: normal; text-transform: none;
  color: #4E556B; white-space: nowrap;
}
/* THE RATE STRIP: three equal columns, hairline dividers, no background.
   All three values one size and one weight (the equal-prominence rule);
   the comparison column is one link to the footnote. */
.hero-right .lt-rates-block { display: flex; flex-direction: column; gap: 0; }
.hero-right .lt-rates { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.hero-right .lt-rate {
  display: flex; flex-direction: column; gap: 2px; padding: 0 12px;
  min-width: 0; color: #0B1020; text-decoration: none;
}
.hero-right .lt-rate:first-child { padding-left: 0; }
.hero-right .lt-rate:last-child { padding-right: 0; }
.hero-right .lt-rate + .lt-rate { border-left: 1px solid #E1E6F0; }
.hero-right .lt-rate-lab { font-size: 11px; line-height: 1.2; color: #4E556B; }
.hero-right .lt-rate-lab sup { font-size: .8em; line-height: 0; }
.hero-right .lt-rate-val {
  font-size: 18px; font-weight: 600; line-height: 1.1; color: #0B1020;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.hero-right .lt-rate-link:hover .lt-rate-val,
.hero-right .lt-rate-link:focus-visible .lt-rate-val { color: #1D4ED8; text-decoration: underline; }
/* THE FOOT: the hairline, the scope and the route on one row (it may
   wrap on a narrow card), what the figure leaves out beneath. */
.hero-right .lt-foot {
  display: flex; flex-direction: column; gap: 2px; margin: 0;
  padding: 10px 0 0; border-top: 1px solid #E1E6F0;
  font-family: var(--font-sans); letter-spacing: normal; text-transform: none;
}
.hero-right .lt-foot-row {
  display: flex; justify-content: space-between; align-items: center;
  gap: 8px; flex-wrap: wrap;
}
.hero-right .lt-foot-scope { font-size: 12.5px; line-height: 1.4; color: #4E556B; }
/* "rates at 1:10pm, 25 Sep" is one unit: when the scope must break it
   breaks after the lender count, never inside the time or the date. */
.lt-when { white-space: nowrap; }
/* Beside the copy (two columns) the route keeps its place at the right
   and a long scope (a dated read, a four-figure lender count) wraps
   inside its own box instead of pushing the route onto a new line. */
@media (min-width: 900px) {
  .hero-right .lt-foot-row { flex-wrap: nowrap; align-items: baseline; }
  .hero-right .lt-foot-scope { flex: 1 1 auto; min-width: 0; }
}
/* Between the stacking breakpoint and 1200 the H1 is fluid and the
   copy column stands a little shorter (269px at 1024), so the card
   gives up 4px of padding and 2px between blocks to stay level with
   it; from 1200 up both columns stand 283px. */
@media (min-width: 900px) and (max-width: 1199px) {
  .hero-right .lt { padding: 18px; gap: 8px; }
}
.lt-method {
  font-size: 13px; font-weight: 600; letter-spacing: 0; white-space: nowrap;
  color: #1D4ED8; text-decoration: none;
}
.lt-method:hover { color: var(--electric-blue); text-decoration: underline; }
.hero-right .lt-note {
  margin: 0; padding: 0; font-size: 12px; line-height: 1.4; font-weight: 400;
  letter-spacing: 0; text-transform: none; color: #4E556B;
}
.lt-sep { opacity: .55; }
/* THE DRUMS ARE NOT TEXT: every wheel carries 0-9 twice so it can
   roll, so copying the card returned digit soup. aria-hidden covers
   screen readers; this covers the copy path, and the visually-hidden
   figcaption supplies the real sentence instead. */
.lt-reg { user-select: none; -webkit-user-select: none; }
@media (max-width: 480px) {
  .hero-right .lt { padding: 20px; }
}
@media (max-width: 420px) {
  .hero-right .lt-amt { font-size: 44px; letter-spacing: -1.5px; }
  .hero-right .lt-rate-val { font-size: 16px; }
}

/* ================================================================== */
/* NAVIGATION (founder's brief, 25 Sep 2026). The bar: logo, a Compare  */
/* button that opens a menu of the eight categories, two quiet links.  */
/* On a phone: a menu button and a full-screen panel with the same     */
/* rows. Both menus render from categoryconfig.mjs (nav.mjs). The      */
/* panel overlays the page - nothing moves when it opens.              */
/* ================================================================== */
.site-header .wrap { position: relative; }
.nav-compare { position: relative; }
/* A text button with its chevron, not a bordered box (founder's layout
   brief, 30 Sep 2026); the tint on hover and while open is its only
   surface. Still a 44px touch target (tests/nav.test.mjs). */
.compare-btn {
  display: inline-flex; align-items: center; gap: 6px; height: 44px; padding: 0 10px;
  border-radius: 8px; border: 0; background: transparent;
  color: var(--frost); font-family: inherit; font-size: 16px; font-weight: 500;
  line-height: 1; cursor: pointer;
}
.compare-btn:hover { background: rgba(255, 255, 255, .06); }
.compare-btn[aria-expanded="true"] { background: rgba(255, 255, 255, .10); }
.compare-btn .chev { transition: transform 140ms ease; }
.compare-btn[aria-expanded="true"] .chev { transform: rotate(180deg); }
.compare-btn:focus-visible, .menu-btn:focus-visible, .menu-close:focus-visible {
  outline: 2px solid var(--cyan); outline-offset: 2px;
}
.compare-menu {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 40;
  width: 480px; max-width: calc(100vw - 32px); box-sizing: border-box;
  background: #fff; border-radius: 14px; padding: 12px;
  box-shadow: 0 20px 48px rgba(5, 6, 15, .28);
}
.compare-menu[hidden] { display: none; }
.compare-menu nav { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 8px; }
.compare-menu-item {
  display: flex; align-items: center; min-height: 44px; padding: 0 14px;
  border-radius: 8px; font-size: 15px; font-weight: 500; color: #0B1020;
  text-decoration: none;
}
.compare-menu-item:hover, .compare-menu-item:focus-visible {
  background: #F3F6FB; color: #0B1020; text-decoration: none;
}
.compare-menu-item[aria-current="page"] { font-weight: 600; color: var(--cobalt); }
.site-links { display: flex; align-items: center; gap: 32px; margin-left: auto; }
.site-link {
  display: inline-flex; align-items: center; min-height: 44px;
  color: #C9CFE0; font-size: 15px; font-weight: 500; white-space: nowrap;
}
.site-link:hover { color: var(--frost); text-decoration: none; }
.menu-btn { display: none; }
/* JavaScript off: a plain row of the eight links, nothing to open */
.nojs-nav { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-left: 8px; }
.nojs-link { color: #C9CFE0; font-size: 14px; font-weight: 500; white-space: nowrap; }

/* the phone panel: the dark surface, edge to edge, over everything */
.mobile-menu {
  position: fixed; inset: 0; z-index: 60; overflow-y: auto;
  background: #05060F; color: #fff;
}
.mobile-menu[hidden] { display: none; }
.mobile-menu-head {
  height: 64px; display: flex; align-items: center; justify-content: space-between;
  padding: 0 20px; border-bottom: 1px solid #1B2036;
}
.mobile-menu-head .logo .logo-mark { height: 30px; }
.menu-close {
  width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
  background: transparent; border: 0; color: #fff; cursor: pointer; margin-right: -10px;
}
.mobile-menu-label {
  margin: 0; padding: 20px 20px 8px; font-size: 13px; font-weight: 600;
  letter-spacing: .8px; text-transform: uppercase; color: #8A92AB;
}
.mobile-menu-list { display: flex; flex-direction: column; }
.mobile-menu-item {
  display: flex; justify-content: space-between; align-items: center;
  min-height: 52px; padding: 0 20px; font-size: 17px; font-weight: 500;
  color: #fff; border-bottom: 1px solid #141828; text-decoration: none;
}
.mobile-menu-item:hover { color: var(--cyan); text-decoration: none; }
.mobile-menu-item[aria-current="page"] { color: var(--cyan); }
.mobile-menu-item .chev { color: #8A92AB; font-size: 24px; line-height: 1; }
.mobile-menu-links { display: flex; flex-direction: column; gap: 4px; padding: 24px 20px 0; }
.mobile-menu-link {
  min-height: 44px; display: flex; align-items: center;
  font-size: 15px; color: #C9CFE0;
}
.mobile-menu-link:hover { color: #fff; text-decoration: none; }
body.menu-open { overflow: hidden; }
@media (max-width: 760px) {
  .site-header .wrap { height: 64px; gap: 16px; }
  .logo .logo-mark { height: 30px; }
  .site-header .logo { margin-left: -4px; }  /* 145/980 of the 30px mark */
  .nav-compare, .site-links { display: none; }
  .menu-btn {
    display: flex; margin-left: auto; width: 44px; height: 44px; margin-right: -10px;
    align-items: center; justify-content: center;
    background: transparent; border: 0; color: #fff; cursor: pointer;
  }
}

/* HOME TILES: the 21 Sep tile on its own grid (founder, 25 Sep 2026:
   "restore the old tile dimensions" - the brief's 4/2/1 columns, 20px
   gap, 24px padding, 16px radius and 19px name are gone). The grid
   fills with 230px-minimum columns at a 16px gap; the tile is 28px of
   padding on the surface radius with the hairline border and the
   resting shadow, the name 17/600. One target each: the name carries a
   stretched link over the whole tile (a nested anchor is invalid HTML,
   and the "^" on a rate must stay a real link to the warning), so the
   tile is clickable everywhere and the marker still works. */
.tile-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 16px; padding: 24px 0 0;
}
.tile {
  position: relative; display: flex; flex-direction: column; background: var(--card);
  border: 1px solid var(--border); border-radius: var(--r-surface);
  padding: 28px; box-shadow: 0 1px 3px rgba(0, 0, 0, .06);
  transition: transform 140ms ease, box-shadow 140ms ease,
              background-color 140ms ease, border-color 140ms ease;
}
.tile:hover, .tile:focus-within {
  transform: translateY(-1px); text-decoration: none;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .10);
  background: #FAFCFE; border-color: #D8E3F3;
}
.tile:focus-within { outline: 2px solid var(--blue); outline-offset: 2px; }
.tile-head { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; }
/* THE CATEGORY ICON rides the name line (founder, 21 Sep; kept 25 Sep).
   One hue per category, each darkened until it clears 4.5:1 on the
   white tile - the bright versions failed badly at this size. Gas is a
   deep teal rather than a blue: the obvious flame blue is the action
   blue, which would make one icon read as a control. */
.tile .cat-ico { flex: none; display: block; color: var(--muted); }
.tile[data-cat="home-loans"] .cat-ico        { color: #C62A2F; }  /* 5.57:1 */
.tile[data-cat="personal-loans"] .cat-ico    { color: #6E3AA7; }  /* 7.43:1 */
.tile[data-cat="savings"] .cat-ico           { color: #C11574; }  /* 5.79:1 */
.tile[data-cat="term-deposits"] .cat-ico     { color: #8A5A00; }  /* 5.93:1 */
.tile[data-cat="credit-cards"] .cat-ico      { color: #0E7A46; }  /* 5.40:1 */
.tile[data-cat="electricity"] .cat-ico       { color: #8A6D00; }  /* 4.92:1 */
.tile[data-cat="gas"] .cat-ico               { color: #0B5A7A; }  /* 7.61:1 */
.tile[data-cat="buy-now-pay-later"] .cat-ico { color: #0F766E; }  /* 5.0:1 */
.tile-name { font-size: 17px; font-weight: 600; line-height: 1.3; color: var(--text); text-decoration: none; }
.tile-name::after { content: ""; position: absolute; inset: 0; }
.tile-name:focus-visible { outline: none; }
.tile:hover .tile-name { text-decoration: none; color: var(--text); }
/* The live figure leads (21 Sep; back on the tile 25 Sep at the
   founder's word): the number alone on its line, its qualifier and the
   count merged into ONE grey line beneath it, then the foot pushed to
   the tile's bottom edge with 18px of air above it. */
.tile-figure { margin: 14px 0 0; }
.tile-figure strong {
  font-size: 26px; font-weight: 700; letter-spacing: -.02em;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.tile-figure .cr-mark { position: relative; z-index: 1; }
.tile-meta {
  margin: 4px 0 18px; font-size: 12.5px; color: var(--muted);
  line-height: 1.45; font-variant-numeric: tabular-nums;
}
/* THE FOOT (founder, 21 Sep): home loans keeps its button to the rate
   check, lifted above the name's stretched link (z-index) so it can be
   clicked; every other tile closes on a quiet "Compare" cue in the
   button's own 38px box, the link role of the control vocabulary,
   answering the tile's hover and focus because the whole tile is the
   target. The button's look is the system's; this rule is placement. */
.tile-cta { position: relative; z-index: 1; margin-top: auto; align-self: flex-start; }
.tile-go {
  margin-top: auto; align-self: flex-start;
  display: inline-flex; align-items: center; height: 38px;
  color: var(--blue); font-size: 14px; font-weight: 600; white-space: nowrap;
}
.tile:hover .tile-go, .tile:focus-within .tile-go { color: var(--cobalt); text-decoration: underline; }

/* COMPARE NEXT (founder's brief, 25 Sep 2026): after a results list,
   three more categories from the config, never the one on the page. */
.compare-next { margin: 36px 0 8px; }
.compare-next h2 { margin: 0 0 14px; font-size: 20px; font-weight: 600; }
.compare-next-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
@media (max-width: 700px) { .compare-next-grid { grid-template-columns: 1fr; } }
.compare-next-card {
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
  min-height: 44px; padding: 20px 24px; box-sizing: border-box;
  background: var(--card); border: 1px solid #E1E6F0; border-radius: 14px;
  color: #0B1020; font-size: 16px; font-weight: 600; text-decoration: none;
}
.compare-next-card:hover {
  text-decoration: none; color: #0B1020; border-color: #D8E3F3;
  box-shadow: 0 4px 14px rgba(0, 0, 0, .08);
}
.compare-next-card .chev { color: var(--blue); font-size: 24px; line-height: 1; }
.cat-crumb { margin: 0 0 4px; font-size: 14px; color: var(--muted); }
.cat-crumb a { color: var(--muted); }
