/* Thermii journey presentation layer
   ---------------------------------------------------------------
   This file recreates the supplied UX screens without changing the
   calculator's DOM contracts, stored state, calculations, validation,
   pricing, product rules or existing event handlers. */

@media screen {
  /* THE PERIMETER-EXAMPLE DIAGRAM BLENDS INTO ITS CONTAINER (Loz, 2026-09-23:
     "remove the white background — or match it to the container colour so there
     is no white showing"). The diagram is a remote SVG (index.html:1209) of
     near-black #231f20 walls on white #fff fills, and we cannot edit that file.
     `mix-blend-mode: multiply` turns its white transparent (white × backdrop =
     backdrop) while leaving the dark line-work, so the room interior takes the
     page tint behind the stage — no white box, and it matches whatever the
     container is without hard-coding a colour. The `.peri-stage` background is
     the byte-locked #fff from css/thermii.css, so it is cleared here (a
     (0,2,1) `body.thermii-skin` rule beats that (0,1,0) one and loads later) —
     otherwise the image would multiply against white and nothing would change.
     The orange overlay SVG is a sibling, not the img, so it stays vivid. Not a
     print path: this help toggle lives on a question step, never cloned into
     the PDF. */
  body.thermii-skin .peri-stage { background: transparent; }
  body.thermii-skin .peri-stage img { mix-blend-mode: multiply; }

  :root {
    /* The single column every question screen aligns to — headline, subtext,
       tile row and the Continue footer all share these two edges. Raised from
       900 on 2026-08-07 alongside the wider shell: 900 left the tile rows
       short of the room now available, and left the System screen's
       right-ranged Continue floating ~290px past where the content stopped.
       1040 is chosen off tile counts, not off the container — a three-up row
       lands at ~347px per tile, which is the widest these read well at. Two-up
       rows keep their own narrower caps; see thermii-system.css. */
    --journey-content-max: 1040px;
    /* The 1181px cliff, made continuous.

       The shell used to step four values at once at that breakpoint — the rail
       250->300, the workspace gutter 24->36, the sidebar's own padding 18->24
       and the stage header's first column 150->180. Stepping them all at a
       single pixel meant content jumped ~73px NARROWER as the viewport got 1px
       WIDER, so a 1280 laptop got less room than a 1180 one.

       These ramp between 1180 and 1500 instead. Both ends are deliberate:
       clamp's floor reproduces exactly what the old max-width:1180px block set,
       and its ceiling is exactly the base values, so nothing at or below 1180
       and nothing at or above 1500 moves at all. Only the gap where the cliff
       was is different, and across it content now grows monotonically
       (d(content)/d(viewport) = 1 - 0.15625 - 2*0.0375 = +0.77).

       Derived, not guessed — each is value_at_1180 + slope * (100vw - 1180px):

         rail     250->300 over 320px  ->  15.625vw + 65.625px
         gutter    24->36              ->   3.75vw  - 20.25px
         sidebar   18->24              ->   1.875vw -  4.125px
         stage    150->180             ->   9.375vw + 39.375px

       vw counts the scrollbar, so each can sit a pixel or two off its ideal
       mid-ramp value. That is invisible and it does not affect the endpoints. */
    --journey-rail: clamp(250px, calc(15.625vw + 65.625px), 300px);
    --journey-gutter: clamp(24px, calc(3.75vw - 20.25px), 36px);
    --journey-sidebar-pad: clamp(18px, calc(1.875vw - 4.125px), 24px);
    --journey-stage-col: clamp(150px, calc(9.375vw + 39.375px), 180px);

    /* Where a screen's headline sits below the workspace. Question screens
       reach 76 structurally — 24px of #waterPanel / #electricPanel padding plus
       52px of .eq-section padding — but screens 1 and 2 have no panel wrapper
       and were landing at 44 and 56, so the journey started high and stepped
       down twice before settling. They use this directly. If the panel or
       section padding ever changes, change this with it: the headline's
       vertical position is the one thing that must not move between screens. */
    --journey-headline-top: 76px;

    /* Measurement field geometry, from the supplied mockup (2026-08-07). The
       crop carries no page context, so it was scaled off the one dimension
       already known in CSS — the toggle track is 42x24 — which gave a factor of
       exactly 2.0. That scale then predicted the field's HEIGHT at 71px against
       the 73px it actually is, which is the independent check that the reading
       is right rather than wishful.

       At that scale the mockup's field is 416px and its unit group 181px, with
       the labels inset ~28px from the separator and the right edge. 416 is
       raised to 440 for one measured reason: the longest placeholder in either
       journey, "Example: 1.2 (optional)", is 194px, and 416 leaves it 170px.
       440 clears it with ~10px to spare. */
    --journey-field-max: 440px;
    --journey-field-unit: 180px;

    --journey-border: #dfe4ee;
    --journey-muted: #596584;
    --journey-blue-soft: #f6f9ff;
    --journey-selected: #5117f7;
    --journey-green: #74b700; /* aligned to the brand green (Loz, 2026-08-27) */
    --journey-orange: #ff5b18;
  }

  body {
    background:
      radial-gradient(circle at 0 88%, rgba(37, 225, 229, .25), transparent 27%),
      radial-gradient(circle at 100% 78%, rgba(246, 61, 218, .23), transparent 28%),
      radial-gradient(circle at 50% 0, rgba(113, 56, 237, .08), transparent 32%),
      #fff;
  }

  /* The shell keeps its 20px side margins and grows with the viewport, so this
     cap is the only thing that stops it. At 1400 a 1920 monitor threw away
     260px of page each side while the kit list sat at 650px — the screen got
     bigger and the content did not. 1720 is a ceiling rather than a target:
     below 1440 nothing here changes at all, because a max-width only binds
     when the element would otherwise exceed it. */
  .kb-wrapper {
    max-width: 1720px;
    min-height: calc(100dvh - 40px);
  }

  /* Overrides components.css:71, which is inside the byte-locked prefix. */
  .thermii-app-grid {
    grid-template-columns: var(--journey-rail) minmax(0, 1fr);
  }

  .thermii-sidebar {
    padding-right: var(--journey-sidebar-pad);
    padding-left: var(--journey-sidebar-pad);
  }

  .thermii-workspace {
    padding: 0 var(--journey-gutter);
  }

  .thermii-option-copy {
    display: block;
    max-width: 190px;
    margin-top: 10px;
    color: var(--journey-muted);
    font-size: 13px;
    font-weight: var(--thermii-font-weight-light);
    line-height: 1.5;
    text-align: center;
  }

  /* Canonical eight-stage shell, based on 01-version.png. */
  .thermii-stage-header {
    min-height: 126px;
    margin: 0 20px;
    grid-template-columns: var(--journey-stage-col) minmax(0, 1fr);
    gap: 18px;
  }

  /* Vertical rule between the stage summary (Step x of xx / title / bar) and
     the stepper — supplied mockup 2026-08-07. A pseudo-element rather than a
     border: the summary cell is only as tall as its content (~67px) and the
     mockup's line is taller than the content, ~76% of the header's 126px, so
     it is drawn at 96px and centred on the cell. Sits at the cell's right
     edge; the 18px grid gap keeps the stepper off it. Desktop only: below
     821px the byte-locked components.css re-lays the header as a tight
     `auto 1fr` row where a rule would sit hard against the text, and the
     whole mobile band is parked. */
  @media (min-width: 821px) {
    .thermii-stage-summary {
      position: relative;
    }

    .thermii-stage-summary::after {
      content: '';
      position: absolute;
      top: 50%;
      right: 0;
      width: 1px;
      height: 96px;
      background: var(--journey-border);
      transform: translateY(-50%);
    }
  }

  .thermii-stage-title {
    font-size: 22px;
    letter-spacing: -.025em;
  }

  .thermii-stage-progress {
    width: 145px;
    margin-top: 16px;
  }

  .thermii-stepper {
    min-width: 610px;
  }

  .thermii-step-orb {
    width: 44px;
    height: 44px;
  }

  /* ── Stepper icons: REMOVED 2026-08-26 ────────────────────────────────────
     Eight steps x three states of baked artwork used to sit on
     .thermii-step-orb::before, switched by is-active / is-complete, with a
     per-step --step-icon-size to even out their optical weight.

     The 2026-08-25 skin replaced the orb-and-label stepper with the pill bar and
     hides .thermii-step-orb outright (css/thermii-skin.css), so all 24 files were
     unreachable: a click-through of the whole journey fetched six icons and not
     one of them. Loz binned the artwork; these rules went with it rather than
     being left pointing at files that are no longer in the repo.

     The orb's own ring and tick styling stays below — those are drawn in CSS and
     cost nothing. Restoring the artwork means restoring the files from history
     (last present in 6bfc837) and this block with them. */

  /* The completed orb was violet (border #8c62f6 on a #f7f3ff tint, from
     components.css) while the completed artwork is green — green inside a
     violet ring read as a mistake rather than a state. This recolours the ring
     and fill to the icons' own green so the orb reads as one "done" signal.

     It mirrors how the active state is already built: there the ring (#5b20ff)
     and the icon (#5c20ff) are the same colour, so here the ring takes the
     icon's #8cc63f and the fill a light tint of it. The connector leaving a
     completed step is a lighter shade again, chosen to sit at the same visual
     weight the violet had (1.76:1 on white against the violet's 2.18:1).

     components.css owns the violet original; because it is prefix-locked to the
     baseline it cannot be edited in place, so this overrides from a later sheet
     at equal specificity — no !important required. */
  .thermii-step.is-complete .thermii-step-orb {
    border-color: #74b700;
    background: #f6fbee;
  }

  .thermii-step.is-complete:not(:last-child)::after {
    background: #9ed35c;
  }

  /* Completed steps swap the ordinal for a tick. This is what actually carries
     "done": the artwork itself is only 1.94:1 against the orb fill, so leaning
     on it alone was weak — a tick states the state outright.

     The badge is the icons' own #8cc63f with a white tick — a deliberate brand
     choice, made knowing the numbers. White on #8cc63f is 2.05:1, well under
     the 3:1 WCAG asks of a graphical object, and well under the 6.78:1 the
     active badge gets from white-on-#5b20ff. Please do not "correct" it to a
     dark tick without asking; that was tried and rejected on look.

     The stroke is 2px rather than the 1.5px a higher-contrast tick would need,
     which is the one lever that helps legibility without touching the colour.
     If it ever needs to read harder, the options in order of least disruption
     are: deepen the badge fill, or deepen the artwork green project-wide.

     The tick is drawn with two borders on a rotated box rather than set as the
     ✓ glyph this file already uses for the selected-option badges (line ~341).
     Not because ✓ is unsafe — it is well supported, unlike the ⌘/▣/▤ that were
     just removed from these orbs — but because at a 17px badge the glyph's
     weight and baseline shift with the font, while a drawn tick is positioned
     to the pixel and stays crisp at any density. If the two tick styles ever
     want unifying, this is the one to change: the badge is the fussier size.

     font-size:0 hides the digit visually but leaves it in the accessibility
     tree, so screen readers still get the step's ordinal. ::after is positioned
     rather than placed in the grid so the zero-sized digit cannot shift it. */
  .thermii-step.is-complete .thermii-step-number {
    border-color: #74b700;
    background: #74b700;
    font-size: 0;
  }

  .thermii-step.is-complete .thermii-step-number::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 4px;
    height: 7px;
    border: solid #fff;
    border-width: 0 2px 2px 0;
    transform: translate(-50%, -60%) rotate(45deg);
  }

  .thermii-step-label {
    font-size: 9px;
  }

  /* The original progress controls keep their handlers and become the
     lower Back/Continue-style action rail shown in the references. */
  #waterPanel,
  #electricPanel {
    /* Not !important, and not keyed off [data-thermii-screen]: the existing
       engine owns visibility, showing a panel with style.display='' and hiding
       it with 'none' (legacy-core.js:302-303, 887, 1739-1740, 2178). A plain
       rule lets that inline 'none' win, so Back and Restart still hide the
       panel. This was previously `display:flex !important` scoped to
       body[data-thermii-screen^="water-"/"electric-"], which overrode the
       inline 'none'; because js/navigation.js derives that attribute from the
       panel's computed display, the visible screen became self-sustaining and
       the System card and the journey panel rendered at the same time. */
    display: flex;
    min-height: 640px;
    flex-direction: column;
  }

  #waterPanel > .filter-card,
  #electricPanel > .filter-card {
    order: 1;
    width: 100%;
    margin: 0;
    padding: 0;
    overflow: visible;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }

  #wqProgressBar,
  #eqProgressBar {
    position: static;
    z-index: auto;
    order: 20;
    width: auto;
    min-height: 82px;
    /* Bleeds to the workspace edge, so it has to negate the SAME gutter it
       pads by — hard-coding either half would tear the edge mid-ramp. */
    margin: 0 calc(var(--journey-gutter) * -1);
    padding: 14px var(--journey-gutter);
    border-top: 1px solid var(--journey-border);
    border-radius: 0;
    background: #fff;
    box-shadow: none;
  }

  #wqProgressBar .eq-progress-center,
  #eqProgressBar .eq-progress-center,
  #wqProgressBar .eq-restart-btn,
  #eqProgressBar .eq-restart-btn {
    display: none;
  }

  .eq-back-btn {
    min-width: 116px;
    min-height: 48px;
    padding: 10px 18px;
    gap: 10px;
    border: 1px solid #cfd6e4;
    border-radius: 10px;
    background: #fff;
    color: var(--thermii-navy);
    font-size: 14px;
    font-weight: 500;
    box-shadow: none;
  }

  .eq-back-btn:hover {
    border-color: #8a96b2;
    background: #f8faff;
  }

  .eq-back-btn svg {
    width: 20px;
    height: 20px;
  }

  .eq-section {
    min-height: 490px;
    padding: 52px 10px 42px;
  }

  .eq-title,
  .kb-section-title {
    width: 100%;
    max-width: var(--journey-content-max);
    margin-right: auto;
    margin-left: auto;
    color: var(--thermii-navy);
    font-size: clamp(30px, 3vw, 38px);
    font-weight: var(--thermii-font-weight-display);
    letter-spacing: var(--thermii-letter-spacing-question);
    line-height: 1.16;
    text-align: left;
  }

  /* The Version screen was the only headline in the product that was centred,
     and it is the FIRST thing anyone sees: measured indent 137 against 44 for
     the System screen and all eighteen question steps behind it, so the
     journey opened in one place and then moved. components.css centres it from
     inside the byte-locked prefix (`.thermii-version-body{text-align:center}`
     plus `margin: 0 auto` on the h1), so it has to be undone here. */
  /* The 10px is the journey's inner content padding — the question steps use
     it, and thermii-system.css:1102 already pulls #systemTypeCard from 58 down
     to it for exactly this reason. The Version screen pads 0, which is why
     left-ranging alone landed it at 34 against everything else's 44. */
  .thermii-version-body {
    padding-top: var(--journey-headline-top);
    padding-right: 10px;
    padding-left: 10px;
    text-align: left;
  }

  .thermii-version-body h1 {
    max-width: var(--journey-content-max);
    margin-left: 0;
    margin-right: auto;
    letter-spacing: var(--thermii-letter-spacing-question);
  }

  /* The two version cards are this screen's tile row, so they range left with
     the headline like every other row. Keeps its own 820px cap — two cards, so
     the same two-up reasoning as Heat Source: on the shared column they would
     be 520px slabs. */
  .thermii-version-options {
    margin-left: 0;
    margin-right: auto;
  }

  /* ── The unheated step's note IS its subtext (Loz, 2026-09-03) ─────────────
     "Measurements / Step 5 of 9 — the field is very close to the subtext;
     match it to the previous & subsequent screen."

     Measured against those neighbours: wq-step-4bb uses `.eq-subtext`
     (left-ranged, 11px above and 32px below) while this screen's subtext is
     an `.eq-note`. TWO separate causes, found in that order:

     THE SPACING WAS AN INLINE STYLE, and no stylesheet could have beaten it.
     index.html's note carried `style="margin-bottom:16px"` — tuned for its
     original position BETWEEN two input rows on wq-step-4b — so on the screen
     the module moves it to, the field sat 16px below the subtext against the
     neighbours' 32px. The attribute is stripped (inline styles are outside the
     DOM contract, which is what makes that the sanctioned fix here rather than
     `!important`), so this rule governs. Adding a rule alone would have
     changed NOTHING, and measuring is what showed it: the first attempt got
     the alignment and the font size right and still read 16px.

     THE ALIGNMENT WAS A SCOPED SELECTOR. js/thermii-unheated-step.js MOVES
     that `<p>` off wq-step-4b onto a step it builds, `#wq-step-4ba`, and the
     rule that left-ranges it (search `#wq-step-4b .eq-note`) names the step it
     came FROM — so the node was moved out from under its own CSS and fell back
     to thermii.css's centred base. The electric twin, `#eq-step-5`, IS named
     in that rule, which is why only one journey looked misaligned.

     JOINING THE SUBTEXT'S OWN RULE RATHER THAN COPYING ITS NUMBERS is the
     point: "match the previous and subsequent screen" is exactly what a shared
     rule guarantees, and a copy would drift the day the subtext moves. It also
     brings the font size from 14 to 15 — correct, since this element is doing
     the subtext's job on this screen. */
  #wq-step-4ba .eq-note,
  #eq-step-5 .eq-note,
  .eq-subtext,
  .thermii-screen-intro {
    width: 100%;
    max-width: var(--journey-content-max);
    margin: 11px auto 32px;
    color: var(--journey-muted);
    font-size: 15px;
    font-weight: var(--thermii-font-weight-light);
    line-height: 1.55;
    text-align: left;
  }

  #waterPanel .wIntroBanner,
  #electricPanel .wIntroBanner {
    display: none;
  }

  /* Holds a gated question open. The water journey's wMarkAnswered() adds
     `is-answered` to the section, and thermii.css:1904 collapses the question
     to its green answered-bar on that class — correct when the engine navigates
     away in the same breath, but with the navigation deferred behind Continue
     it left the tiles gone and nothing to press. (Electric's markAnswered only
     records state, which is why it never needed this.) thermii-gate.js adds
     this class while a gate is showing and removes it on Continue, so the
     collapse happens at the moment it always did — just after the confirm
     rather than before it. */
  .eq-section.is-answered.thermii-gate-open .eq-question-full {
    display: flex;
  }

  .eq-section.is-answered.thermii-gate-open .eq-answered-bar {
    display: none;
  }

  /* Ranged LEFT, not centred. Centring a capped row against an uncapped column
     means every screen whose cap differs starts in a different place: measured
     across the journey the rows began at +0, +18, +43 and +73 from their own
     left-pinned title. The title is the anchor, so the row shares its edge. */
  .electric-covering-row,
  .filter-options {
    width: 100%;
    max-width: var(--journey-content-max);
    margin: 0 auto 0 0;
    gap: 16px;
  }

  .filter-option {
    position: relative;
    width: auto;
    min-width: 0;
    height: 224px;
    padding: 24px 14px;
    border: 1px solid var(--journey-border);
    border-radius: 14px;
    background: #fff;
    box-shadow: none;
  }

  .filter-option:hover {
    border-color: #9aa7bf;
    background: #fff;
    box-shadow: 0 12px 28px rgba(27, 36, 74, .06);
  }

  .filter-option.is-selected {
    border-color: transparent;
    background:
      linear-gradient(#fff, #fff) padding-box,
      var(--thermii-gradient) border-box;
    box-shadow: 0 12px 28px rgba(100, 54, 229, .09);
  }

  /* Selection affordance, matched to the Version cards on the opening screen.
     ---------------------------------------------------------------------
     `.js-radio-tile` is the one honest selector for "a tile you pick": it sits
     on all 79 of them and on nothing else, always paired with .filter-option,
     .thermostat-card, .system-option or .room-tile-solo. Colour swatches
     (.colour-dot) deliberately do not carry it, so they keep their own small
     treatment rather than sprouting a 28px badge.

     Two things are borrowed from .thermii-version-card (components.css:312).

     The lift — a 2px rise on hover AND on selection. Selection previously
     changed only colour, which is easy to miss when the eye is elsewhere on
     the screen; movement is what actually registers.

     The check circle is drawn even when the tile is unselected — an empty
     grey ring that fills purple with a tick when chosen. That empty ring is
     the point: it advertises "this is a choice" before you commit, and the
     fill then confirms it. The old rule only created the badge on selection,
     so there was nothing to advertise and nothing to animate into. */
  .js-radio-tile {
    position: relative;
    transition:
      transform .2s ease,
      border-color .2s ease,
      box-shadow .2s ease;
  }

  .js-radio-tile:hover {
    transform: translateY(-2px);
    border-color: #bba8fb;
    box-shadow: 0 12px 28px rgba(66, 42, 139, .10);
  }

  .js-radio-tile.is-selected {
    transform: translateY(-2px);
    box-shadow:
      0 14px 30px rgba(113, 56, 237, .12),
      0 0 0 1px rgba(212, 20, 196, .06);
  }

  .js-radio-tile::after {
    content: "✓";
    position: absolute;
    z-index: 2;
    top: 18px;
    right: 18px;
    display: grid;
    place-items: center;
    /* 26px, down from 28 (Loz, 2026-09-18, taking the room tiles as the scale).
       ONE rule for every tile's circle: room, system, install, spacing,
       covering and thermostat cards all carry .js-radio-tile, so this is the
       single place the selection circle/tick is sized. The tick eases to 14px
       to stay in proportion in the smaller ring. */
    width: 26px;
    height: 26px;
    border: 1.5px solid #a7b0c9;
    border-radius: 50%;
    background: #fff;
    color: transparent;
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    transition:
      background .18s ease,
      border-color .18s ease,
      color .18s ease;
  }

  .js-radio-tile.is-selected::after {
    border-color: var(--journey-selected);
    background: var(--journey-selected);
    /* SOFTER than the old 0 4px 12px /.24 (Loz, 2026-09-18): that shadow made
       the filled purple disc bulge and read bigger than the unselected ring's
       own footprint. Tighter and lighter, so the selected state sits at the
       same visual weight as the grey ring it replaces. */
    box-shadow: 0 2px 6px rgba(81, 23, 247, .16);
    color: #fff;
  }

  .filter-option.is-recommended {
    border-color: var(--journey-green);
    background: linear-gradient(135deg, #fff 0%, #f4fce9 100%);
  }

  /* Scoped to .is-selected on purpose. The badge now renders on every tile, so
     an unqualified green background would paint a filled green ring with no
     tick in it on a recommended tile the user has not chosen — which reads as
     "already selected". Green is the confirmation of a recommended choice, not
     a label on the recommendation itself. */
  .filter-option.is-recommended.is-selected::after {
    border-color: var(--journey-green);
    background: var(--journey-green);
  }

  .fo-icon {
    width: 78px;
    height: 78px;
  }

  .fo-icon img {
    width: 74px;
    height: 74px;
    object-fit: contain;
  }

  .fo-label {
    margin-top: 18px;
    color: var(--thermii-navy);
    font-size: 17px;
    font-weight: 500;
    line-height: 1.3;
  }

  .fo-rec-badge {
    margin-top: 12px;
    color: #4da113;
    font-size: 11px;
    text-transform: uppercase;
  }

  .eq-unsure-link,
  .eq-help-link {
    margin-top: 28px;
    color: #5b21fa;
    font-size: 14px;
    font-weight: 500;
  }

  .eq-note {
    max-width: var(--journey-content-max);
    color: var(--journey-muted);
    font-size: 14px;
    line-height: 1.55;
  }

  /* System screen */
  .thermii-main-shell #systemTypeCard {
    min-height: 550px;
    margin: 0;
    /* Top matches the question screens; the 58px sides are overridden to 10px
       in thermii-system.css so the headline lines up horizontally too. */
    padding: var(--journey-headline-top) 58px 44px;
    border: 0;
    border-radius: 0;
    background: transparent;
  }

  #systemTypeCard .kb-section-title {
    max-width: 760px;
    margin: 0 auto;
  }

  #systemTypeCard .thermii-screen-intro {
    max-width: 760px;
  }

  #systemTypeRow {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: stretch;
    max-width: 760px;
    gap: 18px;
    margin: 38px auto 0;
  }

  #systemTypeRow .system-option {
    position: relative;
    width: 100%;
    min-height: 286px;
    margin: 0;
    padding: 38px 22px 26px;
    border: 1px solid var(--journey-border);
    border-radius: 16px;
    background: #fff;
    box-shadow: none;
  }

  /* The lift is repeated here rather than left to the shared `.js-radio-tile`
     rules above, because these two selectors are ID-scoped: `#systemTypeRow …`
     is (1,2,0) and no class-only selector can reach it, whatever the load
     order. Keep the values in step with the `.js-radio-tile` block.

     The gradient border on selection is kept in preference to the Version
     card's flat #6326ff — it is the established selected treatment across the
     journey, and the badge plus lift already carry the state. */
  #systemTypeRow .system-option:hover {
    border-color: #bba8fb;
    background: #fff;
    transform: translateY(-2px);
    box-shadow: 0 12px 28px rgba(66, 42, 139, .10);
  }

  #systemTypeRow .system-option.is-selected {
    border-color: transparent;
    background:
      linear-gradient(#fff, #fff) padding-box,
      var(--thermii-gradient) border-box;
    transform: translateY(-2px);
    box-shadow:
      0 14px 30px rgba(113, 56, 237, .12),
      0 0 0 1px rgba(212, 20, 196, .06);
  }

  #systemTypeRow .so-icon {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    opacity: 0;
    pointer-events: none;
  }

  /* These three icons share an artboard HEIGHT (67.26) but differ in width:
     water 48.45, electric 44.33, unsure 84.34. `contain` scales to whichever
     dimension hits the box first, so in a square 82x82 box the landscape
     "unsure" icon was width-limited and came out 65px tall next to the other
     two at 82px — visibly the odd one out.

     Letting the box span the tile's full content width (~195px) makes height
     the limiting dimension for all three, so they sit on a shared baseline and
     keep the proportions they were drawn at. Sticking with `contain` rather
     than `auto 82px` because it also guards against overflow should an icon
     ever be wider than ~2.4:1. */
  #systemTypeRow .system-option::before {
    content: "";
    display: block;
    width: 100%;
    height: 82px;
    flex: 0 0 82px;
    background-position: center;
    background-repeat: no-repeat;
    background-size: contain;
  }

  #systemTypeRow .system-option[data-value="water"]::before {
    background-image: url("../assets/icons/water-system.svg");
  }

  #systemTypeRow .system-option[data-value="electric"]::before {
    background-image: url("../assets/icons/electric-system.svg");
  }

  #systemTypeRow .system-option[data-value="unsure"]::before {
    background-image: url("../assets/icons/unsure-help-choose.svg");
  }

  /* Footer for the injected Continue control (js/thermii-confirm.js). Mirrors
     .thermii-version-footer, but with its own class because that one carries
     `margin: 0 -48px` tuned to the Version stage's padding, which would pull
     the rule outside this card. */
  .thermii-confirm-footer {
    display: flex;
    justify-content: flex-end;
    margin: 34px 0 0;
    padding: 22px 0 0;
    border-top: 1px solid var(--journey-border);
  }

  @media screen and (max-width: 700px) {
    .thermii-confirm-footer {
      justify-content: stretch;
    }

    .thermii-confirm-footer .thermii-continue {
      width: 100%;
    }
  }

  #systemTypeRow .so-label {
    margin-top: 18px;
    color: var(--thermii-navy);
    font-size: 20px;
    font-weight: 600;
  }

  /* Heat source */
  #wq-step-2 .electric-covering-row {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: 720px;
  }

  #wq-step-2 .filter-option {
    min-height: 292px;
  }

  #wq-step-2 .thermii-option-copy {
    max-width: 220px;
  }

  /* Rooms */
  #wq-step-4 .room-tiles-grid,
  #eq-q1 .room-tiles-grid {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    max-width: 880px;
    gap: 14px;
    padding: 0;
  }

  #wq-step-4 .room-tiles-grid .filter-option,
  #eq-q1 .room-tiles-grid .filter-option {
    min-height: 176px;
    padding: 22px 8px 18px;
  }

  #wq-step-4 .room-tiles-grid .fo-icon,
  #eq-q1 .room-tiles-grid .fo-icon {
    width: 66px;
    height: 66px;
  }

  #wq-step-4 .room-tiles-grid .fo-icon img,
  #eq-q1 .room-tiles-grid .fo-icon img {
    width: 62px;
    height: 62px;
  }

  #wq-step-4 .room-tiles-grid .fo-label,
  #eq-q1 .room-tiles-grid .fo-label {
    margin-top: 11px;
    font-size: 15px;
  }

  /* Room measurements.

     These four steps used to cap the whole .eq-question-full at 680, which
     narrowed the TITLE too — so the four numeric-input screens wrapped their
     headline at a different point from the other fourteen, and the column
     visibly changed width mid-journey. The 680 belongs to the input control,
     not to the question: a number field 966px wide is absurd, a headline 966px
     wide is just the rest of the journey. So the wrapper and the title now
     follow the shared column and only .eq-input-row / .eq-input-wrap keep the
     680. */
  .eq-input-row {
    max-width: var(--journey-field-max);
    margin-right: auto;
    margin-left: 0;
  }

  .eq-input-wrap {
    width: 100%;
    max-width: var(--journey-field-max);
    min-height: 66px;
    border: 2px solid var(--journey-orange);
    border-radius: 12px;
    box-shadow: 0 0 0 3px rgba(255, 91, 24, .08);
  }

  .eq-input {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
    min-height: 62px;
    padding: 0 20px;
    font-size: 17px;
  }

  .area-unit-suffix {
    min-height: 62px;
  }

  .area-unit-btn,
  .wdist-unit-btn {
    min-width: 76px;
    border: 0;
    border-left: 1px solid var(--journey-border);
    background: #f7f7f8;
    color: #777;
    font-size: 16px;
    font-weight: 600;
  }

  .area-unit-btn.au-active,
  .wdist-unit-btn.au-active {
    background: var(--journey-orange);
    color: #fff;
  }

  /* Thermii measurement fields
     Room size, manifold distance and the electric journey's two area steps
     share the restrained brand-gradient frame and inset segmented unit
     control (Loz, 2026-08-28: electric matches water exactly). Functional
     inputs and unit hooks remain unchanged beneath this presentation
     treatment. */
  #wq-step-4b .eq-input-wrap,
  #wq-step-4ba .eq-input-wrap,
  #wq-step-4bb .eq-input-wrap,
  #eq-step-4 .eq-input-wrap,
  #eq-step-5 .eq-input-wrap {
    min-height: 72px;
    padding: 6px;
    overflow: hidden;
    border: 1.5px solid transparent;
    border-radius: 16px;
    background:
      linear-gradient(#fff, #fff) padding-box,
      linear-gradient(
        115deg,
        rgba(20, 157, 232, .48),
        rgba(113, 56, 237, .46) 55%,
        rgba(212, 20, 196, .38)
      ) border-box;
    box-shadow:
      0 12px 30px rgba(16, 23, 53, .07),
      0 2px 5px rgba(16, 23, 53, .04);
    transition:
      background .2s ease,
      box-shadow .2s ease,
      transform .2s ease;
  }

  #wq-step-4b .eq-input-wrap:hover,
  #wq-step-4ba .eq-input-wrap:hover,
  #wq-step-4bb .eq-input-wrap:hover,
  #eq-step-4 .eq-input-wrap:hover,
  #eq-step-5 .eq-input-wrap:hover {
    background:
      linear-gradient(#fff, #fff) padding-box,
      linear-gradient(
        115deg,
        rgba(20, 157, 232, .75),
        rgba(113, 56, 237, .68) 55%,
        rgba(212, 20, 196, .58)
      ) border-box;
    box-shadow:
      0 14px 34px rgba(75, 44, 165, .10),
      0 2px 6px rgba(16, 23, 53, .04);
  }

  #wq-step-4b .eq-input-wrap:focus-within,
  #wq-step-4ba .eq-input-wrap:focus-within,
  #wq-step-4bb .eq-input-wrap:focus-within,
  #eq-step-4 .eq-input-wrap:focus-within,
  #eq-step-5 .eq-input-wrap:focus-within {
    border-color: transparent;
    background:
      linear-gradient(#fff, #fff) padding-box,
      var(--thermii-gradient) border-box;
    box-shadow:
      0 0 0 4px rgba(113, 56, 237, .10),
      0 16px 38px rgba(77, 45, 166, .14);
    transform: translateY(-1px);
  }

  #wq-step-4b .eq-input,
  #wq-step-4ba .eq-input,
  #wq-step-4bb .eq-input,
  #eq-step-4 .eq-input,
  #eq-step-5 .eq-input {
    min-height: 58px;
    padding: 0 22px;
    color: var(--thermii-navy);
    font-size: 17px;
    font-weight: 500;
    letter-spacing: -.01em;
  }

  #wq-step-4b .eq-input::placeholder,
  #wq-step-4ba .eq-input::placeholder,
  #wq-step-4bb .eq-input::placeholder,
  #eq-step-4 .eq-input::placeholder,
  #eq-step-5 .eq-input::placeholder {
    color: #8a94ae;
    font-weight: 400;
    opacity: 1;
  }

  #wq-step-4b .area-unit-suffix,
  #wq-step-4bb .area-unit-suffix {
    align-self: stretch;
    min-height: 58px;
    padding: 4px;
    gap: 4px;
    border: 1px solid #e3e7f0;
    border-radius: 11px;
    background: #f3f5fa;
    box-shadow: inset 0 1px 2px rgba(16, 23, 53, .04);
  }

  #wq-step-4b .area-unit-btn,
  #wq-step-4bb .wdist-unit-btn {
    min-width: 66px;
    height: 100%;
    padding: 0 14px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: #68728e;
    font-size: 15px;
    font-weight: 600;
    transition:
      color .18s ease,
      background .18s ease,
      box-shadow .18s ease,
      transform .18s ease;
  }

  #wq-step-4b .area-unit-btn:hover:not(.au-active),
  #wq-step-4bb .wdist-unit-btn:hover:not(.au-active) {
    background: #fff;
    color: #4e20f5;
    box-shadow: 0 2px 8px rgba(16, 23, 53, .06);
  }

  #wq-step-4b .area-unit-btn.au-active,
  #wq-step-4bb .wdist-unit-btn.au-active {
    background: var(--thermii-gradient);
    box-shadow:
      0 7px 16px rgba(113, 56, 237, .22),
      inset 0 1px 0 rgba(255, 255, 255, .25);
    color: #fff;
    text-shadow: 0 1px 1px rgba(32, 21, 94, .15);
  }

  #wq-step-4b .area-unit-btn:active,
  #wq-step-4bb .wdist-unit-btn:active {
    transform: scale(.97);
  }

  #wq-step-4b .area-unit-btn:focus-visible,
  #wq-step-4bb .wdist-unit-btn:focus-visible {
    outline: 3px solid rgba(20, 157, 232, .30);
    outline-offset: 2px;
  }

  /* ── Unit toggle: m²/ft² and m/ft ────────────────────────────────────────
     One control for all five toggles. Three used the plain segmented pair and
     two (#wq-step-4b, #wq-step-4bb) had a bespoke gradient-segment treatment;
     both are replaced here so the app has a single unit control.

     The markup is two buttons, each owning its own inline onclick, and that
     cannot change — so the switch is drawn as pseudo-elements on the container
     and both carry pointer-events:none. Clicks fall straight through to
     whichever button sits underneath: the left half selects the first unit,
     the right half the second. That keeps every hit target real, roughly
     doubles their size, and leaves two properly-labelled buttons for screen
     readers rather than a switch that reads as one control.

     It therefore looks like a switch but behaves as a segmented control —
     tapping the already-active side is a no-op rather than a flip. Deliberate:
     direct selection beats blind toggling for a unit choice, and it needs no
     JavaScript, so the inline handlers stay the sole owners of state.

     The track is blue in both positions rather than colouring on "on". Neither
     unit is an off state, so a grey/blue pair would imply one is unset.

     Flat --thermii-blue, deliberately NOT --thermii-gradient. The knob covers
     43% of a 42px track and which 43% changes with position, so a gradient
     shows a different slice per state — visible range is 0-7% + 50-100% on the
     first unit against 0-50% + 93-100% on the second. The control then appears
     to change colour when the unit changes, signalling something that has not
     happened. At 42px each gradient transition also gets only 21px, which
     bands rather than reads as the brand mark. This was tried and rejected on
     both counts; please don't reinstate it without re-testing at size.

     Placed after the #wq-step-4b/4bb rules above so equal-specificity
     selectors win on source order; the ID-scoped ones are repeated where the
     originals used them. */
  /* The separator IS this element's border-left, so how wide the unit group is
     decides where the separator sits. At the old 113px it was pinned against
     the right edge, 82% across the field; the mockup puts it at 56%, which at
     a 440px field is a 180px group. Nothing about the switch itself changes —
     the track and knob were already right. */
  .area-unit-suffix,
  #wq-step-4b .area-unit-suffix,
  #wq-step-4bb .area-unit-suffix {
    position: relative;
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    min-width: var(--journey-field-unit);
    gap: 0;
    padding: 0;
    border: 0;
    border-left: 1px solid var(--journey-border);
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }

  .area-unit-btn,
  .wdist-unit-btn,
  #wq-step-4b .area-unit-btn,
  #wq-step-4bb .wdist-unit-btn {
    flex: 1 1 0;
    min-width: 56px;
    height: 100%;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--journey-muted);
    font-size: 15px;
    font-weight: 600;
    box-shadow: none;
    text-shadow: none;
    transition: color .18s ease;
  }

  /* Labels sit at the outer edges; the gap between them is the switch. The 28px
     insets are the mockup's, measured from the separator and from the field's
     right edge — with the group at 180px that leaves the track ~23px clear of
     each label, which is what the mockup shows. */
  .area-unit-suffix > :first-child {
    justify-content: flex-start;
    padding: 0 0 0 28px;
  }

  .area-unit-suffix > :last-child {
    justify-content: flex-end;
    padding: 0 28px 0 0;
  }

  /* Repeated ID-scoped, because the two rules above do not reach the water
     screens. The button reset earlier in this file matches them through
     `#wq-step-4b .area-unit-btn` / `#wq-step-4bb .wdist-unit-btn` — (1,1,0) —
     and sets `padding: 0`, which outranks a (0,2,0) selector no matter the
     source order. The electric fields carry no ID-scoped rule, so they took the
     insets while the water ones sat at 0: m² hugging the separator and ft²
     hugging the field edge, on the exact screen the design was drawn for.
     `:first-child` counts in the class column, so these land at (1,2,0) and win
     outright. justify-content needs no repeat — the reset never sets it. */
  #wq-step-4b .area-unit-suffix > :first-child,
  #wq-step-4bb .area-unit-suffix > :first-child {
    padding: 0 0 0 28px;
  }

  #wq-step-4b .area-unit-suffix > :last-child,
  #wq-step-4bb .area-unit-suffix > :last-child {
    padding: 0 28px 0 0;
  }

  .area-unit-btn.au-active,
  .wdist-unit-btn.au-active,
  #wq-step-4b .area-unit-btn.au-active,
  #wq-step-4bb .wdist-unit-btn.au-active {
    background: transparent;
    box-shadow: none;
    color: var(--thermii-navy);
    text-shadow: none;
  }

  .area-unit-btn:hover:not(.au-active),
  .wdist-unit-btn:hover:not(.au-active),
  #wq-step-4b .area-unit-btn:hover:not(.au-active),
  #wq-step-4bb .wdist-unit-btn:hover:not(.au-active) {
    background: transparent;
    box-shadow: none;
    color: var(--thermii-navy);
  }

  /* Track. */
  .area-unit-suffix::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 42px;
    height: 24px;
    margin: -12px 0 0 -21px;
    border-radius: 999px;
    /* Two hundredths under the line, so it moves. --thermii-blue is #149de8,
       which measures 2.99:1 against the white field — and WCAG 1.4.11 asks 3:1
       of a graphic you need in order to read a control's state, which is
       exactly what this track is. #1391da is 3.45:1 and is the same blue to
       look at. Set here rather than on the token: --thermii-blue is the FIRST
       STOP of --thermii-gradient (tokens.css:19), so moving it would restain
       every gradient border in the product to fix one pill. */
    background: #1391da;
    pointer-events: none;
  }

  /* Knob. Travel is (track 42 - inset 3 - knob 18 - inset 3) / 2 = 9px each
     way from centre, so the two positions sit flush inside the track. */
  .area-unit-suffix::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 18px;
    height: 18px;
    margin: -9px 0 0 -9px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(16, 23, 53, .28);
    pointer-events: none;
    transform: translateX(-9px);
    transition: transform .22s cubic-bezier(.22, 1, .36, 1);
  }

  .area-unit-suffix:has(> :last-child.au-active)::after {
    transform: translateX(9px);
  }

  /* ── Disclaimer and copyright: last two screens only ─────────────────────
     render() in the locked engine (legacy-core.js:2088) shows the disclaimer
     as soon as state.covering is set, so on the electric journey it appeared
     from the floor-covering question onwards — most of the way through. It
     belongs on the review and kit screens, where the numbers it qualifies
     actually are.

     !important is required on both rules, not preferred: the engine writes
     display straight onto the element's style attribute from a dozen call
     sites, and a plain declaration cannot beat an inline one.

     The exemption below is the important part. downloadKitPdf clones
     .kb-disclaimer into #thermiiPdfRoot and forces it visible inline
     (legacy-core.js:6142 and 6465) — but html2canvas rasterises screen CSS, so
     an !important hide here would strip the disclaimer out of the generated
     quote. That is a legal notice on a commercial document, so the PDF root is
     exempted explicitly rather than relying on the screen marker happening to
     be *-kit at the moment of generation.

     window.print() is unaffected either way: these rules are inside this file's
     top-level `@media screen` block (opened at line 7, closed at 1965), so the
     print stylesheet never sees them.

     CORRECTED 2026-09-02: this used to say "journey.css is wholly inside
     @media screen", and that is NO LONGER TRUE — the scope-A block at the end
     of the file (2247 to the end) sits at brace depth 0, outside every media
     query. The claim above is now about these rules' POSITION, not the file.
     Anything print-sensitive must go inside the screen block, not at the end
     of the file. */
  .kb-disclaimer {
    display: none !important;
  }

  body[data-thermii-screen$="-summary"] .kb-disclaimer,
  body[data-thermii-screen$="-kit"] .kb-disclaimer {
    display: block !important;
  }

  /* THE PDF CLONE IS PINNED TO WHAT IT HAS TODAY, which is what lets the two
     screen changes below happen at all. downloadKitPdf clones .kb-disclaimer
     into a 760px #thermiiPdfRoot and html2canvas rasterises SCREEN css, so
     every screen rule for this block lands on the customer's quote — and this
     block's HEIGHT is what the page-break spacers and the pass-1-to-pass-2
     drift model measure (legacy-core.js:5564/5578). Measured on a real clone
     before touching anything: 736px wide, 289px tall, margin-left 24px (the
     gutter at that viewport), margin-right 0, max-width 1240. Restating those
     three here is (1,1,0) against the kit marker's (0,2,0), so the clone keeps
     them while the screen's own geometry moves.

     A LATENT ODDITY CAME OUT OF THAT MEASUREMENT and is recorded rather than
     fixed: the clone's width tracks the VIEWPORT's gutter clamp, because the
     kit-screen rule adds a margin that the 760px root then pays for — 724px on
     Loz's 1850, 736px at 1024. So the printed disclaimer's height already
     depends on the window width at the moment the PDF is generated. Correcting
     it moves the page breaks on a document customers keep, so it belongs with
     Loz's PDF list, not with a screen alignment. */
  #thermiiPdfRoot .kb-disclaimer {
    display: block !important;
    max-width: 1240px;
    margin-left: var(--journey-gutter);
    margin-right: 0;
  }

  #thermiiPdfRoot .kb-disclaimer p {
    font-weight: 400;
  }

  /* DISCLAIMER RANGED LEFT (Loz, 2026-09-01). css/thermii.css caps it at 1250
     and centres it with `margin: 40px auto 0`, which is a DIFFERENT column from
     everything above it — at 1900 it started at x=325 while the CAD panel and
     the CTA started at 117, so the small print sat visibly indented from the
     screen it belongs to. Capped to 1240 like the other blocks, so its left
     edge lands on the same x as the summary rows, and text-align is stated
     rather than inherited so nothing above can centre it later. */
  /* The disclaimer is a child of .kb-wrapper, NOT of .thermii-workspace, so the
     structural hide behind the welcome screen cannot reach it — press Home from
     the review or kit screen and the small print would still be sitting under
     the tiles. Ties on specificity with the `-summary` / `-kit` marker rules
     above (0,2,1 each) and wins by coming later in the file. */
  body.thermii-welcome-active .kb-disclaimer {
    display: none !important;
  }

  .kb-disclaimer {
    max-width: 1240px;
    /* PULLED UP UNDER THE CTA (Loz, 2026-09-01). It was 82px below the View
       Recommended Kit button and 1px off the container's bottom edge — a long
       reach down to it, then no air underneath. css/thermii.css spends 40px of
       margin and 28px of padding getting there; 12px of padding is enough to
       read as a separate footer note when the type is already 12px grey against
       a 17px CTA.

       AND IT NEEDED BOTTOM SPACE, not just less above: the container is
       content-height, so shrinking the gap above only shortens the container and
       leaves the small print flush on its edge exactly as before. The 24px is
       what actually answers "very close to the bottom".

       THE WHITE LINE IS GONE. `border-top: 1px solid #ffffff` on a #f2f4f8
       ground is about 1,05:1 — invisible, so it separated nothing while costing
       a pixel of the gap. The size and colour drop from the CTA does that work.
       Print is untouched: this rule is inside the top-level @media screen block
       (see the correction at line 1233 — the FILE is no longer wholly inside
       it, but this rule is), so thermii.css's own `border-top: 1px solid #ddd`
       still rules the PDF, where a visible hairline on a document somebody
       keeps is right. */
    margin: 0 auto;
    padding-top: 12px;
    padding-bottom: 24px;
    border-top: 0;
    text-align: left;
  }

  .kb-disclaimer p,
  .kb-disclaimer .kb-disclaimer-title {
    text-align: left;
  }

  /* ── AND IT READS A SHADE LIGHTER (Loz, 2026-09-08) ───────────────────────
     "Make the 'Disclaimer' text block lighter grey (on all screens where it
     appears)."

     ONE GREY FOR THE WHOLE BLOCK, AND NOT A NEW ONE. `--journey-muted` is the
     colour the reskin already gives every piece of secondary text, so the
     small print joins the app's muted voice rather than adding a fourth grey
     to it. css/thermii.css had the body at #555 and the title DARKER still at
     #3a3a3a; the title keeps its rank on 15px/600, which is what separated it
     anyway, so it does not also need a heavier ink.

     LIGHTER, BUT STILL LEGIBLE, and that decided the value. Measured against
     the page's real ground — **#f4f5f9**, css/thermii-skin.css:25, not the
     #f2f4f8 the comment above this one quotes — #596584 gives **5.33:1** where
     #555 gives 6.84: a real step lighter, still clear of WCAG 1.4.3 AA's 4.5
     for 12px body text. The two obvious alternatives do not survive the same
     test: #6b7280, which would match `.wkit-heat-disc`'s small print exactly,
     lands at **4.44:1** here (it passes only on the heat panel's white), and
     #888 — the grey the printed quote uses — is **3.25:1**. This is a legal
     notice; it can be quiet, but it has to be readable.

     PAPER IS UNTOUCHED. css/thermii.css:2469 sets `#888 !important` inside
     `@media print`, and legacy-core.js:5375 re-emits the print rules scoped
     and !important over the clone, so the printed quote keeps its own grey. */
  .kb-disclaimer p,
  .kb-disclaimer .kb-disclaimer-title {
    color: var(--journey-muted);
  }

  /* THE PARAGRAPHS BREAK CLEARLY (Loz, 2026-09-21) — after "proceeding",
     "solution" and "installation". On the summary/kit screens .kb-disclaimer is
     display:block (above), so thermii.css's flex `gap:10px` is inert and the
     paragraphs ran together as one block. A margin restores the break under
     block flow, and reaches the PDF clone too (it renders in screen media). */
  body.thermii-skin .kb-disclaimer p {
    margin-bottom: 12px;
  }

  /* ── The small print is smaller (Loz, 2026-09-02) ─────────────────────────

     "Reduce the text size of the Disclaimer small print and copyright small
     print on 'Your Selections' & the 'Summary' screens."

     BOTH OF HIS ITEMS ARE ONE SELECTOR. There is a single .kb-disclaimer node
     in the document (index.html:1899) and the © copyright paragraph is simply
     its last <p> — same 12px as the disclaimer body from css/thermii.css:1646.
     So `.kb-disclaimer p` covers the disclaimer AND the copyright, and no
     separate rule is wanted. If they ever need to differ,
     `.kb-disclaimer > p:last-child` is the copyright alone.

     IT LANDS ON FOUR SCREENS, NOT TWO. That one node is revealed by
     `body[data-thermii-screen$="-summary"]` AND `$="-kit"` (see :1185 above),
     so both journeys' review screens and both kit screens get it. That is the
     right answer for the two screens Loz named — "Your Selections" is the
     review and "Your Kit Summary" the kit — but it cannot be narrowed to one
     without scoping on those markers, which is noted here so nobody assumes
     the rule is screen-specific.

     The "Disclaimer" label goes too, 15px → 13px. It was the LOUDEST thing in
     the footer — bigger than the body it introduces — so dropping it is what
     actually makes the block read as small print. 13px is a house size
     (thermii.css:1630) and keeps a clear step over an 11px body.

     11px, NOT LOWER, and that is deliberate: this is a legal notice on a screen
     a customer buys from. There is one step left to 10px if Loz wants it
     quieter still. Below that is not offered on desktop.

     (An earlier draft of this comment justified 10px as matching ".kit-sku /
     .kit-detail, live on the kit screen above this block". That was WRONG and
     is corrected rather than deleted: .kit-sku is 13px on screen
     (thermii.css:1806), 11px under 700px (:2160) and only 10px in @media print
     (:2411). The 10px it cited was a print size, so it was not the screen
     precedent it claimed to be.)

     NO !important AND NO ID, WHICH IS WHAT KEEPS THE PDF OUT OF IT. These are
     (0,1,1) and (0,2,0), tying with thermii.css:1646/:1652 and winning on load
     order alone. The generated quote is unaffected because downloadKitPdf
     injects _thermiiScopedPrintCss('#thermiiPdfRoot') (legacy-core.js:5375),
     which re-emits ONLY @media print rules, scoped and !important — so
     thermii.css:2469's `#thermiiPdfRoot .kb-disclaimer p{font-size:9px
     !important}` still governs the document. That matters beyond tidiness:
     .kb-disclaimer drives the page-break spacers (:5578) and its measured
     bottom sets the pass-1→pass-2 drift model (:5564), with a +60px tail guard
     that exists precisely because "the disclaimer's © copyright lines sit at
     the very bottom" (:5598). Add !important at ID strength here and you start
     moving page breaks on a document somebody keeps.

     MOBILE IS LEFT ALONE — BUT IT TAKES A RULE TO DO IT, which is the trap
     here and cost a correction. These declarations tie css/thermii.css on
     specificity and win on load order, and a MEDIA QUERY ADDS NO SPECIFICITY,
     so they also beat thermii.css:2237-2238's small-screen step: measured at
     375px, the small print went UP, 9px → 11px. The pre-existing sizes are
     therefore restated in this file's own `@media screen and (max-width:
     700px)` block (search .kb-disclaimer there). Mobile stays 9px/11px.

     That 9px is below the desktop floor argued for above, on the same legal
     notice. It is flagged for Loz rather than raised here, because raising it
     is a change he has not asked for. */
  /* ONE WEIGHT THINNER (Loz, 2026-09-17: "make the body copy one weight
     thinner"). css/thermii.css sets these paragraphs at 400 and is byte-locked,
     so the step down is taken here — 300, `--thermii-font-weight-light`, the
     token every subtext in the app already uses, rather than a bare number.

     IT IS A REAL WEIGHT, NOT A SYNTHESISED ONE: index.html loads Sora as a
     variable face at `wght@300..700`, so 300 is an instance the file actually
     carries and the browser has nothing to fake.

     THE TITLE KEEPS ITS 600 — he asked for the body copy, and the heading is
     what separates this block from the page. And PAPER KEEPS 400 both ways:
     the clone is pinned above, and the print block never sets a weight, so
     window.print() is untouched. A lighter face is narrower, so letting this
     reach the clone would re-wrap the notice and move the PDF's page breaks. */
  .kb-disclaimer p {
    font-size: 11px;
    font-weight: var(--thermii-font-weight-light);
  }

  .kb-disclaimer .kb-disclaimer-title {
    font-size: 13px;
  }

  /* ── Restart on the kit screen ───────────────────────────────────────────
     The header already carries a Restart, so the one sitting above the room
     card is a second route to the same destructive action. Hidden rather than
     removed: its onclick is part of the locked DOM contract, and deleting the
     button would drop the inline-handler count below the 240 the parity check
     requires. Both journeys' kit screens carry one; both go.

     The Back button and the flex spacer beside it are untouched, so the nav
     keeps its layout. */
  #kitNav .eq-restart-btn,
  #wKitNav .eq-restart-btn {
    display: none;
  }

  /* Ranged left with the question and subtext above them, per the supplied
     Measurements design. All three of these ALREADY said text-align:left — what
     centred them was `margin: … auto`, which centres the BOX and then
     left-aligns the text inside it. Measured before the change: the question
     and its subtext began at x=390, while "Unheated Area" began at 789, its
     note at 533 and the help link at 756. So the left margin goes to 0 and only
     the right stays auto.

     Extended to the electric equivalents. eq-step-5 carries the same note and
     both eq-step-4 and eq-step-5 carry the same help link, and there is no
     reason for the two journeys to differ on this. */
  #wq-step-4b .wq-unheated-title {
    max-width: 680px;
    margin: 42px auto 8px 0 !important;
    font-size: 22px !important;
    font-weight: var(--thermii-font-weight-display-secondary) !important;
    text-align: left !important;
  }

  /* A SUBTEXT FOLLOWED BY A NOTE READS AS ONE BLOCK (Loz, 2026-09-28: "close
     the gap on these sentences" — the heated-area and insulation screens). The
     subtext's 32px foot is for the space before the tiles; with a note under
     it, that space moves below the note and the two sit 6px apart. The note's
     top is zeroed at (1,2,0), over the shared rule's #eq-step-5 (1,1,0) 11px. */
  .eq-subtext:has(+ .eq-note) { margin-bottom: 6px; }
  /* ...and two subtexts in a row, the same way (Loz, 2026-09-28: the manifold
     distance screen's two lines, which were one paragraph split by a <br>,
     now sit at the heated-area screen's pitch) */
  .eq-subtext:has(+ .eq-subtext) { margin-bottom: 6px; }
  .eq-subtext + .eq-subtext { margin-top: 0; }
  #eq-step-5 .eq-subtext + .eq-note,
  #eq-step-7 .eq-subtext + .eq-note { margin-top: 0; }

  #wq-step-4b .eq-note,
  #eq-step-5 .eq-note {
    max-width: 680px;
    margin-right: auto;
    margin-left: 0;
    text-align: left;
  }

  #wq-step-4b .eq-help-link,
  #wq-step-4bb .eq-help-link,
  #eq-step-4 .eq-help-link,
  #eq-step-5 .eq-help-link {
    display: block;
    max-width: 680px;
    margin: 28px auto 0 0;
    text-align: left;
  }

  /* THE THERMOSTAT CARRIED OVER (js/thermii-tstat-carry.js; Loz 2026-09-14,
     restyled 2026-09-15 from the prototype he approved on the live screen):
     ONE QUIET LINE above the carousel — a purple tick mark, "Same as the
     Kitchen" in the selection purple, the sentence in the body colour. No box:
     the first cut wore the Insights panel's lilac and read as one more
     skippable help panel, and this is STATE, not a tip. The card carries a
     matching chip, and the carousel pages to it. Removed on the first click. */
  .thermii-tstat-carry {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    width: 100%;
    max-width: none;
    box-sizing: border-box;
    margin: 0 0 18px;
    padding: 0;
    background: none;
    border: 0;
    font-family: 'Poppins', sans-serif;
    font-size: 14.5px;
    line-height: 1.5;
    color: #333a57;
    text-align: left;
  }
  /* mark, lead-in and chip are ONE colour — the tick disc's own selection
     purple (--journey-selected), not the Insights purple */
  .thermii-tstat-carry strong {
    font-weight: 600;
    color: var(--journey-selected);
  }
  .thermii-tstat-carry-mark {
    flex: 0 0 20px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--journey-selected);
    box-shadow: 0 3px 8px rgba(81, 23, 247, .22);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-top: 1px;
  }
  .thermii-tstat-carry-mark svg { display: block; }
  /* the chip sits where the skin puts the Wireless pill (top-left), so on a
     wireless card the pill steps down a row; pointer-events:none so a tap on
     the chip is a tap on the card */
  .thermostat-card.thermii-tstat-carried { position: relative; }
  .thermii-tstat-carry-chip {
    position: absolute;
    top: 8px;
    left: 8px;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 4px 9px 4px 7px;
    border-radius: 999px;
    background: var(--journey-selected);
    color: #fff;
    font: 600 10.5px/1.2 'Poppins', sans-serif;
    letter-spacing: .3px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .14);
    pointer-events: none;
    white-space: nowrap;
  }
  .thermii-tstat-carry-chip svg { display: block; }
  .thermii-tstat-carried .tstat-wireless-badge { top: 36px; }

  /* Five-card material/floor screens */
  #wq-step-4flr .electric-covering-row,
  #wq-step-4c .electric-covering-row,
  #eq-q2 .electric-covering-row {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }

  #wq-step-4flr .filter-option,
  #wq-step-4c .filter-option,
  #eq-q2 .filter-option {
    min-height: 235px;
    padding-right: 10px;
    padding-left: 10px;
  }

  /* Installation */
  #wq-step-4d .electric-covering-row {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    max-width: 650px;
  }

  #wq-step-4d .filter-option {
    height: 200px;
  }

  /* Pipe spacing */
  #wq-step-4ps .electric-covering-row {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    max-width: 820px;
  }

  #wq-step-4ps .filter-option {
    min-height: 242px;
  }

  #wq-step-4ps .filter-option.is-recommended .fo-label {
    color: var(--journey-green);
    font-weight: 600;
  }

  /* Floor covering */
  #wq-step-4fc .electric-covering-row {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    max-width: 930px;
    gap: 10px;
  }

  #wq-step-4fc .filter-option {
    min-height: 190px;
    padding: 22px 8px 16px;
  }

  #wq-step-4fc .fo-icon {
    width: 58px;
    height: 58px;
  }

  #wq-step-4fc .fo-icon img {
    width: 56px;
    height: 56px;
  }

  #wq-step-4fc .fo-label {
    font-size: 14px;
  }

  /* Thermostats */
  #wq-step-4e .eq-title,
  #eq-q6 .eq-title {
    text-align: center;
  }

  #wTstatViewport.tstat-carousel-viewport,
  #electricPanel .tstat-carousel-viewport {
    max-width: 970px;
    padding: 10px;
  }

  /* thermii.css:1268 pins the water grid nowrap/left through #wTstatViewport,
     an id the runtime-built electric carousel (js/thermii-skin.js) cannot
     carry — the file is byte-locked, so the rule is replicated for the
     electric panel rather than extended in place. */
  #electricPanel .tstat-carousel-viewport .tstat-grid-5 {
    flex-wrap: nowrap;
    justify-content: flex-start;
    margin: 0;
    max-width: none;
    width: max-content;
  }

  .tstat-carousel .tstat-grid-5 .thermostat-card,
  .thermostat-card {
    position: relative;
    flex-basis: 250px;
    width: 250px;
    max-width: 250px;
    min-height: 400px;
    border: 1px solid var(--journey-border);
    border-radius: 14px;
    box-shadow: none;
  }

  .thermostat-card:hover {
    border-color: #9aa7bf;
    background: #fff;
    box-shadow: 0 12px 28px rgba(27, 36, 74, .06);
  }

  .thermostat-card.is-selected {
    border-color: transparent;
    background:
      linear-gradient(#fff, #fff) padding-box,
      var(--thermii-gradient) border-box;
  }

  .thermostat-img img {
    height: 180px;
    padding: 14px;
    object-fit: contain;
  }

  .tstat-brand-logo {
    padding-top: 4px;
  }

  .thermostat-name {
    padding-top: 12px;
    color: var(--thermii-navy);
    font-size: 17px;
    font-weight: 500;
  }

  .thermostat-desc {
    color: #60677a;
    font-size: 11px;
  }

  .thermostat-price {
    color: #b51f22;
    font-size: 19px;
  }

  /* ── The carousel says there is more, 2026-08-25 ──────────────────────────

     WHY IT DID NOT BEFORE, and it was arithmetic rather than styling. The
     viewport is 970px less 2x10px padding = 950px usable, and the cards were
     218px on a 16px gap, so the pitch was 234px and 950/234 = 4.06 CARDS. The
     fifth card was clipped by about two pixels, which reads as a row that
     simply ends at four — the one width where nothing peeks. The scrollbar is
     hidden too (thermii.css sets scrollbar-width:none), so with seven
     thermostats in there, three were invisible with no hint they existed.

     Cards are now 250px, so the pitch is 266px and 950/266 = 3.57 CARDS:
     three whole ones and a decisive half. A partial card is the strongest
     "there is more" signal available and it costs no extra markup. Widening
     the cards and fixing the affordance are therefore the SAME change, which
     is why the card width above and this block must move together — pick a
     width that lands near .5 of a card, never near a whole one.

     DO NOT widen the viewport to buy back the fourth card. The neighbouring
     journey content is 930px (see .eq-* above) and a wider carousel visibly
     bulges out of the column. */

  .tstat-carousel {
    position: relative;
  }

  /* Scroll shadows that hide themselves at the ends, with NO JavaScript and no
     scroll listener. The two `local` white gradients are attached to the
     content, so they travel with it; the two fixed shadows sit on the
     viewport. At the left end the local gradient covers the left shadow, and
     at the right end it covers the right one — so a shadow is only ever
     visible on a side that actually has more content. Preferred over
     animation-timeline: scroll(), which needs a very recent Safari. */
  #wTstatViewport.tstat-carousel-viewport,
  #electricPanel .tstat-carousel-viewport {
    background:
      linear-gradient(to right, #fff 30%, rgba(255, 255, 255, 0)) left center / 44px 100% no-repeat local,
      linear-gradient(to left, #fff 30%, rgba(255, 255, 255, 0)) right center / 44px 100% no-repeat local,
      linear-gradient(to right, rgba(16, 24, 40, 0.10), rgba(16, 24, 40, 0)) left center / 26px 100% no-repeat scroll,
      linear-gradient(to left, rgba(16, 24, 40, 0.10), rgba(16, 24, 40, 0)) right center / 26px 100% no-repeat scroll;
  }

  /* Arrows BESIDE the row, not under it. Below the viewport they read as
     pagination for the whole step; overlaid on the row's own edges they read
     as "this row scrolls", which is the thing being communicated.

     The nav keeps its box but goes out of flow, so it contributes no gap to
     .tstat-carousel's column and inset:0 makes it exactly the viewport's box —
     which is what `top:50%` then centres against. pointer-events are dropped
     on the overlay and restored on the buttons, or the invisible layer would
     swallow every card click. */
  .tstat-carousel-nav {
    position: absolute;
    inset: 0;
    gap: 0;
    pointer-events: none;
    z-index: 3;
  }

  .tstat-carousel-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: auto;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 2px 10px rgba(16, 24, 40, 0.16);
  }

  /* thermii.css is byte-locked and sets `transform: scale(1.08)` on hover,
     which would drop the translateY and make the arrow jump half its height.
     Both parts have to be restated together. */
  .tstat-carousel-arrow:hover {
    transform: translateY(-50%) scale(1.08);
    background: #fff;
    box-shadow: 0 3px 14px rgba(16, 24, 40, 0.22);
  }

  .tstat-carousel-arrow-left {
    left: -6px;
  }

  .tstat-carousel-arrow-right {
    right: -6px;
  }

  /* Arrow states, driven by js/thermii-tstat-carousel.js.

     ADDITIVE ON PURPOSE. No class means both arrows show, which is exactly the
     behaviour before that module existed — so if it fails to load, or is removed,
     the carousel is no worse than it was rather than losing its arrows. The
     module also sets `disabled`, which is what actually blocks the click and
     drops the button from the tab order; this is only the appearance. */
  .tstat-carousel-arrow {
    transition: opacity 0.18s ease, transform 0.15s ease, box-shadow 0.15s ease;
  }

  .tstat-carousel.is-at-start .tstat-carousel-arrow-left,
  .tstat-carousel.is-at-end .tstat-carousel-arrow-right,
  .tstat-carousel.is-not-scrollable .tstat-carousel-arrow {
    opacity: 0;
    pointer-events: none;
  }

  /* thermii.css is byte-locked and gives every arrow `transform: scale(1.08)` on
     hover. A disabled arrow is at opacity 0 but the pointer can still be over
     where it was mid-fade, so the hover growth has to be cancelled too or it
     twitches under the cursor. */
  .tstat-carousel-arrow:disabled,
  .tstat-carousel-arrow:disabled:hover {
    transform: translateY(-50%);
    box-shadow: 0 2px 10px rgba(16, 24, 40, 0.16);
    cursor: default;
  }

  .tstat-carousel-arrow img {
    width: 22px;
    height: 22px;
  }

  /* Selection review */
  body[data-thermii-screen="water-summary"] .thermii-workspace,
  body[data-thermii-screen="electric-summary"] .thermii-workspace {
    padding-bottom: 14px;
  }

  /* ONE CENTRED COLUMN FOR THE WHOLE REVIEW SCREEN (Loz, 2026-09-01:
     "Center ALL the Content within the container").

     `margin: 0` here was the fault. Every .kb-card is capped at 1280px by the
     skin and centred with `margin: 0 auto`; this rule overrode the side margins
     and LEFT-PINNED the review card alone. Below ~1440 the cap never binds so
     it looked right, but at Loz's 1900 the card sat at x=97 and stopped at
     1377, leaving 460px of dead space down the right — while the CAD panel and
     the CTA beneath it, which carry no cap, ran on to 1783. Three different
     right edges on one screen.

     Restoring `auto` centres the card; the CAD panel, the CTA and the
     disclaimer below are capped to match it so all four share one left edge.
     The insets are chosen to land on the ROWS' edge, not the card's: the card
     carries 20px of side padding, so a 1240 cap on the blocks outside it puts
     every left edge on the same x.

     `width: 100%` IS LOAD-BEARING AND MUST COME FIRST. The review card and the
     CAD panel are FLEX ITEMS of their journey panel, and auto side margins on a
     flex item make it SHRINK TO FIT — with the cap alone the card collapsed to
     468px and the rows to 428px. Fill, then cap, then centre; the same order
     css/thermii-skin.css records for the question screens. */
  #wq-summary,
  #eq-summary {
    width: 100%;
    margin: 0 auto;
    /* SIDE PADDING 20 -> 0 (Loz, 2026-09-09): his redesigned card runs to the
       panel's own edges, so the white card is 1280 rather than 1240. The
       comment above records that the 20px and the 1240 caps on the CAD panel,
       the CTA and the disclaimer are ONE decision — all four sharing a left
       edge — so all four move together, and his mock confirms it: the gradient
       "View Recommended Kit" bar measures 1283 there against the card's 1280.
       The disclaimer's widening is scoped to the -summary screens, because its
       1240 cap is deliberately pinned for the KIT screen's PDF (see :3587). */
    padding: 30px 0 14px !important;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
  }

  .eq-summary-title {
    margin-bottom: 4px;
    color: var(--thermii-navy);
    font-size: clamp(34px, 4vw, 42px);
    font-weight: var(--thermii-font-weight-display);
    letter-spacing: -.04em;
  }

  /* 26 BELOW THE SUB-LINE, up from 12 (Loz, 2026-09-10: "Add more space below
     the subtext here"). Measured before and after rather than nudged by feel:
     12px box-to-card read 14.9px from the sub-line's INK to the card's top
     border, because the 14px line box carries ~2.9px of descender space below
     the glyphs — which is why it looked tighter than its number.

     26 IS HIS OWN FIGURE FOR THIS, not a new one: it is the gap he asked for
     between this card and the CAD panel below it a day earlier ("moved further
     away"), so the screen separates a panel from what sits beside it by one
     value in both places rather than two that happen to look similar.

     PRINT IS UNTOUCHED, checked in the parser and not assumed: this rule is
     inside the top-level `@media screen`, so paper keeps
     css/thermii.css's own `.eq-note { margin: 0 auto 20px }`. That matters
     because #wq-summary carries `page-break-after: always`, so the review
     screen IS a printed page and growing it is how you get a second one. */
  #wq-summary > .eq-note,
  #eq-summary > .eq-note {
    margin: 0 auto 26px;
    font-size: 14px;
    text-align: center;
  }

  #wq-sum-rows,
  #eq-sum-rows {
    overflow: hidden;
    border: 1px solid var(--journey-border);
    border-radius: 12px;
    background: #fff;
  }

  .eq-sum-row {
    min-height: 42px;
    padding: 7px 14px;
    border-bottom-color: #e8ebf1;
  }

  /* ── 'Your Selections' ticks, reduced slightly (Loz, 2026-09-03) ────────────
     21px -> 18px disc, 14px -> 12px glyph. PROPORTIONAL, so the mark is the
     same tick at a smaller size and nothing else changes: 12/18 is 14/21 to
     three decimals. He asked for a size reduction, not a redraw.

     SCOPED TO .eq-sum-row, WHICH IS THE WHOLE POINT. `.eq-ans-check` also
     draws the in-journey `.eq-answered-bar` ticks on every question step, and
     those keep the base 26px from css/thermii.css:1917 — Loz named the
     summary screen, and the two are different marks at different sizes. One
     rule covers BOTH journeys' summaries, since wShowSummary
     (legacy-core.js:2491) and showSummary (:563) emit the same .eq-sum-row,
     and thermii-build-year-step.js's Property age row follows for free.

     THE GLYPH NEEDED ITS OWN RULE — IT WAS NEVER SCALED WITH THE DISC. The
     14px came from thermii.css:1924 and applied unchanged inside the 21px
     disc, so the summary tick was ALREADY more crowded than the 26px bar it
     was shrunk from (67% fill against 54%). Sizing only the disc here would
     have made that worse rather than smaller.

     MOBILE IS RESTATED, AND IT IS NOT OPTIONAL — see the max-width: 700px
     block at the end of this file (search .eq-sum-row .eq-ans-check).
     thermii.css:2136-2137 sets 22px/11px with `!important` under 700px, and a
     MEDIA QUERY ADDS NO SPECIFICITY, so nothing here can beat it — the same
     trap that shipped the disclaimer defect, in the harder direction, because
     that one was merely more specific and this one is !important.

     Which also means the mobile disc is NOT A CIRCLE today, measured 21x22:
     `flex: 0 0 21px` is the flex-basis and wins the WIDTH from the !important
     22px, which only lands on the height. Changing the basis to 18px alone
     would have taken it to 18x22 — squashing it further — so the restatement
     is repairing something this change would otherwise have made worse, not
     tidying at Loz's expense. */
  /* ── EYEBROW LEGENDS (Loz's design, 2026-09-03) ───────────────────────────
     Injected by js/thermii-kit-screen.js. Same treatment as the kit table's
     column header, the sticky bar's label and the sidebar's "TOTAL KIT COST",
     so every legend on this screen speaks with one voice: 10.5px, 600, grey,
     letter-spaced, uppercase. The trailing note is the same size but lighter
     and NOT uppercase, which is how the design distinguishes "Guide figure"
     from the label it qualifies. */
  .thermii-kit-eyebrow {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin: 26px 0 10px;
  }

  /* 12px AND 13px SINCE 2026-09-08 (Loz: "increase the point size"), up from
     10.5 and 11.5. His design implies more still — its column headings fit
     ~14-15px — but that scale could never be pinned (room-panel.png's card is
     1.20x the app's while its row pitch is 1.32x, so the crop is not 1:1), and
     these are uppercase micro labels whose job is to be quiet. This is a
     deliberate half-step toward the design rather than a guess at it; going
     further is one value.

     PAPER KEEPS 10.5/11.5, restored in the print block: these legends are
     injected into #wKitItems, which IS cloned into the PDF, and the room
     cards' heights drive its page-break spacers. */
  .thermii-kit-eyebrow-t {
    color: #6b7280;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }

  .thermii-kit-eyebrow-n {
    color: #9aa2b4;
    font-size: 13px;
    font-weight: 400;
  }

  /* ── AND THE LEGEND GETS ITS BREATHING ROOM BACK (Loz, 2026-09-08) ────────
     "Add more space beneath 'ESTIMATED HEAT REQUIREMENT Guide figure' — it's
     currently touching the top of the panel below it." Measured: a 0px gap,
     while the identical legend above the room card had 20px.

     THE CAUSE IS A BLANKET RULE 2,000 LINES DOWN. `#wKitItems > div,
     #kitItems > div { margin-bottom: 0 !important }` (this file, inside the
     same @media screen block — confirmed in the CSSOM, not by reading, since
     this file's braces do not scan reliably) zeroes the bottom margin of every
     direct child of the kit container. That rule is load-bearing: it is what
     makes the category groups stack with no gaps, so it must NOT be relaxed.
     The room card's legend escapes it only because it lives inside
     `.wkit-rooms-col` and is not a direct child.

     So the exception is scoped to the legends and needs to out-weigh an
     !important ID rule: (1,1,1) against its (1,0,1), with !important of its
     own. Paper restores 0. */
  #wKitItems > .thermii-kit-eyebrow,
  #kitItems > .thermii-kit-eyebrow {
    margin-bottom: 12px !important;
  }

  /* The first legend sits directly under the hero, which already carries its
     own bottom margin. */
  .thermii-kit-eyebrow:first-child {
    margin-top: 6px;
  }

  /* ── THE HEAT PANEL (Loz's design, 2026-09-03) ────────────────────────────
     Two columns: the question, the era choice and the per-room coverage on the
     left, the total in a peach panel with the disclaimer under it on the
     right. js/thermii-kit-screen.js MOVES the engine's own blocks into that
     shape; this lays them out.

     Measured off the design: card WHITE (the engine paints it #f7f9fc),
     peach panel #fff4e8 403px wide, selected era box 198x63 with a
     #7138ed border on a #f5f3ff tint, coverage bar 12px tall.

     THE ENGINE'S INLINE STYLES ARE STRIPPED, NOT OVERRIDDEN, wherever the
     module already touches the node — inline beats a stylesheet, and the
     stripping route needs no `!important`. Where it does not (the panel, the
     header, the meter) `!important` is unavoidable, and every one of those is
     PAINT OR LAYOUT INSIDE THE PANEL — none of it reaches the printed quote,
     because css/thermii.css's print block hides this panel's chevron and the
     PDF clone re-emits that scoped. */
  .wkit-heat-panel {
    background: #fff !important;
    /* THE PANEL GIVES UP ITS TOP PADDING TO THE HEADER (Loz, 2026-09-07) —
       see the header rule below for why it has to be the header that owns
       that space rather than the panel. */
    padding-top: 0 !important;
  }

  /* ── THE HEAT PANEL KEEPS ITS DISTANCE WITHOUT THE LEGEND (Loz, 2026-09-10:
     "The Heat Requirement Check panel is touching the Rooms panel") ─────────
     On his single-room test the panel sat flush against the room card, and the
     cause is that NOTHING ELSE was providing that gap. Measured: the engine
     writes `margin-bottom: 28px` inline on `.wkit-rooms-col` and 36px on the
     panel, and the rule above — `#wKitItems > div { margin-bottom: 0
     !important }`, load-bearing for the category groups — zeroes BOTH, because
     each is a direct child of the kit container. The water panel carries no
     margin-top at all. So the whole 55px gap was the legend's own
     `26px / 12px` margins, and without a legend the two boxes meet.

     ADJACENT-SIBLING, SO IT CANNOT DOUBLE UP. With the legend present it sits
     between the two and neither selector matches, leaving today's 55px exactly
     as it is; these fire only when the panel directly follows the room column
     (a single-room quote, where js/thermii-kit-screen.js has moved the design-
     temperature note inside the card) or directly follows that note (a
     multi-room quote). No `!important`: water's inline margin-top is absent,
     and electric's own 24px inline deliberately still wins.

     SCREEN ONLY. The legend is in the printed DOM, so neither selector can
     match on paper — but this is geometry, and the printed quote's page-break
     spacers measure geometry, so it is fenced rather than left to reasoning. */
  .wkit-rooms-col + .wkit-heat-panel,
  .wkit-design-temps + .wkit-heat-panel {
    margin-top: 28px;
  }

  /* Left-ranged, where the engine centres it.

     ── THE HEADER ROW IS CENTRED IN ITS BAND (Loz, 2026-09-07) ──────────────
     "The section header and pills and collapsable arrow icon need to be
     centred top and bottom in the panel." It had 24px above (the panel's own
     20px inline padding plus the header's 4px) and 16px below, so the row sat
     low against its rule. The design has it even: its panel starts at y=838,
     the title's ink runs 872..897 and the rule is at 928 — **34 above, 31
     below**.

     THE PANEL'S TOP PADDING HAD TO MOVE INTO THE HEADER, and that is the whole
     reason this touches two rules. Balancing the band by padding the header
     asymmetrically (4 above, 24 below) would centre the TEXT and throw the
     CHEVRON out: it is absolutely positioned at `top: 50%` of the header with
     `translateY(-50%)`, so it centres on the header's own box — which
     asymmetric padding moves away from the text's centre. With the panel's
     padding-top zeroed the header can carry 28px on both sides: the row is
     centred in the band, and the chevron and the two pills stay centred on the
     row because they are centred on a box that is now symmetric. */
  /* ── AND IT IS THE ROOM HEADER'S HEIGHT (Loz, 2026-09-07) ────────────────
     "The Heat requirement Check header section needs to be the same height as
     the Room head section." Measured: the room card's header band is **68px**
     (its own `padding: 10px 16px` around the 47px Edit/Duplicate buttons)
     against this one's 86. 19px each side puts this band on 68 too — the
     title's line box is 30px, so 19 + 30 + 19 = 68 exactly.

     STILL SYMMETRIC, which is the constraint from the change above: the
     chevron is absolutely centred on this box, so the two paddings have to
     stay equal or it leaves the text's centre. 47px of chevron inside a 68px
     band is the same fit the room header already has with its buttons. */
  .wkit-heat-hdr {
    justify-content: flex-start !important;
    padding-top: 16px !important;
    padding-bottom: 16px !important;
    margin-bottom: 4px;
  }

  /* ── THE CLOSED PANEL BUTTS UP (Loz, 2026-09-08) ──────────────────────────
     "When closing the panel there is a line still showing. It needs to butt-up
     on closing the panel."

     MEASURED CLOSED: the panel stood 85px around a 59px header, so 25px of
     dead air sat under it, in three pieces — the header's 4px margin, the
     panel's 20px bottom padding (inline, legacy-core.js:4451) and its 1px
     border. The rule he can see is the header's own `::after`, drawn at
     `bottom: 0` of a box that does not collapse. All three survive because the
     engine collapses ONLY `#wHeatBody` (max-height 0, opacity 0, :4205).

     I TRIED TO MOVE THE SPACE INSIDE THE COLLAPSING BOX FIRST, and it does not
     work: `box-sizing: border-box` (css/thermii.css:1) makes `max-height`
     include the padding, but a box can never be shorter than its own padding
     and border — the content box floors at 0 and the body measured 24px, not
     0. Recorded because it is the plausible-looking wrong answer here.

     SO IT IS A CLOSED-STATE RULE, keyed on `aria-expanded` — the ENGINE's own
     attribute, written on the header at render (legacy-core.js:4452) and kept
     in step by thermiiToggleHeatPanel (:4211). No new state to maintain, and
     the chevron rule below already keys off it. The header's OWN symmetric
     16px stays: that is what centres the title in its band, and it is what the
     closed panel is left with. 59 + 2 borders = 61px shut, from 85.

     `:has()` CARRIES THE PANEL'S PADDING because the padding is on the parent
     and the state is on the child. It is already used in this codebase
     (css/thermii.css:1751) and has shipped in Safari since 15.4 — and if it
     ever failed to parse, the panel simply keeps the 20px it has today rather
     than breaking.

     ALL THREE ARE RESTORED IN THE PRINT TAIL, AND THAT IS NOT OPTIONAL.
     downloadKitPdf forces the body open in the clone (legacy-core.js:5417) but
     never touches `aria-expanded`, so a customer who downloads with the panel
     shut would otherwise get a printed quote carrying the full panel with no
     rule under its title and its last row flush to the panel edge. */
  .wkit-heat-hdr[aria-expanded="false"]::after {
    content: none;
  }

  .wkit-heat-hdr[aria-expanded="false"] {
    margin-bottom: 0;
  }

  .wkit-heat-panel:has(.wkit-heat-hdr[aria-expanded="false"]) {
    padding-bottom: 0 !important;
  }

  /* ── THE RULES RUN EDGE TO EDGE (Loz, 2026-09-07) ─────────────────────────
     "Have the horizontal separator lines go edge to edge of the panel." They
     were borders on boxes sitting inside the panel's own `padding: 20px 24px`
     (inline, legacy-core.js), so each stopped 25px short of both edges. The
     design is unambiguous when measured: its header rule spans **x 28..1169
     against a panel of exactly 28..1169** — the full width, no inset — while
     the era rule runs from the panel's left edge to the COLUMN DIVIDER at
     x=703 and no further. So "edge to edge" means each rule fills its own
     box's width, and the era rule still stops at the divider.

     DRAWN AS A PSEUDO-ELEMENT, NOT BY WIDENING THE BOX. Negative margins
     would work, but the header is `position: relative` with the collapse
     chevron absolutely positioned at `right: 0` inside it, so growing the
     header 24px would carry the chevron out onto the panel's border. An
     absolute `::after` needs no geometry change at all — and being absolute it
     is OUT OF FLOW, so it is not a phantom flex item in either of these two
     flex containers. -24px is the panel's own inline padding; the era rule's
     -28px on the right is `.thermii-heat-main`'s padding-right, which is what
     puts it on the divider. */
  .wkit-heat-hdr::after,
  .wkit-heat-pills::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: -24px;
    height: 1px;
    background: #f1f1f3;
  }

  .wkit-heat-hdr::after {
    right: -24px;
  }

  .wkit-heat-pills::after {
    right: -28px;
  }

  /* 21px, down from 24 (Loz, 2026-09-07) — and the room name comes down from
     25 to the same 21 in the same breath, so the two panel titles match
     rather than sitting a point apart as they did. */
  .wkit-heat-title {
    text-align: left !important;
    font-size: 21px !important;
    /* AN EXPLICIT 27px LINE BOX, so the band's arithmetic is exact rather than
       hostage to the font's default leading: 16 + 27 + 16 = 59, which is the
       room card's band to the pixel. At `normal` this measured 26.3 and left
       the two headers 0.7px apart — invisible, and still the sort of thing
       that makes a derived number stop deriving. */
    line-height: 27px !important;
  }

  /* The same rounded square as the room cards' — but its transform must KEEP
     `translateY(-50%)`, because this chevron is absolutely positioned and that
     is its own vertical centring. Neutralising the transform outright, as the
     room cards do, would drop it down the header. */
  /* ── ALIGNED WITH THE ROOM CARD'S, AND THE SAME SIZE (Loz, 2026-09-07) ────
     "The collapsable arrow on the Heat requirement check panel is not aligned
     with the one in the Your Room panel above." Measured: the room card's sat
     **17px** from the card's right edge and this one **25px** from the
     panel's, both cards ending at the same x — an 8px step between two
     controls a customer reads as a column.

     THE 8px IS THE DIFFERENCE BETWEEN TWO ENGINE PADDINGS, not a nudge: this
     panel carries `padding:20px 24px` inline while the room card's header
     carries `padding:10px 16px`, and both are manifest-locked, so the offset
     cannot drift. `right` has to be `!important` because the engine writes
     `right:0` inline on this span. */
  /* ── THE HEAT PILL MATCHES THE ROOM PILL (Loz, 2026-09-09) ──────────────
     "The 2 orange pills on those 2 sections are not the same weight — match
     the heat requirement pill to the your room pill."

     THE DIFFERENCE IS THE BORDER'S COLOUR, NOT ITS WIDTH, and measuring his
     screenshot said so before the CSS did: both borders come to 1px, but the
     room pill's is `#ec662c` rendered and the heat pill's `#f7d2bb` — solid
     orange against pale peach. Confirmed live: `#ff5b00` against `#ffd0b8`,
     both written INLINE by the engine (legacy-core.js:4454 and :4322), which
     is why this needs !important.

     THE TYPE COMES WITH IT: 14px/600 to 15px/600 and the padding from 3/12 to
     4/13, so the two pills are the same size of voice as well as the same
     weight of edge. Measured after: 71x26 -> 84x29 against the room pill's
     84x29.

     ITS min-width IS DELIBERATELY NOT COPIED. thermii.css:1745 gives the room
     badge `min-width: 84px` so that a 7.0m² pill and a 12.0m² pill stay the
     same width down a list of rooms. The heat panel shows ONE figure, so the
     same rule would only pad it with air. */
  .wkit-heat-hdr-badge {
    font-size: 15px !important;
    padding: 4px 13px !important;
    border-color: #ff5b00 !important;
  }

  #wHeatChev,
  #eHeatChev {
    right: -8px !important;
    width: 38px !important;
    height: 38px !important;
    min-width: 38px !important;
    /* ── THE ARROW HAS TO ANNOUNCE ITSELF (Loz, 2026-09-09) ───────────────
       "Du think we need to make the collapsable arrow more obvious to the
       user that this can reveal the info within?"

       YES, AND ONLY BECAUSE OF THIS MORNING. Both panels now default to
       CLOSED, so this control is the ONLY way into the room's figures and the
       heat check — it went from a convenience to the entrance. Measured, all
       THREE chevrons on the screen were byte-identical (white, 1px #d3d8e6, a
       #5b6480 glyph), so the entrance looked exactly like the two buttons
       beside it and exactly like the kit list's arrows.

       SAMPLED FROM HIS MOCK, and both values turn out to be the screen's own:
       the fill is #f4f2fe in his file, which is the pale lilac the kit
       CATEGORY BARS already carry (measured live at #f4f2ff), and the border
       and glyph are #4b28ed, within 2% of `--journey-selected` — the purple
       the engine already writes on every category subtotal. So the token is
       used rather than a fourth purple, and the screen's interactive colour
       stays one value.

       A FILL RATHER THAN A HEAVIER OUTLINE, because purple is what says
       "interactive" here while orange says "information" — the room card's
       Edit, Duplicate and area badge are all orange. An outline in any colour
       would still read as a third button in that row.

       AND THE KIT LIST'S ARROWS ARE LEFT GREY, which was his instruction and
       is the right one for a reason worth recording: those sit ON the category
       bars, whose background IS #f4f2ff. A lilac-filled arrow there would
       vanish into its own bar — the exact opposite of standing out. */
    border: 1px solid var(--journey-selected) !important;
    border-radius: 10px !important;
    background: #f4f2ff !important;
    transform: translateY(-50%) !important;
  }

  #wHeatChev svg,
  #eHeatChev svg {
    stroke: var(--journey-selected);
    transition: transform 0.25s ease;
    transform: rotate(180deg);
  }

  .wkit-heat-hdr[aria-expanded="false"] #wHeatChev svg,
  .wkit-heat-hdr[aria-expanded="false"] #eHeatChev svg {
    transform: rotate(0deg);
  }

  .thermii-heat-cols {
    display: grid;
    /* Loz, 2026-09-21: narrow the 'Property needs' panel (the first, wider
       track — .thermii-heat-side) which carried a lot of empty space. 1.45:1
       -> 1:1.2 takes it ~728 -> ~559px at 1850, giving the right column (rooms
       + disclaimer) the freed width. Below 1100px this stacks to one column
       (rule further down), so paper — rendered at the ~760px clone width — is
       untouched. */
    /* Loz, 2026-09-25: narrower again, so the rooms get the width. 1:1.2 ->
       1:2 takes the panel ~559 -> ~410px at 1850. The 320px floor is its
       content's own: the widest line, "Estimate your running costs ->", is
       239px, inside the total block's 24px padding each side and this
       column's 28px right inset. */
    grid-template-columns: minmax(320px, 1fr) minmax(0, 2fr);
    align-items: start;
  }

  /* ── BREATHING ROOM UNDER THE HEADER RULE (Loz, 2026-09-07) ───────────────
     "Move 'Roughly when was the property built?' down further." It sat 8px
     under the rule; the design puts its ink **34px** below it (rule at y=928,
     ink at 963) and starts the peach panel at the same 32px. 26px of padding
     plus the header's 4px margin and the question's own 4px lands on it.

     THE PADDING GOES ON THE TWO ITEMS, NOT ON `.thermii-heat-cols`. Padding on
     the grid would push the items down and take the vertical divider with
     them — it is a border on `.thermii-heat-main` — leaving the divider
     starting 26px below the header rule with a gap above it. The design runs
     it from the rule. */
  .thermii-heat-main {
    padding-top: 26px;
    padding-right: 28px;
    border-right: 1px solid #f1f1f3;
  }

  .thermii-heat-side {
    padding-top: 26px;
    padding-left: 28px;
  }

  /* ── THE PANEL HOLDS ONE HEIGHT WHILE THE BAND CHANGES ───────────────────
     Loz, 2026-09-10: "yeah hold the panel height steady too mate", after the
     scroll anchor stopped the PAGE moving but the panel itself still resized
     under the cursor.

     THE FIGURE CANNOT BE WRITTEN HERE, which is why it arrives as a property.
     The panel's height depends on how many rooms fall short at the chosen
     band, and a short room's row grows 88px -> 170px because it gains a
     warning whose own height depends on its wrapped sentence — measured across
     the three eras on a two-room project: 406 / 433 / 520px, with the 45px
     all-clear line showing only while nothing is short. No stylesheet can know
     that, and js/thermii-kit-screen.js measures it and sets
     --thermii-heat-reserve.

     ON `.thermii-heat-main` AND NOT ON THE BODY, which is load-bearing rather
     than tidy: #wHeatBody is the element the collapse animates with
     `max-height: 0`, and min-height BEATS max-height in the cascade — a
     reservation there would hold the panel open and break the chevron
     outright. The column inside it is clipped by that same overflow, so the
     collapse still works.

     AND SWITCHED OFF FOR THE PDF, which is not theoretical: the clone renders
     in SCREEN media (legacy-core.js:5322 builds #thermiiPdfRoot and only
     @media print rules are re-emitted over it), so a screen reservation would
     otherwise put reserved white space into the document a customer keeps —
     the same class of defect that shipped in V.1.27. An id-scoped screen rule
     (1,1,0) beats the class rule with no `!important` anywhere, which the PDF
     note at :609 requires of this file. `.kb-disclaimer` at :1222 is the
     precedent. */
  .thermii-heat-cols {
    min-height: var(--thermii-heat-reserve, 0px);
  }

  #thermiiPdfRoot .thermii-heat-cols {
    min-height: 0px;
  }

  .wkit-heat-sub {
    text-align: left !important;
    font-size: 17px !important;
    font-weight: 600 !important;
    color: #1f2a44 !important;
    margin: 4px 0 2px !important;
  }

  .thermii-heat-note {
    color: #6b7280;
    font-size: 15px;
    font-weight: 400;
    margin-bottom: 18px;
  }

  /* ── the era choice as radio boxes ──────────────────────────────────────── */
  .wkit-heat-pills {
    position: relative;
    justify-content: flex-start !important;
    gap: 8px !important;
    margin-bottom: 0 !important;
    padding-bottom: 18px;
  }

  /* The design drops the per-era hints. Hidden rather than removed: the engine
     rebuilds this block every render and the copy is its own. */
  .wkit-heat-hint {
    display: none;
  }

  /* 14px of padding and an 8px gap, not the design's 18/10: measured, three
     boxes came to 216 + 225 + 237 = 678px plus gaps in a 676px column and the
     third wrapped onto its own row. The engine's own labels are longer than
     the design's ("100W/m²" against "00 W/m²", its own typo), so the design's
     padding cannot be taken literally here. */
  .thermii-era-box {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    box-sizing: border-box;
    min-height: 63px;
    padding: 0 14px;
    border: 1px solid #d3d8e6;
    border-radius: 10px;
    background: #fff;
    font-family: 'Poppins', sans-serif;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
  }

  /* The radio itself is drawn, not an input — the engine's control is a
     <button>, and swapping the element would take its inline onclick with it. */
  .thermii-era-box::before {
    content: "";
    flex: 0 0 auto;
    width: 18px;
    height: 18px;
    border: 2px solid #c8cede;
    border-radius: 50%;
    background: #fff;
  }

  .thermii-era-box.is-on {
    border-color: var(--thermii-purple, #7138ed);
    background: #f5f3ff;
  }

  .thermii-era-box.is-on::before {
    border-color: var(--thermii-purple, #7138ed);
    /* the filled dot, drawn with an inset ring rather than a second element */
    box-shadow: inset 0 0 0 3px #fff, inset 0 0 0 9px var(--thermii-purple, #7138ed);
  }

  .thermii-era-l {
    font-size: 15px;
    font-weight: 600;
    color: #1f2a44;
  }

  .thermii-era-box.is-on .thermii-era-l {
    color: var(--thermii-purple, #7138ed);
  }

  .thermii-era-v {
    font-size: 14px;
    font-weight: 400;
    color: #6b7280;
  }

  /* ── the per-room coverage ──────────────────────────────────────────────── */
  .wkit-heat-row {
    border-bottom: 0 !important;
    padding: 16px 0 0 !important;
  }

  .wkit-heat-name {
    font-size: 17px !important;
  }

  /* The bar runs full width with its figures BELOW it, where the engine sets
     them side by side. */
  .wkit-heat-meter {
    display: block !important;
    margin-top: 10px !important;
  }

  .wkit-heat-bar {
    height: 12px !important;
    margin-bottom: 8px;
  }

  .wkit-heat-meter > span {
    font-size: 15px !important;
  }

  .wkit-heat-allclear {
    justify-content: flex-start !important;
    text-align: left !important;
    background: #f0f9eb;
    border: 1px solid #cbe8b4;
    border-radius: 10px;
    padding: 12px 14px;
    /* Loz, 2026-09-21: a touch smaller and one weight lighter (15/600 -> 14/500)
       so the state box reads calmer. font-weight beats the engine's inline 600.
       Safe for the PDF: the heat panel is bracketed by forced page breaks on
       both sides (thermii-pdf-clean.js BREAKS), so a ~1px height delta cannot
       move another page's break. The amber twins get the same below. */
    font-size: 14px !important;
    font-weight: 500 !important;
    /* ── THE TICK CENTRES ON THE TEXT (Loz, 2026-09-08) ────────────────────
       "have the green tick aligned centred with the text (currently it looks
       higher than the text)."

       MEASURED, and he is right to the pixel: `flex-start` topped the 14px
       icon against a 19px line box, so its centre sat at y=20 where the
       text's sat at y=22.5 — **2.5px high**. `center` puts both on 22.5.

       `flex-start` CAME IN WITH `justify-content: flex-start` ABOVE, as part
       of left-ranging a box the engine centres; nothing here ever needed the
       cross-axis value, and no comment claimed it did.

       WHERE THE MESSAGE WRAPS — it can, inside the 520px column — `center`
       centres the icon on the block rather than on the first line. That is the
       right reading of "centred with the text" for a two-line message, and it
       is the design's own arrangement in `match-green.png`. */
    align-items: center !important;
  }

  /* THE BOLD LEAD LINE, matching the amber top-up note's strong (Loz,
     2026-09-23): the all-clear now reads as a bold title then a lighter
     follow-on, the same shape as .thermii-heat-topup. The remainder keeps the
     box's 500 weight above. */
  .wkit-heat-allclear strong { font-weight: 600 !important; }

  /* THE AMBER TWINS GET THE SAME CALMER TREATMENT (Loz, 2026-09-21) — a touch
     smaller and one weight step lighter than the engine's inline values, so
     the short-state messages match the green box above. Both beat inline
     styles the engine/wire write. */
  /* the "N rooms need top-up" header pill: 12/600 -> 11/500 */
  .wkit-heat-hdr-warn {
    font-size: 11px !important;
    font-weight: 500 !important;
  }
  /* the per-room amber warning box: main text 12 -> 11px, and its bold "X W
     short" figure and edit link drop 600 -> 500 (the 400 body is already light) */
  .wkit-heat-warn span {
    font-size: 11px !important;
  }
  .wkit-heat-warn span span,
  .wkit-heat-warn .wkit-heat-editlink {
    font-weight: 500 !important;
  }

  /* ── the total, in the design's peach panel ─────────────────────────────── */
  .wkit-heat-total {
    text-align: left !important;
    margin-top: 0 !important;
    background: #fff4e8 !important;
    border: 0 !important;
    border-radius: 12px !important;
    padding: 22px 24px !important;
  }

  .wkit-heat-total-lbl {
    font-size: 13px !important;
    font-weight: 600 !important;
    color: #ff5b00 !important;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    line-height: 1.35;
  }

  /* ONE SEPARATOR, NOT TWO (Loz, 2026-09-10: "There are two separator lines
     on the left panel. We only need one - the top one is running through the
     year band text"). This rule's own border-bottom dated from before the band
     line existed; the band was then pulled up under it with `margin-top: -12px`
     and the old rule drew straight through the words. The band carries the
     single rule now. */
  .wkit-heat-total-val {
    font-size: 44px !important;
    font-weight: 600 !important;
    margin-top: 4px !important;
  }

  .wkit-heat-total-sub {
    font-size: 15px !important;
    color: #b4551f !important;
    margin-top: 14px !important;
  }

  .wkit-heat-runcost {
    margin-top: 10px !important;
  }

  /* ── THE DISCLAIMER IS SMALLER (Loz, 2026-09-07) ──────────────────────────
     14px -> 12px, which is also the size the engine writes inline and the
     house small-print size elsewhere on this screen.

     AND ITS COLOUR IS FIXED IN THE SAME BREATH, because shrinking text that
     already fails contrast is the wrong change to ship quietly: the engine
     writes `color:#888` inline, which measures **3.54:1** on this panel's
     white — under WCAG 1.4.3 AA's 4.5:1 for normal text, at 14px and worse to
     read at 12. #6b7280 measures 4.83:1 and is the grey this screen already
     uses for the room card's note and its column headings.

     THE "How is this calculated?" BUTTON TAKES IT TOO. It sits in this block
     at the same 12px and carried the same #888, so leaving it would put a
     failing control directly under fixed text and give the block two greys. */
  .wkit-heat-disc {
    text-align: left !important;
    margin-top: 18px !important;
    font-size: 12px !important;
    line-height: 1.6;
    color: #6b7280 !important;
  }

  .wkit-heat-calcbtn {
    margin: 8px 0 0 !important;
    color: #6b7280 !important;
  }

  /* "HOW IS THIS CALCULATED?" WEARS THE INSIGHTS PANEL'S COSTUME (Loz,
     2026-09-14: "the thermii tooltip in this section needs to match the
     colours, styling of the Insights panel on the question screens").

     It was the last speech bubble in the app still in the old dress — the
     pink-to-blue gradient, 2px #72d9ff border and magenta glow of
     css/thermii.css:329-347 — exactly what the compare screen's bubble shed on
     2026-09-01. Every value below is READ from the insight panel's own rules in
     css/thermii-skin.css (.thermii-tip, -avatar, -bubble, -text and the title),
     so the two are one look; the gate cross-reads them.

     NOT PROMOTED TO .thermii-tip, deliberately, where the compare bubble was:
     thermii.css:322-325 says why — the customer opens this one explicitly, so
     it must survive the Tips toggle, and `body.tips-off .thermii-tip` would
     hide it. Same costume, its own class. The title is added by
     js/thermii-kit-screen.js with the skin's own title class, so it is the
     skin's rule that paints it. The panel fills its column rather than sitting
     centred at 560px, because the insight panel it now resembles fills its
     column too. Paper is untouched: thermii.css:2380 hides it in print. */
  .wkit-heat-calcinfo {
    max-width: none;
    width: 100%;
    margin: 14px 0 4px;
    padding: 18px 20px;
    background: #f6f4ff;
    border: 1px solid #e4defb;
    border-radius: 16px;
    box-sizing: border-box;
  }

  .wkit-heat-calc-avatar {
    width: 34px;
    height: 34px;
    margin-top: 0;
    animation: thermii-tip-float 2.8s ease-in-out infinite;
  }

  .wkit-heat-calc-bubble {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
  }

  .wkit-heat-calc-bubble::before {
    display: none;
  }

  /* the Insights panels' body copy as it RENDERS — the LAST
     .thermii-tip-text rule in thermii-skin.css (14.5px / 1.6 / the muted grey
     / light weight). This first copied the earlier rule, which that later one
     overrides, so it stayed 13.5px #333a57 at 400 while every other Insight
     moved on (Loz, 2026-09-28: "not the same font weight and color"). */
  .wkit-heat-calc-line {
    font-size: 14.5px;
    line-height: 1.6;
    color: var(--journey-muted, #596584);
    font-weight: var(--thermii-font-weight-light);
  }

  /* the markers take the title's purple, not the engine's orange — orange is
     this screen's colour for figures and edits, purple for guidance */
  .wkit-heat-calc-line span:first-child {
    color: #6c4bd8;
  }

  /* One column below the sidebar breakpoint: at 900px the left half is too
     narrow for three era boxes side by side. */
  @media (max-width: 1100px) {
    .thermii-heat-cols {
      grid-template-columns: minmax(0, 1fr);
    }

    .thermii-heat-main {
      padding-right: 0;
      border-right: 0;
      border-bottom: 1px solid #f1f1f3;
      padding-bottom: 18px;
    }

    .thermii-heat-side {
      padding-left: 0;
      padding-top: 18px;
    }

    /* STACKED, THERE IS NO DIVIDER TO STOP AT. `.thermii-heat-main` drops its
       padding-right above, so -28px would put the era rule 4px outside the
       panel's own border; -24px is the panel's padding and lands the rule on
       its edge, which is where an edge-to-edge rule belongs when the columns
       are one. */
    .wkit-heat-pills::after {
      right: -24px;
    }
  }

  /* ── THE HERO PANEL: RIGHT EDGE AND THE GAP BELOW (Loz, 2026-09-07) ───────
     Two complaints, one cause each.

     THE RIGHT EDGE OVERHUNG BECAUSE THE HERO IS NOT A `.kb-card`.
     `body.thermii-skin .kb-card` (css/thermii-skin.css) caps the panel at
     **max-width: 1280px**; `#wKitPanelHeading` carries no such cap, so it
     filled the whole 1326px grid column and stood 46px proud of the panel
     beneath it — measured here, and 68px on Loz's own wider screen.

     THE 1280 IS COPIED, AND THE GATE CROSS-CHECKS IT rather than trusting the
     copy: it reads the skin's own `.kb-card` max-width and fails if the two
     numbers ever part company. There is no variable to share, and the skin
     hard-codes this figure in three places already.

     THE GAP: measured 0 — the gradient's bottom edge and the panel's white top
     were both at y=1290, so the hero sat directly on the panel. The design
     keeps page background between them (its eyebrow sits on grey, where the
     app's sits inside the white panel), and 20px puts the gradient-to-room-card
     distance at ~89px against the design's measured 90px. */
  #wKitPanelHeading,
  #kitPanelHeading {
    max-width: 1280px;
    margin-bottom: 20px;
  }

  /* ── THE ROOM CARD'S HORIZONTAL RULES (Loz's design, 2026-09-07) ──────────
     "The room panel is missing its lines." Measured off room-panel.png: the
     card carries a hairline under the header (y=190 in a 1526px-wide capture)
     and another above the design-temperature footer (y=545), with the column
     dividers running only between the two. The app had the verticals and
     neither horizontal.

     SAME HAIRLINE AS THE COLUMN DIVIDERS, so the three read as one grid rather
     than as two borrowed borders.

     THESE DO REACH THE PRINTED QUOTE — the room cards are cloned into
     #thermiiPdfRoot — and that is accepted rather than overlooked: a 1px rule
     per card cannot meaningfully re-paginate a document whose page-break
     spacers measure real heights, and the printed quote gains the same
     structure the screen has. */
  .kit-room-hdr {
    border-bottom: 1px solid #f1f1f3;
  }

  /* The design-temperature note, once js/thermii-kit-screen.js has moved it
     inside the card. Left-ranged, as the design has it.

     22px, NOT 14 — measured off room-panel.png against the row pitch, which is
     the one scale-free ruler inside this card: the design's footer rule sits
     37px above the note's ink where its row pitch is 50px, i.e. 0.74 of a
     pitch, and the app's pitch is 38px, so ~28px to the ink and 22px of
     padding once the note's own line box is allowed for. */
  .wkit-room-card .thermii-room-temps {
    /* ── THE FOOTER BAND IS TINTED (Loz, 2026-09-07) ────────────────────────
       #fafbfd, and it is sampled rather than taken on trust: the band in
       room-panel.png measures (250,251,253) across every pixel clear of the
       text, against pure white in the three columns above it. So the design
       separates the project-level note from the room's own figures by TONE,
       which is the same device the third column's #fbfaf7 uses — note the two
       are different greys and deliberately so: this one is cool, the
       calculated column's is warm.

       NO CORNER RADIUS IS NEEDED HERE, and that is measured, not assumed. The
       card is `overflow: hidden` with a 14px radius and this note is its last
       child, already running the full width (inset 1px each side, which is the
       card's own border) and flush to the bottom — so the card clips the band
       to its own rounded corners. A radius on the note would be a second place
       for that 14px to live. */
    background: #fafbfd;
    border-top: 1px solid #f1f1f3;
    margin: 0 !important;
    padding: 22px 18px;
    text-align: left !important;
  }

  /* ── THE NOTE IS LIGHT GREY (Loz, 2026-09-07) ─────────────────────────────
     "Needs to be in a light grey colour as per the uploaded example." The
     engine writes `color:#555` INLINE on the note's inner span
     (legacy-core.js:4413), so js/thermii-kit-screen.js strips that property
     per render and this rule paints it — the sanctioned route, since inline
     styles are outside the DOM contract and `!important` here would also hit
     the PDF clone.

     #6b7280 IS SAMPLED FROM THE DESIGN, and it is the grey this card already
     uses for its column headings, so the two read as one family: the note's
     stroke cores measure (85,94,116) to (113,119,135) against #6b7280's
     (107,114,128). The BOLD figures keep the engine's own #222846, which the
     design also shows dark — the contrast between the sentence and the
     temperatures is the point of the line.

     CONTRAST CHECKED, because this makes text lighter: #6b7280 on white is
     4.83:1, which clears WCAG 1.4.3 AA for normal text (4.5:1) at the note's
     13px. #555 was 7.4:1, so this spends real contrast and still passes.

     NOT scoped to the room card: the note is grey wherever it sits, including
     the project-level position a multi-room quote keeps it in. Colour only, so
     no geometry moves and nothing re-paginates. */
  .wkit-design-temps {
    color: #6b7280;
  }

  /* ── THE ROOM NAME (Loz's design, 2026-09-03) ────────────────────────────
     25px/600, measured: "Bathroom" is 127px wide in the design and Poppins
     25px/600 renders 127.6. The engine writes `font-size:20px` inline
     (legacy-core.js:4321 water, :993 electric), so js/thermii-kit-screen.js
     strips that property per render — inline beats a stylesheet, and this
     needs no `!important`. The weight and colour are left to the engine,
     which already writes the 600 and the navy the design shows. */
  .kit-room-hdr .kit-room-name {
    font-size: 21px;
  }

  /* ── THE THREE HEADER CONTROLS, AS ONE OUTLINED SET (Loz's design) ───────
     Measured off the design itself (attach4.jpg, 1:1 in CSS pixels — the
     "Bathroom" label measures 127px, which is Poppins 25px/600 to a tenth,
     and "Duplicate" 76px, which is 15px/600):

         height 47px · Edit 79px · Duplicate 125px · collapse 47x47 square
         radius 10px · padding 0 24px · label 15px/600 · 1px #d3d8e6 on white
         gaps 10px (Edit->Duplicate) and 15px (Duplicate->collapse)

     WHAT CHANGES: the radius (pill -> 10px rounded rect), the height (25 ->
     47), the label (12px/500 -> 15px/600), the icons (gone — the design has
     none), the position (left of the badge -> grouped at the right), and the
     collapse control, which was a 36px orange CIRCLE with a 2px border.
     Resting colours were already right: thermii-system.css:49 had made these
     grey-on-white long before the design arrived.

     SCOPED TO `.kit-room-hdr`, WHICH IS LOAD-BEARING. `.eq-ans-edit` also
     draws the review screens' Edit buttons (legacy-core.js:2546) and the
     mobile `.kit-body-edit-row`; those are Loz's existing design and keep
     their pill and their pencil.

     GROUPED RIGHT VIA `order`, NOT BY MOVING NODES. The engine puts a
     `flex:1` spacer between Duplicate and the chevron; giving the three
     controls a higher order sends them past it. The three interactive
     controls keep their DOM order relative to each other, so nothing reads
     or tabs out of sequence — only the spacer moves, and it is not
     focusable.

     PRINT CANNOT SEE ANY OF THIS, so the geometry is free: css/thermii.css's
     print block hides `.eq-ans-edit` AND every chevron outright
     (display:none!important), which the PDF clone re-emits scoped. Checked,
     not assumed — a screen rule DOES otherwise reach that clone, as the
     caption above records. */
  /* ── SMALLER, AND THE ICONS ARE BACK (Loz, 2026-09-07) ────────────────────
     "Reduce the size of the Edit button, Duplicate button and collapsable
     arrow. Let's add the pencil icon and duplicate icon to the buttons."

     47px -> 38px, 15px -> 14px, and the 24px side padding -> 16px so the pair
     do not grow wider as the icons return. That REVERSES the design's own
     measurements (47px, no icons) and reverses my own note of 2026-09-03 that
     removing the marks answered the original complaint — two identical
     pencils. It no longer does: at 14px the labels are the distinguishing
     thing and each button now carries its own mark, which is where this
     started on 2026-09-02.

     THE MARKS NEEDED NOTHING BUILDING. Both were left in the file when the
     design dropped them — the pencil on `.eq-ans-edit::before` and the
     Duplicate mark on `.eq-ans-edit[onclick*="wDuplicateRoom"]::before` — and
     suppressed here with `content: none`. Deleting that suppression is the
     whole change; the ink measurement that matched the two marks (23.2%
     against the pencil's 21.5% at 12px) still stands. */
  .kit-room-hdr .eq-ans-edit {
    order: 1;
    box-sizing: border-box;
    height: 38px;
    padding: 0 16px;
    border-radius: 10px;
    font-size: 14px;
    font-weight: 600;
    color: #1f2a44;
  }

  /* A neutral hover, in keeping with the set. The engine's own inline
     onmouseover/onmouseout are REMOVED per render by
     js/thermii-kit-screen.js — they filled the button solid orange and, worse,
     left `color:#ff5b00` in the inline style afterwards, so one hover turned
     the label permanently orange. Inline beats a stylesheet; that is the trap
     this screen has now hit four times. */
  .kit-room-hdr .eq-ans-edit:hover {
    border-color: #b9c0d4;
    background: #f7f8fb;
    color: #1f2a44;
  }

  /* THE ICON SUPPRESSION IS GONE (Loz, 2026-09-07) — it was two rules of
     `content: none` here, and removing them is what puts the pencil and the
     Duplicate mark back. Worth knowing if they ever need hiding again: the
     `[onclick]` variant was NOT decoration, because the Duplicate mark lower
     in this file is `.eq-ans-edit[onclick*="wDuplicateRoom"]::before`, also
     (0,2,0), and a tie between them would be settled by source order. */

  .kit-room-hdr [id^="wRoomChev-"],
  .kit-room-hdr [id^="eRoomChev-"] {
    order: 2;
    /* 38px, down from 47 with the two buttons (Loz, 2026-09-07) — the three
       controls are one set and only read as one at a shared height. */
    width: 38px !important;
    height: 38px !important;
    min-width: 38px !important;
    margin-left: 5px;
    /* PURPLE, FILLED — see the note on #wHeatChev above for why, and why the
       kit list's arrows stay grey. The two are kept on the same values
       deliberately: the room card and the heat panel are the two sections that
       now open from closed, and they should offer the same door. */
    border: 1px solid var(--journey-selected) !important;
    border-radius: 10px !important;
    background: #f4f2ff !important;
    /* THE BOX MUST NOT ROTATE. Both toggles write `transform:
       rotate(-90deg)` onto this span when the card collapses
       (legacy-core.js wToggleRoom / eToggleRoomTile) — invisible on a circle,
       but it tips a rounded square through 45° on the way and lands it on its
       corner mid-transition. */
    transform: none !important;
  }

  /* CSS beats the SVG's own `stroke` presentation attribute, which is the only
     reason this reaches the glyph inside manifest-locked markup. The 18px size
     comes from note #4's rule and is deliberately NOT reduced to the design's
     tiny caret — "bigger arrow" was Loz's own annotation ON this design. */
  .kit-room-hdr [id^="wRoomChev-"] svg,
  .kit-room-hdr [id^="eRoomChev-"] svg {
    stroke: var(--journey-selected);
    transition: transform 0.25s ease;
    /* OPEN POINTS UP, per the design — the engine's glyph is a down-caret, so
       open is 180° and collapsed is 0°. This ABANDONS the engine's
       left-pointing collapsed state, deliberately.

       THE DESIGN CONTRADICTS ITSELF HERE and the room card is the half being
       followed: measured on the ink, its collapse caret points UP while the
       card is open, and the HEAT PANEL's caret three inches below points DOWN
       while that panel is equally open. Loz asked for the up-caret, so both
       are unified on it rather than importing the inconsistency. The kit's
       category rows keep the engine's rotation — the design gives them no
       chevron at all, so there is nothing to match them to. */
    transform: rotate(180deg);
  }

  /* The caret rotates instead of the box, keeping the ENGINE'S convention —
     down when open, left when collapsed — off the header's own
     `aria-expanded`, which both toggles maintain. The design shows an UP caret
     when open; that convention is shared with the category panels' chevrons,
     so changing it here alone would make the two disagree on the same screen.
     Flagged for Loz rather than changed. */
  .kit-room-hdr[aria-expanded="false"] [id^="wRoomChev-"] svg,
  .kit-room-hdr[aria-expanded="false"] [id^="eRoomChev-"] svg {
    transform: rotate(0deg);
  }

  /* MOBILE IS RESTATED, AND IT IS NOT OPTIONAL — third instance of this on
     the project. css/thermii.css:2012 shrinks these chevrons to 26px under
     700px with `!important`, but at `[id^="wRoomChev-"]` that is (0,1,0),
     where the rule above is (0,2,0) and also !important. A MEDIA QUERY ADDS
     NO SPECIFICITY, so without this the phone would have kept a 47px control
     in a header whose own padding drops to 8px — the same shape as the bug
     that shipped on the small print, and as the one the summary ticks nearly
     shipped. The buttons need no restatement: thermii.css hides them here at
     (0,2,0) and nothing above touches `display`. */
  @media (max-width: 700px) {
    .kit-room-hdr [id^="wRoomChev-"],
    .kit-room-hdr [id^="eRoomChev-"] {
      width: 26px !important;
      height: 26px !important;
      min-width: 26px !important;
      margin-left: 0 !important;
      border-radius: 8px !important;
    }
  }

  /* ── THE DESIGN-TEMPERATURE CAPTION vs THE TOTALS TILES (2026-09-03, Loz)
     The caption carries an inline `margin:-8px 0 28px` (legacy-core.js:4413)
     to tuck it up against whatever precedes it. That is right when the thing
     above is the room-cards column — and wrong on a MULTI-ROOM quote, where
     `.wkit-stats-grid` is emitted as that column's last child (:4383, only
     when `rooms.length > 1`) and the caption's grey text was measured sitting
     **8px inside the dark tiles**. So the negative margin was tuned for the
     single-room case and never revisited for the other one.

     SCREEN ONLY, AND THAT IS CORRECTNESS RATHER THAN CAUTION. The PDF's own
     scoped print CSS carries `#thermiiPdfRoot .wkit-stats-grid { display:
     none !important }` — verified by running `_thermiiScopedPrintCss` against
     a real clone — so the tiles are not on paper at all, the caption's
     previous content there IS the room cards, and the -8px does exactly what
     it was written to do. Fixing print would break the printed quote.

     CONDITIONAL ON THE TILES EXISTING, via `:has()`. A blanket override would
     also undo the tuck on a single-room quote, which has no defect to fix.
     `:has()` has been in Safari since 15.4 — Loz's browser — and where it is
     unsupported the whole rule is dropped, leaving today's behaviour rather
     than a half-applied one.

     `!important` IS UNAVOIDABLE: the margin is inline, inside manifest-locked
     code, and inline loses only to !important.

     AND A SCREEN RULE *DOES* REACH THE PDF — I had this backwards, and
     measuring the clone is what caught it. `downloadKitPdf` rasterises the
     clone from the LIVE document, so it renders in SCREEN media; that is the
     whole reason `_thermiiScopedPrintCss` exists, to re-emit print rules that
     would otherwise never apply. Measured: with only this rule in place the
     clone's caption read **10px**, not -8px. The tiles are `display: none`
     there, and a display:none element still matches `:has()`. So paper is
     restored explicitly, in the @media print block at the foot of this file —
     which that same machinery re-emits as `#thermiiPdfRoot
     .wkit-design-temps`, (1,1,0) against this rule's (0,3,0), so it wins
     among the !important declarations.

     10px MATCHES THE COLUMN'S OWN RHYTHM — `.wkit-rooms-col` sets `gap:10px`
     between its cards (:978/:4318), so the caption sits at the same spacing
     as everything else stacked above it rather than at a value picked by eye. */
  .wkit-rooms-col:has(.wkit-stats-grid) + .wkit-design-temps {
    margin-top: 10px !important;
  }

  /* ── THE HEAT REQUIREMENT CHECK, TO LOZ'S REVISED DESIGN (2026-09-10) ────
     js/thermii-kit-screen.js moves the era pills into the header as a
     segmented control and marks the rows; this lays it out. Everything here is
     inside @media screen, and the print block at the foot of this file puts
     every one of these choices back for paper, because the PDF clone renders
     in screen media. The totals block is STATE-COLOURED — green when every
     room is covered, amber when one needs a top-up — rather than the mock's
     red, which would have said "alarm" over a good result. */
  /* The pill comes and goes with the band ("1 room needs top-up", then none),
     and it shares this row with the picker — so the row must not be allowed to
     wrap or the header's height would change with the band and take the whole
     page with it. */
  .wkit-heat-hdr {
    justify-content: flex-start !important;
    flex-wrap: nowrap !important;
  }
  .thermii-heat-picker { flex: 0 0 auto; }
  .wkit-heat-hdr-badge {
    display: none !important;             /* the figure lives in the totals block */
  }
  .thermii-heat-picker {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-left: auto;
    margin-right: 46px;                    /* clear of the absolute chevron */
  }
  .thermii-heat-picker-l {
    font-size: 13px;
    color: var(--journey-muted);
    white-space: nowrap;
  }
  .thermii-heat-picker .wkit-heat-pills {
    position: static;
    display: inline-flex !important;
    flex-wrap: nowrap !important;
    gap: 2px !important;
    margin: 0 !important;
    padding: 3px;
    border: 1px solid var(--journey-border);
    border-radius: 10px;
    background: #fff;
  }
  .thermii-heat-picker .wkit-heat-pills::after { content: none; }
  .thermii-heat-picker .thermii-era-box {
    min-height: 0;
    padding: 6px 12px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    gap: 0;
  }
  .thermii-heat-picker .thermii-era-box::before { display: none; }
  .thermii-heat-picker .thermii-era-v { display: none; }
  .thermii-heat-picker .thermii-era-l {
    font-size: 13px;
    font-weight: 500;
    color: #1f2a44;
  }
  .thermii-heat-picker .thermii-era-box.is-on { background: var(--thermii-purple, #7138ed); }
  .thermii-heat-picker .thermii-era-box.is-on .thermii-era-l { color: #fff; font-weight: 600; }
  .thermii-heat-picker .thermii-era-box:focus-visible { outline: 2px solid var(--thermii-purple, #7138ed); outline-offset: 1px; }

  /* the question and its note stay for the radiogroup's label, unseen */
  .wkit-heat-sub, .thermii-heat-note { display: none !important; }

  /* totals on the LEFT, rooms on the right */
  .thermii-heat-side {
    order: -1;
    padding-left: 0;
    padding-right: 28px;
    border-right: 1px solid #f1f1f3;
  }
  .thermii-heat-main {
    padding-left: 28px;
    padding-right: 0;
    border-right: 0;
  }

  /* the totals block, coloured by state */
  .wkit-heat-total {
    background: #f0f9eb !important;
  }
  .wkit-heat-total-lbl { color: #5f9500 !important; }
  .wkit-heat-total-val { color: #1f2a44 !important; border-bottom-color: rgba(31, 42, 68, .1) !important; }
  .thermii-heat-band {
    font-size: 13px;
    color: var(--journey-muted);
    margin-top: 6px;
    padding-bottom: 16px;
    border-bottom: 1px solid rgba(31, 42, 68, .1);
  }
  /* A RULE ABOVE THE RUNNING-COSTS LINK, BOTH JOURNEYS (Loz, 2026-09-24: "you
     have a horizontal line on this panel for the electric summary — I like
     this, do the same for water"). Electric's line was the band's own border,
     landing above the link only because the electric figures come first
     there. The link now carries the rule itself, so water gets it too; where
     the band sits straight above the link (electric) its border stands down,
     or there would be two. */
  .wkit-heat-total .wkit-heat-runcost {
    margin-top: 16px !important;
    padding-top: 16px;
    border-top: 1px solid rgba(31, 42, 68, .1);
  }
  .wkit-heat-total .thermii-heat-band:has(+ .wkit-heat-runcost) {
    padding-bottom: 0;
    border-bottom: 0;
  }
  .wkit-heat-total-sub {
    display: flex !important;
    flex-direction: column;
    margin-top: 16px !important;
    color: inherit !important;
  }
  .wkit-heat-total-sub-lbl {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #5f9500;
  }
  .wkit-heat-total-sub-val {
    font-size: 24px;
    font-weight: var(--thermii-font-weight-display, 600);
    color: #1f2a44;
    line-height: 1.2;
    margin-top: 2px;
  }
  .thermii-heat-gap {
    font-size: 13px;
    margin-top: 4px;
    color: #5f9500;
  }
  .wkit-heat-panel:has(.wkit-heat-hdr-warn) .wkit-heat-total { background: #fff8e6 !important; }
  .wkit-heat-panel:has(.wkit-heat-hdr-warn) .wkit-heat-total-lbl,
  .wkit-heat-panel:has(.wkit-heat-hdr-warn) .wkit-heat-total-sub-lbl,
  .wkit-heat-panel:has(.wkit-heat-hdr-warn) .thermii-heat-gap { color: #a06000 !important; }

  /* the rows as meters: name | bar | status, nothing moved */
  /* ── THE ROW'S GEOMETRY IS ERA-INVARIANT (Loz, 2026-09-10: "the whole
     page still jumps around when clicking on the various 'property built'
     toggles") ──────────────────────────────────────────────────────────────
     THE STATUS COLUMN IS A FIXED WIDTH, not `auto`. Content-sized, it was as
     wide as its widest text, and the text changes with the band — "Covered
     with headroom" against "1,140 W short" — so every toggle re-measured the
     track and shifted the two columns beside it. A fixed track cannot.

     172px IS THE WIDEST STATUS, not a guess: "Covered with headroom" at
     12.5px/600 with its 13px icon and 5px gap measures ~166, and the shortfall
     forms are shorter.

     AND IT ALSO ENDS THE OVERLAP (his issue 2, the other half): the engine
     writes `white-space: nowrap` inline on the meter's caption, so at
     1400px "Needs 826 W • Delivers ~1,105 W" ran 79px PAST its own track and
     under the status — measured, the two boxes genuinely intersected. The
     caption may wrap inside its own column now, and the fixed track gives it a
     hard edge to stop at. */
  .thermii-heat-r {
    display: grid !important;
    grid-template-columns: minmax(140px, 220px) minmax(0, 1fr) 184px;
    column-gap: 18px;
    row-gap: 7px;
    align-items: center;
    padding: 14px 0 !important;
    border-bottom: 1px solid #eef0f5 !important;
  }

  /* TABULAR FIGURES EVERYWHERE A NUMBER ANIMATES. The engine counts every
     `.wkit-anim-num` up over 420ms of requestAnimationFrame frames
     (legacy-core.js:4234) — rewriting the text ~25 times per toggle. With
     proportional digits each rewrite is a different width, which is the
     jitter inside the jump; with tabular figures the box cannot change at
     all. Applied to the captions and the big totals, not just the spans, so a
     comma appearing at 1,000 cannot nudge anything either. */
  .wkit-anim-num,
  .thermii-heat-r .wkit-heat-meter > span,
  .wkit-heat-total-val,
  .wkit-heat-total-sub-val,
  .thermii-heat-gap {
    font-variant-numeric: tabular-nums;
  }
  /* !important: the engine writes `display:flex` INLINE on this div. */
  .thermii-heat-r-head { display: contents !important; }
  .thermii-heat-r .wkit-heat-name { grid-column: 1; grid-row: 1; }
  .thermii-heat-r .wkit-heat-name > span {
    display: block;
    font-size: 13px !important;
    margin-top: 2px;
  }
  .thermii-heat-dot { display: none; }
  /* ── THE CAPTION TAKES ITS OWN LINE, WHICH IS WHAT HIS MOCK SHOWS ────────
     The bar sits on the name's line and "Needs N W • Delivers ~N W" runs
     beneath it. Both live inside the engine's `.wkit-heat-meter`, so that div
     becomes `display: contents` and its two children are grid items in their
     own right — the bar in the middle track on row one, the caption spanning
     the middle and status tracks on row two.

     THE ALTERNATIVE WAS A WRAPPED CAPTION, and the measurement is what ruled
     it out: at his 1.45fr/1fr the rooms column is 488px, and inside it the
     middle track came to 122 against a caption whose natural width is 273 — so
     it wrapped to two lines. Spanning the caption across the middle and status
     tracks gives it ~442px, one line, and leaves his column proportions alone.

     `!important` on both: the engine writes `display: flex` inline on the
     meter and `white-space: nowrap` inline on the caption. */
  .thermii-heat-r .wkit-heat-meter {
    display: contents !important;
  }
  /* THE BAR FILLS ITS TRACK (Loz, 2026-09-25: "should the needs bars be
     wider?"). It was capped at 240px inside a track that runs ~580px at his
     width, so each bar stopped short with a gap before the status and a 77%
     room barely read differently from a covered one. The cap is gone and the
     NAME track is bounded instead (140-220px, where it was an equal 1fr
     share), so the bar takes the rest: every row's bar starts at the same x
     (the name track is the same in each row's separate grid) and ends 18px
     before the fixed 184px status track, whatever the band says. */
  .thermii-heat-r .wkit-heat-bar {
    grid-column: 2;
    grid-row: 1;
    margin: 0 !important;
    max-width: none;
  }
  .thermii-heat-r .wkit-heat-meter > span {
    grid-column: 2 / -1;
    grid-row: 2;
    font-size: 13px !important;
    white-space: nowrap !important;
  }
  .thermii-heat-r .wkit-heat-status,
  .thermii-heat-r .wkit-heat-badge { grid-column: 3; grid-row: 1; justify-self: end; }
  /* !important: the engine writes display:flex INLINE on the warning too. */
  .thermii-heat-r .wkit-heat-warn { display: none !important; }

  .thermii-heat-topup {
    margin-top: 14px;
    padding: 12px 14px;
    border-radius: 10px;
    background: #fff8e6;
    border: 1px solid #f3dfae;
    font-size: 14px;
    line-height: 1.5;
    color: #7a4b00;
  }
  .thermii-heat-topup strong { color: #a06000; font-weight: 600; }

  @media (max-width: 1100px) {
    .thermii-heat-side { order: 0; padding-right: 0; border-right: 0; }
    .thermii-heat-main { padding-left: 0; }
    .thermii-heat-picker { margin-left: 0; margin-right: 0; flex-basis: 100%; }
    /* ...and the header must be ALLOWED to wrap for that full row to happen
       (mobile sweep, 2026-09-25): the row above is nowrap !important so its
       height never changes with the top-up pill, which left the picker's 100%
       basis nowhere to go — measured at 768px it ran 64px off the screen, and
       232px on a mixed electric kit. The phone block restated the wrap at
       700px; 701-1100 had none. The pill still sits on the title's row here,
       so the header's height stays fixed whether it shows or not. */
    .wkit-heat-hdr { flex-wrap: wrap !important; row-gap: 10px; }
  }
  @media (max-width: 700px) {
    /* the picker takes its own line under the title and may wrap */
    .thermii-heat-picker { margin: 4px 0 0; flex-basis: 100%; }
    .thermii-heat-picker .wkit-heat-pills { flex-wrap: wrap !important; }
    .thermii-heat-picker .thermii-era-box { padding: 5px 9px; }
    .thermii-heat-r { grid-template-columns: minmax(0, 1fr) auto; }
    .thermii-heat-r .wkit-heat-bar { grid-column: 1 / -1; grid-row: 2; max-width: none; }
    .thermii-heat-r .wkit-heat-meter > span { grid-column: 1 / -1; grid-row: 3; white-space: normal !important; }
    .thermii-heat-r .wkit-heat-status, .thermii-heat-r .wkit-heat-badge { grid-column: 2; grid-row: 1; }
    .thermii-heat-picker-l { display: none; }
  }

  /* ── THE MULTI-ROOM TOTALS, TO LOZ'S DESIGN (2026-09-10) ──────────────────
     js/thermii-room-totals.js lifts the engine's five navy tiles into a hero
     row (heated area with a room bar, manifold flow with the design temps as
     pills) and three quiet strips. Every figure is the engine's own node; this
     lays them out. The mock was style reference at ~1.5x, so the sizes are the
     screen's own scale: labels match the sidebar's eyebrows, the big figure
     the sidebar's total.

     THE GRID ITSELF STAYS `.wkit-stats-grid`, deliberately: css/thermii.css
     already lays those tiles out as label-left / value-right strips on a
     phone (:2181) and hides them in print (:2428, re-emitted over the clone),
     and both of those are exactly what the strips want. Only the hero row is
     new, and it is hidden on paper in the print block below. */
  .thermii-totals-hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 20px;
    margin-top: 28px;
  }
  .thermii-totals-hero-2 {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
  .thermii-totals-hero-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  .thermii-totals-card {
    border-radius: 14px;
    padding: 26px 30px 24px;
    background: #fff;
    border: 1px solid var(--journey-border);
  }
  .thermii-totals-label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--journey-muted);
  }
  .thermii-totals-figure {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    margin-top: 10px;
  }
  .thermii-totals-value {
    font-size: 40px;
    font-weight: var(--thermii-font-weight-display, 600);
    line-height: 1;
    letter-spacing: -.03em;
    color: #1f2a44;
    white-space: nowrap;
  }
  .thermii-totals-unit {
    font-size: 16px;
    font-weight: 400;
    letter-spacing: 0;
    color: var(--journey-muted);
    margin-left: 6px;
  }
  .thermii-totals-across {
    font-size: 13px;
    color: var(--journey-muted);
    white-space: nowrap;
  }

  /* The room bar: flex-grow carries each room's HEATED area, so the segments
     are the proportion without any arithmetic here. */
  .thermii-totals-bar {
    display: flex;
    gap: 3px;
    height: 9px;
    margin-top: 16px;
  }
  .thermii-totals-seg {
    display: block;
    min-width: 6px;
    border-radius: 999px;
  }
  .thermii-totals-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    margin-top: 12px;
    font-size: 13px;
    color: var(--journey-muted);
  }
  .thermii-totals-key {
    display: inline-flex;
    align-items: center;
    gap: 7px;
  }
  .thermii-totals-dot {
    width: 9px;
    height: 9px;
    border-radius: 2px;
  }

  /* ── THE FLOW CARD IS WHITE (Loz, 2026-09-11) ─────────────────────────────
     Its third design (2026-09-10) was a lilac card with a purple-to-magenta
     gradient figure, a solid fallback under it, a halo behind it and print and
     forced-colour reversals — all of it machinery to keep a gradient number
     legible. That made sense when it was one of TWO hero cards. It is one of
     THREE system totals now, beside two white cards with solid figures, and
     three peers should read as peers: white ground, the figure in the same
     near-black as the others, and the colour carried by the two pills — warm
     for flow, cool for return — which is where the meaning is. The gradient,
     the halo and every reversal for them are gone with the need. */
  .thermii-totals-flow .thermii-totals-label { color: #6b5fa8; }
  .thermii-totals-flow .thermii-totals-value {
    color: #1f2a44;
  }
  /* the note sits under the pills at three-across, where a row will not hold
     both — the pills are the instruction's figures and come first */
  .thermii-totals-flow .thermii-totals-temps { row-gap: 8px; }
  .thermii-totals-flow .thermii-totals-note { flex-basis: 100%; margin-left: 0; }

  .thermii-totals-temps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 18px;
    font-size: 13px;
  }
  .thermii-totals-pill {
    display: inline-block;
    padding: 6px 12px;
    border-radius: 999px;
    font-weight: 600;
    white-space: nowrap;
  }
  /* WARM OUT, COOL BACK — the colour says something true about the water, and
     both tints already exist on this screen. The orange is DARKENED from the
     brand #ff5b00, which measures 2.88:1 on its own peach: this pill carries
     the temperature an installer sets a valve to, so it is the wrong one to
     leave under AA. #b84200 gives 5.09:1 and still reads as the brand orange.
     The return pill's purple on the category lilac is already 6.72:1. */
  .thermii-totals-pill-flow {
    background: #fff4ee;
    color: #b84200;
  }
  .thermii-totals-pill-return {
    background: #f4f2ff;
    color: var(--journey-selected, #4b28ed);
  }
  .thermii-totals-arrow { color: var(--journey-muted); }
  .thermii-totals-note {
    margin-left: auto;
    color: #1f2a44;
    white-space: nowrap;
  }

  /* The strips. Three for water, two for electric. */
  .thermii-totals-spare {
    margin-top: 10px;
    font-family: 'Poppins', sans-serif;
    font-size: 13px;
    color: #7b7f95;
  }

  .thermii-totals-strips {
    display: grid;
    gap: 16px;
    margin-top: 16px;
  }
  .thermii-totals-strips-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .thermii-totals-strips-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .thermii-totals-strip {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    min-width: 0;
    padding: 18px 24px;
    border-radius: 12px;
    background: #fff;
    border: 1px solid var(--journey-border);
  }
  .thermii-totals-strip-label {
    font-size: 14px;
    font-weight: 500;
    color: #1f2a44;
  }
  .thermii-totals-qualifier {
    font-weight: 300;
    color: var(--journey-muted);
  }
  .thermii-totals-strip-value {
    font-size: 20px;
    font-weight: var(--thermii-font-weight-display, 600);
    color: #1f2a44;
    white-space: nowrap;
  }
  .thermii-totals-strip-value .thermii-totals-unit { font-size: 13px; margin-left: 4px; }

  /* Once the flow card carries the design temperatures, the engine's own
     caption is redundant ON SCREEN. It is hidden, not moved: it stays in the
     DOM and prints exactly as it always has. Conditional on the card, so a
     single-room quote — which has no totals — keeps its caption. */
  .wkit-rooms-col:has(.thermii-totals-flow) + .wkit-design-temps {
    display: none;
  }

  @media (max-width: 1100px) {
    .thermii-totals-hero-2,
    .thermii-totals-hero-3 { grid-template-columns: minmax(0, 1fr); }
    .thermii-totals-value { font-size: 34px; }
  }
  @media (max-width: 700px) {
    .thermii-totals-card { padding: 20px 20px 18px; }
    .thermii-totals-value { font-size: 30px; }
    .thermii-totals-note { margin-left: 0; white-space: normal; }
    .thermii-totals-strips-3, .thermii-totals-strips-2 { grid-template-columns: minmax(0, 1fr); }
  }
  /* ── THE ROOM CARD, REDESIGNED (Loz's mock, 2026-09-11) ──────────────────
     A narrow SPEC column on the left and a PIPE LAYOUT table on the right, one
     row per manifold port. It replaces the three equal columns of 2026-09-03.

     js/thermii-kit-screen.js's roomLayout() builds it; this lays it out. Every
     figure is still the ENGINE'S own node, moved rather than reprinted — with
     one documented exception, the per-port flow and pressure, which have to
     appear in N rows and so are read once off the engine's chip and printed.

     THE SPEC COLUMN IS NOT A THIRD OF THE CARD. Measured off the mock: its
     divider sits at 460 of 1929px of card interior, i.e. 23.8%. At Loz's
     1850px window the card is 1280px, so ~305px — which is also about the
     width its longest row ("Sub floor  New concrete") needs at 15px. Hence a
     fixed-ish track rather than a fraction: a fraction would stretch it on a
     wide screen and leave the value drifting away from its label, the exact
     fault the three-column version was rebuilt to fix. */
  /* TO THE PANEL'S EDGES (Loz, 2026-09-11: "the lilac background within the
     panel is not going to the edges"). The grid sits inside the engine's own
     `padding:0 16px 14px` wrapper (legacy-core.js:4331), so the layout half's
     ground stopped 16px short of the card on the right and 14px short of the
     bottom, with the card's white showing round it. The negative margins are
     that wrapper's padding, exactly, so the two halves run to the card edge
     and the card's own overflow:hidden rounds the bottom corners. Each half
     then carries its own inset, so the content sits where it did. */
  .thermii-room-grid {
    display: grid;
    grid-template-columns: minmax(0, 305px) minmax(0, 1fr);
    align-items: stretch;
    margin: 0 -16px -14px;
    border-top: 1px solid var(--journey-border, #ececf4);
  }

  /* LOOSER, TO HIS MOCK (item 5). Measured off it at 1929px of card interior
     against the app's 1280: his spec rows sit on a ~40px pitch and the group
     headings carry ~16px beneath them, where the first cut had 27px and 6px.
     7px each side of a 15px/1.5 line is a 36.5px pitch — the nearest the type
     allows without the column outgrowing the table beside it. */
  .thermii-spec {
    padding: 24px 26px 24px 16px;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }

  .thermii-spec-h {
    font-family: 'Poppins', sans-serif;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.7px;
    text-transform: uppercase;
    color: #9aa0b4;
    margin-bottom: 10px;
  }
  /* every group after the first gets its air above, and its own hairline */
  .thermii-spec-h + .thermii-spec-r { margin-bottom: 0; }
  .thermii-spec-r + .thermii-spec-h {
    margin-top: 22px;
    padding-top: 20px;
    border-top: 1px solid var(--journey-border, #ececf4);
  }

  .thermii-spec-r {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    padding: 4px 0;
  }

  .thermii-spec-l {
    font-family: 'Poppins', sans-serif;
    font-size: 13px;
    font-weight: 400;
    color: #5b6480;
  }

  /* THE VALUE TAKES ITS INK FROM HERE, NOT FROM THE ENGINE. legacy-core.js
     writes `color:#ff5b00` inline on every chip value (:4305), which painted
     the customer's own answers the same orange as Thermii's workings. The pass
     strips the property; this supplies the neutral the design draws. */
  .thermii-spec-v {
    font-family: 'Poppins', sans-serif;
    font-size: 13px;
    font-weight: 600;
    color: #222846;
    text-align: right;
  }

  /* ── THE AREA PILL TAKES THE MOCK'S LILAC (Loz, 2026-09-11: "the orange
     pills don't match the design"). The engine writes the badge inline as
     white with an orange border and orange text (legacy-core.js:4323 water,
     :993 electric — two sizes, one look), and inline loses only to
     !important. The mock's pill is lilac with purple text and a 10px radius,
     no border.

     THE BOX DOES NOT MOVE. Padding and font-size are left to the engine, and
     the 1px border is kept — painted the pill's own colour rather than removed
     — because the header's height feeds the PDF's page-break spacers. Colour
     and radius are the whole change. 6.33:1 on the lilac, comfortably AA.

     THE FIGURE IS UNCHANGED, and it is the room's TOTAL area: the engine
     prints thermiiDispArea(r.area) here, where the mock's pill reads "11.0m²
     heated". Making it the heated figure is a data change, not a restyle, and
     the heated area already has a home under the heat panel's room rows. */
  .kit-area-badge {
    background: #efeaff !important;
    color: var(--journey-selected, #4b28ed) !important;
    border-color: #efeaff !important;
    border-radius: 10px !important;
  }


  /* ── the header's three figures ──────────────────────────────────────── */
  .thermii-room-sum {
    display: inline-flex;
    align-items: baseline;
    gap: 7px;
    margin-left: 16px;
    font-family: 'Poppins', sans-serif;
    white-space: nowrap;
  }

  .thermii-room-sum-l {
    font-size: 14.5px;
    font-weight: 400;
    color: #6b7280;
  }
  /* THE PAIRS ARE RULED OFF (Loz, 2026-09-24): "Total Pipe: xx.0m | Flow:
     xx.0 L/min | 4 Loops". The rule is a drawn hairline, not a "|" character,
     so a screen reader reads the figures and not the word "vertical line".
     7px flex gap + 5px margin puts 12px either side of it. */
  .thermii-room-sum-v + .thermii-room-sum-l,
  .thermii-room-sum-loops { margin-left: 5px; }
  .thermii-room-sum-v + .thermii-room-sum-l::before,
  .thermii-room-sum-loops::before {
    content: '';
    display: inline-block;
    width: 1px;
    height: 14px;
    margin-right: 12px;
    vertical-align: -2px;
    background: #d3d8e6;
  }

  .thermii-room-sum-v {
    font-size: 15px;
    font-weight: 600;
    color: #222846;
    font-variant-numeric: tabular-nums;
  }

  .thermii-room-sum-loops {
    font-size: 14.5px;
    font-weight: 400;
    color: #6b7280;
  }

  /* It is the first thing to go when the header runs out of room: the name,
     the area and the controls all outrank it, and everything it says is in the
     body a click away. */
  @media (max-width: 1180px) {
    .thermii-room-sum { margin-left: 12px; gap: 5px; }
    .thermii-room-sum-l, .thermii-room-sum-loops { font-size: 13.5px; }
  }
  @media (max-width: 900px) {
    .thermii-room-sum { display: none; }
  }

  /* ── the pipe layout ─────────────────────────────────────────────────── */
  .thermii-layout {
    padding: 24px 18px 22px 26px;
    border-left: 1px solid var(--journey-border, #ececf4);
    background: #fbfaff;
    min-width: 0;
  }

  .thermii-layout-hdr {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 18px;
  }

  .thermii-layout-t {
    font-family: 'Poppins', sans-serif;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.7px;
    text-transform: uppercase;
    color: var(--journey-selected, #4b28ed);
  }

  /* GREEN ONLY WHEN IT IS TRUE. `ports = min(12, ceil(total/100))` makes a
     loop over 100 m impossible until the 12-port cap bites — so this badge is
     a real check with a real failing state, not a decoration that is always
     on. The amber below is the same #a06000 the heat panel's short rooms use.

     THE BRAND LIME, AT LOZ'S WORD (2026-09-11: "Green color: #8cc63f").
     Measured at 2.05:1 on white and 1.97:1 on the lilac — under WCAG 1.4.3
     for this 13px text, told to him once, and chosen. His screen. The heat
     panel keeps its own #497b02, his pick of 2026-09-09. */
  .thermii-loops-ok {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-family: 'Poppins', sans-serif;
    font-size: 13px;
    font-weight: 600;
    color: #74b700;   /* the fill green, not the text green: Loz put the badge back (2026-09-16) */
    white-space: nowrap;
  }
  .thermii-loops-over { color: #a06000; }
  /* the tick takes currentColor — green with the words, amber with the
     over-length words; no pin needed now the badge is the fill green */
  .thermii-loops-ok svg { flex-shrink: 0; }

  .thermii-loops { display: grid; row-gap: 8px; }

  /* SIX TRACKS: port, the floor/run split, length, flow, pressure, tick. The
     three figure columns are FIXED rather than content-sized so a 9.9 and a
     19.1 do not move the column, and the split takes the slack. */
  .thermii-loop-head,
  .thermii-loop-r {
    display: grid;
    grid-template-columns:
      minmax(120px, auto) minmax(0, 1fr) 92px 104px 112px 26px;
    align-items: center;
    column-gap: 14px;
  }

  .thermii-loop-head {
    padding: 0 14px 2px;
    font-family: 'Poppins', sans-serif;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.7px;
    text-transform: uppercase;
    color: #9aa0b4;
  }
  .thermii-loop-h-port { grid-column: 1 / 3; }
  .thermii-loop-h-len,
  .thermii-loop-h-flow,
  .thermii-loop-h-drop { text-align: right; }

  .thermii-loop-r {
    padding: 12px 14px;
    background: #fff;
    border: 1px solid var(--journey-border, #ececf4);
    border-radius: 10px;
  }

  .thermii-loop-port {
    display: inline-flex;
    align-items: center;
    gap: 10px;
  }

  /* The port's own number, so a fitter can match a row to the manifold without
     reading the words. */
  .thermii-loop-n {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex-shrink: 0;
    border-radius: 7px;
    background: #f1effd;
    font-family: 'Poppins', sans-serif;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--journey-selected, #4b28ed);
  }

  .thermii-loop-name {
    font-family: 'Poppins', sans-serif;
    font-size: 15px;
    font-weight: 600;
    color: #222846;
  }

  .thermii-loop-split {
    font-family: 'Poppins', sans-serif;
    font-size: 13px;
    font-weight: 400;
    color: #8a90a6;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .thermii-loop-len,
  .thermii-loop-flow,
  .thermii-loop-drop {
    font-family: 'Poppins', sans-serif;
    font-size: 13px;
    font-weight: 600;
    color: #222846;
    text-align: right;
    /* the figures line up column to column, and the length changes by a tenth
       between rooms */
    font-variant-numeric: tabular-nums;
  }
  .thermii-loop-flow,
  .thermii-loop-drop { font-weight: 500; color: #3b4360; }

  .thermii-loop-tick {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    color: #74b700;
  }
  .thermii-loop-tick-over { color: #a06000; }

  /* ── the spare pipe row ──────────────────────────────────────────────── */
  /* FOUR TRACKS, VALUE BESIDE ITS LABEL (Loz, 2026-09-11: "move the result
     closer to 'Spare pipe'"). The first cut parked the figure in the table's
     length column; his mock reads label, figure, then the note pushed right,
     which is also the DOM order — so the grid no longer has to reorder
     anything and the coil planner's nextElementSibling contract is met by
     source order alone. */
  .thermii-spare-r {
    display: grid;
    /* the FIGURE lands on the port rows' description column (Loz,
       2026-09-16): 30 + 12 + 80 + 12 = 134 = the loop row's 120px port
       column + its 14px gap, so "22m" starts where "42.5m floor + 46m
       run" starts above it. minmax, so a longer label still fits. */
    grid-template-columns: 30px minmax(80px, auto) auto minmax(0, 1fr);
    align-items: center;
    column-gap: 12px;
    margin-top: 10px;
    padding: 12px 14px;
    background: #f4f2ff;
    border: 1px solid #e4e0fb;
    border-radius: 10px;
  }

  .thermii-spare-icon {
    display: inline-flex;
    align-items: center;
    color: var(--journey-selected, #4b28ed);
  }

  .thermii-spare-l {
    font-family: 'Poppins', sans-serif;
    /* 14px, nudged up with the value below it so the pair stays balanced
       (Loz, 2026-09-23) — the grey note stays 13px */
    font-size: 14px;
    font-weight: 600;
    color: var(--journey-selected, #4b28ed);
  }

  /* THE LABEL AND THE VALUE STAY ADJACENT IN THE DOM because
     js/thermii-coil-plan.js writes the corrected offcut into the label's
     `nextElementSibling` — and since Loz's mock puts the figure right beside
     the label, source order and screen order now agree. */
  .thermii-spare-v {
    font-family: 'Poppins', sans-serif;
    font-size: 14px;
    font-weight: 700;
    color: var(--journey-selected, #4b28ed);
    font-variant-numeric: tabular-nums;   /* no margin-left: the 6px it had put the figure 6px off the description column */
  }

  .thermii-spare-note {
    font-family: 'Poppins', sans-serif;
    font-size: 13px;
    font-weight: 400;
    color: #7b7f95;
    text-align: right;
  }

  /* ── the room's total ────────────────────────────────────────────────── */
  /* THE ADD-ROOM ROW SITS UNDER THE ROOM CARDS (Loz, 2026-09-15): the
     engine's "+ Add Another Room" moves up from below the heat check into
     this row, with the other-system door beside it — the review screen's
     pair, same 330x54, 16px apart, centred. #wKitItems > div zeroes every
     direct child's bottom margin with !important (the category groups lean
     on it), so the row's margins are declared at the id's specificity with
     an !important of their own — the legends' escape. The engine's emptied
     wrapper is a husk: hidden here, kept for paper (see the print block). */
  .thermii-kit-addroom {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 16px;
  }
  #wKitItems .thermii-kit-addroom,
  #kitItems .thermii-kit-addroom { margin: 24px 0 40px !important; }
  .thermii-kit-addroom-husk { display: none; }
  /* THE KIT DOOR NOW CARRIES A VERTICAL DIVIDER (Loz, 2026-09-18: "The line in
     between the add room buttons"). Its box was `display: contents` so the
     magenta button was a bare flex item with nothing between it and the green
     one. It is a flex item again, with a left hairline — but the gap is
     controlled this time (the earlier revert measured a stray 45px gap because
     the box still carried the review card's 28px padding on top of the row's
     own gap). Here: the row's 16px gap sits between the green button and this
     border, and 16px of padding sits between the border and the magenta button,
     so the divider is a hairline centred in a 32px channel — not a chasm. */
  .thermii-kit-addroom .thermii-other-door-kit {
    display: flex;
    align-items: center;
    border-left: 1px solid #e3e7ee;
    padding-left: 16px;
  }

  /* THE TOTAL IS A BAND, AT THE FAR RIGHT (Loz, 2026-09-15, then 2026-09-16:
     "Move 'Kitchen Total: xx.0m' over to the right"). The spare row's tint
     and radius, so the two read as one footer block; "Kitchen Total:" bold in
     the selection purple, the 24px figure 10px after it, the pair right-
     aligned to the band's edge. The 2026-09-15 cut put the figure UNDER the
     LENGTH column — the sum under what it sums — on the loop row's own
     template; Loz looked at it and moved the pair to the right edge, his
     mock over my column argument. One flex rule now serves every width (the
     mid-width tail and the phone template went with the grid). SCREEN ONLY:
     paper keeps the row it had — see the print block. */
  .thermii-room-tot {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 10px;
    margin-top: 10px;
    padding: 12px 14px;
    background: #f4f2ff;
    border-radius: 10px;
  }

  /* mark, lead-in and chip are ONE colour — the tick disc's own selection
     purple (--journey-selected), not the Insights purple */
  .thermii-room-tot-l {
    font-family: 'Poppins', sans-serif;
    font-size: 16px;
    font-weight: 600;
    color: var(--journey-selected, #4b28ed);
  }

  .thermii-room-tot-v {
    font-family: 'Poppins', sans-serif;
    font-size: 24px;
    font-weight: 600;
    color: #222846;
    text-align: right;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }


  /* ── narrower windows ────────────────────────────────────────────────── */
  @media (max-width: 1180px) {
    .thermii-room-grid { grid-template-columns: minmax(0, 260px) minmax(0, 1fr); }
    .thermii-layout { padding-left: 18px; }
    .thermii-loop-head,
    .thermii-loop-r {
      grid-template-columns:
        minmax(104px, auto) minmax(0, 1fr) 80px 92px 98px 24px;
      column-gap: 10px;
    }
  }

  /* One column below the sidebar breakpoint: 305 + a six-track table does not
     fit beside the answers, and the table is the half worth keeping whole. */
  @media (max-width: 980px) {
    .thermii-room-grid { grid-template-columns: minmax(0, 1fr); }
    .thermii-spec { padding: 18px 2px; }
    .thermii-layout {
      padding: 18px 16px;
      border-left: 0;
      border-top: 1px solid var(--journey-border, #ececf4);
      background: none;
    }
    .thermii-spec { padding: 18px 16px; }
  }

  /* A phone cannot hold six tracks. The row becomes two lines: the port and
     its length, then the split, flow and pressure beneath — which is the same
     information in the order a fitter reads it. */
  @media (max-width: 700px) {
    .thermii-loop-head { display: none; }
    .thermii-loop-r {
      grid-template-columns: minmax(0, 1fr) auto 26px;
      row-gap: 6px;
    }
    .thermii-loop-port { grid-column: 1; grid-row: 1; }
    .thermii-loop-len { grid-column: 2; grid-row: 1; }
    .thermii-loop-tick { grid-column: 3; grid-row: 1; }
    .thermii-loop-split { grid-column: 1 / 3; grid-row: 2; text-align: left; }
    .thermii-loop-flow { grid-column: 1; grid-row: 3; text-align: left; }
    .thermii-loop-drop { grid-column: 2 / 4; grid-row: 3; }
    .thermii-spare-r {
      grid-template-columns: 30px auto minmax(0, 1fr);
      row-gap: 4px;
    }
    .thermii-spare-v { grid-column: 3; grid-row: 1; text-align: left; }
    .thermii-spare-note { grid-column: 2 / 4; grid-row: 2; text-align: left; }
  }

  /* ── CATEGORY ROWS: NAVY -> LAVENDER (Loz's design, 2026-09-03) ────────────
     The design's group rows are pale lavender with a purple mark, a purple
     label and a grey count, where the engine draws them dark navy with white
     text.

     `!important` IS UNAVOIDABLE HERE and is the narrower of the two options.
     legacy-core.js writes `background:#222846` and `color:#fff` INLINE on the
     header and on its child spans (:4578-4580), and inline loses only to
     !important. The alternative — stripping those attributes on every render —
     would take the row's flex layout with them and mean reproducing it here,
     which is a much larger surface to get wrong for the same result. Paint
     only: no geometry, so nothing moves and the PDF's page-break model is
     untouched.

     THESE DO REACH THE PRINTED QUOTE, deliberately. thermii-system.css records
     that restoring these headers also fixed the downloaded quote, where
     customers had been getting an undifferentiated wall of line items — so the
     colours are left to print rather than being scoped to screen. Layout-
     neutral, so it cannot shift a page break.

     ONE ROW SERVES BOTH JOURNEYS: water groups by category and electric by
     room, and both reuse `.wkit-cat-hdr`. */
  .wkit-cat-hdr {
    background: #f4f2ff !important;
    border: 1px solid #e6e1ff !important;
  }

  /* The label and the count. The engine gives the label white and the count
     white-at-55%, both inline. */
  .wkit-cat-hdr > span > span:first-child {
    color: var(--thermii-purple, #4e20f5) !important;
    font-weight: 600 !important;
  }

  .wkit-cat-hdr .wkit-item-count {
    color: #6b7280 !important;
  }

  /* ── THE PURPLE MARK IS GONE (Loz, 2026-09-07) ────────────────────────────
     "Remove the vertical line in front of the kit part section title." It was
     a 3px x 15px flex item on `.wkit-cat-hdr::before`, taken from the design's
     own category rows.

     THE RULE IS DELETED RATHER THAN HIDDEN, and one thing from its diagnosis
     is worth keeping on record because it fixed something else: that mark drew
     NOTHING when it was first added, because thermii-system.css had restored
     these headers with `display: block !important` (only to undo the skin's
     `display: none`) — so the row was not a flex container, and that is also
     why the collapse chevron had sat on its own line in every screenshot of
     this screen. The row is flex again at source; only the mark goes. */

  /* ── BREATHING ROOM UNDER "+ Need more Tools?" (Loz, 2026-09-07) ──────────
     The dashed button butted straight into the next category's lavender bar.
     Measured: its bottom edge and the following group's top were both at
     y=4053 — a gap of exactly ZERO.

     THERE WAS NO RHYTHM TO RESTORE, WHICH IS WHY THIS IS AN ADDITION RATHER
     THAN A FIX. Every group-to-group gap in the kit table measures 0, and
     every group's tail (last row to group bottom) measures 0 as well — the
     separation elsewhere comes from the lavender header bar itself. The tools
     group is the only one that ends in a CONTROL instead of an item row (its
     62px tail is entirely this button plus the engine's 6px top margin), and a
     full-width dashed outline against a solid bar is the one place that reads
     as cramped.

     24px, TAKEN FROM THE BUTTON'S OWN PADDING (`padding: 18px 20px` inline at
     legacy-core.js:4712) rather than picked by eye — the space below reads as
     a continuation of the button's own breathing room, a shade more so the
     dashed edge clears the bar.

     ON THE BODY, NOT THE BUTTON, and that is the whole reason it works in both
     states. `#wOptionalToolsBody` is the collapsible panel: it is 0-tall when
     the panel is shut, so the margin lands under the button; when the panel is
     open it lands under the tools list. A margin on the BUTTON would open a
     gap between the button and the list it expands into.

     PRINT CANNOT SEE IT: css/thermii.css's print block hides
     `#wOptionalToolsBtn` and `#wOptionalToolsBody` outright
     (:2289/:2392), so this cannot move a page break in the printed quote.
     Water only — there is no electric optional-tools panel. */
  #wOptionalToolsBody {
    margin-bottom: 24px;
  }

  /* ── THE SAME GAP UNDER THE ADDITIONAL-PRODUCTS PANEL ─────────────────────
     js/thermii-extra-products.js builds a second dashed bar in the engine's
     own shape, at the foot of the System Components category rather than
     Tools. The reasoning above applies to it unchanged, so the value is the
     same 24px rather than a second judgement about the same bar.

     ON THE BODY, NOT THE WRAPPER, for the reason the tools panel gives: the
     body is 0-tall when the panel is shut, so the gap lands under the button
     in that state and under the list when it is open.

     PRINT CANNOT SEE IT either — the whole wrapper is hidden in the print
     block at the foot of this file, which is where it has to be: the tools
     panel's own print rule lives in css/thermii.css, and that file is
     byte-locked. */
  #wExtraProductsBody {
    margin-bottom: 24px;
  }

  /* ── THE CATEGORY SUBTOTAL, AND THE COLLAPSE CUE ON HOVER ─────────────────
     The design puts the group's own total at the right-hand end of the row, in
     the label's purple, and shows NO collapse control.

     THE CUE IS BACK ON HOVER (Loz, 2026-09-03). Hiding it outright matched the
     design but left a working control with no visible affordance, and
     "collapsible categories" was his own note #10. So the row is bare at rest
     and reveals the control when pointed at.

     ITS BOX IS KEPT, ONLY ITS PAINT IS HIDDEN — `opacity`, never
     `display: none`. Two things depend on that:

       * NOTHING MOVES ON HOVER. The control is a flex child sitting to the
         LEFT of the subtotal, so removing it from layout would slide the
         subtotal 36px sideways every time the pointer crossed the row.
       * THE ROW'S HEIGHT IS UNCHANGED, and the design agrees with it: the
         lavender row measures **55px** there, which is exactly what this 36px
         control plus the engine's 10px padding already gives. These rows DO
         reach the printed quote, so a height change here would re-paginate a
         document customers keep.

     `box-sizing: border-box` IS WHAT MAKES THE BORDER FREE. The engine writes
     `width:36px` with a 2px border, so thinning it to 1px would otherwise
     shrink the box by 2px and take the row's height with it.

     TOUCH GETS IT PERMANENTLY. There is no hover on a phone, so
     `@media (hover: none)` shows the control at rest rather than leaving
     those customers with no cue at all — which is the hole a hover-only
     affordance would otherwise open.

     KEYBOARD TOO: `:focus-within` covers tabbing onto the row, which carries
     `tabindex="0"` and is the button.

     `pointer-events: none` because the ROW is the control — the glyph must not
     become a second, smaller target inside it.

     PRINT NEEDS NOTHING: css/thermii.css's print block already hides every
     chevron by id prefix with `display:none!important`, which outranks an
     opacity of 1 whatever the state. */
  /* ── SHOWN AT REST, NOT ON HOVER (Loz, 2026-09-07) ────────────────────────
     "The collapsable arrow only appears on the bar on mouseover. Do we even
     need this or shall we have it displayed another way so it's more
     obvious?" The control is worth keeping — a water kit runs to five
     categories and a dozen-plus rows, and collapsing them is how you read it
     — so the answer is the second half of his question: it is displayed at
     rest now.

     THIS COSTS NO LAYOUT AT ALL, which is exactly why the hover version was
     built on `opacity` rather than `display`: the box was always in the row,
     holding its 36px open. So going from 0 to 1 moves nothing, changes no
     row height, and cannot re-paginate the printed quote. It also makes the
     desktop behave like touch, which has shown it at rest since the cue was
     built (`@media (hover: none)` below), rather than giving the two
     different affordances. */
  [id^="wCatChev-"],
  [id^="eKitRoomChev-"] {
    box-sizing: border-box !important;
    border: 1px solid #d3d8e6 !important;
    border-radius: 10px !important;
    background: #fff !important;
    opacity: 1;
    transition: opacity 0.15s ease;
    pointer-events: none;
    /* the engine writes the rotation onto this span; the caret turns instead,
       so a rounded square cannot tip onto its corner */
    transform: none !important;
  }

  .wkit-cat-hdr:hover [id^="wCatChev-"],
  .wkit-cat-hdr:hover [id^="eKitRoomChev-"],
  .wkit-cat-hdr:focus-within [id^="wCatChev-"],
  .wkit-cat-hdr:focus-within [id^="eKitRoomChev-"] {
    opacity: 1;
  }

  @media (hover: none) {
    [id^="wCatChev-"],
    [id^="eKitRoomChev-"] {
      opacity: 1;
    }
  }

  [id^="wCatChev-"] svg,
  [id^="eKitRoomChev-"] svg {
    stroke: #5b6480;
    transition: transform 0.25s ease;
    transform: rotate(180deg);
  }

  .wkit-cat-hdr[aria-expanded="false"] [id^="wCatChev-"] svg,
  .wkit-cat-hdr[aria-expanded="false"] [id^="eKitRoomChev-"] svg {
    transform: rotate(0deg);
  }

  /* ── THE SUBTOTAL'S BOX IS FIXED SO THE CHEVRONS LINE UP ─────────────────
     Loz, 2026-09-07: "Can we have the collapsable arrows all aligned
     vertically when closed — if you look at the attached you can see they are
     not aligned when all the sections are closed up."

     THE CAUSE IS THIS FIGURE, NOT THE CHEVRON. The control is a flex child
     sitting to the LEFT of the subtotal, so its x is the row's right edge less
     the subtotal's width — and the subtotals are different widths. Measured at
     1850px before the fix:

         £110.00 / £351.97   69.2px wide   chevron at x 1207.8
         £29.99 / £39.98 / £14.49   58.4   chevron at x 1218.6
         £0.00                      47.6   chevron at x 1229.4

     a 21.6px spread, which is exactly the two characters' difference at this
     size. So the fix is to stop the figure sizing the row: a fixed box with
     the text ranged right inside it.

     80px IS DERIVED, NOT PICKED. These figures are already tabular-nums, so
     every character is the same 9.886px measured here: 80px is eight of them —
     `£9999.99`. A category subtotal above that would push its own chevron
     left again, degrading exactly as today and only for that row; there is no
     width that covers every possible figure, so this covers every plausible
     one and fails gracefully rather than clipping.

     The right EDGE does not move — the figure still ends where the line
     totals and the 'TOTAL' label end — because only the box grows leftwards
     and the text is right-ranged in it. */
  .thermii-cat-sub {
    flex-shrink: 0;
    min-width: 80px;
    margin-left: 12px;
    color: var(--journey-selected, #5117f7) !important;
    font-family: 'Poppins', sans-serif;
    font-size: 16px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    text-align: right;
  }


  /* ── THE KIT SUMMARY SIDEBAR, TO LOZ'S DESIGN (2026-09-03) ────────────────
     He sent the Final Screen design a second time: "nothing seems to have
     changed". Fair — the twelve notes had been done and the screen's structure
     had not. MEASURED at 1850px first, which halved the work: the white card,
     the 12px radius, the cart-and-title header, the Items/VAT rows and the
     purple total already matched. These are the five things that did not.

     Applied at every width, not just the sidebar's: this is the ARRANGEMENT of
     the panel's content, and it reads correctly in the two-column layout below
     1500px too. Only the 2x2 grids are width-aware. */

  /* 1. "Total Kit Cost:" at 28px was competing with the figure under it. The
        design makes it a legend: small, grey, uppercase, letter-spaced — the
        same treatment as the kit table's column header and the sticky bar's
        own label, so the three agree.

        IT NEEDED ITS INLINE STYLE STRIPPED, and only measuring showed that:
        the rule matched and `text-transform` took effect while `font-size` and
        `color` did not, because index.html carried 28px and #222 inline. The
        attribute is gone and the div carries `.kit-total-lbl` instead — the
        THIRD inline-margin/size trap on this screen today (water step 5's
        subtext, the note's 16px, this). When a rule half-applies, look for an
        inline style before doubting the selector. */
  .kit-total-lbl {
    margin-bottom: 4px;
    color: #6b7280;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }

  /* 2. THE TRUST ROWS ARE PAIRED 2x2 (Loz, 2026-09-07), reversing the stacking
        he chose on 2026-09-04 — and the reversal is a MEASUREMENT, not a
        change of mind about the width. Both of the reasons the pairing did not
        fit have since gone:

            2026-09-04   sidebar 330, 2x2, WITH the 32px icon tiles   157px a
                         cell, every label wrapped -> he chose 1 column at 360
            2026-09-07am the icons went (43px a row), but a 2x2 at 360 still
                         wrapped, because equal halves give 148px a cell
            2026-09-07pm columns sized to CONTENT, and the band goes full-bleed
                         for 8px more -> it fits, with 9px to spare

        WHY EQUAL COLUMNS CANNOT WORK HERE AND `auto` CAN. The two columns do
        not need the same room: the left one is governed by "We'll match any
        like-for-like" at 162px, the right by "100% secure payments" at 133px.
        Equal halves size BOTH to the wider one's requirement and there is not
        318px of room for that; `auto` tracks take what each column's own
        content needs — 162 + 14 + 133 = 309 against the 318 the full-bleed
        band leaves. So the design's pairing is available at 360 after all, and
        the sidebar keeps the width Loz picked.

        `auto`, NOT `max-content`, and the difference only shows in the case
        that matters: the sidebar column is `minmax(280px, 360px)`, so if it
        ever does resolve below 360 an `auto` track SHRINKS and the text wraps,
        where `max-content` would overflow the card. Wrapping is the graceful
        end of that. */
  #wKitFooterCard .tks-trust,
  #kitFooterCard .tks-trust {
    grid-template-columns: auto auto;
    /* 17px, which is his screenshot's own row pitch (99 device px at 2x = 49.5
       CSS) less the 32.4px cell the two line boxes make. 14px across, matching
       the gap this block already used. */
    gap: 17px 14px;
    text-align: left;
  }

  /* THE LABEL IS 13px AND THE SUB-LINE 11.5px, which is what his screenshot
     measures rather than a guess. Rendering the strings in the app's own Sora
     at 2x DPR: "Price match promise" is 276 device px, and 276/2 divided by
     Sora's 10.579 px-per-px at 600 gives 13.04; the sub-lines solve to
     11.3-11.7. The row pitch confirms it independently — 13x1.3 + 11.5x1.35 is
     32.4, and his rows pitch at 49.5 with a 17px gap.

     This REPLACES a blanket `.tks-trust *` at 11.5px, which flattened the pair
     into one size. It costs no width: the left column is governed by its
     SUB-LINE at 162px, and "Price match promise" at 13px is 137px. */
  #wKitFooterCard .tks-trust strong,
  #kitFooterCard .tks-trust strong {
    font-size: 13px;
    line-height: 1.3;
  }

  #wKitFooterCard .tks-trust span,
  #kitFooterCard .tks-trust span {
    font-size: 11.5px;
    line-height: 1.35;
  }

  /* 3. The primary's solid purple lives in css/thermii-system.css:760, where
        its colour was already declared. A duplicate here LOST: (1,1,0) against
        that rule's (1,1,1) with both !important, and it loads earlier too.
        Measured — the button stayed a gradient until it was changed at source. */

  /* 4. The sidebar's avatar and "What would you like to do with your Kit?" are
        not in the design, and the panel does not need a question when its two
        buttons say what they do. HIDDEN, NOT REMOVED — legacy-core.js:3311
        animates that avatar when the kit screen is revealed, so the node has to
        stay findable. The hero's avatar is untouched: Loz's note 1 protects it
        and it is a different element. */
  /* `!important` BECAUSE THIS CLASS NOW LANDS ON NODES THAT CARRY AN INLINE
     display. It used to go on the intro CONTAINER, which has none — but that
     container also holds the CTA block, so hiding it took the buttons with it
     (measured on the live site). The class goes on the copy's own children
     now, and the engine writes `display:inline-block` inline on the avatar's
     wrapper, which a plain rule cannot beat. */
  .thermii-kit-intro {
    display: none !important;
  }

  /* 5. The five stacked buttons become the design's three groups. The OR rule
        is a label with a line either side, drawn with borders on a flex row so
        it cannot fall out of alignment with the text. */
  .thermii-kit-or {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 14px 0;
    color: #9aa2b4;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }

  .thermii-kit-or::before,
  .thermii-kit-or::after {
    content: '';
    flex: 1 1 0;
    height: 1px;
    background: var(--journey-border, #e4e7ef);
  }

  /* The promoted secondary. It keeps its own hover handlers — they write
     inline background, which is why no background is declared here. */
  /* The promoted secondary. WHITE IS DECLARED and that was a real bug, not a
     tidy-up: index.html gives this button `background:#ff5b00` inline, so
     setting only the border and the ink left purple text on an orange fill —
     measured, and visibly wrong. Same shape as the add-room button's missing
     white (thermii-skin.js:1425 records it). The hover handlers still win the
     moment the pointer arrives, which is why no hover colour is declared. */
  .thermii-kit-save {
    width: 100%;
    min-height: 52px;
    border: 1.5px solid var(--thermii-purple, #4e20f5) !important;
    border-radius: 12px !important;
    background: #fff !important;
    color: var(--thermii-purple, #4e20f5) !important;
  }

  /* The engine's Save-to-Account button carries a person icon, which sits hard
     against the promoted label with no gap — and the design has no icon on
     this control at all. The DOCUMENTS buttons keep theirs: note #7 asked for
     those specifically. */
  .thermii-kit-save svg {
    display: none;
  }

  .thermii-kit-docs-lbl {
    margin: 20px 0 10px;
    color: #6b7280;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }

  /* Four buttons, two up. At 240px inside the sidebar each lands ~114px, which
     holds "Download Quote" on one line at 13px. */
  .thermii-kit-docs {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: 10px !important;
  }

  @media screen and (max-width: 620px) {
    /* One up on a phone: two 13px labels across a narrow screen wrap. */
    #wKitFooterCard .tks-trust,
    #kitFooterCard .tks-trust,
    .thermii-kit-docs {
      grid-template-columns: minmax(0, 1fr) !important;
    }
  }

  /* ── #3: the sticky total bar, BELOW THE SIDEBAR ONLY (Loz, 2026-09-03) ───
     "Sidebar on desktop, bottom bar on mobile only."

     ONE STICKY SURFACE PER BREAKPOINT. The sidebar appears at 1500px
     (thermii-system.css:363) and already carries the total and the primary
     action, so above that a bar would show the same number and the same
     button twice while eating vertical space on the app's longest scroll.
     Below 1500 there is no sidebar, so this is the only persistent price —
     which is why the cut is at the SIDEBAR's breakpoint and not at a phone
     width. Laptops between 900 and 1500 are exactly the case that needs it.

     HIDDEN BY DEFAULT, shown only on the kit screen under 1500. Visibility is
     entirely CSS on the body marker, so navigating away from the kit hides it
     with no teardown in JS — the element simply stays in the body, inert.

     z-index 60: above the sticky header's 40 (thermii-skin.css:175) and well
     below the modal scrim's 200 (:3051), so a dialog still covers it.

     THE PAGE GETS BOTTOM PADDING TO MATCH, or the bar covers the last of the
     small print — which on this screen is the disclaimer, i.e. the one block
     a customer might need to read in full. */
  .thermii-kit-bar {
    display: none;
  }

  @media screen and (max-width: 1499px) {
    body[data-thermii-screen$="-kit"] .thermii-kit-bar {
      display: flex;
      position: fixed;
      right: 0;
      bottom: 0;
      left: 0;
      z-index: 60;
      align-items: center;
      gap: 16px;
      padding: 12px 20px;
      border-top: 1px solid var(--journey-border, #e4e7ef);
      background: #fff;
      box-shadow: 0 -4px 18px rgba(16, 23, 53, 0.08);
    }

    /* Room for the bar, so it never sits over the disclaimer. */
    body[data-thermii-screen$="-kit"] .kb-disclaimer {
      padding-bottom: 92px;
    }

    /* AND THE ENGINE'S OWN OLD BAR STANDS DOWN WHERE THIS ONE SHOWS (Loz's
       phone check, 2026-09-28). legacy-core.js's _thermiiStickySync builds
       #thermiiStickyTotal — orange price, lime button, Poppins — whenever the
       total block is below the fold, and nothing had ever hidden it on screen,
       so under 1500px the two bars stacked at the foot of the kit page. Its
       display is written INLINE every 600ms, hence !important. */
    body[data-thermii-screen$="-kit"] #thermiiStickyTotal {
      display: none !important;
    }

    .thermii-kit-bar-money {
      display: flex;
      align-items: baseline;
      gap: 8px;
      flex-shrink: 0;
    }

    .thermii-kit-bar-label {
      color: #6b7280;
      font-size: 10.5px;
      font-weight: 600;
      letter-spacing: 0.06em;
      text-transform: uppercase;
    }

    .thermii-kit-bar-total {
      color: var(--thermii-purple, #4e20f5);
      font-size: 22px;
      font-weight: 600;
      font-variant-numeric: tabular-nums;
    }

    .thermii-kit-bar-vat {
      color: #6b7280;
      font-size: 11px;
    }

    /* The meta line is the first thing to go when the bar runs out of room:
       the total and the actions are what it exists for. */
    .thermii-kit-bar-meta {
      flex: 1 1 auto;
      min-width: 0;
      overflow: hidden;
      color: #6b7280;
      font-size: 11.5px;
      white-space: nowrap;
      text-overflow: ellipsis;
    }

    .thermii-kit-bar-acts {
      display: flex;
      gap: 10px;
      flex-shrink: 0;
    }

    .thermii-kit-bar-btn {
      min-height: 42px;
      padding: 0 16px;
      border-radius: 11px;
      font-family: inherit;
      font-size: 13.5px;
      font-weight: 600;
      cursor: pointer;
      white-space: nowrap;
    }

    .thermii-kit-bar-btn-ghost {
      border: 1.5px solid var(--thermii-purple, #4e20f5);
      background: #fff;
      color: var(--thermii-purple, #4e20f5);
    }

    .thermii-kit-bar-btn-primary {
      border: 0;
      background: var(--thermii-purple, #4e20f5);
      color: #fff;
    }
  }

  /* On a phone the meta line has nowhere to go and the buttons need the
     width. The total and the actions survive; the rest is decoration. */
  @media screen and (max-width: 620px) {
    .thermii-kit-bar-meta,
    .thermii-kit-bar-vat {
      display: none;
    }

    body[data-thermii-screen$="-kit"] .thermii-kit-bar {
      gap: 10px;
      padding: 10px 12px;
    }
  }

  /* THE ELECTRIC KIT'S STRAY RULE COMES OFF THE SCREEN (Loz, 2026-09-24: "remove
     the line here"). The electric renderer writes a bare 1px #c8c8c8 div between
     the heat panel and the kit list (legacy-core.js:1098); water has none. Hidden
     on screen only — the print block puts it back, because the PDF clone renders
     in screen media and its 21px feed the page-break model. */
  #kitItems div[style*="height:1px;background:#c8c8c8"] { display: none; }
  /* and the electric description sits 2px under the name, water's spacing
     (its detail carries margin-top:2px), now the SKU follows it */
  #kitItems .kit-row .kit-detail { margin-top: 2px; }
  /* the electric "Need more heat output?" strip runs the row's full width, as
     water's does: the engine writes it with `margin:8px 16px 10px` inline where
     water's is `0 0 10px` (Loz, 2026-09-24). Hidden on paper by pdf-clean. */
  #kitItems .kit-switch-strip { margin: 0 0 10px !important; }
  /* the electric heat panel sits under its heading as water's does (Loz,
     2026-09-24: "the title needs to be closer to the panel below it"). The
     electric renderer writes margin-top:24px inline (water's panel has none),
     so the "Estimated heat requirement" legend stood 44px above it against
     water's 12. Screen only: the print block puts the 24px back, so paper and
     its page breaks are unchanged. */
  #kitItems .wkit-heat-panel { margin-top: 0 !important; }
  /* ...and electric's legend sits inside the room column, whose flex gap adds
     10px to its own 10px margin; 2px here makes the 12 water measures */
  #kitItems .wkit-rooms-col > .thermii-kit-eyebrow-heat { margin-bottom: 2px !important; }

  /* ── THE ELECTRIC ROOM CARD'S GROUPS SIT SIDE BY SIDE (Loz, 2026-09-24) ──
     js/thermii-kit-screen.js wraps each group in .thermii-spec-g when a card
     has no pipe layout. Three equal columns, a hairline between them, the
     same 24px inset as the water card's halves. Two columns from 1180px down,
     one on a phone. */
  .thermii-room-grid.is-spec-only { grid-template-columns: minmax(0, 1fr); }
  .thermii-room-grid.is-spec-only .thermii-spec {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0;
    padding: 24px 0;
  }
  .thermii-room-grid.is-spec-only .thermii-spec-g {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 0 26px;
  }
  .thermii-room-grid.is-spec-only .thermii-spec-g + .thermii-spec-g {
    border-left: 1px solid var(--journey-border, #ececf4);
  }
  @media (max-width: 1180px) {
    .thermii-room-grid.is-spec-only .thermii-spec { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 24px; }
    .thermii-room-grid.is-spec-only .thermii-spec-g:nth-child(odd) { border-left: 0; }
  }
  @media (max-width: 700px) {
    .thermii-room-grid.is-spec-only .thermii-spec { grid-template-columns: minmax(0, 1fr); }
    .thermii-room-grid.is-spec-only .thermii-spec-g { border-left: 0 !important; padding: 0 16px; }
    .thermii-room-grid.is-spec-only .thermii-spec-g + .thermii-spec-g {
      margin-top: 4px;
      padding-top: 20px;
      border-top: 1px solid var(--journey-border, #ececf4);
    }
  }

  /* ── THE KIT SCREEN FITS A 375px PHONE (Loz, 2026-09-24) ──────────────────
     Measured at 375: the page was 613px wide. Two things ran off the edge —
     the property-age picker beside the heat panel's title (253px wide, from
     x=360) and the sticky bar's two buttons (234px beside a 199px total). */
  @media screen and (max-width: 480px) {
    /* The bar goes to two rows: the total, then both buttons sharing the
       width. The page's clearance grows to match, so the bar still never
       sits over the disclaimer. */
    body[data-thermii-screen$="-kit"] .thermii-kit-bar {
      flex-wrap: wrap;
      padding-bottom: calc(10px + env(safe-area-inset-bottom));
    }
    body[data-thermii-screen$="-kit"] .thermii-kit-bar-money { flex: 1 1 100%; }
    body[data-thermii-screen$="-kit"] .thermii-kit-bar-acts { flex: 1 1 100%; }
    body[data-thermii-screen$="-kit"] .thermii-kit-bar-btn { flex: 1 1 0; min-width: 0; }
    /* the LIVE disclaimer only (a child of .kb-wrapper): the PDF clone renders
       in screen media, and its disclaimer's height feeds the page-break model */
    body[data-thermii-screen$="-kit"] .kb-wrapper > .kb-disclaimer { padding-bottom: 140px; }
  }
  @media screen and (max-width: 700px) {
    /* The picker takes a row of its own under the title. The header stays
       nowrap on wider screens so its height never changes with the top-up
       pill; here the picker is ALWAYS on the second row, pill or no pill, so
       the height is still fixed. */
    .wkit-heat-hdr { flex-wrap: wrap !important; row-gap: 10px; }
    .thermii-heat-picker { flex: 1 0 100%; margin: 0; }
    .thermii-heat-picker .wkit-heat-pills { display: flex !important; width: 100%; }
    /* the share goes on the box's WRAPPER: each box sits in a pillwrap the
       engine lays out as an inline flex COLUMN, where a 0 basis on the box
       would take its height, not its width (measured: 12px boxes) */
    .thermii-heat-picker .wkit-heat-pillwrap { flex: 1 1 0; min-width: 0; }
    .thermii-heat-picker .thermii-era-box { align-self: stretch; padding: 6px 4px; justify-content: center; }
    .thermii-heat-picker .thermii-era-l { font-size: 12.5px; white-space: nowrap; text-align: center; }
    /* The top-up pill comes and goes with the band, and at this width it takes
       a row of its own under the title — so its row is HELD when there is no
       pill, at the pill's own 21.5px, and an era toggle never changes the
       header's height (measured before this: 167px with the pill, 134
       without — a 33px jump on every click). Title, pill-or-reserve, picker. */
    .wkit-heat-hdr-warn,
    .thermii-heat-okpill,
    .thermii-heat-hdrfigs { order: 1; }   /* the green pill holds the same row */
    .thermii-heat-picker { order: 2; }
    .wkit-heat-hdr:not(:has(.wkit-heat-hdr-warn, .thermii-heat-okpill)):not(#thermiiPdfRoot .wkit-heat-hdr)::before {
      content: '';
      order: 1;
      flex: 1 0 100%;
      height: 21.5px;
    }
  }

  /* ── #12: the small print ranges left with the panels (Loz, 2026-09-03) ───
     "Range the Disclaimer and copyright text to the left of the panels as per
     the 'Your Selections' screen."

     MEASURED at 1850px before: the panels start at x=81 and the disclaimer at
     x=305 — 224px adrift, with the version line centred below it.

     THE CAUSE IS THE SIDEBAR, WHICH IS WHY THIS IS A KIT-SCREEN PROBLEM ONLY.
     `.kb-disclaimer` is a child of `.kb-wrapper`, not of the workspace, and
     centres itself with `margin: 0 auto` inside a 1762px container. On Your
     Selections the panel is centred too, so the two happen to line up — that
     is what the existing note at :1195 means by "its left edge lands on the
     same x as the summary rows". On the kit screen the sidebar pushes the
     content into grid column 1, the panel goes hard left, and the centred
     disclaimer is left behind. So the fix is scoped to the kit marker and the
     summary screen keeps working as it does.

     `var(--journey-gutter)` RATHER THAN A MEASURED NUMBER. The workspace is
     inset by exactly that (`padding: 0 var(--journey-gutter)` at :109), and it
     is a clamp that changes with the viewport — 36px at 1850, 24px at the
     bottom of its range. Writing 36px here would align on Loz's monitor and
     drift on a laptop.

     MAX-WIDTH WAS LEFT AT 1240px BY THIS CHANGE and is no longer, which the
     note below records — the reason given here (the PDF's page-break model
     measures this block's height) is now discharged by pinning the clone
     explicitly at :1222 instead of by leaving the screen alone. Inside
     @media screen as well, so window.print() cannot see any of it. */
  body[data-thermii-screen$="-kit"] .kb-disclaimer {
    /* ── IT ENDS WHERE THE KIT PANEL ENDS (Loz, 2026-09-17: "end its container
       aligned with the kit components container above it — currently its
       running over to beneath the right hand sticky sidebar").

       MEASURED FIRST, and the fault is a FIXED WIDTH against a COLUMN that
       moves. The block was 1240px wide from the panel's own left edge, while
       #wKitPanel is a grid item capped at 1280 inside column one, so the two
       agree at no width at all:

           1500   panel 958   disclaimer 1240   282px past it, 262 under the card
           1600   panel 1058  disclaimer 1240   182px past it, 162 under the card
           1850   panel 1280  disclaimer 1240   40px short
           2000   panel 1280  disclaimer 1240   40px short
           1400   panel 1246  disclaimer 1240   6px short   (no sidebar)

       So it ran beneath the sticky summary card between 1500 and ~1700 — what
       Loz is looking at — and stopped short of the panel everywhere else.

       THE FIX IS A MARGIN ON BOTH SIDES, NOT A SECOND CAP. The left margin
       already lands on the workspace's own inset; giving the right the same
       var makes the box the workspace's width, and `max-width: 1280px` is
       .kb-card's own cap read out of css/thermii-skin.css — so the box IS the
       panel's box at every width, derived rather than copied. Below the
       sidebar breakpoint that is the whole story. */
    margin-right: var(--journey-gutter);
    margin-left: var(--journey-gutter);
    max-width: 1280px;
    /* AND IT SITS FURTHER DOWN (Loz, 2026-09-07). Measured: it began exactly
       where the app grid ended — a gap of ZERO — so the small print ran
       straight on from the last card. 40px is the workspace's own gutter at
       Loz's width, which is the one spacing unit this screen already uses.

       MARGIN, NOT PADDING, because the PDF's page-break spacers and its
       pass-1-to-pass-2 drift model measure this block's HEIGHT
       (legacy-core.js:5564/5578) — a margin moves it without changing that,
       where padding would grow it and re-paginate a document customers keep.
       Restored to 0 in this file's print block for the same reason. */
    margin-top: 40px;
  }

  /* AND FROM 1500px IT STOPS AT COLUMN ONE. css/thermii-system.css turns
     #waterPanel into `minmax(0, 1fr) minmax(280px, 360px)` with a 20px
     column-gap on the kit screen, and the sticky summary card holds column
     two — so the panel's right edge is the workspace's right edge less 380px.
     The disclaimer is a child of .kb-wrapper and outside that grid entirely,
     which is why it needs the offset stated rather than inherited.

     380 IS DERIVED FROM THAT RULE, not measured off a screenshot: 360 is the
     column's max track and 20 the gap. Measured after, at every width the
     sidebar exists: 1500 / 1600 / 1850 / 2000 all report the disclaimer's
     right edge exactly on #wKitPanel's, and the left edge unmoved. */
  @media screen and (min-width: 1500px) {
    body[data-thermii-screen$="-kit"] .kb-disclaimer {
      margin-right: calc(var(--journey-gutter) + 380px);
    }
  }

  /* AND THE INSET FOLLOWS THE WORKSPACE'S OWN LADDER, because the gutter is
     not the workspace's padding at every width: `.thermii-workspace` takes
     `0 var(--journey-gutter)` by default but is overridden to 20px at 900 and
     14px at 700 (this file, search `.thermii-workspace` in those blocks). The
     clamp bottoms out at 24, so below 900 the disclaimer would sit inside the
     panel on both edges while claiming to align with it.

     MEASURED AT 375, and it is a pre-existing miss rather than this change's:
     before today the block sat 10px right of the panel AND 14px past its right
     edge — the 2026-09-03 fix set the left margin to the gutter and left the
     right at 0, so it ran to the very edge of the workspace. Both edges land
     on the panel's now: 0/0 at 375, 700, 900 and every width above. */
  @media screen and (max-width: 900px) {
    body[data-thermii-screen$="-kit"] .kb-disclaimer {
      margin-right: 20px;
      margin-left: 20px;
    }
  }

  @media screen and (max-width: 700px) {
    body[data-thermii-screen$="-kit"] .kb-disclaimer {
      margin-right: 14px;
      margin-left: 14px;
    }
  }

  /* The version and copyright line is centred everywhere else; on this screen
     it joins the small print it sits beneath.

     IT NEEDS THE WRAPPER'S BOX, NOT A FIXED INSET, and that is the whole
     difficulty. js/thermii-skin.js:597 MOVES this line OUT of `.kb-wrapper`
     (the wrapper's overflow:hidden made it a node move rather than a margin),
     so it spans the body — measured left edge 20px against the disclaimer's
     81px. The gap between them is the wrapper's own `margin: 0 auto`, which
     is viewport-dependent: any padding I wrote here would align at 1850px and
     drift at every other width.

     So it borrows the wrapper's max-width and centring, then takes the same
     gutter inset the disclaimer does. Same mechanism, so it tracks the
     viewport identically instead of approximating it.

     `body.thermii-skin` IS IN THE SELECTOR TO WIN A TIE, not for scoping:
     css/thermii-skin.css sets `text-align: center` on this element at (0,2,1)
     and LOADS AFTER journey.css, so an equally specific rule here loses on
     order. Adding the skin class makes it (0,3,1). Verified by measuring —
     the first version of this rule changed nothing at all. */
  body.thermii-skin[data-thermii-screen$="-kit"] .kb-version {
    /* `align-self: stretch` and `width: 100%` come FIRST in the reasoning, not
       as belt and braces: the node skin.js moves this into centres its
       children, so the element SHRINK-WRAPPED to its text — measured 177px
       wide sitting at x=837, and `margin: auto` then centred that little box.
       Left-aligning text inside a shrink-wrapped box does nothing. It has to
       fill the cross axis before the box can be positioned at all. */
    align-self: stretch;
    /* THE WRAPPER'S OWN BOX, COPIED EXACTLY — `width: calc(100% - 48px)` and
       `max-width: 1780px` are what css/thermii-skin.css:81 gives .kb-wrapper,
       and this element's parent spans the same body content box (measured: it
       started at x=20 with width 1810, the body's content box to the pixel).
       So the two boxes now coincide at EVERY viewport width and the shared
       gutter lands both left edges on the same x.

       My first two attempts were both viewport-specific and wrong. A fixed
       `padding-left` aligned at 1850 and drifted everywhere else, because the
       gap between the two containers IS the wrapper's `margin: auto`. Then
       `max-width: 1720px` — journey.css's own value at :94 — put the text at
       101 against the panel's 81, because the SKIN overrides that to 1780 and
       the boxes were 40px different in width. Copy the effective value, not
       the one this file declares. */
    width: calc(100% - 48px);
    max-width: 1780px;
    margin-right: auto;
    margin-left: auto;
    /* +4px is the disclaimer's OWN horizontal padding (`padding: 28px 4px 0`
       in css/thermii.css), so this lines the two blocks' TEXT edges up rather
       than their boxes — measured 80 against 85 without it. */
    padding-left: calc(var(--journey-gutter) + 4px);
    /* ── CENTRED AGAIN (Loz, 2026-09-07) ──────────────────────────────────
       "Center this on the final kit summary screen: V.1.xx · © 2026
       Thermii™." That REVERSES the left-ranging above, which was the second
       half of note #12 — and the box machinery is still doing work, so the
       rule stays rather than being deleted: without `align-self: stretch` and
       the wrapper's own width this element shrink-wraps to its text and sits
       wherever the flex parent puts it, which at 1850px measured x=837, i.e.
       not centred on the panel either. So it now fills the same box the
       disclaimer occupies and centres INSIDE it, which is what makes it
       centred on the content column rather than on the body. The gutter and
       the +4px stay for the same reason: they are what make the box coincide
       with the disclaimer's. */
    text-align: center;
  }

  /* ── #4: the collapse arrows are bigger (Loz, 2026-09-03) ──────────────────
     "make the arrow bigger in the button — it's currently very small on the
     attached design."

     Measured first: every one of the six chevrons on the kit screen is a
     13px glyph inside a 36px disc — 36% of the disc, which is why it reads
     small however bold the stroke. 18px takes it to 50%, matching the
     proportion the skin's own drawn chevrons use, and the disc is left alone:
     36px is already a comfortable target, well over the 24px WCAG minimum the
     theme toggle had to be grown to reach.

     SIX FAMILIES, ONE RULE, reached by attribute selector because the engine
     gives them INDEXED IDS and no class:
       eRoomChev-N / wRoomChev-N     the room cards
       eHeatChev   / wHeatChev       the heat requirement panel
       eKitRoomChev-<key>            the electric kit's per-room blocks
       wCatChev-<key>                the water kit's category blocks

     CSS BEATS THE SVG'S width/height ATTRIBUTES, which is what makes this
     possible without editing manifest-locked code: the engine writes
     `<svg width="13" height="13">` inline at all six sites, and a
     presentation attribute loses to any stylesheet rule.

     The stroke stays at the engine's 3.5. Scaling a stroked chevron scales
     its stroke with the viewBox, so the mark grows in proportion; thickening
     it too would make it heavier than the skin's other chevrons rather than
     simply larger. */
  [id^="wRoomChev-"] svg,
  [id^="eRoomChev-"] svg,
  [id^="eKitRoomChev-"] svg,
  [id^="wCatChev-"] svg,
  #wHeatChev svg,
  #eHeatChev svg {
    width: 18px;
    height: 18px;
  }

  .eq-sum-row .eq-ans-check {
    display: grid;
    place-items: center;
    width: 18px;
    height: 18px;
    flex: 0 0 18px;
    border-radius: 50%;
    background: var(--journey-green);
  }

  .eq-sum-row .eq-ans-check svg {
    width: 12px;
    height: 12px;
  }

  .eq-sum-row .eq-ans-q,
  .eq-sum-row .eq-ans-v {
    font-size: 13px;
  }

  /* THE ELECTRIC VALUES LINE UP (Loz, 2026-09-24: "Self-Levelling Compound is
     pushing it out of alignment"). Labels do not wrap and css/thermii.css
     gives them min-width:150px; "Self-Levelling Compound:" measures 170 at
     this 13px, so its value started 20px right of every other. 180 fits the
     longest electric label with the others' breathing room. Water's longest
     ("Manifold Distance:") fits 150, so water is untouched. The <=700px block
     still releases the minimum for the phone's wrapped layout. */
  #eq-summary .eq-sum-row .eq-ans-q {
    min-width: 180px;
  }

  .eq-sum-row .eq-ans-v {
    color: var(--thermii-navy);
    font-weight: 600;
  }

  /* ── THE MULTI-ROOM ROOM LIST READS AT THE REVIEW'S OWN SCALE (Loz,
     2026-09-14: the listed selections and "Rooms:" looked large) ──────────
     wShowSummary / showSummary render the "Rooms:" block with INLINE sizes on
     manifest-locked markup — "Rooms:" at 18px, the per-room detail label/value
     at 15px — larger than the 13px the review's other rows (.eq-ans-q /
     .eq-ans-v, just above) sit at. Brought down to that same 13px so the room
     block matches the rest of the panel; the room NAME keeps a 14px/600 step
     up so it still reads as a per-room heading. !important because the sizes
     are inline; both journeys use these classes. */
  body.thermii-skin .wsel-rooms-lbl,
  body.thermii-skin .wsel-sub-q,
  body.thermii-skin .wsel-sub-v {
    font-size: 13px !important;
  }

  /* ONE LABEL GREY ON THE REVIEW (Loz, 2026-09-25: "bring them onto one").
     The review's own rows ("Heat Source:") are css/thermii.css's
     `.eq-sum-row .eq-ans-q` — 300 / #6f6f6f, 5.1:1 on white — while the room
     details and "Rooms:" wore the engine's INLINE #888 (3.5:1, under AA's
     4.5). Now all one: that rule's values, restated with !important only
     because the engine's are inline. The mixed Rooms cards
     (js/thermii-project-review.js) use these same classes, so they follow. */
  body.thermii-skin .wsel-rooms-lbl,
  body.thermii-skin .wsel-sub-q {
    font-weight: 300 !important;
    color: #6f6f6f !important;
  }

  body.thermii-skin .wsel-room-name {
    font-size: 14px !important;
  }

  /* THE PENCIL (Loz, 2026-09-01: "shall we add a pencil icon to the edit button
     to make it more obvious?").

     A ::before, NOT an injected node, and that is load-bearing rather than
     tidiness: both review builders rebuild #wq-sum-rows / #eq-sum-rows with
     `innerHTML = html` on EVERY render, and each is reached from four call
     sites, so an icon added by script would have to be re-added after every one
     of them. CSS survives all of it and needs no wrapper — which also means the
     era row added by js/thermii-build-year-step.js gets the mark for free.

     A MASK, not a background image. The button's colour is
     var(--journey-orange), which css/thermii-system.css re-points to
     --thermii-quiet — so the name says orange and the paint is #5b6480. A data:
     URI is a separate document and inherits neither currentColor nor custom
     properties, so a colour written into the SVG would be a fourth place that
     name has to be kept in step. Masking currentColor instead means the pencil
     is whatever the label is, including through hover and disabled states, and
     there is nothing to drift. -webkit- first for Safari, which is Loz's
     browser.

     Decorative: the button still reads "Edit", so nothing is lost to a screen
     reader and no aria-label is wanted. */
  /* 'WHY DOES THIS MATTER?' GOES FROM THE COVERING SCREEN (Loz, 2026-09-01:
     "this is repeating in the Thermii insights panel").

     It is repeating VERBATIM, which is worth recording: the insight copy for
     this step (js/legacy-core.js:1994) is character-for-character the paragraph
     inside #wRoomCoveringInfo (index.html:1496) — same two sentences, same
     double break. The link opened a panel saying exactly what the insight panel
     already said a few centimetres above it.

     HIDDEN, NOT DELETED. The button carries `onclick="toggleWRoomCoveringInfo()"`
     and inline on* handlers are part of index.html's frozen contract, so the
     markup stays put — the same reason the kit screen's second Restart is hidden
     rather than removed.

     THE PANEL GOES WITH IT, and for a reason beyond tidiness: it collapses via
     `max-height: 0; opacity: 0` (css/thermii.css:1179), which hides it from the
     eye but NOT from a screen reader or find-on-page, so the duplicate paragraph
     was still being announced. With the link hidden it can never open anyway.

     Scoped to this one step on purpose — six other screens carry the same
     `.eq-unsure-link` affordance and keep it. ID specificity beats the base
     rules, so no !important is needed. */
  #wq-step-4fc .eq-unsure-link,
  #wq-step-4fc .eq-unsure-info {
    display: none;
  }

  /* ── "NOT SURE WHICH TO CHOOSE?" BECOMES A GLOSSARY CARD (Loz, 2026-09-14) ──
     Five screens open a glossary of their own tiles (water subfloor, water pipe
     spacing x2, electric subfloor, electric self-levelling; the floor-covering
     one is hidden above by an earlier call). The engine's markup is a run of
     <p class="eq-note"> each led by an inline-styled <strong>, and it carried
     no styling of its own beyond the open/close animation — so it inherited
     the column: 15px body lines up to 1,040px long, and headings still CENTRED
     by their inline style while the skin had left-ranged the body. Measured at
     1850px: heading text-align center, body left, note 944-1040px wide.

     A CARD IN THE INSIGHTS PANEL'S CLOTHES, so the screen's two help features
     read as one family: same lilac, same hairline, same radius. TWO COLUMNS on
     desktop for the GLOSSARIES (see the :has() rule below) — a five-term
     glossary is 400px tall in one column and ~240 in two, and every pixel it
     saves keeps the Insights panel and the action bar nearer the fold. One
     column under 901px, and one column for prose at any width.

     EVERYTHING GOES ON .is-open, NOT ON THE ELEMENT. Closed, the engine holds
     it at max-height:0 and opacity:0; a padding or a border on the base rule
     would be a 46px INVISIBLE GAP, because a box can never be shorter than its
     own padding and border (the heat panel lesson, 2026-09-08).

     THE CAP IS RAISED, and that is a fix, not a side effect. thermii.css opens
     it to max-height:600px; measured at 390px the five-item glossary is 622px
     of content and was being CLIPPED — the last entry cut off on a phone. 1000
     clears the tallest with the card's padding on top.

     The two !importants are for INLINE styles the engine writes on every item
     (a 28px margin-top on the first, text-align:center on each heading); the
     rest is ordinary cascade. Not a print path — question screens are never in
     the PDF. */
  body.thermii-skin .eq-unsure-info.is-open {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
    max-height: 1000px;
    margin: 22px 0 0;
    padding: 22px 26px;
    background: #f6f4ff;
    border: 1px solid #e4defb;
    border-radius: 16px;
    text-align: left;
  }

  /* ── ALL FOUR GLOSSARIES ARE ONE FLAT TWO-COLUMN CARD (Loz, 2026-09-14) ──
     He tried the project-type grouping, then a headed version, then chose the
     leanest: NO header — the card opens straight into the tiles' terms in a
     two-column grid with a hairline between rows. Applied to all four (subfloor
     x2, pipe spacing x2) so they are one component. index.html wraps the items
     as .eq-unsure-items (the head block and its .eq-unsure-cat / -cat-desc are
     gone). The self-levelling panel is prose, not a glossary, so it has no
     .eq-unsure-items wrapper and stays the single-column card it was. */
  /* the terms grid: one column on a phone, two from 901px (the width two
     ~470px columns fit, the old glossary breakpoint) */
  body.thermii-skin .eq-unsure-items {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    column-gap: 40px;
  }

  @media screen and (min-width: 901px) {
    body.thermii-skin .eq-unsure-items {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }

  /* a hairline between rows: every item takes a top rule, and the FIRST ROW
     is exempted — the first item always, and the second item too once there
     are two columns (so the top row of the grid carries no rule). The 40px
     column gap breaks each row's rule in the middle, as the mock draws it. */
  body.thermii-skin .eq-unsure-items .eq-note {
    padding: 16px 0;
    border-top: 1px solid #ece7f7;
  }

  /* Loz, 2026-09-21: the glossary term titles read a weight heavier than the
     tile labels above (inline 600 vs the tiles' .fo-label 500). Drop them to
     500 to match; !important because the weight is inline on each of the 18
     term <strong>s. Size (16px) and the block/centre layout are left as the
     inline sets them — only the weight is out of step. */
  body.thermii-skin .eq-unsure-items .eq-note strong {
    font-weight: 500 !important;
    font-size: 15px !important;
  }

  body.thermii-skin .eq-unsure-items .eq-note:first-child {
    border-top: 0;
    padding-top: 0;
  }

  @media screen and (min-width: 901px) {
    body.thermii-skin .eq-unsure-items .eq-note:nth-child(2) {
      border-top: 0;
      padding-top: 0;
    }
  }

  /* the item body reads at the Insights panel's size, not the 15px --t-body it
     would inherit (Loz, "a little large"); the term is dark and left, beating
     the engine's inline centre. margin:0 !important kills thermii.css's base
     `margin:0 auto 20px` — the padding above spaces the rows now. */
  body.thermii-skin .eq-unsure-info .eq-note {
    margin: 0 !important;
    max-width: none;
    text-align: left;
    font-size: 14.5px;
    line-height: 1.6;
  }

  body.thermii-skin .eq-unsure-info .eq-note strong {
    text-align: left !important;
    color: #1b2140;
  }

  @media screen and (max-width: 900px) {
    body.thermii-skin .eq-unsure-info.is-open {
      padding: 18px 16px;
    }
  }

  /* ── ON A PHONE THE GLOSSARY IS A BOTTOM SHEET (js/thermii-unsure-sheet.js,
     Loz 2026-09-14). The module moves the open panel to <body> — a shown
     step carries transform:translateY(0), which would pin a fixed child to
     the step — adds `.thermii-sheet`, a head (handle, title, close) and a
     scrim, and locks the page behind. These rules only paint that state;
     everything else about the card (lilac, items, headings) is the same
     rule set as the inline card above. z-index 200 is the skin's own modal
     level, above the sticky header's 40. The head is STICKY so the close
     stays put while a long glossary scrolls beneath it, and carries the
     card's lilac so nothing shows through. */
  @media screen and (max-width: 700px) {
    body.thermii-skin .eq-unsure-info.is-open.thermii-sheet {
      position: fixed;
      left: 0;
      right: 0;
      bottom: 0;
      z-index: 200;
      margin: 0;
      max-height: 82vh;
      max-height: 82dvh;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      padding: 0 18px calc(24px + env(safe-area-inset-bottom));
      border: 0;
      border-radius: 20px 20px 0 0;
      box-shadow: 0 -12px 40px rgba(27, 33, 64, 0.18);
    }

    .thermii-sheet-scrim {
      position: fixed;
      inset: 0;
      z-index: 199;
      background: rgba(27, 33, 64, 0.45);
    }

    .thermii-sheet-head {
      position: sticky;
      top: 0;
      display: grid;
      grid-template-columns: minmax(0, 1fr) auto;
      align-items: center;
      gap: 8px 12px;
      padding: 10px 0 12px;
      background: #f6f4ff;
    }

    .thermii-sheet-handle {
      grid-column: 1 / -1;
      justify-self: center;
      width: 40px;
      height: 4px;
      border-radius: 2px;
      background: #cfc9ea;
    }

    .thermii-sheet-title {
      font-family: 'Poppins', sans-serif;
      font-size: 15px;
      font-weight: 600;
      color: #1b2140;
    }

    .thermii-sheet-close {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 32px;
      height: 32px;
      padding: 0;
      border: 0;
      border-radius: 50%;
      background: #ece6ff;
      color: #6c4bd8;
      cursor: pointer;
    }

    .thermii-sheet-close:focus-visible {
      outline: 3px solid rgba(108, 75, 216, 0.35);
      outline-offset: 2px;
    }

    html.thermii-sheet-lock,
    html.thermii-sheet-lock body {
      overflow: hidden;
    }
  }

  /* THE COMPARE CARDS TAKE STEP 1'S ICONS (Loz, 2026-09-01: "shall we have the
     Water & Electric Icons from the Step 1 screen instead of the ones currently
     there?").

     Step 1 stopped using the old remote marks a while ago: journey.css hides its
     `.so-icon` outright and paints assets/icons/water-system.svg and
     electric-system.svg as a ::before background instead. The compare cards were
     never brought along, so they were still serving water-icon.svg and
     electric-icon.svg off the WordPress site — which is why they looked like a
     different set. index.html now points at the same two local files.

     THE SIZE HAD TO CHANGE WITH THEM. css/thermii.css pins .sys-compare-icon to
     a 56x56 SQUARE, and these marks are not square — they share an artboard
     height of 67.26 at widths 48.45 (water) and 44.33 (electric), so a square
     box stretches both. Height only, width auto, exactly as the welcome tiles
     resolved the same problem: one shared height, each mark keeps its
     proportions. Step 1 gets this for free from `background-size: contain`. */
  .sys-compare-card .sys-compare-icon {
    width: auto;
    height: 64px;
    max-width: 100%;
  }


  /* ── HELP ME CHOOSE: THREE GAPS OPEN UP (Loz, 2026-09-09) ─────────────────

     "I have tweaked the spacing between content, buttons etc in various places
     on this page as our current one looks a bit tight in places... They are
     only spacing/margin tweaks here and there."

     HIS DESIGN IS THIS APP AT 1898 CSS px, RASTERISED AND SCALED TO 2000 —
     and establishing that is the whole of the work, because it means EVERY
     figure in the PNG is 1.0538x its CSS value.

     THE SCALE COMES FROM A HARD CSS LENGTH, NOT FROM INK. The two cards'
     outer border-box span is 1011.5 image px over `.sys-compare-grid`'s
     `max-width: 960px`, which binds and is a literal: 1.05366. Text ink
     corroborates but must not be the ruler — thresholding a blurred edge
     inflates an ink span, so the four text runs read ~0.1% high (h2 673.8
     against the app's 672.5, intro 828.5/827.5, "Still unsure..."
     718.2/717.9, card title 287.5/287.5) and a first pass that trusted them
     put the scale at 1.0542. Corrected by a glyph-profile fit against Sora's
     own font tables: 1.05364 on the h2 at corr 0.9907, 1.05343 on the intro,
     1.05395 on "Still unsure...", weighting to 1.05383 +/- 0.0006.

     TAKEN AT FACE VALUE THE PNG LIES ABOUT FIVE THINGS. It says the grid is
     1012 wide, the cards 493, the gap between them 25, the button 52 tall and
     the icon 67 — i.e. that all five grew by about 5%. Divided by the scale
     they are 960 / 468 / 24 / 49 / 64, which is what this app already renders
     TO THE PIXEL. So not one of them is a tweak, and "widen the cards" would
     have been a change he never asked for. The card edges are read off the
     2px border rows rather than the white interior, because the drop shadow
     between the two cards reads darker than the page and turns a 24px gap
     into an apparent 35.

     WHAT ACTUALLY MOVED IS THREE GAPS INSIDE THE COMPARE CARD. Measured
     ink-to-ink, they reconcile to his card height within 1px — 409.4 here
     becomes 441.4 against his 441.2 — and an INDEPENDENT pass, predicting
     baseline-to-baseline from these three values and Sora's real metrics,
     matched the image on all three (40.99 predicted / 40.97 measured;
     36.77 / 36.84; 32 / 32.26):

         title -> description     11.4 -> 26.4    margin-bottom  4 -> 19
         bullets -> button        21.0 -> 34.0    margin-bottom 14 -> 27
         button -> card foot      30.0 -> 34.0    padding-bottom 28 -> 32

     THE PAGE'S OUTER RHYTHM IS ALREADY RIGHT, and was checked rather than
     assumed: heading->intro 26.8 against his 27.2, intro->grid 35.9 against
     35.6, "Still unsure"->buttons 23.8 against 23.3 — every one inside the
     antialiasing error of reading ink off a rescaled PNG. So nothing out here
     is touched. The cards->"Still unsure" stretch is NOT comparable at all:
     the tip block between them post-dates his design (its runtime class is
     .thermii-tip, swapped by js/thermii-skin.js) and he has kept it.

     SCOPED TO .sys-compare-card, AND THAT IS LOAD-BEARING. `.wic-title`,
     `.wic-bullets` and `.wic-switch-btn` are also worn by the install-method
     comparison cards the ENGINE builds (js/legacy-core.js), so an unscoped
     rule would re-space a screen he was not looking at. `.sys-compare-card`
     is on these two cards in index.html and appears nowhere in the engine.

     NO `!important`. css/thermii.css is byte-locked but declares these at
     (0,1,0) and (0,2,0) — `.wic-title` :1200, `.wic-bullets` :1206,
     `.sys-compare-card` :1609 — so a `body.thermii-skin` prefix outranks it,
     and this file loads later in any case.

     PAPER CANNOT SEE IT, checked rather than hoped: css/thermii.css's print
     block hides `#unsurePanel` outright (:2267), so unusually for this project
     there is no pagination risk and nothing to restore. Pinned as a premise. */
  body.thermii-skin .sys-compare-card .wic-title {
    margin-bottom: 19px;
  }

  body.thermii-skin .sys-compare-card .wic-bullets {
    margin-bottom: 27px;
  }

  body.thermii-skin .sys-compare-card {
    padding-bottom: 32px;
  }

  .eq-ans-edit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-width: 52px;
    padding: 4px 13px;
    border: 1px solid var(--journey-orange);
    border-radius: 999px;
    background: #fff;
    color: var(--journey-orange);
    font-size: 12px;
    font-weight: 500;
  }

  .eq-ans-edit::before {
    content: "";
    flex: 0 0 auto;
    width: 12px;
    height: 12px;
    background-color: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2016%2016%27%20fill%3D%27none%27%20stroke%3D%27%23000%27%20stroke-width%3D%271.6%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M11.1%202.3a1.3%201.3%200%200%201%201.9%200l.7.7a1.3%201.3%200%200%201%200%201.9l-7%207-3%20.8.8-3z%27%2F%3E%3Cpath%20d%3D%27M10.3%203.1l2.6%202.6%27%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2016%2016%27%20fill%3D%27none%27%20stroke%3D%27%23000%27%20stroke-width%3D%271.6%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M11.1%202.3a1.3%201.3%200%200%201%201.9%200l.7.7a1.3%201.3%200%200%201%200%201.9l-7%207-3%20.8.8-3z%27%2F%3E%3Cpath%20d%3D%27M10.3%203.1l2.6%202.6%27%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
  }

  /* ── DUPLICATE GETS ITS OWN MARK (Loz, 2026-09-02) ────────────────────────

     "On the 'Duplicate' buttons replace the pencil icon with the
     duplicate-icon.svg". Edit and Duplicate are the SAME class — the engine
     emits both as `.eq-ans-edit` (js/legacy-core.js:2546 the review row,
     :4327 the kit room card) — so until now the pencil sat on both and
     Duplicate looked like a second Edit.

     TARGETED BY ITS onclick, and that is the robust choice rather than a
     clever one. Both sites carry `wDuplicateRoom(` in the attribute (:2546
     bare, :4327 behind an `event.stopPropagation();`), and `*=` catches both.
     The alternative — a js module adding a class — is worse here for exactly
     the reason the pencil is a ::before and not an injected node (see above):
     both review builders reset innerHTML on every render from four call sites
     each, so a class would have to be re-applied after all of them. An
     attribute selector needs no wiring and cannot go stale. legacy-core.js is
     manifest-locked, so nothing about the markup can drift underneath it
     without a deliberate manifest change.

     THE ARTWORK IS LOZ'S, PATH FOR PATH, but it could not be used as supplied.
     assets/icons/duplicate-icon.svg is an Illustrator export whose front
     square is occluded by a `fill:#fff` rect. A MASK USES ALPHA, NOT COLOUR —
     white is fully opaque — so masking the file as-is paints that rect solid
     and the icon becomes a featureless blob. Measured in the browser: 67% ink
     coverage against 23% for the correct render. So the white knockout is
     replaced by a real hole, as a clip on the back ring only.

     WHY A CLIP AND NOT fill-rule="evenodd": evenodd gives symmetric
     difference, not subtraction — the knockout subpath re-fills the back
     square's hollow centre where the two overlap. Measured, same 67% blob.
     The clip is an internal reference inside the data: URI, which is a
     separate document, so it collides with nothing in the page and the repo's
     ban on ids in inline SVG does not reach it. If an engine were ever to
     ignore the clip, the failure is graceful: the back ring simply draws
     unoccluded and the icon still reads as two squares and a plus.

     NO STROKE IS ADDED, and that corrects my own first instinct. Loz drew
     ~2.48-unit lines on a 59.53 box (4.2%) against the pencil's 1.6 on 16
     (10%), so the lines look far thinner — but line WIDTH is not what the eye
     reads as weight, total ink is, and this icon carries two full squares and
     a plus against the pencil's single diagonal. Measured at the real 12px:
     23.17% ink against the pencil's 21.48%, i.e. 1.08x. Already a match.
     Stroking it to 1.2 took it to 1.38x — heavier than the pencil, not lighter.

     Inlined rather than url("assets/icons/..."), for three reasons: a relative
     url() in a stylesheet resolves against css/, not the document; the pencil
     directly above sets the idiom; and it keeps currentColor masking, so this
     mark follows the label through hover and disabled exactly as the pencil
     does. The cost is that the geometry is duplicated from the asset file, so
     tests/verify-duplicate-icon.py parses both and fails if they drift.

     The PDF and native print never see it: `.eq-ans-edit` is in the
     @media print hide list at css/thermii.css:2281. */
  .eq-ans-edit[onclick*="wDuplicateRoom"]::before {
    -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2059.53%2059.53%27%20fill%3D%27%23000%27%3E%3Cdefs%3E%3CclipPath%20id%3D%27k%27%3E%3Cpath%20clip-rule%3D%27evenodd%27%20d%3D%27M0%200h59.53v59.53H0Z%20M1.71%2012.6%20h45.01%20v45.01%20h-45.01%20Z%27%2F%3E%3C%2FclipPath%3E%3C%2Fdefs%3E%3Cg%20clip-path%3D%27url%28%23k%29%27%3E%3Cpath%20d%3D%27M51.11%2C46.94v-1.22h-31.58c-1.52%2C0-2.88-.61-3.89-1.61-.99-.99-1.6-2.36-1.61-3.89V8.64c0-1.52.61-2.88%2C1.61-3.89.99-.99%2C2.36-1.6%2C3.89-1.61h31.58c1.52%2C0%2C2.88.61%2C3.89%2C1.61s1.6%2C2.36%2C1.61%2C3.89v31.58c0%2C1.52-.61%2C2.88-1.61%2C3.89s-2.36%2C1.6-3.89%2C1.61v2.44c4.38%2C0%2C7.93-3.55%2C7.93-7.93V8.64c0-4.38-3.55-7.93-7.93-7.94h-31.58c-4.38%2C0-7.93%2C3.55-7.94%2C7.94v31.58c0%2C4.38%2C3.55%2C7.93%2C7.94%2C7.93h31.58v-1.22h0Z%27%2F%3E%3Cpath%20d%3D%27M51.11%2C46.94h.49v-1.71H19.53c-1.39%2C0-2.62-.55-3.54-1.47-.9-.91-1.46-2.15-1.47-3.54h0s0-31.58%2C0-31.58c0-1.39.55-2.62%2C1.47-3.54.91-.9%2C2.15-1.46%2C3.54-1.47h0s31.58%2C0%2C31.58%2C0c1.39%2C0%2C2.62.55%2C3.54%2C1.47.9.91%2C1.46%2C2.15%2C1.47%2C3.54h0s0%2C31.58%2C0%2C31.58c0%2C1.39-.55%2C2.62-1.47%2C3.54-.91.9-2.15%2C1.46-3.54%2C1.47h-.49v3.42h.49c4.65%2C0%2C8.42-3.77%2C8.42-8.42V8.64c0-4.65-3.77-8.42-8.42-8.43h-31.59c-4.65%2C0-8.42%2C3.77-8.43%2C8.43h0v31.59c0%2C4.65%2C3.77%2C8.42%2C8.43%2C8.42h32.08v-1.71h-.98s.98%2C0%2C.98%2C0h-.49.49s-.98%2C0-.98%2C0v.73h-31.09c-2.06%2C0-3.92-.83-5.27-2.18-1.35-1.35-2.18-3.2-2.18-5.26V8.64h0c0-4.12%2C3.34-7.45%2C7.45-7.45h31.58%2C0c4.11%2C0%2C7.44%2C3.34%2C7.44%2C7.45v31.59c0%2C2.05-.83%2C3.91-2.18%2C5.26-1.35%2C1.35-3.2%2C2.18-5.26%2C2.18v.49h.49v-2.44h-.49v.49c1.66-.01%2C3.15-.68%2C4.23-1.76%2C1.09-1.09%2C1.76-2.58%2C1.76-4.23V8.63h0c-.01-1.66-.68-3.15-1.76-4.23-1.09-1.09-2.58-1.76-4.23-1.76h-31.59c-1.65.01-3.15.68-4.23%2C1.76-1.09%2C1.09-1.76%2C2.58-1.76%2C4.23v31.59h0c.01%2C1.66.68%2C3.15%2C1.76%2C4.23%2C1.09%2C1.09%2C2.58%2C1.76%2C4.23%2C1.76h31.09v.73h.98-.49Z%27%2F%3E%3C%2Fg%3E%3Cpath%20d%3D%27M8.42%2C12.6v.49h31.59c1.72%2C0%2C3.27.7%2C4.4%2C1.82%2C1.13%2C1.13%2C1.82%2C2.68%2C1.82%2C4.4v31.59c0%2C1.72-.7%2C3.27-1.82%2C4.4-1.13%2C1.13-2.68%2C1.82-4.4%2C1.82H8.42c-1.72%2C0-3.27-.7-4.4-1.82-1.13-1.13-1.82-2.68-1.82-4.4v-31.59c0-1.72.7-3.27%2C1.82-4.4%2C1.13-1.13%2C2.68-1.82%2C4.4-1.82v-.49s0-.49%2C0-.49c-3.98%2C0-7.2%2C3.22-7.2%2C7.2v31.59c0%2C3.98%2C3.22%2C7.2%2C7.2%2C7.2h31.59c3.98%2C0%2C7.2-3.22%2C7.2-7.2v-31.59c0-3.98-3.22-7.2-7.2-7.2H8.42v.49Z%27%2F%3E%3Cpath%20d%3D%27M40.01%2C57.61v-1.22H8.43c-1.52%2C0-2.88-.61-3.89-1.61-.99-.99-1.6-2.36-1.61-3.89v-31.58c0-1.52.61-2.88%2C1.61-3.89.99-.99%2C2.36-1.6%2C3.89-1.61h31.58c1.52%2C0%2C2.88.61%2C3.89%2C1.61s1.6%2C2.36%2C1.61%2C3.89v31.58c0%2C1.52-.61%2C2.88-1.61%2C3.89-.99.99-2.36%2C1.6-3.89%2C1.61v2.44c4.38%2C0%2C7.93-3.55%2C7.93-7.93v-31.59c0-4.38-3.55-7.93-7.93-7.94H8.43c-4.39%2C0-7.94%2C3.56-7.94%2C7.94v31.58c0%2C4.38%2C3.55%2C7.93%2C7.94%2C7.93h31.58v-1.22h0Z%27%2F%3E%3Cpath%20d%3D%27M40.01%2C57.61h.49v-1.71H8.43c-1.39%2C0-2.62-.55-3.54-1.47-.9-.91-1.46-2.15-1.47-3.54h0s0-31.58%2C0-31.58c0-1.39.55-2.62%2C1.47-3.54.91-.9%2C2.15-1.46%2C3.54-1.47h0s31.58%2C0%2C31.58%2C0c1.39%2C0%2C2.62.55%2C3.54%2C1.47.9.91%2C1.46%2C2.15%2C1.47%2C3.54h0s0%2C31.58%2C0%2C31.58c0%2C1.39-.55%2C2.62-1.47%2C3.54-.91.9-2.15%2C1.46-3.54%2C1.47h-.49v3.42h.49c4.65%2C0%2C8.42-3.77%2C8.42-8.42v-31.59c0-4.65-3.77-8.42-8.42-8.43H8.43h0C3.77%2C10.89%2C0%2C14.66%2C0%2C19.31v31.58c0%2C4.65%2C3.77%2C8.42%2C8.43%2C8.42h0s32.08%2C0%2C32.08%2C0v-1.71h-.98s.98%2C0%2C.98%2C0h-.49.49s-.98%2C0-.98%2C0v.73H8.43c-2.06%2C0-3.92-.83-5.27-2.18-1.35-1.35-2.18-3.2-2.18-5.26v-31.58c0-4.1%2C3.33-7.44%2C7.45-7.45h31.58%2C0c4.11%2C0%2C7.44%2C3.34%2C7.44%2C7.45v31.59c0%2C2.05-.83%2C3.91-2.18%2C5.26-1.35%2C1.35-3.2%2C2.18-5.26%2C2.18v.49h.49s0-2.44%2C0-2.44h-.49v.49c1.66-.01%2C3.15-.68%2C4.23-1.76%2C1.09-1.09%2C1.76-2.58%2C1.76-4.23v-31.59h0c-.01-1.66-.68-3.15-1.76-4.23-1.09-1.09-2.58-1.76-4.23-1.76H8.43c-1.65.01-3.15.68-4.23%2C1.76-1.09%2C1.09-1.76%2C2.58-1.76%2C4.23v31.59h0c.01%2C1.66.68%2C3.15%2C1.76%2C4.23%2C1.09%2C1.09%2C2.58%2C1.76%2C4.23%2C1.76h31.09v.73h.98-.49Z%27%2F%3E%3Cpath%20d%3D%27M37.21%2C33.89h-11.77v-11.77c0-.68-.55-1.22-1.22-1.22s-1.22.55-1.22%2C1.22v11.77h-11.77c-.68%2C0-1.22.55-1.22%2C1.22s.55%2C1.22%2C1.22%2C1.22h11.77v11.77c0%2C.68.55%2C1.22%2C1.22%2C1.22s1.22-.55%2C1.22-1.22v-11.77h11.77c.68%2C0%2C1.22-.55%2C1.22-1.22s-.55-1.22-1.22-1.22Z%27%2F%3E%3Cpath%20d%3D%27M37.21%2C33.89v-.49h-11.28v-11.28c0-.95-.77-1.71-1.71-1.71-.94%2C0-1.71.77-1.71%2C1.71v11.28h-11.28c-.95%2C0-1.71.77-1.71%2C1.71%2C0%2C.94.77%2C1.71%2C1.71%2C1.71h11.28v11.28c0%2C.95.77%2C1.71%2C1.71%2C1.71.94%2C0%2C1.71-.77%2C1.71-1.71v-11.28h11.28c.95%2C0%2C1.71-.77%2C1.71-1.71%2C0-.94-.77-1.71-1.71-1.71v.98c.2%2C0%2C.38.08.51.21.13.13.21.32.21.51%2C0%2C.4-.32.73-.73.73h-12.27v12.27c0%2C.2-.08.38-.21.51-.13.13-.32.21-.51.21-.4%2C0-.73-.32-.73-.73v-12.27h-12.27c-.2%2C0-.38-.08-.51-.21-.13-.13-.21-.32-.21-.51%2C0-.4.32-.73.73-.73h12.27v-12.27c0-.2.08-.38.21-.51.13-.13.32-.21.51-.21.4%2C0%2C.73.32.73.73v12.27h12.27v-.49Z%27%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2059.53%2059.53%27%20fill%3D%27%23000%27%3E%3Cdefs%3E%3CclipPath%20id%3D%27k%27%3E%3Cpath%20clip-rule%3D%27evenodd%27%20d%3D%27M0%200h59.53v59.53H0Z%20M1.71%2012.6%20h45.01%20v45.01%20h-45.01%20Z%27%2F%3E%3C%2FclipPath%3E%3C%2Fdefs%3E%3Cg%20clip-path%3D%27url%28%23k%29%27%3E%3Cpath%20d%3D%27M51.11%2C46.94v-1.22h-31.58c-1.52%2C0-2.88-.61-3.89-1.61-.99-.99-1.6-2.36-1.61-3.89V8.64c0-1.52.61-2.88%2C1.61-3.89.99-.99%2C2.36-1.6%2C3.89-1.61h31.58c1.52%2C0%2C2.88.61%2C3.89%2C1.61s1.6%2C2.36%2C1.61%2C3.89v31.58c0%2C1.52-.61%2C2.88-1.61%2C3.89s-2.36%2C1.6-3.89%2C1.61v2.44c4.38%2C0%2C7.93-3.55%2C7.93-7.93V8.64c0-4.38-3.55-7.93-7.93-7.94h-31.58c-4.38%2C0-7.93%2C3.55-7.94%2C7.94v31.58c0%2C4.38%2C3.55%2C7.93%2C7.94%2C7.93h31.58v-1.22h0Z%27%2F%3E%3Cpath%20d%3D%27M51.11%2C46.94h.49v-1.71H19.53c-1.39%2C0-2.62-.55-3.54-1.47-.9-.91-1.46-2.15-1.47-3.54h0s0-31.58%2C0-31.58c0-1.39.55-2.62%2C1.47-3.54.91-.9%2C2.15-1.46%2C3.54-1.47h0s31.58%2C0%2C31.58%2C0c1.39%2C0%2C2.62.55%2C3.54%2C1.47.9.91%2C1.46%2C2.15%2C1.47%2C3.54h0s0%2C31.58%2C0%2C31.58c0%2C1.39-.55%2C2.62-1.47%2C3.54-.91.9-2.15%2C1.46-3.54%2C1.47h-.49v3.42h.49c4.65%2C0%2C8.42-3.77%2C8.42-8.42V8.64c0-4.65-3.77-8.42-8.42-8.43h-31.59c-4.65%2C0-8.42%2C3.77-8.43%2C8.43h0v31.59c0%2C4.65%2C3.77%2C8.42%2C8.43%2C8.42h32.08v-1.71h-.98s.98%2C0%2C.98%2C0h-.49.49s-.98%2C0-.98%2C0v.73h-31.09c-2.06%2C0-3.92-.83-5.27-2.18-1.35-1.35-2.18-3.2-2.18-5.26V8.64h0c0-4.12%2C3.34-7.45%2C7.45-7.45h31.58%2C0c4.11%2C0%2C7.44%2C3.34%2C7.44%2C7.45v31.59c0%2C2.05-.83%2C3.91-2.18%2C5.26-1.35%2C1.35-3.2%2C2.18-5.26%2C2.18v.49h.49v-2.44h-.49v.49c1.66-.01%2C3.15-.68%2C4.23-1.76%2C1.09-1.09%2C1.76-2.58%2C1.76-4.23V8.63h0c-.01-1.66-.68-3.15-1.76-4.23-1.09-1.09-2.58-1.76-4.23-1.76h-31.59c-1.65.01-3.15.68-4.23%2C1.76-1.09%2C1.09-1.76%2C2.58-1.76%2C4.23v31.59h0c.01%2C1.66.68%2C3.15%2C1.76%2C4.23%2C1.09%2C1.09%2C2.58%2C1.76%2C4.23%2C1.76h31.09v.73h.98-.49Z%27%2F%3E%3C%2Fg%3E%3Cpath%20d%3D%27M8.42%2C12.6v.49h31.59c1.72%2C0%2C3.27.7%2C4.4%2C1.82%2C1.13%2C1.13%2C1.82%2C2.68%2C1.82%2C4.4v31.59c0%2C1.72-.7%2C3.27-1.82%2C4.4-1.13%2C1.13-2.68%2C1.82-4.4%2C1.82H8.42c-1.72%2C0-3.27-.7-4.4-1.82-1.13-1.13-1.82-2.68-1.82-4.4v-31.59c0-1.72.7-3.27%2C1.82-4.4%2C1.13-1.13%2C2.68-1.82%2C4.4-1.82v-.49s0-.49%2C0-.49c-3.98%2C0-7.2%2C3.22-7.2%2C7.2v31.59c0%2C3.98%2C3.22%2C7.2%2C7.2%2C7.2h31.59c3.98%2C0%2C7.2-3.22%2C7.2-7.2v-31.59c0-3.98-3.22-7.2-7.2-7.2H8.42v.49Z%27%2F%3E%3Cpath%20d%3D%27M40.01%2C57.61v-1.22H8.43c-1.52%2C0-2.88-.61-3.89-1.61-.99-.99-1.6-2.36-1.61-3.89v-31.58c0-1.52.61-2.88%2C1.61-3.89.99-.99%2C2.36-1.6%2C3.89-1.61h31.58c1.52%2C0%2C2.88.61%2C3.89%2C1.61s1.6%2C2.36%2C1.61%2C3.89v31.58c0%2C1.52-.61%2C2.88-1.61%2C3.89-.99.99-2.36%2C1.6-3.89%2C1.61v2.44c4.38%2C0%2C7.93-3.55%2C7.93-7.93v-31.59c0-4.38-3.55-7.93-7.93-7.94H8.43c-4.39%2C0-7.94%2C3.56-7.94%2C7.94v31.58c0%2C4.38%2C3.55%2C7.93%2C7.94%2C7.93h31.58v-1.22h0Z%27%2F%3E%3Cpath%20d%3D%27M40.01%2C57.61h.49v-1.71H8.43c-1.39%2C0-2.62-.55-3.54-1.47-.9-.91-1.46-2.15-1.47-3.54h0s0-31.58%2C0-31.58c0-1.39.55-2.62%2C1.47-3.54.91-.9%2C2.15-1.46%2C3.54-1.47h0s31.58%2C0%2C31.58%2C0c1.39%2C0%2C2.62.55%2C3.54%2C1.47.9.91%2C1.46%2C2.15%2C1.47%2C3.54h0s0%2C31.58%2C0%2C31.58c0%2C1.39-.55%2C2.62-1.47%2C3.54-.91.9-2.15%2C1.46-3.54%2C1.47h-.49v3.42h.49c4.65%2C0%2C8.42-3.77%2C8.42-8.42v-31.59c0-4.65-3.77-8.42-8.42-8.43H8.43h0C3.77%2C10.89%2C0%2C14.66%2C0%2C19.31v31.58c0%2C4.65%2C3.77%2C8.42%2C8.43%2C8.42h0s32.08%2C0%2C32.08%2C0v-1.71h-.98s.98%2C0%2C.98%2C0h-.49.49s-.98%2C0-.98%2C0v.73H8.43c-2.06%2C0-3.92-.83-5.27-2.18-1.35-1.35-2.18-3.2-2.18-5.26v-31.58c0-4.1%2C3.33-7.44%2C7.45-7.45h31.58%2C0c4.11%2C0%2C7.44%2C3.34%2C7.44%2C7.45v31.59c0%2C2.05-.83%2C3.91-2.18%2C5.26-1.35%2C1.35-3.2%2C2.18-5.26%2C2.18v.49h.49s0-2.44%2C0-2.44h-.49v.49c1.66-.01%2C3.15-.68%2C4.23-1.76%2C1.09-1.09%2C1.76-2.58%2C1.76-4.23v-31.59h0c-.01-1.66-.68-3.15-1.76-4.23-1.09-1.09-2.58-1.76-4.23-1.76H8.43c-1.65.01-3.15.68-4.23%2C1.76-1.09%2C1.09-1.76%2C2.58-1.76%2C4.23v31.59h0c.01%2C1.66.68%2C3.15%2C1.76%2C4.23%2C1.09%2C1.09%2C2.58%2C1.76%2C4.23%2C1.76h31.09v.73h.98-.49Z%27%2F%3E%3Cpath%20d%3D%27M37.21%2C33.89h-11.77v-11.77c0-.68-.55-1.22-1.22-1.22s-1.22.55-1.22%2C1.22v11.77h-11.77c-.68%2C0-1.22.55-1.22%2C1.22s.55%2C1.22%2C1.22%2C1.22h11.77v11.77c0%2C.68.55%2C1.22%2C1.22%2C1.22s1.22-.55%2C1.22-1.22v-11.77h11.77c.68%2C0%2C1.22-.55%2C1.22-1.22s-.55-1.22-1.22-1.22Z%27%2F%3E%3Cpath%20d%3D%27M37.21%2C33.89v-.49h-11.28v-11.28c0-.95-.77-1.71-1.71-1.71-.94%2C0-1.71.77-1.71%2C1.71v11.28h-11.28c-.95%2C0-1.71.77-1.71%2C1.71%2C0%2C.94.77%2C1.71%2C1.71%2C1.71h11.28v11.28c0%2C.95.77%2C1.71%2C1.71%2C1.71.94%2C0%2C1.71-.77%2C1.71-1.71v-11.28h11.28c.95%2C0%2C1.71-.77%2C1.71-1.71%2C0-.94-.77-1.71-1.71-1.71v.98c.2%2C0%2C.38.08.51.21.13.13.21.32.21.51%2C0%2C.4-.32.73-.73.73h-12.27v12.27c0%2C.2-.08.38-.21.51-.13.13-.32.21-.51.21-.4%2C0-.73-.32-.73-.73v-12.27h-12.27c-.2%2C0-.38-.08-.51-.21-.13-.13-.21-.32-.21-.51%2C0-.4.32-.73.73-.73h12.27v-12.27c0-.2.08-.38.21-.51.13-.13.32-.21.51-.21.4%2C0%2C.73.32.73.73v12.27h12.27v-.49Z%27%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
  }

  /* Capped and centred onto the rows' left edge (see the review-card note
     above). The bottom margin is Loz's 2026-09-01 request for more space under
     the CAD panel — it pairs with the CTA's own top padding below, so the
     confirm line no longer sits tight under the card. */
  /* ADD ANOTHER ROOM — the Continue's shape, its own green (Loz, 2026-09-01).

     The button is inline-styled in index.html as a 52px pill (radius 50px), so
     it read as a different species from every other button in the journey.
     Matched to .thermii-continue's metrics — 54px min-height, 13px radius,
     600 15px Poppins — while keeping the outlined green it already had.

     THE INLINE STYLE IS STRIPPED AT RUNTIME by js/thermii-skin.js; without that
     these declarations lose to the style attribute. Stripping is the sanctioned
     alternative to !important here (the style attribute is not in the DOM
     contract). What CANNOT be removed is the pair of inline onmouseover /
     onmouseout handlers — those ARE in the contract — and they set
     `this.style.background` to #f0f7e6 and back to #fff. Those are the colours
     this design wants, so the HOVER is left entirely to them — but the RESTING
     background is declared below, and must be. */
  /* The row's own inline style is stripped with the button's (see
     wireAddRoomButtons), so its spacing is re-stated here — WITHOUT the
     `border-top: 1px solid #f0f0f0` that drew the grey line above the button. */
  /* ── THE TWO DOORS SIT SIDE BY SIDE (Loz, 2026-09-09) ─────────────────────
     His mock-up of 'Your Selections': the green "+ Add Another Room" and the
     navy "+ Add an Electric Room" as a pair either side of a vertical rule,
     where they were stacked with a horizontal one.

     THEY ARE PEERS AND WERE NOT PRESENTED AS PEERS. Stacked, with a hairline
     between them, one ranked above the other and read as "the action, then an
     afterthought" — and because the row already carries its own rule above the
     green button, ONE decision was drawn as THREE sections. A single vertical
     divider says what it is: two doors out of the same choice.

     `align-items: flex-end` PUTS THEM ON ONE BASELINE, which is how his mock
     has them (measured: both button bottoms at y=761). The electric side is
     the taller column — it carries the "Heating another room a different way?"
     label above its button — so centring would have dropped the green button
     below the electric one's centre line.

     `text-align: center` IS LEFT IN PLACE and is not dead — but the reason
     given here was wrong, and it was wrong when it was written (corrected
     2026-09-09). It said index.html sets it inline "so it cannot be removed
     from here anyway": js/thermii-skin.js:1447 removes the WHOLE style
     attribute at boot, so the inline copy is gone before either summary is
     shown and this declaration is the ONLY thing centring that text. Dropping
     it in the redesign's first pass left the green button flush left below
     820px.

     BELOW 700px IT GOES BACK TO A COLUMN — see the media query further down.
     Two 330px buttons, a divider and its padding need about 730px, and this
     row lives inside a card that is narrower than that on a phone. */
  /* ── SUPERSEDED 2026-09-09 BY THE TWO-COLUMN REDESIGN, and the reasoning
     above is kept because it explains what the row USED to be. Loz: "I have
     completely re-designed the 'Your Selections' screen ... copy EXACTLY
     (layout design, spacing, alignments etc.)". The two doors are no longer a
     row beneath the answers; they are a CENTRED COLUMN in the right-hand half
     of the white card, which is what the block below builds. `align-items:
     flex-end` and the 28px gap went with the row. */
  #wq-add-room-row,
  #eq-add-room-row {
    grid-row: 3;
    grid-column: 2;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    /* RESTORED, and dropping it was a defect. The row's inline style is
       removed wholesale by js/thermii-skin.js:1447 (`row.removeAttribute
       ('style')`), so `text-align: center` has to come from a stylesheet — and
       the rule this replaced carried it. Without it the ≤820px block below
       (:5625, `display: block` at the same (1,0,0) specificity but later in
       the file, so it wins) left the green button flush LEFT on a phone. */
    text-align: center;
    /* THE DIVIDER. It is this column's own left border, so it cannot be a
       phantom grid item and it runs the card's full height for free. Measured
       off his mock at (241,241,241) against the horizontal rule's (240,240,240)
       — one value inside JPEG noise, and #f0f0f0 is already what this screen's
       hairlines use, so no new colour ships. */
    border-left: 1px solid #f0f0f0;
    /* index.html sets these three INLINE on the row, but nothing here depends
       on stripping them: js/thermii-skin.js:1447 already removes the WHOLE
       style attribute at boot, before either summary is ever shown (verified
       in the browser — it reads null). Declared as 0 so the layout's intent
       lives in the file that owns it, and so the row is right even on a path
       where skin's pass bailed. */
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
  }

  /* ── 'YOUR SELECTIONS' IS TWO COLUMNS IN ONE CARD (Loz, 2026-09-09) ───────

     Measured off his mockup, which is this app at ~1:1 — the scale was taken
     from the white card's own 1280px width and the page ground in his image
     reads (247,248,252), exactly .kb-wrapper's. All figures below are CSS px
     relative to that card's box.

     HIS NOTE 1 IS ONE DECLARATION: "the existing content within the panel has
     been given more margin space top and bottom, and the green ticks and
     selection have been moved in from the left more" is
     `.eq-sum-row { padding: 9px 25px }`, up from 7px 14px — 25 lands the
     tick's inset on his measured 25.8, and 9 lands the row on 44 against his
     43.4. See the note on that rule for why the first attempt at the vertical
     figure was wrong.

     THE WHITE CARD IS #wq-sum-rows TODAY and holds only the rows; his design
     has ONE card holding both columns. Rather than move any node, the PANEL
     becomes the grid and its own `::before` draws the card behind row 3. That
     matters for a reason beyond neatness: tests/fixture-runner.js:695 drives
     every multi-room fixture with `$('button', row)` on #wq-add-room-row, and
     the fixture suite freezes real quoted prices — so a restructure that
     reordered or re-parented that row's children is a money path. Nothing
     moves here; only a heading DIV is prepended, and a div is not a button.

     `display: grid` NEEDS NO !important, which was checked rather than hoped:
     the engine reveals this panel with `sum.style.display = ''`
     (js/legacy-core.js:2587 water, :608 electric) — it CLEARS the inline value
     instead of writing one. And neither renderer touches the panel or the
     add-room row: both write only `*-sum-rows.innerHTML` (:2582 and :582), so
     the grid, the ::before and the injected heading all survive every render.

     THE COLUMN SPLIT IS DERIVED FROM HIS DIVIDER. He draws it at x 777.1 of a
     1280 card. With 1px borders the content box is 1278, so a 502px right
     column puts its left border at 1 + (1278 - 502) = 777. */
  #wq-summary,
  #eq-summary {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 502px;
    /* 1240 -> 1280: his card runs to the panel's own edges. */
    padding-left: 0;
    padding-right: 0;
  }

  /* The heading and the sub-line span both columns and stay on the page ground
     — the panel is transparent, so they already read that way and his design
     shows them there. */
  #wq-summary > .eq-summary-title,
  #eq-summary > .eq-summary-title,
  #wq-summary > .eq-note,
  #eq-summary > .eq-note {
    grid-column: 1 / -1;
  }

  /* THE CARD ITSELF, as a grid item placed in the same cell as both columns.
     A ::before is the first child in paint order, so it lands behind them with
     no z-index and no stacking context of its own. */
  #wq-summary::before,
  #eq-summary::before {
    content: "";
    grid-row: 3;
    grid-column: 1 / -1;
    background: #fff;
    border: 1px solid var(--journey-border);
    border-radius: 12px;
  }

  /* The white ground moves OFF the rows, onto the card above. */
  #wq-sum-rows,
  #eq-sum-rows {
    grid-row: 3;
    grid-column: 1;
    background: transparent;
    border: 0;
    border-radius: 0;
    /* 12px top and bottom is what lands his measured card height. */
    padding: 12px 0;
  }

  /* 9px, NOT the 12 first fitted — and the correction is worth recording
     because the arithmetic that produced 12 was wrong in a way that still
     matched. A row's height is `max(min-height 42, content + 2 x padding)`,
     and I took the CONTENT to be the label's 19.5px line box. It is not: the
     row is a flex container and its tallest child is the 25px Edit button, so
     the content is 26. Measured live, 12px produced a 50px row against his
     43.4. With 26 the padding solves to 8.7, and 9 gives 44 — which also
     keeps the tick's inset at 25 (his 25.8). So the tick figure was right and
     the row figure was luck; only measuring the built row found it. */
  #wq-sum-rows .eq-sum-row,
  #eq-sum-rows .eq-sum-row {
    padding: 9px 25px;
  }

  /* "Do you need to add anymore rooms?" — injected, because it is NOT in the
     tree today (its sibling "Heating another room a different way?" already is,
     at js/thermii-other-system-room.js:340). 18px is measured, not guessed: his
     ink is 16.9 tall, which is 18px Sora's own cap-plus-descender, and the
     string's ink width fits 18px to about a pixel once the JPEG's edge
     inflation is taken off. 600 is the house display weight.

     AND SORA IS WHY THE FONT-FAMILY BELOW IS INERT, measured rather than
     assumed: css/components.css carries `body * { font-family:
     var(--thermii-font-sans) !important }`, so the 'Poppins' declared here
     never applies and this heading computes Sora — checked in the browser
     against the real sheet order. It is kept because every inline style
     beside it (Loz's own green button at index.html:1661 included) declares
     Poppins the same way, so removing just this one would make the odd rule
     out; but the SIZE was fitted to Sora, which is what actually paints.

     HIS SPELLING IS HIS — "anymore rooms" is reproduced verbatim, per the
     recorded rule about matching his copy including its typos.

     PURE BLACK, WHICH LOZ CHOSE ON 2026-09-10 ("yeah make the heading black
     mate") — and his mock had drawn it that way all along. The measurement
     that flagged it: his heading samples (0,0,0) while "Your Selections" in
     the SAME image samples (18,23,52) — i.e. --navy rendered faithfully — so
     the black was real ink rather than JPEG crush. It stays a literal and not
     a token because no token in the four stylesheets resolves to #000, and it
     is deliberately the only pure black on the screen: 21:1 on white against
     navy's 17.2:1, both far past AA, so this is his eye and not a contrast
     fix. Do not "tidy" it back to --navy. */
  .wsel-add-h {
    font-family: 'Poppins', sans-serif;
    font-size: 18px;
    font-weight: var(--thermii-font-weight-display, 600);
    line-height: 1.3;
    color: #000;
    text-align: center;
    /* 25 lands his measured 27px from this heading's ink to the button's box. */
    margin-bottom: 25px;
  }

  /* The second door stops being a side-by-side sibling and becomes the lower
     half of the column, its rule horizontal. These three are his measured
     gaps: 61 from the green button to the rule, 54 from the rule to the label
     (his 56.7 to the label's INK, which sits 3.2 below its line box at 13px),
     and 26 from that label to the navy button (his 27.3 ink-to-box). */
  /* The hairline turns horizontal at every width — that part is the layout, not
     a two-column figure. */
  #wq-add-room-row > .thermii-other-door,
  #eq-add-room-row > .thermii-other-door {
    padding-left: 0;
    border-left: 0;
    border-top: 1px solid #f0f0f0;
  }

  /* ── AND THE TWO-COLUMN FIGURES ARE SCOPED TO THE TWO-COLUMN RANGE ───────
     These four are only meaningful beside the answers: 61px of air below a
     button is a desktop number, and a 330px door does not fit a 375px card at
     all. Left unscoped they also OUT-SPECIFIED the narrow layouts that already
     existed — this selector is (1,1,0) where journey.css's own ≤820 rule for
     `.thermii-other-door` is (0,1,0), so it won regardless of source order and
     silently replaced a phone rhythm the file records as deliberate. Measured
     at 800px before the fix: 24/24/12 where :5625's block asks for 14/14/8.

     330 is THE BUTTONS' OWN WIDTH, so the hairline is as wide as the two
     controls it separates and cannot drift from them. His rule measures 356.8
     against his buttons' 340 — the same relationship — and he settled the
     buttons at 330 himself, which is the figure shared with the kit screen's
     own button. The label inside measures 291, so it still sits on one line. */
  @media (min-width: 1181px) {
    #wq-add-room-row > .thermii-other-door,
    #eq-add-room-row > .thermii-other-door {
      width: 330px;
      margin-top: 61px;
      padding-top: 54px;
      gap: 26px;
    }
  }

  /* ── AND IT STACKS BELOW 1100px, DERIVED RATHER THAN PICKED ───────────────
     Loz: "we can tweak the mobile version of this newly designed screen once
     you have built the desktop", so desktop is the deliverable — but a fixed
     502px right column must not be allowed to crush the answers in the
     meantime, and leaving it would ship a broken laptop layout.

     The right column is fixed, so the LEFT one absorbs every pixel the
     viewport loses. It needs the label's own `min-width: 150px`
     (css/thermii.css:1747) plus the row's new 50px of side padding plus the
     70px Edit button — 270px of furniture — before a value gets any room at
     all, and the longest value on this screen ("Heatmiser neoStat Touch
     (Carbon)") measures about 230. So the left column's floor is ~500px, the
     card's is ~1002. TRANSLATING THAT TO A VIEWPORT NEEDED MEASURING rather
     than arithmetic: the workspace's gutter is a clamp that grows with the
     window, so at a viewport of 1101 the card measured only 963 and the left
     column 461 — below its own floor. Stepped at 1180 instead, where the card
     measures ~1043 and the left column ~541, and which is a breakpoint
     journey.css already uses. Same lesson as the doors' 790px wrap point: the
     arithmetic is a hypothesis, the width is a measurement.

     THE ::before SPANS BOTH ROWS when stacked, or the white card would end at
     the answers and leave the two doors sitting on the page ground. */
  @media (max-width: 1180px) {
    #wq-summary,
    #eq-summary {
      grid-template-columns: minmax(0, 1fr);
    }

    #wq-summary::before,
    #eq-summary::before {
      grid-row: 3 / 5;
    }

    #wq-sum-rows,
    #eq-sum-rows {
      grid-column: 1;
      padding-bottom: 0;
    }

    #wq-add-room-row,
    #eq-add-room-row {
      grid-row: 4;
      grid-column: 1;
      border-left: 0;
      border-top: 1px solid #f0f0f0;
      margin-top: 12px;
      padding: 24px 25px 12px;
    }

    /* Full width once stacked — a 330px door would overflow a phone card. */
    #wq-add-room-row > .thermii-other-door,
    #eq-add-room-row > .thermii-other-door {
      width: 100%;
    }

    .wsel-add-h {
      margin-bottom: 16px;
    }
  }

  /* 821..1180: stacked, but with more room than a phone — so the doors get a
     middling rhythm rather than either the two-column 61/54/26 or the phone's
     14/14. Bounded ABOVE 820 on purpose, so journey.css's own ≤820 block
     (:5625 and the rule below it) keeps the narrow rhythm it records as
     deliberate instead of being out-specified by an id-scoped selector. */
  @media (min-width: 821px) and (max-width: 1180px) {
    #wq-add-room-row > .thermii-other-door,
    #eq-add-room-row > .thermii-other-door {
      margin-top: 24px;
      padding-top: 24px;
      gap: 12px;
    }
  }

  /* ── #5 + #6: THE KIT SCREEN'S BUTTON JOINS THIS RULE (Loz, 2026-09-03) ────
     "Match it in style to the 'Your Selections' screen", and "match any green
     colours to the ones we implemented last week".

     Both notes are answered by widening this selector rather than writing a
     second rule: the kit screen's button was a 50px pill in the OLD green
     (#6cba15, engine inline at legacy-core.js:1096 and :4555) where these are
     a 13px radius in --journey-green. Sharing one rule means the two screens
     cannot drift again, which a copy would have allowed within the week.

     The kit ones are reached by their onclick because the engine gives them no
     class and no id — the same handle the Duplicate mark uses. They need
     js/thermii-kit-screen.js to strip their inline style first (inline beats a
     stylesheet), and that runs after EVERY render because the renderer rebuilds
     them; these two are static and thermii-skin.js strips them once. */
  #wq-add-room-row > button,
  #eq-add-room-row > button,
  #kitItems [onclick="eAddAnotherRoom()"],
  #wKitItems [onclick="wAddAnotherRoom()"] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    min-width: 330px;
    min-height: 54px;
    height: auto;
    padding: 14px 24px;
    border: 2px solid var(--journey-green, #74b700);
    border-radius: 13px;
    /* WHITE IS DECLARED, and leaving it out was a real bug (Loz: "there is grey
       color going on the button when landing on the page"). Stripping the
       inline style took `background: #fff` away with it, and the note above was
       only half right: the handlers do own the hover colours, but neither fires
       until the pointer arrives — so until then the button fell back to the
       browser's own ButtonFace grey, rgb(239,239,239), and went white only on
       the first mouseout. Declaring it here fixes the RESTING state and cannot
       fight the handlers, because their inline value still wins the moment
       either of them runs. */
    background: #fff;
    color: var(--journey-green, #74b700);
    font-size: 15px;
    font-weight: var(--thermii-font-weight-display, 600);
  }

  /* ── THE CAD PANEL, TO HIS NOTE 2 (Loz, 2026-09-09) ──────────────────────
     "The CAD drawings panel has been moved further away from the existing
     panel above and its 'Add to my Kit' button has been centered in the panel
     and moved in from the right to align with the right edge of the add rooms
     buttons above it. Also the CAD icon, headline and text has been moved in
     more from the left. The text has been moved further in from the CAD icon."

     Four measurements off his mock, all card-relative CSS:

         gap from the panel above    10  ->  25.8      margin-top 26
         width                     1240 ->  1280       matches the card above
         padding-left                16 ->  ~28
         icon -> text gap            16 ->  ~76

     THE BUTTON'S RIGHT EDGE IS DERIVED, NOT COPIED, which is the only way the
     alignment he asked for can hold. The add-room buttons are 330 wide centred
     in a 502px column whose content is 501, so their right edge lands at
     1280 - 1 - 85.5 = 1193.5 from the card's box. This card's inner content
     ends at 1280 - 1 - 28 = 1251, so 58px of margin puts the pill's right edge
     at 1193 — half a pixel from the buttons above it, and it moves with them
     if either ever changes. His own mock measures 1201.5 against 1198.5, i.e.
     he drew the same intent 3px out. verify-* cross-reads the two. */
  #wCadDrawingCard {
    width: 100%;
    max-width: 1280px;
    margin: 26px auto 18px;
    padding: 12px 28px;
    border: 1px solid var(--journey-border);
    border-radius: 10px;
    box-shadow: none;
  }

  /* "The text has been moved further in from the CAD icon" — 16 -> 76,
     measured from his headline's ink against the FREE badge's right edge (the
     badge is a solid pill, so its box and its ink are the same thing, which
     makes it the reliable anchor in that column rather than the icon). */
  /* ── SCOPED ABOVE THE PHONE BREAKPOINT, and leaving them unscoped was a
     defect. Both of these are ID-scoped at (1,1,0) to beat the engine's own
     (0,1,0) base rules — which also made them beat every PHONE rule for the
     same properties: css/thermii.css:1952/1953 and :2143/2144 (byte-locked)
     and journey.css:5223/5228 are all (0,1,0), so at ≤700px the panel kept a
     76px gap and the pill kept its 58px right margin instead of stacking. */
  @media (min-width: 701px) {
    #wCadDrawingCard .wcad-inner {
      gap: 40px;
    }

  /* A BIGGER CAD ICON (Loz, 2026-09-21). The engine's box is 52px with a 48px
     img (css/thermii.css:1677); grown here so the mark carries the row the way
     his mock draws it. Beats the img's inline width/height, which CSS wins. */
    #wCadDrawingCard .wcad-icon {
      width: 72px;
      height: 72px;
    }
    #wCadDrawingCard .wcad-icon img {
      width: 64px;
      height: 64px;
    }

  /* THE TWO PILLS RIGHT-ALIGN TO THE KIT ROWS' OWN COLUMNS (Loz, 2026-09-21).
     The FREE badge left the icon stack (index.html) so it can sit rightmost:
     .wcad-title's div is flex:1, so add-btn and badge are pushed to the card's
     content right edge. Measured against a real 2-room kit at 1850px, the kit
     table's line-total column ends at x=1344 and its trash icon centres on
     x=1225 (panel 81..1361). The card's 22px padding puts the content right
     edge at 1339 ≈ the price column, so FREE lands under the prices with no
     nudge; the gap below lands "Add to my Kit"'s centre on the trash column. */
    #wCadDrawingCard .wcad-add-btn {
      margin: 0;
    }
  /* -12 against the 40px inner gap = 28px between the two pills, which lands
     "Add to my Kit"'s right edge on the trash column's right edge (x=1246) and
     its centre on the qty column centre (1180), while FREE stays centred on the
     line-total column (1302). Measured on the 2-room kit at 1850px. */
    #wCadDrawingCard .wcad-badge {
      margin-left: -12px;
      flex-shrink: 0;
    }
  }

  /* THE SMALL PRINT JOINS THE 1280 FAMILY, BUT ONLY ON THIS SCREEN. The
     unscoped `.kb-disclaimer { max-width: 1240px }` at :1243 is pinned for the
     KIT screen on purpose — :3587 records that its height feeds the PDF's
     page-break spacers and its pass-1-to-pass-2 drift model
     (legacy-core.js:5564/5578), so rewrapping that text moves page breaks on a
     document a customer keeps. Widening it here is scoped to the review
     screens, where no PDF is generated, which is the same `-summary` scope
     :1217 already uses. Measured on his mock: the "Disclaimer" label's ink
     starts 7px inside the card's left edge, against 20 today. */
  body[data-thermii-screen$="-summary"] .kb-disclaimer {
    max-width: 1280px;
  }

  #wq-summary-cta,
  #eq-summary-cta {
    max-width: 1280px;
    margin: 0 auto;
    /* 20 -> 0 with the panel above it, so the gradient bar keeps landing on
       the card's edges rather than 20px inside them. */
    padding: 22px 0 20px !important;
  }

  /* 14px put the confirm sentence almost against the View Recommended Kit
     button. Loz asked for breathing room beneath the text block, and this is
     the gap that provides it — the CTA's own top padding above handles the
     space under the CAD panel. Text stays ranged left: it is a sentence to
     read, not a label. */
  .confirm-check-wrap {
    margin-bottom: 28px;
    text-align: left;
  }

  /* #eViewKitBtn was written as #viewProductsBtn — an id that exists nowhere.
     The class is .view-products-btn; the id is #eViewKitBtn. So this rule only
     ever reached the water CTA, and the electric one fell through to
     thermii.css:1134 — flat var(--accent) purple, 20px, 1px border, radius 12,
     no min-height, gradient only on :hover. The two journeys' primary buttons
     have looked different ever since, which is invisible unless you compare
     them side by side. */
  #wViewKitBtn,
  #eViewKitBtn {
    width: 100%;
    min-height: 54px;
    border: 0;
    border-radius: 11px;
    background: var(--thermii-gradient);
    color: #fff;
    font-size: 17px;
    font-weight: 600;
    /* PIN THE PADDING SO HOVER CANNOT MOVE THE BUTTON (Loz, 2026-09-18: "slight
       jump/movement" on hover). thermii.css:1151/1152 bump `.view-products-btn`
       from 18px 24px to 19px 25px on :hover and :active — and because the box is
       `border-box` at a fixed 100% width, that +1px top/bottom grows it ~2px
       taller with nothing to transition it, so it snaps. That padding change was
       there to compensate for the base rule's 1px border vanishing on hover, but
       THIS button already has `border: 0` at rest (above), so there is no border
       to compensate for and the bump is pure wobble.

       This id selector (1,0,0) outranks `.view-products-btn:hover` (0,2,0), so
       stating the padding here fixes it in every state at once. thermii.css is
       byte-locked, which is why the fix lives here and not beside the rule that
       causes it. Value is the base rest padding unchanged, so the button looks
       exactly as it does now — it simply stops moving. */
    padding: 18px 24px;
    /* A SUBTLE HOVER CUE THAT CANNOT WOBBLE (Loz, 2026-09-18). Left as-is this
       button reacted to nothing on hover except the jump above — the base
       rule's :hover gradient never showed, because this id rule's own gradient
       outranks it. `filter` and `box-shadow` change how the button LOOKS
       without touching its geometry, so there is nothing to snap; only these
       two are transitioned. */
    transition: filter .18s ease, box-shadow .18s ease;
  }

  /* :not(:disabled) so the greyed-out button — disabled until the confirm box is
     ticked — does not light up under the cursor. #id:hover (1,1,0) outranks the
     base .view-products-btn:hover (0,2,0). */
  #wViewKitBtn:hover:not(:disabled),
  #eViewKitBtn:hover:not(:disabled) {
    filter: brightness(1.06) saturate(1.03);
    box-shadow: 0 6px 18px rgba(34, 40, 70, .22);
  }

  #wViewKitBtn:active:not(:disabled),
  #eViewKitBtn:active:not(:disabled) {
    filter: brightness(.97);
    box-shadow: 0 2px 7px rgba(34, 40, 70, .2);
  }

  #wViewKitBtn:disabled,
  #eViewKitBtn:disabled {
    opacity: .45;
  }

  /* Recommended kit / item table */
  body[data-thermii-screen="water-kit"] .thermii-sidebar {
    padding-top: 26px;
  }

  #wKitNav,
  #kitNav {
    margin: 0 !important;
    padding: 12px 0;
    background: transparent;
    box-shadow: none;
  }

  #wKitPanelHeading,
  #kitPanelHeading {
    display: none !important;
  }

  /* ── THE CONTAINER PANEL GOES, AND THE THREE PANELS TAKE ITS EDGES ────────
     (Loz, 2026-09-08) "Remove the overall container panel from around the
     'Your Room / Heat Requirement Check / Kit Summary' panels. THEN: move the
     3 panels to where the overall panel was."

     A CARD INSIDE A CARD SAYS NOTHING. Measured, all three of them already
     carry their own white ground, their own 1px --journey-border and their own
     14px radius — the room cards, `.wkit-heat-panel` and `.thermii-kit-table`.
     So the outer card drew a fourth border around three that were already
     reading as panels, on the same white, and its only visible contribution
     was to hold them 19px in from the width they were entitled to. Without it
     they sit on the page's own ground and the border each one owns does the
     separating, which is what a border is for.

     THE 19px IS IN TWO PIECES AND BOTH HAVE TO GO — the 1px border here and
     the 18px `.kit-items` inset below:

         1242 + (18 x 2) + (1 x 2) = 1280

     1280 is the `body.thermii-skin .kb-card` cap (css/thermii-skin.css:937),
     so it is exactly the box this panel occupied. The panels do not merely get
     wider; they land on the edges the container had, which is the second half
     of what he asked for.

     `overflow: hidden` GOES WITH IT rather than being left behind. It was here
     to clip the children to the card's rounded corners — with no corners left
     to clip, all it could ever do is cut something off. It has to be named
     explicitly because css/thermii.css:1756 (byte-locked) also declares it on
     `.kit-panel-card`; the ID selector is what overrides that.

     THE PHONE KEEPS ITS GUTTER and needs no separate rule, which is worth
     recording because it looks like it should. The inset removed here is not
     what holds the cards off a 390px viewport — `#waterPanel`, the parent,
     is itself at x=14 w=362, so the panels inherit that 14px gutter. Measured
     at 390px: room card 33 w324 before, 14 w362 after. */
  #wKitPanel,
  #kitPanel {
    margin: 0 !important;
    padding: 0 !important;
    overflow: visible;
    border: 0;
    border-radius: 0 !important;
    background: transparent;
    box-shadow: none;
  }

  .kit-items {
    padding: 0;
  }

  .wkit-cat-hdr,
  .kit-category-header {
    display: none !important;
  }

  #wKitItems > div,
  #kitItems > div {
    margin-bottom: 0 !important;
  }

  /* THE QTY TRACK IS DEFINITE, NOT `auto`, AND THAT IS WHAT MAKES THE COLUMN
     HEADER LINE UP. Each row and the header are SEPARATE grids that merely
     share a template, so an `auto` track resolves against each element's OWN
     content: measured, the row's controls came to 126px while the header's
     word "Qty" came to 24px, and the 1fr column then absorbed the difference,
     so every label after Item sat ~100px right of the column it named. A
     definite track resolves identically in both. 132px against the 126 the
     controls measure leaves room for a two-digit quantity, which is the most
     any kit line reaches. */
  .kit-row,
  .thermii-kit-colhdr {
    display: grid;
    grid-template-columns: 78px minmax(220px, 1fr) 74px 132px 86px;
    gap: 14px;
    min-height: 104px;
    padding: 10px 0;
  }

  /* NO BORDER (Loz, 2026-09-07): "remove the grey border from the product
     thumbnails." Deleted at SOURCE rather than overridden in the theme layer —
     this rule is the only thing that draws it (css/thermii.css's base rule
     gives the thumbnail its size, its object-fit and a radius, no border), so
     an override would leave two places disagreeing about it. The radius stays:
     the image keeps its shape, it just loses the box drawn round it. */
  .kit-img {
    width: 72px;
    height: 72px;
    border-radius: 9px;
  }

  .kit-name {
    margin-bottom: 3px;
    color: var(--thermii-navy);
    font-size: 14px;
    font-weight: 600;
  }

  .kit-sku,
  .kit-detail {
    font-size: 10px;
  }

  /* ── #9: the column header row (Loz, 2026-09-03) ─────────────────────────
     Injected by js/thermii-kit-screen.js — the engine emits nothing like it.
     Small, quiet, uppercase: it is a legend for three numbers, not a heading
     competing with the category names beneath it.

     IT SHARES THE ROW'S GRID TEMPLATE RATHER THAN CARRYING ITS OWN, at both
     desktop widths — see the base `.kit-row` rule above and the sidebar rule
     in thermii-system.css. One template per breakpoint; a parallel set would
     drift the moment either moved, and a header one column out of step is
     worse than no header.

     HIDDEN BELOW 900px, DELIBERATELY. The narrow variants are not the same
     table: at 900 the row drops to four columns AND `.kit-unit-price` is
     hidden outright, at 700 to three. Labels for columns that are not there,
     over cells in a different order, would actively mislead — so the legend
     goes and the rows keep their own inline meaning.

     HIDDEN IN PRINT for the same reason the sticky rules are neutralised
     there: this row is injected into #kitItems, which IS cloned into the PDF,
     and the page-break model measures real heights. The printed quote is no
     worse than it is today (its columns are unlabelled now), and adding a row
     to a paginated document is a change that should be made deliberately with
     the drift model re-checked, not as a side effect of a screen tweak. */
  .thermii-kit-colhdr {
    /* THE SHARED RULE CARRIES A 104px min-height for the item rows'
       thumbnails, and this header has no thumbnail — so it rendered as a
       104px empty band with 13px of labels sitting at the bottom of it, which
       is the gap Loz reported above the kit table. Sharing the TEMPLATE is
       what keeps the columns in step; sharing the row's height is not. */
    min-height: 0;
    align-items: end;
    gap: 14px;
    padding: 0 0 8px;
    /* NO RULE UNDER THE LABELS (Loz, 2026-09-07): "remove the horizontal
       separator line just above the lilac 'Pipe' header container." That line
       was this border — the labels row sits immediately above the first
       category bar, so its bottom border read as a rule on the bar rather
       than as an underline for the header. The lavender bars are their own
       separators; a grey rule stacked on the first one was one device too
       many. */
    margin-bottom: 4px;
    color: #8b93a7;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }

  /* ── THE LABELS REALLY DO SIT OVER THEIR NUMBERS NOW (Loz, 2026-09-07) ────
     The comment here used to claim they "line up the same way the numbers do"
     and then set `text-align: left` on all three — which was TRUE of nothing:
     both price columns are right-aligned and the qty pill is neither. Loz
     pointed at the result from two directions ("centre QTY above the quantity
     field", "the prices are not aligned to the bars").

     Each label now takes its own column's alignment, measured at 1900px:

         UNIT PRICE   right, over figures ending at 1114
         QTY          centred over the PILL, not over the cell — the cell is
                      132px and holds an 85px pill plus a 32px bin, so
                      centring in the cell would put the label over the gap
                      between the two. A 47px right inset (the bin plus its
                      gap) leaves exactly the pill's width to centre in.
         TOTAL        right, with the same 17px inset the line totals take, so
                      the label, the figures and the lilac bar's subtotal all
                      end on one edge. */
  .thermii-kit-colhdr > span:nth-child(3) {
    text-align: right;
  }

  .thermii-kit-colhdr > span:nth-child(4) {
    padding-right: 47px;
    text-align: center;
  }

  .thermii-kit-colhdr > span:nth-child(5) {
    padding-right: 17px;
    text-align: right;
  }

  /* ── #9: costs, quantity fields and bin, to Loz's reference ───────────────
     From costs-and-quantity.png: the UNIT PRICE is quiet grey, the TOTAL is
     bold navy, the stepper is a rounded outlined box and the bin is a plain
     grey glyph. The hierarchy is the point — three numbers on one row, and
     only the one the customer is being asked to accept is heavy.

     THE TWO REFERENCES DISAGREE ON COLUMN ORDER, and this follows the one Loz
     pointed at for this note. costs-and-quantity.png reads UNIT PRICE, QTY,
     TOTAL; the full Final Screen reads QTY, UNIT PRICE, TOTAL. The reference
     order is also the DOM's own order, so following it needs no `order`
     property — which matters beyond tidiness: reordering grid items visually
     leaves the tab order and the screen-reader order following the DOM, so the
     numbers would be read in a different sequence from the one on screen.
     Flagged for Loz; switching is one line if he wants the other. */
  .kit-unit-price {
    width: auto;
    color: #6b7280;
    font-size: 13px;
    font-weight: 400;
    font-variant-numeric: tabular-nums;
  }

  /* ── THE KIT LIST IS ITS OWN PANEL (Loz, 2026-09-08) ─────────────────────
     "Let's separate the whole of the 'Your Kit' section into its own panel
     with rounded corners." js/thermii-kit-screen.js wraps the column header
     and every category group in `.thermii-kit-table` and lifts the "YOUR KIT"
     legend back out above it, which is where his design has it.

     THE TREATMENT IS THE ROOM CARD'S AND THE HEAT PANEL'S, not a new one:
     white, a 1px `--journey-border`, a 14px radius. Those two are already
     bordered boxes inside this screen's big white card, so the kit list
     becomes the third panel in a set rather than a one-off.

     16px OF PADDING, AND IT GOES ON THE WRAPPER RATHER THAN THE ROWS — which
     is a real decision with a cost, so it is written down. His design runs the
     lavender category bars FULL BLEED to the panel's edges and insets only the
     item rows. Doing that here would break the alignment he asked for on
     2026-09-07 ("the black product prices are not aligned to the lilac and
     grey bars"): the line totals, the TOTAL label and the bars' subtotals all
     end on one x today, and they only do so because the bars and the rows
     share a right edge. The bars' own padding is inline in engine markup and
     the category-row rules are deliberately pinned to carry NO geometry,
     because those rows reach the printed quote. So the whole content insets
     together, every relationship inside the panel is preserved, and the bars
     stop 16px short of the panel's edge instead of bleeding to it. Flagged for
     him: the design's full-bleed bars are available, at the price of that
     alignment.

     16px lands the bars' labels 36px from the panel's edge, which is the
     design's own 33-34 for the same ink, and the thumbnails at 16 — the same
     relationship it draws (thumbnail left of the label by ~17px). */
  .thermii-kit-table {
    /* 12px LIFTS THE NEW BORDER OFF THE LEGEND. The legend used to sit on open
       white, so its 0px gap to the list was invisible; with a border under it
       the label reads as stuck to the panel. Measured on the design: 29px from
       the legend's ink to the panel, with 85px of air ABOVE the legend — the
       app gives it 26px above, so matching 29 exactly would leave the label
       floating between the two blocks rather than attached to the panel. 12px
       keeps it attached (12 below, 26 above) and clears the border. The
       design's fuller rhythm is a separate change; flagged, not smuggled. */
    margin-top: 12px;
    padding: 16px;
    border: 1px solid var(--journey-border);
    border-radius: 14px;
    background: #fff;
  }

  /* THE LAST GROUP'S OWN 24px MARGIN LANDS INSIDE THE PANEL NOW, so the foot
     measured 41px against the 17px at the head. The engine writes that margin
     inline on every group, so this needs `!important` — and it is restored on
     paper, where the height feeds the page-break model. */
  .thermii-kit-table > div:last-child {
    margin-bottom: 0 !important;
  }

  /* ── AND IT HIDES ITSELF WHERE IT REPEATS THE TOTAL (Loz, 2026-09-07) ─────
     "Are we repeating the unit costs on the grey text before the qty field and
     the black cost afterwards? Maybe just have the black price/cost showing?"

     On a quantity of 1 the two cells carry the SAME STRING — the engine builds
     them as `£price` and `£price * qty` (legacy-core.js:4594-4595) — and a
     'Price TBC' row renders the identical badge twice. Above 1 they differ,
     and the grey figure is then the only per-unit price on the screen. So
     js/thermii-kit-screen.js marks the cell only when its text matches the
     total's, and this hides exactly that.

     `visibility`, NOT `display`, and the reason is the shared template: these
     rows and the column header are separate grids that resolve the same
     five-track rule, and every track has to stay definite or 'TOTAL' stops
     sitting over its column. `visibility: hidden` keeps the box.

     PAPER IS UNAFFECTED — restored in the print block at the end of this file.
     The clone renders in SCREEN media, so a screen rule reaches the PDF; the
     printed quote is an itemised document a customer keeps and nobody asked
     for a column to come off it. */
  .kit-unit-price.thermii-unit-dup {
    visibility: hidden;
  }

  .kit-controls {
    gap: 10px;
  }

  /* The stepper: one outlined box holding both buttons and the value, rather
     than three loose controls. */
  .kit-qty-pill {
    border-color: #dce1eb;
    border-radius: 9px;
    background: #fff;
    overflow: hidden;
  }

  .kit-qty-btn {
    width: 30px;
    height: 30px;
    color: #6b7280;
    font-weight: 500;
  }

  .kit-qty-btn:hover {
    background: #f4f6fa;
    color: var(--thermii-navy);
  }

  /* Tabular figures so the digit does not shift the box between 1 and 2, and
     a min-width so a two-digit quantity does not widen the stepper mid-list. */
  .kit-qty-val {
    min-width: 22px;
    color: var(--thermii-navy);
    font-size: 13px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    text-align: center;
  }

  /* The bin is a DESTRUCTIVE control, so it stays quiet until reached for —
     grey at rest, and only red on hover. Colouring it red at rest would make
     the loudest thing in every row the one action a customer does not want. */
  .kit-del-btn {
    color: #9aa2b4;
    transition: color 0.15s ease, background 0.15s ease;
  }

  .kit-del-btn:hover {
    color: #d64545;
    background: transparent;
  }

  /* ── THE LINE TOTAL ALIGNS WITH THE BARS (Loz, 2026-09-07) ────────────────
     "The black product prices are not aligned to the lilac and grey bars."
     Measured at 1900px: the item prices right-aligned to **1358**, the card's
     own right edge, while the lilac bar's subtotal ends at **1341** — the
     engine writes `padding:10px 16px 10px 20px` INLINE on that bar, so its
     figure is inset by 16px plus the card's 1px border.

     THE PRICES MOVE, NOT THE BAR, and that is forced rather than chosen: the
     category-row rules are pinned to carry NO geometry, because those rows DO
     reach the printed quote and a padding change there would re-paginate a
     document customers keep — there is a mutation that catches exactly that.
     A right inset on this column is horizontal only, so it moves nothing
     vertically and paper is unaffected either way. */
  .kit-line-price {
    width: auto;
    padding-right: 17px;
    color: var(--thermii-navy);
    font-size: 14px;
    font-weight: 600;
  }

  #wKitFooterCard,
  #kitFooterCard {
    display: grid;
    grid-template-columns: minmax(240px, .36fr) minmax(0, 1fr);
    gap: 14px;
  }

  #wKitFooterCard .kit-total-block,
  #kitFooterCard .kit-total-block {
    border-color: var(--journey-border) !important;
    border-radius: 12px !important;
    background: #fff !important;
  }

  #wKitTotal,
  #kitTotal {
    color: #4e20f5 !important;
  }

  #wKitFooterCard .thermii-whatnext,
  #kitFooterCard .thermii-whatnext {
    margin-top: 0 !important;
    background: var(--thermii-gradient) !important;
    box-shadow: none !important;
  }

  /* ── THE GRADIENT SLAB BELOW THE ACTIONS CARD (found 2026-09-03) ───────────
     Below the 1500px sidebar breakpoint, #kitFooterCard is a TWO-COLUMN grid
     (the total block, then this panel). Grid items stretch to the tallest row
     by default, so the gradient WRAPPER grew to match the total column while
     the white card inside it kept its content height — leaving bare gradient
     under the buttons. MEASURED at 1400px: wrapper 1810px around 390px of
     content, so 1420px of raw gradient. At 1850px the sidebar stacks them and
     the tail is 4px, which is why it never shows on a wide screen and why my
     first estimate from a screenshot (~180px) was far too small.

     THE INNER CARD FILLS THE WRAPPER rather than the wrapper hugging its
     content, and that is a design choice not a mechanical one:
     `align-items: start` would also remove the slab, but it would leave the
     two cards at ragged heights. Filling keeps them level — which is what the
     2-column arrangement is for — and turns the slab into white card.

     `height: 100%` resolves against the wrapper's CONTENT box, and the wrapper
     carries `padding: 2px` (that padding IS the gradient border), so the inner
     lands exactly 4px shorter and the 2px rule survives on all four sides.

     NO PDF RISK AT ALL, unusually for this screen: `.thermii-whatnext` is in
     css/thermii.css's @media print hide list (:2287), so none of this reaches
     the printed quote. */
  #wKitFooterCard .thermii-whatnext > div,
  #kitFooterCard .thermii-whatnext > div {
    height: 100%;
    box-sizing: border-box;
  }
}

/* The rail, gutter, sidebar padding and stage column used to step here, all at
   once — that was the 1181px cliff. They are now the clamps in :root, whose
   floors are exactly the values this block used to set, so this breakpoint no
   longer carries any width of its own. What is left is genuinely about the
   narrow layout. */
/* ── #9: the column header never reaches the printed quote ──────────────────
   It is injected into #kitItems, and legacy-core.js clones that into
   #thermiiPdfRoot — so without this the PDF would gain a row and the
   page-break model would be measuring a document it was not tuned against.
   The printed quote is no worse than today (its columns are unlabelled now);
   putting a legend on paper is a deliberate change with the drift model
   re-checked, not a side effect.

   Deliberately outside `@media screen`, and stated because this file's tail
   is at brace depth 0 while its body is not: a screen-scoped rule could not
   reach print at all. */
@media print {
  /* The sticky bar is `position: fixed`, which in print either repeats on
     every page or sits over the content. It is a body child so it is not in
     the PDF clone either — this is the belt to that brace. */
  .thermii-kit-bar {
    display: none !important;
  }
}

@media print {
  .thermii-kit-colhdr {
    display: none !important;
  }
}

/* ── THE ADDITIONAL-PRODUCTS PANEL IS SCREEN FURNITURE ───────────────────────
   An upsell the customer has NOT taken has no place on the quote they hand to
   a builder — which is the same call css/thermii.css:2289/:2392 already makes
   for "Need more Tools?". This is that rule for the panel in
   js/thermii-extra-products.js, and it is HERE rather than beside its twin
   because css/thermii.css is byte-locked.

   THE WHOLE WRAPPER, not the button and body separately: this panel's nodes
   are built by the module inside one `#wExtraProductsWrap`, so hiding the
   wrapper cannot leave a stray bar behind the way naming two of three ids
   could. The PDF path removes the same id outright
   (js/thermii-pdf-clean.js), so neither route to paper shows it. */
@media print {
  #wExtraProductsWrap {
    display: none !important;
  }
}

/* ── #6: THE HEAT PANEL'S GREEN (Loz, 2026-09-03) ────────────────────────────
   "Match any green colours to the ones we implemented last week."

   The add-room buttons were the easy half. These two are the OTHER visible
   old-green on the kit screen — the "covered with headroom" status and the
   "all rooms covered" line — and they arrive from TWO sources:

     js/thermii-demand-band-wire.js   normally, and it is FREE, so its four
                                      literals were simply corrected at source
     js/legacy-core.js:1072/4501/4527 the engine's own, shown when the wire
                                      does not rewrite the panel

   `!important` IS THE RIGHT TOOL HERE, and that needs saying because this file
   avoids it elsewhere. The engine writes `color:#6cba15` as an INLINE style
   inside manifest-locked code, and inline loses only to !important. The
   alternative — a third post-render pass stripping those spans — would be
   fighting the band wire for the same nodes on the same tick, which is worse
   than a colour override by a wide margin.

   Superseded 2026-09-08 — see the block immediately below for the colour that
   now ships and why. */
/* ── THE GREEN IS #497b02 — HIS GREEN, MADE LEGIBLE (Loz, 2026-09-09) ───────
   He asked for #70bc03, was shown what it measured, and took the darker
   variant: "Use #497b02 instead mate."

   IT IS THE SAME GREEN, NOT A DIFFERENT ONE, and that is the point worth
   keeping. Both are hsl(85, 97%) — his hue and his saturation to the degree —
   and #497b02 is simply 13 points darker in lightness (24.5% against 37.5%).
   It was derived by stepping HIS colour down until it cleared the threshold,
   not chosen from a palette, which is why it still reads as the green he
   picked rather than as a correction of it.

   AND IT CLEARS WCAG 1.4.3 AA, which #70bc03 did not. Measured on the
   all-clear box's own #f0f9eb, for 15px text needing 4.5:1:

       #8cc63f  1.90:1     the old brand lime
       #70bc03  2.19:1     his first pick — brighter than the lime, still short
       #497b02  4.73:1     THIS, and 5.10:1 on the white the row labels sit on
       #006400  6.89:1     the dark green sampled from his match-green.png

   THREE GREENS IN THREE DAYS, so the gate pins the value AND asserts the
   threshold again. It stopped asserting the threshold for one revision, while
   a value its owner had knowingly chosen was below it — a check that fails on
   a decision is noise, and noise teaches the next reader to ignore a red gate.
   That exception is over and the assertion is back; the number is still
   published in the check's own label, computed rather than typed.

   BOTH CLASSES MOVE TOGETHER, as they always have: `.wkit-heat-status` is the
   per-room "Covered with headroom" label, the same statement at row scale, and
   he named both in the same breath. Splitting them would put two greens in one
   panel.

   DELIBERATELY OUTSIDE `@media screen`, unlike the tick and chevron rules
   above. The heat panel appears in the generated PDF, so a screen-only rule
   would leave the printed quote in the old green while the screen showed the
   new one. This is COLOUR ONLY — no geometry — so it cannot move a page break,
   which is the thing CLAUDE.md warns about for !important reaching the PDF.
   No id selector either, for the same reason. */
.wkit-heat-allclear,
.wkit-heat-status {
  color: #5f9500 !important;
}

.wkit-heat-allclear svg,
.wkit-heat-status svg {
  stroke: #74b700 !important;
}

/* ── THE ENGINE'S OWN LIMES ON THE KIT SCREEN (Loz, 2026-09-16: "the greens on
   this page haven't been updated to #74b700") ─────────────────────────────
   Measured over every element of the rendered kit: one #8cc63f left — the
   FREE pill on the CAD Drawings line, written inline by manifest-locked code
   (legacy-core.js:4593). Four more are STATES the bench does not reach: the
   electric kit's switch pills once a variant or cable laying is added (:1119,
   :1201), the two save buttons after a save (:1811, :3777 — set through
   .style, which the browser serialises to rgb()), and the "added to your
   kit" toast (:3365, :3370). Inline needs !important. Outside @media screen
   on purpose: the FREE pill reaches the PDF, and colour only moves no page
   break. The toast's caption is a text line, so it takes the text green. */
.kit-line-price span[style*="background:#8cc63f"] { background: #74b700 !important; }
.kit-switch-pill[style*="#8cc63f"] { color: #74b700 !important; border-color: #74b700 !important; }
[onclick="thermiiSaveToAccount(this)"][style*="rgb(140, 198, 63)"],
#wSaveQuoteBtn[style*="rgb(140, 198, 63)"] { background: #74b700 !important; border-color: #74b700 !important; }
div[style*="background:#8cc63f;border-radius:50%"] { background: #74b700 !important; }
div[style*="color:#8cc63f"] { color: #5f9500 !important; }

/* ── A SHORTFALL IS NOT GREEN (Loz, 2026-09-10: "The green 'covered with
   headroom' clashes. This needs fixing!") ────────────────────────────────────
   The rule above sets the panel green on `.wkit-heat-status` unconditionally
   and with `!important`, which beats the amber the engine writes INLINE on a
   short room — measured, a row reading "70 W short" rendered in #497b02 beside
   an amber warning triangle. Both colours are fine on white (5.10:1 green,
   5.04:1 amber); the fault was applying the wrong one, and it needed a state
   to fix rather than a colour. js/thermii-kit-screen.js marks the row from the
   presence of its warning block, and (0,2,0) beats (0,1,0) among the
   !important declarations.

   OUTSIDE `@media screen`, deliberately and for the same reason the rule above
   is: the heat panel appears in the generated PDF, so a screen-only fix would
   leave the printed quote calling a shortfall green. Colour only, no geometry,
   so it cannot move a page break. */
.thermii-heat-r-short .wkit-heat-status {
  color: #a06000 !important;
}

.thermii-heat-r-short .wkit-heat-status svg {
  stroke: #c08000 !important;
}

@media screen and (max-width: 1180px) {
  #wq-step-4fc .electric-covering-row {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    max-width: 760px;
  }

  #wq-step-4fc .filter-option {
    min-height: 170px;
  }
}

@media screen and (max-width: 900px) {
  .thermii-app-grid {
    grid-template-columns: 1fr;
  }

  .thermii-workspace {
    padding-right: 20px;
    padding-left: 20px;
  }

  .thermii-main-shell #systemTypeCard {
    padding-right: 20px;
    padding-left: 20px;
  }

  #wqProgressBar,
  #eqProgressBar {
    margin-right: -20px;
    margin-left: -20px;
    padding-right: 20px;
    padding-left: 20px;
  }

  #wq-step-4 .room-tiles-grid,
  #eq-q1 .room-tiles-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  #wq-step-4flr .electric-covering-row,
  #wq-step-4c .electric-covering-row,
  #eq-q2 .electric-covering-row {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .kit-row {
    grid-template-columns: 68px minmax(180px, 1fr) auto 76px;
  }

  .kit-unit-price {
    display: none;
  }

  /* The legend goes with it: four columns here, and the unit price hidden
     outright, so its labels would name columns that are not there. */
  .thermii-kit-colhdr {
    display: none;
  }
}

@media screen and (max-width: 700px) {
  body {
    background: #fff;
  }

  /* ── The small print keeps its own small-screen sizes ─────────────────────

     RESTATED, NOT INHERITED, and this is a correction: the desktop reduction
     added above (search .kb-disclaimer p) ties css/thermii.css:1646 on
     specificity and wins on load order — and A MEDIA QUERY ADDS NO
     SPECIFICITY, so it also beat thermii.css:2237-2238's existing small-screen
     step. Measured at 375px: the body and copyright went UP 9px → 11px and the
     title 11px → 13px. A change asked for as "reduce the text size" had become
     an ENLARGEMENT on a phone, which is the opposite of the request.

     So the pre-existing small-screen sizes are restated here verbatim
     (thermii.css:2237-2238) and mobile is genuinely unchanged.

     9px is small — below the 11px floor argued for on desktop, on the same
     legal notice — so it was flagged rather than quietly raised.

     DECIDED 2026-09-02, AND IT IS NOT AN OVERSIGHT: Loz asked for 11px on the
     phone, then said "keep as is: on a phone the small print is 9px". So 9px
     stands by decision, not by inheritance. Do not "fix" it, and do not
     re-raise it as an open question. Raising it would also have collapsed the
     hierarchy here unless the title moved with it: at 11px the body would have
     matched the 11px title exactly, so the heading would have stopped reading
     as a heading. */
  .kb-disclaimer p {
    font-size: 9px;
  }

  .kb-disclaimer .kb-disclaimer-title {
    font-size: 11px;
  }

  /* ── 'Your Selections' ticks on a phone (Loz, 2026-09-03) ───────────────────
     The companion to the desktop reduction (search .eq-sum-row .eq-ans-check
     in the @media screen block above). 22px/11px -> 19px/10px.

     `!important` IS REQUIRED HERE AND IS NOT A SHORTCUT. css/thermii.css:2136
     sets `.eq-ans-check{width:22px!important;height:22px!important}` under
     700px, and !important cannot be beaten by specificity or by load order —
     only by !important. This is the same family as the .kb-disclaimer
     correction above, one step harder: that rule was merely more specific,
     this one is flagged.

     IT IS SAFE FOR THE PDF PRECISELY BECAUSE IT IS INSIDE @media screen.
     downloadKitPdf injects _thermiiScopedPrintCss('#thermiiPdfRoot')
     (legacy-core.js:5375), which re-emits ONLY @media print rules, scoped and
     !important — so nothing here can reach the generated quote. Worth stating
     because the electric summary's ticks DO print: thermii.css:2340 hides
     `.eq-ans-check` for `#wq-summary` only, so #eq-summary keeps its ticks on
     paper, at the base 26px, unchanged by this block. And #eq-summary carries
     `page-break-after: always`, so a size change that DID reach print would be
     moving page breaks on a document a customer keeps.

     ALL THREE OF width/height/flex-basis, because the disc is a flex item and
     the basis wins the width: today's measures 21x22, an oval, and setting
     fewer than three would leave it one.

     10px not 9.5px: the glyph is a 2px-stroke polyline on a 16-unit viewBox,
     so a half-pixel box lands the stroke off the device grid and the tick goes
     soft. 19/10 also keeps the disc a whole pixel larger than desktop's 18,
     which is the relationship that already existed (22 against 21) rather
     than a new one invented here. */
  .eq-sum-row .eq-ans-check {
    width: 19px !important;
    height: 19px !important;
    flex: 0 0 19px !important;
  }

  .eq-sum-row .eq-ans-check svg {
    width: 10px !important;
    height: 10px !important;
  }

  .thermii-stage-header {
    margin-right: 14px;
    margin-left: 14px;
  }

  .thermii-workspace {
    padding-right: 14px;
    padding-left: 14px;
  }

  .eq-section {
    min-height: 0;
    padding: 34px 0 28px;
  }

  .eq-title,
  .kb-section-title {
    font-size: 28px;
  }

  .eq-subtext,
  .thermii-screen-intro {
    margin-bottom: 24px;
    font-size: 14px;
  }

  #systemTypeRow {
    grid-template-columns: 1fr;
    max-width: 430px;
  }

  #systemTypeRow .system-option {
    min-height: 190px;
  }

  #wq-step-2 .electric-covering-row,
  #wq-step-4d .electric-covering-row,
  #wq-step-4ps .electric-covering-row,
  #wq-step-4fc .electric-covering-row,
  #wq-step-4 .room-tiles-grid,
  #eq-q1 .room-tiles-grid,
  #wq-step-4flr .electric-covering-row,
  #wq-step-4c .electric-covering-row,
  #eq-q2 .electric-covering-row {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }

  .filter-option,
  #wq-step-2 .filter-option,
  #wq-step-4flr .filter-option,
  #wq-step-4c .filter-option,
  #eq-q2 .filter-option,
  #wq-step-4ps .filter-option {
    width: auto;
    min-height: 170px;
    height: auto;
    padding: 20px 8px;
  }

  .fo-icon,
  .fo-icon img {
    width: 58px;
    height: 58px;
  }

  /* The mobile tile-label size lives in css/thermii-system.css as --t-h4.
     A plain `.fo-label{font-size:15px}` used to sit here, but at (0,0,1,0) it
     was outranked by thermii.css's (0,0,3,0) rule on the covering rows and by
     the ID-scoped exceptions below, so it never applied anywhere. It has been
     removed rather than left to imply a size it could not set. */

  /* On phones these eleven rows lay the icon and label out side by side in a
     155px tile, which leaves about 91px for the label. thermii.css sets
     white-space:nowrap on it, so the longer options — "Existing Concrete",
     "Suspended Floor", "Not Sure Yet" — painted straight through the tile edge.

     Letting them wrap is enough: the longest single word fits the column, and
     the tiles already reserve min-height:170px so a second line costs no extra
     height. min-width:0 lets the label shrink as a flex item rather than
     forcing its intrinsic width back onto the row.

     thermii.css is byte-locked to the baseline so the nowrap cannot be removed
     at source, but it carries no !important — this simply matches its
     specificity from a later sheet. Scoped to the same selector and the same
     breakpoint; .room-tiles-grid is excluded there and stacks vertically, so it
     is excluded here too. */
  .electric-covering-row:not(.room-tiles-grid) .fo-label {
    white-space: normal;
    min-width: 0;
  }

  .eq-input-wrap {
    min-height: 60px;
  }

  .area-unit-btn,
  .wdist-unit-btn {
    min-width: 58px;
  }

  #wqProgressBar,
  #eqProgressBar {
    min-height: 70px;
    margin-right: -14px;
    margin-left: -14px;
    padding: 10px 14px;
  }

  .eq-back-btn {
    min-width: 104px;
    min-height: 46px;
  }

  .tstat-carousel .tstat-grid-5 .thermostat-card {
    flex-basis: 78vw;
    width: 78vw;
    max-width: 300px;
  }

  /* One card already fills the screen here, so there is nothing to peek at and
     no room to overlay an arrow on a 78vw card. Put the nav back in flow
     underneath, exactly as it was. */
  .tstat-carousel-nav {
    position: static;
    inset: auto;
    gap: 24px;
    pointer-events: auto;
  }

  .tstat-carousel-arrow,
  .tstat-carousel-arrow:hover {
    position: static;
    transform: none;
    width: auto;
    height: auto;
    background: none;
    box-shadow: none;
  }

  .tstat-carousel-arrow img {
    width: 30px;
    height: 30px;
  }

  .eq-sum-row {
    flex-wrap: wrap;
  }

  .eq-sum-row .eq-ans-q {
    min-width: 0 !important;
  }

  #wCadDrawingCard {
    margin-right: 0;
    margin-left: 0;
  }

  .wcad-inner {
    align-items: flex-start;
    flex-wrap: wrap;
  }

  /* No left indent on the phone button. css/thermii.css:2144 (byte-locked)
     makes it flex:0 0 100% with text-align:center at <=700px, so a 68px
     margin-left just pushed a full-width button 68px past the card's right
     edge — measured 415 against a 376 card at 390px. 0 keeps it centred and
     in-bounds. */
  .wcad-add-btn {
    margin-left: 0;
  }

  .kit-row {
    grid-template-columns: 64px minmax(0, 1fr) auto;
    gap: 10px;
    padding: 14px 0;
  }

  .kit-img {
    width: 60px;
    height: 60px;
  }

  .kit-controls {
    grid-column: 2;
  }

  .kit-line-price {
    grid-column: 3;
    grid-row: 1;
  }

  #wKitFooterCard,
  #kitFooterCard {
    grid-template-columns: 1fr;
  }
}

@media screen and (max-width: 430px) {
  .thermii-main-shell #systemTypeCard {
    padding: 32px 6px;
  }

  #wq-step-4 .room-tiles-grid,
  #eq-q1 .room-tiles-grid {
    gap: 10px;
  }

  #wq-step-4 .room-tiles-grid .filter-option,
  #eq-q1 .room-tiles-grid .filter-option {
    min-height: 150px;
  }

  #wq-step-4fc .electric-covering-row {
    grid-template-columns: 1fr 1fr !important;
  }

  .confirm-check-wrap {
    align-items: flex-start;
  }
}

/* ── Mixed project: the other system's kit, read-only ──────────────────────
   js/thermii-project-quote.js appends this after the engine's own kit list when
   a project holds BOTH water and electric rooms. Deliberately quieter than
   .kit-row — these lines are facts about the project, not controls: they carry
   no thumbnail, no qty stepper and no bin, because every one of those in the
   engine's render is indexed into its OWN array and would edit the wrong list.
   The visual weight difference IS the affordance. */
.thermii-project-other {
  margin: 8px 0 4px;
  padding: 20px 22px;
  border: 1px solid #e4e6ef;      /* solid, not dashed (Loz, 2026-09-24) */
  border-radius: 14px;
  background: #fff;
}
.thermii-project-other-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.thermii-project-other-title {
  font-family: 'Poppins', sans-serif;
  font-size: 15px;
  font-weight: 600;
  color: #222846;
}
.thermii-project-other-rooms {
  font-family: 'Poppins', sans-serif;
  font-size: 13px;
  color: #7b8496;
  white-space: nowrap;
}
.thermii-project-other-note {
  margin-top: 2px;
  font-family: 'Poppins', sans-serif;
  font-size: 12px;
  color: #8b93a4;
}
.thermii-project-other-list { margin-top: 12px; }
.thermii-project-other-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 0;
  border-bottom: 1px solid #eceff5;
  font-family: 'Poppins', sans-serif;
  font-size: 14px;
  color: #333;
}
.thermii-project-other-row:last-child { border-bottom: none; }
.thermii-project-other-qty {
  min-width: 34px;
  color: #7b8496;
  font-variant-numeric: tabular-nums;
}
.thermii-project-other-name { flex: 1; min-width: 0; }
.thermii-project-other-price {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: #222846;
}
.thermii-project-other-foot {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid #dfe4ee;
  font-family: 'Poppins', sans-serif;
  font-size: 14px;
  font-weight: 600;
  color: #222846;
}
.thermii-project-other-subtotal { font-variant-numeric: tabular-nums; }

/* the split under the project total — a combined figure with no breakdown is a
   number the customer cannot check */
.thermii-project-split {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 10px;
  margin-top: 6px;
  font-family: 'Poppins', sans-serif;
  font-size: 14px;
  font-weight: 500;
  color: #5a6472;
  font-variant-numeric: tabular-nums;
}
.thermii-project-split-plus { color: #6c4bd8; font-weight: 600; }

/* ── Mixed project: the whole-project panel ────────────────────────────────
   Sits BELOW the engine's own total block and never rewrites it. The engine's
   block keeps describing the kit it built (its caption just gains the system
   name); this panel is the project. Deliberately a different shape from the
   pale-green total block so the two are never mistaken for each other. */
/* A FAINT PANEL, NOT A BLACK FRAME (Loz, 2026-09-24: "not keen on the thick
   black border"). The Insights panels' own lilac and hairline, so the mixed
   project's summaries read as one family with the rest of the screen. */
.thermii-project-panel {
  margin-top: 12px;
  padding: 24px 22px;
  border: 1px solid #e4defb;
  border-radius: 16px;
  background: #f6f4ff;
  text-align: left;
}
.thermii-project-panel-head {
  font-family: 'Poppins', sans-serif;
  font-size: 20px;
  font-weight: 600;
  color: #222846;
}
.thermii-project-panel-sub {
  margin-top: 4px;
  font-family: 'Poppins', sans-serif;
  font-size: 13px;
  color: #7b8496;
}
.thermii-project-panel .thermii-project-other { margin-top: 16px; }
.thermii-project-panel .thermii-project-split { justify-content: flex-start; }
.thermii-project-grand {
  /* stacked, not a row: the sidebar this sits in is ~308px, and a
     label-beside-figure row broke "Total Project Cost:" over three lines
     against the figure. Measured in the sidebar, not assumed. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid #e4defb;
}
.thermii-project-grand-label {
  font-family: 'Poppins', sans-serif;
  font-size: 20px;
  font-weight: 600;
  color: #222846;
}
.thermii-project-grand-figure {
  font-family: 'Poppins', sans-serif;
  font-size: 34px;
  font-weight: 600;
  color: #4f7d00;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.thermii-project-panel-foot {
  margin-top: 6px;
  font-family: 'Poppins', sans-serif;
  font-size: 12px;
  color: #8b93a4;
}

/* The list half of the project panel sits in the MAIN column beside the
   engine's own kit rows; the total half sits in the ~308px sidebar. Measured:
   an 11-line item list in the sidebar became a tower of wrapped names. */
.thermii-project-panel-list { margin: 4px 0 20px; }
.thermii-project-panel-list .thermii-project-other { margin-top: 14px; }
.thermii-project-panel .thermii-project-split { flex-wrap: wrap; }

/* ── Mixed project: the OTHER system's kit below this one (Loz, 2026-09-25) ──
   js/thermii-project-quote.js appends .thermii-po into #wKitItems / #kitItems.
   A legend like the kit list's own ("YOUR KIT · 13 items…"), then ONE white
   card like the kit table's, grouped as that system's own screen groups it,
   read-only rows with a small thumbnail and SKU. NOT screen-scoped: it is in
   the cloned kit list, so the PDF quote carries it too. */
#wKitItems > .thermii-po, #kitItems > .thermii-po { margin-top: 36px !important; }
.thermii-po { text-align: left; }
.thermii-po-legend { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin: 0 4px 12px; }
.thermii-po-ey {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #222846;
}
.thermii-po .thermii-drawer-sys { display: inline-block; flex: 0 0 13px; width: 13px; height: 13px; background-color: currentColor; }
.thermii-po-meta { font-size: 13px; font-weight: 300; color: #8b93a4; }
.thermii-po-where { margin-left: auto; font-size: 12.5px; font-weight: 300; color: #6b7280; }
.thermii-po-card {
  padding: 16px; background: #fff;
  border: 1px solid var(--journey-border, #dfe4ee); border-radius: 14px;
}
.thermii-po.is-electric .thermii-po-card { border-top: 3px solid #e8a020; }
.thermii-po.is-water .thermii-po-card { border-top: 3px solid #149de8; }
.thermii-po-bar {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 16px 11px 20px; border-radius: 10px; background: #f4f2ff;
}
.thermii-po-rows + .thermii-po-bar { margin-top: 14px; }
.thermii-po-bar-n { font-size: 16px; font-weight: 600; color: #4b28ed; }
.thermii-po-bar-pill {
  padding: 2px 9px; border-radius: 999px; background: #fff;
  font-size: 12px; font-weight: 600; color: var(--journey-selected, #5117f7);
}
.thermii-po-bar-c { font-size: 13px; font-weight: 300; color: #8b93a4; }
.thermii-po-bar-sub {
  margin-left: auto; font-size: 15px; font-weight: 700;
  color: var(--journey-selected, #5117f7); font-variant-numeric: tabular-nums;
}
/* THE KIT ROWS' OWN TRACKS FOR THE TAIL (Loz, 2026-09-25: "aligned with the
   water ones above") — .kit-row ends in a 132px controls column and an 84px
   total with 17px of right padding, gap 14, no side padding, so the steppers,
   bins and totals here land on exactly the same x as the kit list above. */
.thermii-po-row {
  display: grid; grid-template-columns: 44px minmax(0, 1fr) 132px 86px; align-items: center; gap: 14px;
  padding: 10px 0; border-bottom: 1px solid #eef0f5;
}
.thermii-po-row .thermii-po-tot { padding-right: 17px; font-size: 14px; }
/* ...and follow .kit-row where its tail changes: the sidebar layout's 132/84
   (css/thermii-system.css, >=1500px) and the <=900px auto/76 */
@media screen and (min-width: 1500px) {
  body[data-thermii-screen="electric-kit"] .thermii-po-row,
  body[data-thermii-screen="water-kit"] .thermii-po-row { grid-template-columns: 44px minmax(0, 1fr) 132px 84px; }
}
@media screen and (min-width: 701px) and (max-width: 900px) {
  .thermii-po-row { grid-template-columns: 44px minmax(0, 1fr) auto 76px; }
}
.thermii-po-row:last-child { border-bottom: 0; }
.thermii-po-thumb {
  width: 44px; height: 44px; border-radius: 8px; overflow: hidden;
  background: #f6f7fa; display: flex; align-items: center; justify-content: center;
}
.thermii-po-thumb img { width: 100%; height: 100%; object-fit: contain; }
.thermii-po-qty {
  justify-self: start; min-width: 40px; height: 26px; padding: 0 8px; border-radius: 8px;
  background: #f6f7fa; display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 600; color: #5b6480; font-variant-numeric: tabular-nums;
}
.thermii-po-info { display: flex; flex-direction: column; min-width: 0; }
/* the other system's list reads like the kit rows beside it (Loz, 2026-09-28:
   "the water kits product titles have dropped a weight … match them to the
   electric ones"): .kit-name's own tokens — --t-h4 (16px, 15px mid-width),
   --tt-h3 tracking, 600, navy — and its 14px phone step (css/thermii.css), and
   the sub-line takes .kit-detail's 12px / 400 grey rather than a light 300 */
.thermii-po-name {
  font-size: var(--t-h4, 16px); letter-spacing: var(--tt-h3, -.012em); line-height: 1.5;
  font-weight: 600; color: var(--thermii-navy, #101735);
}
.thermii-po-dt { margin-top: 2px; font-size: 12px; font-weight: 400; line-height: 1.4; color: #6b7280; }
@media screen and (max-width: 700px) { .thermii-po-name { font-size: 14px; } }
.thermii-po-tot { font-size: 14.5px; font-weight: 600; color: #222846; font-variant-numeric: tabular-nums; white-space: nowrap; text-align: right; }
/* ── A MIXED KIT LIST READS AS ONE PROJECT (Loz, 2026-09-28) ─────────────────
   js/thermii-project-quote.js arrangeMixed(). "Your Kit" becomes the section
   title over both kits (kit-screen's own legend, restyled — text-transform
   capitalises its "Your kit"); each system's legend follows it 14px down
   rather than the blocks' 36px; the page's own kit card takes its system's
   top edge. */
#wKitItems > .thermii-kit-eyebrow.is-mixed-title,
#kitItems > .thermii-kit-eyebrow.is-mixed-title {
  display: flex; align-items: baseline; gap: 12px; margin-bottom: 16px !important;
}
#wKitItems .is-mixed-title .thermii-kit-eyebrow-t,
#kitItems .is-mixed-title .thermii-kit-eyebrow-t {
  font-size: 20px; font-weight: 700; letter-spacing: -0.005em; text-transform: capitalize; color: #222846;
}
#wKitItems .is-mixed-title .thermii-kit-eyebrow-n,
#kitItems .is-mixed-title .thermii-kit-eyebrow-n { display: none; }
.thermii-po-titlen { font-size: 13px; font-weight: 300; color: #8b93a4; }
#wKitItems > .is-mixed-title + .thermii-po,
#kitItems > .is-mixed-title + .thermii-po { margin-top: 0 !important; }
.thermii-po-own .thermii-po-legend { margin-bottom: 12px; }
.thermii-kit-table.thermii-po-owncard.is-electric { border-top: 3px solid #e8a020; }
.thermii-kit-table.thermii-po-owncard.is-water { border-top: 3px solid #149de8; }
.thermii-po-ownfoot.is-electric { background: #fef7ea; }
.thermii-po-ownfoot.is-water { background: #f1f8fe; }

.thermii-po-foot {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 12px;
  margin-top: 14px; padding: 14px 16px 14px 20px; border-radius: 10px;
}
.thermii-po.is-electric .thermii-po-foot { background: #fef7ea; }
.thermii-po.is-water .thermii-po-foot { background: #f1f8fe; }
.thermii-po-foot-l { font-size: 15px; font-weight: 600; color: #222846; }
.thermii-po-foot-note { font-size: 12.5px; font-weight: 300; color: #6b7280; }
.thermii-po-foot-fig { margin-left: auto; font-size: 18px; font-weight: 700; color: #222846; font-variant-numeric: tabular-nums; }
@media (max-width: 700px) {
  .thermii-po-card { padding: 10px; }
  .thermii-po-row { grid-template-columns: 40px minmax(0, 1fr) auto; gap: 8px; padding: 10px 8px; }
  .thermii-po-row .thermii-po-thumb { grid-row: 1 / span 2; }
  .thermii-po-row .kit-controls { grid-column: 2; }
  .thermii-po-row .thermii-po-tot { grid-column: 3; grid-row: 1 / span 2; }
  .thermii-po-thumb { width: 40px; height: 40px; }
  .thermii-po-where { margin-left: 0; width: 100%; }
}

/* ── Mixed project: the second neoHub card says the project has one ────────
   js/thermii-hub-shared.js marks the card `is-included` when the other side of
   the project already carries the hub. It wears the added colours (the
   project IS covered) but no longer reads as a thing to click. */
.neohub-card.is-included,
.neohub-card.is-included:hover { border-color: var(--green); background: #f4fbe8; cursor: default; }
.neohub-card.is-included .neohub-name { font-weight: 600; }
.neohub-card.is-included .neohub-add-btn { background: var(--green); border-color: var(--green); color: #fff; }
/* no price: beside "Already in your project" even a grey £169.99 reads as a
   second charge — it is paid once, in the other kit (Loz, 2026-09-25) */
.neohub-card.is-included .neohub-price { display: none; }
/* 10px each side = .neohub-card-wrap's and .neohub-question's own side margins,
   so the line starts on the card's edge */
.thermii-hub-shared-note {
  margin: 10px 10px 0; font-size: 13.5px; font-weight: 300; line-height: 1.5;
  color: var(--journey-muted, #596584);
}

/* ── Mixed project: ONE Rooms list on "Your Selections" (Loz, 2026-09-25) ──
   js/thermii-project-review.js draws every room of a mixed project as a card,
   grouped by system under one "Rooms:" line. The engine's own room rows are
   hidden (never removed — it rebuilds them each render); the cards carry the
   engine's own Edit/Duplicate handlers. Details use the engine's .wsel-sub-q /
   .wsel-sub-v, so both systems read at the review's own size. */
.thermii-review-engine-room { display: none !important; }
.thermii-review-rooms { align-items: flex-start; }
.thermii-review-rooms-body { flex: 1; min-width: 0; }
.thermii-review-rooms-h {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-bottom: 4px;
}
/* the engine writes its value styles inline on its own spans; ours carry none
   (the LABEL grey and weight come from the shared review rule above) */
.thermii-review-rooms-h .wsel-rooms-lbl { margin: 0; }
.thermii-review-f .wsel-sub-v { font-weight: 400; color: #222846; flex: 1; min-width: 0; }
.thermii-review-rooms-n {
  font-size: 13px; font-weight: 600; color: #222846;
}
.thermii-review-legend { margin-left: auto; display: inline-flex; flex-wrap: wrap; gap: 8px; }
.thermii-review-chip {
  display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 11px;
  border-radius: 999px; font-size: 12.5px; font-weight: 600;
}
/* ink darkened from the mockup's to clear AA at 12.5px on each tint */
.thermii-review-chip.is-water { background: #f1f8fe; color: #0d6aa0; }
.thermii-review-chip.is-electric { background: #fef7ea; color: #875600; }
.thermii-review-rooms .thermii-drawer-sys {
  display: inline-block; flex: 0 0 13px; width: 13px; height: 13px; background-color: currentColor;
}
.thermii-review-chip .thermii-drawer-sys.is-water { color: #149de8; }
.thermii-review-chip .thermii-drawer-sys.is-electric { color: #e8a020; }
/* BLOCK, not flex: as a flex row the heading's text and its "· 2 rooms" were
   separate items, so on a phone the count fell into a column of its own at the
   right edge (mobile sweep, 2026-09-25). As text it wraps like a sentence. */
.thermii-review-group-h {
  display: block; margin: 16px 0 10px;
  font-size: 11.5px; font-weight: 700; line-height: 1.5;
  letter-spacing: .08em; text-transform: uppercase; color: #8b93a4;
}
.thermii-review-group-h .thermii-drawer-sys { margin-right: 8px; vertical-align: -1px; }
.thermii-review-group-n { font-weight: 400; letter-spacing: 0; text-transform: none; white-space: nowrap; }
.thermii-review-card {
  margin-bottom: 12px; overflow: hidden; background: #fff;
  border: 1px solid var(--journey-border, #dfe4ee); border-radius: 12px;
}
.thermii-review-card-h {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding: 10px 14px; border-left: 3px solid transparent;
}
.thermii-review-card.is-water .thermii-review-card-h {
  border-left-color: #149de8; background: linear-gradient(90deg, #f1f8fe, #fff 40%);
}
.thermii-review-card.is-electric .thermii-review-card-h {
  border-left-color: #e8a020; background: linear-gradient(90deg, #fef7ea, #fff 40%);
}
.thermii-review-card-h .thermii-drawer-sys { flex-basis: 15px; width: 15px; height: 15px; }
.thermii-review-card-name {
  font-size: 14px; font-weight: 600; color: #222846;
}
.thermii-review-card-area {
  padding: 3px 10px; border-radius: 999px; background: #f4f2ff;
  font-size: 12.5px; font-weight: 600;
  color: var(--journey-selected, #5117f7);
}
.thermii-review-card-acts { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
.thermii-review-card-acts .eq-ans-edit { margin: 0; }
.thermii-review-card-note {
  font-size: 12.5px; font-weight: 300; color: #6b7280;
}
.thermii-review-card-f { padding: 8px 14px 12px 30px; border-top: 1px solid #eef0f5; }
/* collapsible (Loz, 2026-09-25): the header opens and closes the card; closed,
   it shows a one-line gist of the answers that move the kit most */
.thermii-review-card-h { cursor: pointer; }
.thermii-review-card-gist {
  /* basis 0px, not auto: at auto its full text is the basis, the header wraps and
     the controls drop a line. 0px, never a bare 0 (Safari). */
  flex: 1 1 0px; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  font-size: 12.5px; font-weight: 300; color: #6b7280;
}
.thermii-review-card:not(.is-open) .thermii-review-card-f { display: none; }
.thermii-review-card.is-open .thermii-review-card-gist { display: none; }
.thermii-review-card-toggle {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 32px;
  width: 32px; height: 32px; padding: 0; border-radius: 9px; cursor: pointer;
  border: 1px solid #c5b8ff !important; background: #f4f2ff; color: #4b28ed;
}
.thermii-review-card-toggle svg { transition: transform .2s ease; }
.thermii-review-card.is-open .thermii-review-card-toggle svg { transform: rotate(180deg); }
.thermii-review-all {
  padding: 0; border: 0 !important; background: none; cursor: pointer;
  font-size: 12.5px; color: var(--journey-selected, #5117f7);
  text-decoration: underline; text-underline-offset: 3px;
}
@media (max-width: 700px) {
  .thermii-review-card-gist { order: 5; flex-basis: 100%; white-space: normal; }
}
.thermii-review-f { display: flex; flex-wrap: wrap; gap: 8px; padding: 3px 0; }
.thermii-review-f .wsel-sub-q { min-width: 200px; white-space: nowrap; }
@media (max-width: 700px) {
  .thermii-review-card-f { padding-left: 14px; }
  .thermii-review-f .wsel-sub-q { min-width: 150px; }
  .thermii-review-legend { margin-left: 0; }
}

/* ── The second door: add a room in the other system ──────────────────────
   js/thermii-other-system-room.js appends this INSIDE the add-room row, after
   the engine's own "+ Add Another Room" button — tests/fixture-runner.js:655
   clicks whichever button it finds first, so order is behaviour, not style.
   Deliberately quieter than the green pill: it is the less common choice, and
   it should read as a second option rather than a rival to the primary one. */
/* ── THE DOOR BECOMES THE RIGHT-HAND COLUMN (Loz, 2026-09-09) ──────────────
   It was a block BELOW the green button with its own `border-top` hairline —
   the second of two rules in a row. The hairline turns 90 degrees: same 1px
   #f0f0f0, now on the left as the divider between the pair. */
.thermii-other-door {
  padding-left: 28px;
  border-left: 1px solid #f0f0f0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
/* 13px/300 #8b93a4 -> 16px/400 #5b6480 (Loz, 2026-09-24: "increase the font
   size", both journeys). At 13px light it read as a caption; it is a question
   the customer answers with the button under it. #8b93a4 was 3.2:1 on white;
   #5b6480 is 6.1:1. */
.thermii-other-door-q {
  font-family: 'Poppins', sans-serif;
  font-size: 16px;
  font-weight: 400;
  color: #5b6480;
}
/* ── AND IT MATCHES THE GREEN BUTTON'S BOX (Loz, 2026-09-09) ───────────────
   "Keep them identical at 330x54 mate" — his call, when the alternative was
   his mock's 287x46 for both. 330x54 is the number the green button already
   uses, and it is SHARED with the kit screen's own "+ Add Another Room"
   (see the rule above): shrinking it here would have re-split two screens his
   2026-09-03 note deliberately unified. So the electric button grows instead.

   THE 50px PILL WAS THE LAST ONE ON THE SCREEN. On 2026-09-03 he asked to
   "match it in style to the 'Your Selections' screen"; the green button moved
   from a pill to a 13px radius then and this one never did. So three of the
   four changes in his mock were finishing that job, not new styling.

   2px, UP FROM 1.5 — measured off his mock, where both borders come to 4px at
   2x retina. `box-sizing: border-box` is global (css/thermii.css:1), so the
   extra half-pixel does not change the 54px box; it makes the pair read as one
   weight instead of two.

   NAVY STAYS, which was his decision against his own mock's #6a41e4: purple is
   this app's primary-action colour and the real primary action on this screen
   is the gradient "View Recommended Kit" bar below. With both buttons the same
   size, colour is what distinguishes them, and green against navy already does
   that. */
.thermii-other-door-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 330px;
  min-height: 54px;
  height: auto;
  padding: 14px 24px;
  background: #fff;
  /* MAGENTA, the brand gradient's own end-stop (Loz's 2026-09-18 mock, sampled
     off it at ~#bd3bbf). This reverses the earlier "navy stays" call on this
     button: green = water, magenta = electric, mirroring the two journeys, and
     the gradient "View Recommended Kit" bar is still the screen's one primary.
     Width is 1px from the app-wide button-stroke token (thermii-system.css); the
     2px here is the pre-token colour+width shorthand, left so the colour reads
     even if the token rule is ever removed. */
  border: 2px solid #ba39b5;
  border-radius: 13px;
  font-family: 'Poppins', sans-serif;
  /* 15, NOT the 14 this shipped with (Loz, 2026-09-10: "Is the '+ Add an
     Electric Room' the same point size as the green button above it? (Looks
     smaller to me)" — it was not, and he could see a single pixel). The green
     button's size lives in the shared rule at :4349, which serves BOTH summary
     screens and both kit screens, so 15 is the figure to meet rather than a
     new one. Measured before changing anything: 15px/600 against 14px/600,
     both computing Sora, both boxes 330x54.

     It cannot be read off his mock, which is why it went unnoticed: the mock
     drew both buttons at 287x46 and he overrode that with "keep them identical
     at 330x54 mate", so the box came from his instruction and the type size
     came from the mock's own smaller button. His own stated logic settles it —
     "with both buttons the same size, colour is what distinguishes them" — so
     the type matching is the same decision as the box matching. */
  font-size: 15px;
  font-weight: 600;
  color: #ba39b5;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, color 0.15s;
}
.thermii-other-door-btn:hover,
.thermii-other-door-btn:focus-visible {
  /* A PALE MAGENTA TINT, NOT A SOLID FILL (Loz, 2026-09-18: "does the magenta
     solid colour need to be paler, similar to the green?"). The green "+ Add
     Another Room" hovers to a pale wash (#f0f7e6) and keeps its green text; this
     was filling solid magenta with white text, so the pair behaved differently
     under the cursor. #f7ebf6 is the magenta equivalent of that green wash — the
     same near-white lightness on the button's own hue — and the text stays
     magenta, so hover now reads as "the same button, lifted" on both. */
  background: #f7ebf6;
  color: #ba39b5;
}

/* ── BREATHING ROOM ABOVE THE ACTION BAR (Loz, 2026-09-09) ─────────────────
   "The NeoHub selection panel is butt-up to the bottom of the container — I
   want more space/breathing room below it."

   IT IS NOT THE CONTAINER, IT IS THE ACTION BAR, and that took measuring to
   see. `.thermii-skin-actionbar` is `position: sticky` and **91px tall**, so it
   sits in flow at the end of `#wq-q4e` — and `#wq-q4e` is a flex COLUMN, so
   nothing collapses. Measured, the gap from the neoHub card's bottom edge to
   the bar's top edge was **exactly 0**. The 91px I first measured "below the
   panel" WAS the bar.

   SO THE SPACE GOES ON THE CONTENT, NOT THE BAR. A margin on the bar would
   move it on every step in both journeys; this puts it on the question block
   that actually ends against it, and only where the extras exist.

   `:has()` PICKS THE THERMOSTAT STEPS WITHOUT NAMING THEM. Both journeys have
   these panels and both would have needed hard-coding by step id — which is
   exactly the kind of anchor that rots when a step is renumbered. Measured:
   the rule reaches the two steps with extras and **0 of the other 20**
   `.eq-question-full` blocks. `:has()` is already used in this codebase
   (css/thermii.css:1751), and if it ever failed to parse the gap simply does
   not appear rather than anything breaking.

   36px IS THE SCREEN'S OWN NUMBER, not a new one: it is the gap the neoHub
   section already keeps above itself (`margin-top: 36px`), so the panel now
   has the same air below it as above. Measured after: 0 -> 36. */
body.thermii-skin .eq-question-full:has(#wFloorSensorExtras, .neohub-section) {
  margin-bottom: 36px;
}

/* ── AND BELOW 820px IT STACKS AGAIN (Loz, 2026-09-09) ─────────────────────
   `flex-wrap: wrap` on the row stops it overflowing on its own, but wrapping
   ALONE leaves the divider drawn down the left edge of a now-full-width
   electric block, where it reads as an indent rather than a rule. The two
   arrangements are different designs, not one design at two widths — so the
   stacked layout is restored wholesale, hairline included, turning back
   through 90 degrees.

   820px, AND THE NUMBER WAS MEASURED RATHER THAN REASONED. My first attempt
   put this at 700 because the pair "needs about 730px" — two 330px buttons,
   the divider, its 28px padding and the row's 28px gap. Stepping the width
   down 10px at a time found it actually wraps at **790**, so between 701 and
   790 the row was flex, wrapped, and drawing the divider down the side of a
   full-width block: exactly the failure this block exists to prevent, shipped
   by the fix for it. 820 leaves 30px of headroom for the label's own width,
   which is the part that can move with a font or a wording change. */
@media screen and (max-width: 820px) {
  #wq-add-room-row,
  #eq-add-room-row {
    display: block;
  }

  .thermii-other-door {
    margin-top: 14px;
    padding-top: 14px;
    padding-left: 0;
    border-left: 0;
    border-top: 1px solid #f0f0f0;
  }
}

/* ── AND ON A PHONE IT MATCHES THE GREEN BUTTON'S OWN TREATMENT ────────────
   css/thermii.css:2179 (byte-locked) already gives the green button a phone
   size at this exact width — `width:100%; min-width:0; height:42px` — so
   raising the electric button to a 330px min-width introduced an overflow the
   green one had been immune to. Measured at 390px: the row overflowed by 4px,
   because 330 does not fit a 322px card.

   ONLY THE WIDTH IS MIRRORED, AND THAT IS THE CORRECTION. My first version
   copied the locked rule's `height: 42px` too — and measuring showed the green
   button is **54px** at that width, not 42: journey.css's own `min-height:
   54px` floors thermii.css's `height`, since a min-height beats a height. So
   copying the 42 made the pair 632x54 against 632x42 — breaking the one thing
   he asked for by name. The height is left to the shared 54px and only the
   330px min-width is released. */
@media screen and (max-width: 700px) {
  .thermii-other-door-btn {
    width: 100%;
    min-width: 0;
    /* AND THE MISMATCH WAS INVERTED DOWN HERE, which only measuring at two
       widths found: css/thermii.css:2179 takes both green buttons to
       `font-size:13px!important` at this breakpoint, so before this line the
       navy button was 1px SMALLER than green above 700 and 1px BIGGER at or
       below it. 13 keeps the pair in step at both ends. Loz's mobile pass on
       this screen is still to come (his note 3, 2026-09-09) — this is here so
       the two agree meanwhile, not as the final phone figure. */
    font-size: 13px;
  }
}

/* ── Which room the step counter is counting ───────────────────────────────
   js/thermii-room-progress.js owns this span; the skin owns .thermii-skin-count
   beside it and overwrites its own text from a MutationObserver, which is why
   this is a sibling rather than a longer count. */
.thermii-room-counter {
  font-family: 'Poppins', sans-serif;
  font-size: 13px;
  font-weight: 500;
  color: #4f7d00;
  margin-right: 8px;
  white-space: nowrap;
}

/* ── The whole project's heat requirement ──────────────────────────────────
   js/thermii-project-heat.js appends this AFTER the engine's own
   .wkit-heat-total block and never touches its number — the engine's block
   carries its own "System delivers ~N W" line, which would then describe one
   system while the total described two. Shaped differently from that block on
   purpose, so the per-system figure and the project figure are never confused. */
.thermii-project-heat {
  margin-top: 12px;
  padding: 22px 24px;
  border: 1px solid #e4defb;       /* the faint panel, not a black frame (Loz, 2026-09-24) */
  border-radius: 16px;
  background: #f6f4ff;
  text-align: left;                /* ranged like the totals block above it */
}
.thermii-project-heat-lbl {
  font-family: 'Poppins', sans-serif;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;       /* the totals block's own label style */
  color: #6c4bd8;
}
.thermii-project-heat-val {
  font-family: 'Poppins', sans-serif;
  font-size: 32px;
  font-weight: 600;
  color: #1f2a44;
  line-height: 1.2;
  margin-top: 4px;
  font-variant-numeric: tabular-nums;
}
.thermii-project-heat-split {
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
  font-family: 'Poppins', sans-serif;
  font-size: 13px;
  font-weight: 400;
  color: #5a6472;
  font-variant-numeric: tabular-nums;
}
.thermii-project-heat-plus { color: #6c4bd8; font-weight: 600; }
.thermii-project-heat-sub {
  margin-top: 4px;
  font-family: 'Poppins', sans-serif;
  font-size: 12px;
  font-weight: 300;
  color: #888;
}

/* ── THE ROOM CARD'S THREE COLUMNS ON PAPER (2026-09-03) ────────────────────
   `downloadKitPdf` clones #kitPanel, so the regrouping above reaches the PDF
   whether or not a screen rule follows it. Chrome cannot fragment a grid or a
   flex container across printed pages — the reason thermii.css already forces
   this screen's tall flex columns block-level for print — so the columns are
   laid out as plain blocks on paper rather than left to a grid that may break
   mid-row. The page-break spacers measure the card's REAL height, so a taller
   card re-paginates correctly; an unfragmentable grid would not.

   Deliberately outside @media screen, unlike the layout above. */
@media print {
  /* THE SYSTEM TOTALS ARE ON PAPER (Loz, 2026-09-17: "put the System Totals
     on the PDF too mate"). They never were — the hero was hidden here and the
     navy tiles it lifts its figures from are hidden by byte-locked
     css/thermii.css:2428 — so this block now has to state the whole paper
     layout rather than merely stop hiding it. Two reasons it cannot be left to
     the screen rules:

       THE PDF CLONE renders in SCREEN media inside an iframe whose viewport is
       the root's own 760px, so journey.css's own `max-width: 1100px` block
       applies there and stacks the cards into a tall column. Paper gets the
       row the count class asks for instead, which is what the screen shows at
       the width this quote is actually read at.

       NATIVE PRINT (window.print) sees no screen rules at all, so without a
       `display` here the hero would be a plain block.

     Everything is compacted to the print scale the rest of this screen uses
     (the heat panel's 14/16/10, the category bars' 6/12). The figures stay the
     engine's own nodes — nothing is reprinted. */
  .thermii-totals-hero {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr) !important;
    gap: 10px !important;
    margin-top: 14px !important;
    page-break-inside: avoid;
    break-inside: avoid;
  }
  /* THE COUNT CLASS DRIVES THE COLUMNS, as it does on screen — the row holds
     three cards on a water quote, two on some, and ONE on electric (there is no
     manifold and so no flow card). A flat three-column rule here squeezed
     electric's single card into a third of the page with two empty columns
     beside it: measured on a real clone, `thermii-totals-hero-1` at
     `228px 228px 228px`. */
  .thermii-totals-hero-2 { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  .thermii-totals-hero-3 { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; }
  .thermii-totals-card { padding: 12px 14px 12px !important; }
  .thermii-totals-label { font-size: 9.5px !important; }
  .thermii-totals-value { font-size: 22px !important; }
  .thermii-totals-unit { font-size: 11px !important; }
  .thermii-totals-note { font-size: 9px !important; }
  /* THE FIGURE'S TRAILING NOTE OVERFLOWED ITS CARD, measured on a real clone:
     "16mm PERT-AL-PERT" renders 137px beside a 76px figure in a 198px content
     box, and both carry `white-space: nowrap`, so the Pipe card clipped its own
     material at the card edge. Smaller, and allowed to drop to its own line
     when it still will not fit — the heated-area card's "across 2 rooms" is
     short enough to stay inline either way. */
  .thermii-totals-figure { flex-wrap: wrap !important; }
  .thermii-totals-across { font-size: 9.5px !important; white-space: normal !important; }
  .thermii-totals-legend { font-size: 9px !important; }
  .thermii-totals-pill { font-size: 9px !important; padding: 2px 8px !important; }

  /* THE STRIPS' HIDE IS IN BYTE-LOCKED thermii.css (:2428,
     `.wkit-stats-grid{display:none!important}`), so it cannot be deleted — it
     has to be beaten. The re-emitter scopes both rules to `#thermiiPdfRoot `
     verbatim, so this is (2,1,0)+important against its (1,1,0)+important, and
     in native print (1,1,0) against (0,1,0). The ids are what make it win; a
     bare class rule here would lose on both paths. */
  #wKitItems .wkit-stats-grid,
  #kitItems .wkit-stats-grid {
    display: grid !important;
    gap: 10px !important;
    margin-top: 10px !important;
  }


  /* THE HEAT CHECK'S SCREEN LAYOUT IS UNDONE FOR PAPER (2026-09-10). The clone
     renders in screen media, so each choice above has an explicit reverse
     here; no node was moved except the era pills, which are hidden on paper
     with the band stated under the total instead. Rows go back to the
     engine's own flow — the head div gets its flex back and the per-room
     warnings show again, so the printed reasoning is unchanged. */
  .thermii-heat-picker, .thermii-heat-topup, .thermii-heat-gap, .thermii-heat-band {
    display: none;
  }
  .wkit-heat-hdr-badge { display: inline-flex; }
  .wkit-heat-sub { display: block; }
  .thermii-heat-side { order: 0; }
  /* !important throughout: the screen rules above carry their own, and the
     clone is styled by both sets at once. */
  .thermii-heat-r { display: block !important; }
  .thermii-heat-r-head { display: flex !important; }
  .thermii-heat-r .wkit-heat-warn { display: flex !important; }
  .thermii-heat-r .wkit-heat-meter { display: flex !important; }
  .thermii-heat-dot { display: inline; }
  .wkit-heat-total-sub { display: block; }

  /* PAPER KEEPS THE ENGINE'S -8px TUCK, and it is correct there: the totals
     tiles are hidden in the printed quote (`#thermiiPdfRoot .wkit-stats-grid
     { display: none !important }`, verified by running _thermiiScopedPrintCss
     against a real clone), so the caption's previous content IS the room-cards
     column and the negative margin does exactly what it was written to do.
     Without this the screen fix above would follow the clone onto paper and
     shift a document customers keep by 18px. */
  /* PAPER KEEPS THE CENTRED TOTAL BLOCK. The screen ranges the label, the
     figure and the VAT note left (thermii-system.css, Loz 2026-09-07), and the
     PDF clones this card and renders in SCREEN media — so without this the
     printed quote would follow. The clone has its own layout for this block
     (css/thermii.css:2441+ resizes all three), and re-ranging a document
     customers keep was not what was asked for. Text alignment only, so no
     height moves and the page-break model is untouched. */
  .kit-total-lbl,
  #wKitTotal,
  #kitTotal,
  .tks-vat-note {
    text-align: center !important;
  }

  /* The screen pushes the small print 40px down the page; paper's own layout
     puts it at the foot of the last page and its position feeds the spacer
     model, so the clone keeps what it had. */
  .kb-disclaimer {
    margin-top: 0 !important;
  }

  /* ── PAPER KEEPS YESTERDAY'S ROOM CARD (Loz's changes are screen-only) ───
     Four of 2026-09-08's tweaks land inside `.wkit-room-card` and #wKitItems,
     both of which ARE cloned into #thermiiPdfRoot — and the clone renders in
     SCREEN media, so every one of them would reach the printed quote. The
     cards' heights drive the PDF's page-break spacers and its
     pass-1-to-pass-2 drift model (legacy-core.js:5564/5578), and the net on
     paper would be about +17px per room card: +6 for the three column
     headings, +33 for the new Floor area row, -22 for the tighter rows. Rather
     than move the breaks on a document customers keep, paper keeps what it had
     — restored here, where the re-emitter scopes each rule to #thermiiPdfRoot
     with !important.

     Offered rather than assumed: if the printed quote SHOULD carry the floor
     area row, it is one deletion from this block. */
  .thermii-spec-h,
  .thermii-loop-head {
    font-size: 10.5px !important;
  }

  /* THE GROUP HEADINGS ARE BLOCKS ON PAPER (2026-09-17). This block flattens
     .thermii-spec to display:block, which un-blockifies its flex children —
     and .thermii-spec-h is a <span>, so it fell back to INLINE. An inline box
     paints its border-top and padding-top around its own line box instead of
     pushing content down, so the group hairline (border-top, 20px of padding
     above it) was drawn THROUGH the previous row's text: "Covering  Tiles /
     Stone" and "Install  Screed" read as struck out on every printed room
     card. Measured on a real clone: heading display inline, border at y=1237
     through a row whose text spans 1229-1246. A block heading pushes the
     hairline below the row, as on screen. */
  .thermii-spec-h { display: block !important; }

  /* THE BODY TEXT NEEDS NO PIN HERE, and a first cut had one that was DEAD.
     Screen went 15 -> 13px on the spec rows and the loop table / spare row
     beside them (Loz, 2026-09-15: "reduce the text size on this panel"). A
     15px paper pin was written, then measured on a real clone: every one of
     those cells is a <span> inside #wRoomBody-*, and byte-locked
     css/thermii.css's own print rule `[id^="wRoomBody-"] span { font-size:
     11px !important }` re-emits as #thermiiPdfRoot [id^=..] span — (1,1,1),
     beating a class pin's (1,1,0). Paper has been 11px for these since the
     redesign put them in spans, before and after this change, so the PDF is
     untouched by the engine's rule and a pin here would only claim to govern
     what it does not. verify-kit-screen.py pins that premise instead. */

  /* THE ELECTRIC CARD IS NOT COVERED BY THAT RULE. It keys on the WATER body id
     and electric bodies are #eRoomBody-*, so left alone electric paper would
     take the new 13px and its room card would shorten — moving the page breaks
     on an electric quote. Electric IS pinned, scoped to its own body, and this
     pin GOVERNS (nothing locked outranks it), unlike a water one would. */
  [id^="eRoomBody-"] .thermii-spec-l,
  [id^="eRoomBody-"] .thermii-spec-v {
    font-size: 15px !important;
  }

  .thermii-kit-eyebrow-t {
    font-size: 10.5px !important;
  }

  .thermii-kit-eyebrow-n {
    font-size: 11.5px !important;
  }

  #wKitItems > .thermii-kit-eyebrow,
  #kitItems > .thermii-kit-eyebrow {
    margin-bottom: 0 !important;
  }

  .thermii-spec-r,
  .thermii-loop-r {
    line-height: 1.5 !important;
    /* AND THE PITCH THE ROWS HAD, for the same reason as the line-height: this
       card is measured by the page-break spacers, so screen closing to 5px
       must not reach paper (Loz, 2026-09-09). */
    padding: 7px 0 !important;
  }

  /* THE HEADER'S SUMMARY IS SCREEN-ONLY. It exists because a closed card is a
     strip; on paper every card is open and the same three figures are in the
     table below it, so printing them would only add height to a document whose
     page breaks are modelled. */
  .thermii-room-sum {
    display: none !important;
  }

  /* the totals legend comes with the hero it labels (Loz, 2026-09-17) */
  .thermii-kit-eyebrow-totals {
    display: flex !important;
  }

  /* PAPER ALWAYS SHOWS THE PANEL OPEN, so it always gets the open look.
     downloadKitPdf forces `#wHeatBody` open in the clone (legacy-core.js:5417)
     but leaves `aria-expanded` reading "false", so without these three a quote
     downloaded with the panel collapsed on screen would print the full panel
     with no rule under its title and its last row flush to the panel's edge.
     The geometry and the colour come from the screen rules the clone already
     has; only the values the closed state overrode need putting back. */
  .wkit-heat-hdr[aria-expanded="false"]::after {
    content: "" !important;
  }

  .wkit-heat-hdr[aria-expanded="false"] {
    margin-bottom: 4px !important;
  }

  .wkit-heat-panel:has(.wkit-heat-hdr[aria-expanded="false"]) {
    padding-bottom: 20px !important;
  }

  /* PAPER GETS NO PANEL ROUND THE KIT LIST. The wrapper is in the live DOM,
     so the clone carries it, and the clone renders in SCREEN media — but its
     16px of padding would change the block heights the page-break spacers and
     the pass-1-to-pass-2 drift model measure (legacy-core.js:5564/5578), which
     is the one thing on this screen that must not move quietly. Neutralised
     to nothing, so the printed quote is byte-identical to yesterday's. */
  .thermii-kit-table {
    margin-top: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    border-radius: 0 !important;
    background: none !important;
  }

  /* and the last group keeps the 24px the spacer model measured */
  .thermii-kit-table > div:last-child {
    margin-bottom: 24px !important;
  }

  /* PAPER KEEPS THE UNIT PRICE, INCLUDING WHERE IT REPEATS THE TOTAL. The
     screen hides a duplicate (Loz, 2026-09-07); the printed quote is an
     itemised document a customer keeps, css/thermii.css:2412 styles this cell
     inside its own print block, and taking a column off it was not the ask.
     The clone renders in SCREEN media, so without this the screen rule follows
     it onto paper. Visibility only, so no height moves and the page-break
     model is untouched. */
  .kit-unit-price.thermii-unit-dup {
    visibility: visible !important;
  }

  .wkit-design-temps {
    margin-top: -8px !important;
    /* AND IT IS SHOWN, because the screen HIDES it once the totals' flow card
       carries its content (`.wkit-rooms-col:has(.thermii-totals-flow) +
       .wkit-design-temps`, 2026-09-10) — and the clone renders in screen
       media with that flow card still in its DOM, so the caption vanished from
       the printed quote. Measured on a real clone: display none. Restored here
       at (1,1,0) once scoped, over the screen rule's (0,3,0); nothing inline
       sets display on this node, so no !important. The same class of defect
       as V.1.27's, caught on the clone before it shipped. */
    display: block;
    /* THE INLINE `color:#555` IS STRIPPED IN THE LIVE DOM AND THE PDF CLONES
       THAT DOM, so without this the printed quote would silently take the
       screen's lighter grey. Restored here rather than left to chance: the
       re-emitter scopes this as `#thermiiPdfRoot .wkit-design-temps` (1,1,0),
       which outranks the screen rule's (0,1,0). Paper is byte-identical. */
    color: #555 !important;
    /* AND NO TINT ON PAPER, for the same reason the calculated column's is
       dropped below: the clone renders in screen media, so a screen background
       would print. Paper is byte-identical. */
    background: none !important;
  }

  /* ...AND IT IS HIDDEN AGAIN WHENEVER THE FLOW CARD CARRIES IT (Loz put the
     System Totals on paper, 2026-09-17). The restore above was written when the
     hero was hidden on paper and the caption was the only place the design
     temperatures appeared; now the flow card prints "43°C flow → 38°C return"
     and "Set your mixing valve to 43°C" directly above it, so leaving both
     printed the same instruction twice — measured on a real clone.

     The condition is the SCREEN'S OWN (`:has(.thermii-totals-flow)`), so the
     two media agree by construction and a single-room quote — which has no
     totals and no flow card — keeps its caption on paper exactly as before.
     (0,3,0) + !important beats the restore's (0,1,0) in native print, and
     (1,3,0) beats (1,1,0) once the re-emitter scopes both onto the clone. */
  .wkit-rooms-col:has(.thermii-totals-flow) + .wkit-design-temps {
    display: none !important;
  }

  /* PAPER STACKS THE CARD. The clone renders in SCREEN media, so without this
     the two-column grid and the six-track table would carry onto a page that
     is 794px wide — the table's fixed figure columns alone are 308px of it. */
  .thermii-room-grid,
  .thermii-spec,
  .thermii-layout,
  .thermii-loops {
    display: block !important;
  }

  /* THE DIVIDERS MUST GO WITH THE GRID. On paper the halves are blocks, and a
     `border-left` on a stacked block draws a vertical rule down its left edge
     beside the text — the defect the gate caught on the three-column version
     before it shipped. */
  .thermii-layout {
    border-left: 0 !important;
    background: none !important;
    padding: 0 !important;
  }

  .thermii-spec {
    padding: 0 !important;
  }

  /* The rows keep their two ends, which is what makes them readable as a list
     when the grid is gone. */
  .thermii-spec-r,
  .thermii-loop-r,
  .thermii-spare-r,
  .thermii-room-tot {
    display: flex !important;
    flex-wrap: wrap;
    gap: 6px;
    align-items: baseline;
    justify-content: flex-start;
    background: none !important;
    border: 0 !important;
    border-radius: 0 !important;
  }

  .thermii-spec-l { color: #555; }
  .thermii-spec-v,
  .thermii-loop-len,
  .thermii-loop-flow,
  .thermii-loop-drop,
  .thermii-spare-v,
  .thermii-room-tot-v { text-align: left; }

  /* THE TOTAL BAND IS SCREEN-ONLY (2026-09-15). The room card is cloned into
     the PDF and its height drives the page-break spacers, so the band's
     tighter margin and deeper padding are put back to the row paper has had:
     12px above, 14/14/2 inside. The clone renders in SCREEN media and the
     re-emitter scopes this to #thermiiPdfRoot, which out-specifies the screen
     rule without !important. NO font-size pin: byte-locked css/thermii.css
     prints every span in a water room body at 11px !important (measured
     11px on a real clone), so the 24px never reaches paper and a class pin
     here would be a comment, not a control — the spec column's lesson. */
  .thermii-room-tot { margin-top: 12px; padding: 14px 14px 2px; }
  /* the moved add-room row is screen-only; the engine's husk stays in the
     clone with its inline margin-top:28px, so paper is unchanged — and it has
     to be PUT BACK here, because the clone renders in screen media and the
     screen hide reached it (measured: 26px where paper had 28) */
  .thermii-kit-addroom { display: none; }
  .thermii-kit-addroom-husk { display: block; }
}


/* ── 'YOUR SELECTIONS': NO PRINT BLOCK IS NEEDED, AND ONE WAS A DEFECT ──────
   (2026-09-09 — this replaces a print block that shipped in V.1.27.)

   The redesign came with a full set of @media print restores on the reasoning
   that "this screen is printed, so every figure would reach paper". The first
   half is true and the second is not: EVERY declaration the redesign makes
   lives inside this file's own top-level `@media screen` block, so print never
   sees the grid, the ::before card, the rows' transparency, the row padding,
   the divider or any of the CAD changes. Paper was never at risk from them,
   and the restores were therefore either no-ops or changes to paper in their
   own right.

   AND ONE OF THEM WAS A REAL DEFECT ON A DOCUMENT A CUSTOMER KEEPS.
   css/thermii.css's print block (:2241) hides both add-room rows outright —
   `#wq-add-room-row, #eq-add-room-row { display:none!important }`. The restore
   re-declared `display: block !important` on the same ids at the same (1,0,0)
   specificity, and journey.css is linked AFTER thermii.css, so it WON: the
   add-room buttons became visible on paper and, because
   `_thermiiScopedPrintCss` re-emits print rules into the PDF clone, in the
   customer's PDF. Two further restores did the same kind of thing more
   quietly — `#wq-summary, #eq-summary { display: block !important }` beats
   index.html's inline `display:none` and would have printed the ELECTRIC
   review screen for the first time, and re-declaring the summary's side
   padding at 20px narrowed a printed content box that is actually the inline
   36px, moving where values wrap on a multi-room quote.

   THE INJECTED HEADING NEEDS NO RULE EITHER. `.wsel-add-h` is a real node in
   the live DOM, so it exists in every medium — but it is a child of
   #wq-add-room-row, which thermii.css already hides on paper, so it cannot
   render there. A rule for it would be one more thing claiming to do
   something it does not.

   THE LESSON, AND IT IS THE SECOND TIME THIS PROJECT HAS LEARNED IT: a print
   restore is only correct if the screen rule it is restoring FROM actually
   reaches print. Check the enclosing at-rule before writing one — and check
   what the byte-locked stylesheet already says about the same selector, since
   a later file wins a tie between two `!important` declarations. */


/* ── COPYRIGHT MOVED TO THE FOOTER (2026-09-15, Loz) ──────────────────────────
   The full trademark notice moved out of the disclaimer card into the footer,
   beside the version stamp (.kb-copyright, lifted next to .kb-version by
   js/thermii-skin.js's liftVersionLine). It shows on every screen. The SAME
   notice stays as the last paragraph of .kb-disclaimer, because THAT block is
   what the PDF clones — so on screen the disclaimer copy and the hairline above
   it are hidden here and RESTORED for print below. The PDF clone renders in
   SCREEN media, so the print restore (re-emitted scoped to #thermiiPdfRoot by
   legacy-core.js's _thermiiScopedPrintCss, !important) is what keeps the © on the
   customer's quote — the disclaimer clone is byte-identical to before, so the
   page-break model is untouched. .kb-copyright is never cloned, so it is
   screen-only by construction (the @media print hide is for the live page's own
   native print, matching .kb-version). */
body.thermii-skin .kb-copyright {
  text-align: center;
  font-family: 'Poppins', sans-serif;
  font-size: 11px;
  line-height: 1.5;
  color: #6a6f85;   /* match the version stamp above it (thermii-skin.css:3862) */
  max-width: 760px;
  margin: 0 auto;
  padding: 2px 20px 10px;
}
body.thermii-skin .kb-disclaimer > p:last-of-type,
body.thermii-skin .kb-disclaimer > div[style*="border-top"] {
  display: none;   /* screen only — the footer .kb-copyright carries it instead;
                      restored for print/PDF in the @media print block below */
}
@media print {
  .kb-copyright { display: none !important; }
  .kb-disclaimer > p:last-of-type,
  .kb-disclaimer > div[style*="border-top"] { display: block !important; }
}
/* THE TRADEMARK NOTICE ON ONE LINE (Loz, 2026-09-16: "Put this into one
   line"). The run — "The Thermii™ brand … strictly prohibited." — measures
   1072px at 11px Sora, and the footer's 760px cap wrapped it to three lines
   everywhere. From 1140px (1072 + the gutters) the cap lifts and the line is
   held whole; the <br> still separates the © line above it. Below 1140 it
   wraps as before: nowrap at every width would overflow a phone by ~700px.
   Screen only — .kb-copyright is never cloned, and the disclaimer's own copy
   (the one paper prints, whose height the page-break model measures) is
   untouched. */
@media screen and (min-width: 1140px) {
  body.thermii-skin .kb-copyright {
    --kb-notice-run-px: 1072; /* the measured run at 11px Sora; 1140 = 1072 + the gutters */
    max-width: none;          /* the 760px cap is what wrapped it */
    white-space: nowrap;      /* the <br> still separates the © line from the trademark line */
  }
}

/* ── ONE GREEN, #74b700, THROUGHOUT (Loz, 2026-09-15) ────────────────────────
   "Change the greens throughout the journeys and on the Summary screen to
   #74b700." The token (--thermii-recommend, which --journey-green and
   --thermii-success alias) and every code literal in the free layers moved —
   the brand lime #8cc63f, the heat panel's darkened #497b02 and the old
   add-room #6cba15 are all this one value now. NOT the demand-band wire's own
   inline #8cc63f: that is its pass SENTINEL (it sniffs its emission back to
   decide whether a room passed), and the stylesheet's !important paints over
   it — recolouring the emission without the sniff breaks the pass/short
   detection. Three visible greens live in byte-locked thermii.css and are
   overridden here instead. MEASURED, TOLD, AND SPLIT (Loz, same day):
   #74b700 is 2.47:1 on white and 2.28:1 on the all-clear box, under AA for
   text, so the TEXT LINES — heat status and all-clear, the total block's
   label, sub-label and gap line, the loops badge, the room counter, the
   mixed project's 34px grand total — take
   #4f7d00: the same hue (82°, 100% saturation) stepped darker until it
   clears 4.5:1 (4.92 on white, 4.56 on the all-clear box). Fills, borders,
   bars, tick glyphs and button labels stay #74b700 — his split, not a
   tidy-up. */
body.thermii-skin .confirm-check-wrap input[type="checkbox"]:checked {
  background-color: #74b700;          /* keeps the locked rule's tick image */
}
body.thermii-skin .unsure-quote-btn:hover {
  background: #74b700;                /* the green hovers stay green (Loz) */
  border-color: #74b700;
}
body.thermii-skin .wcad-badge {
  background: #74b700;
}
/* "Estimate your running costs →" wears the skin's purple, not a green
   (Loz, 2026-09-15). The engine writes it orange inline (legacy-core.js:4536),
   so !important; hidden in print by thermii.css:2381, so no paper effect. */
body.thermii-skin .wkit-heat-runcost button {
  color: #6c4bd8 !important;
  /* 12.5 -> 15px, the "Underfloor delivers" line's size above it, and Title
     Case (Loz, 2026-09-24). Case by CSS: the words are the engine's, inline in
     manifest-locked markup, and capitalize leaves the arrow alone. */
  font-size: 15px !important;
  text-transform: capitalize;
}
/* THE CAD DRAWINGS PANEL LIVES ON THE KIT SCREEN (Loz, 2026-09-17: "selecting
   it will cover ALL rooms the customer needs … in its own container/panel …
   below the 'Need more Tools' button"). js/thermii-cad-kit.js re-homes the
   engine's #wCadDrawingCard straight after #wKitPanel on every kit entry; its
   look (the 1280 cap, 26px above, the flat border) is the review-screen rule
   above, which was never screen-scoped and so follows the node.

   Two things the move needs from the cascade:
   1. From 1500px css/thermii-system.css lays #waterPanel out as a two-column
      grid on the kit screen — kit list in column 1, #wKitFooterCard sticky in
      column 2. An unplaced grid child would still auto-flow into the first
      free cell of column 1, but the pin says so rather than relying on it.
      AND THE AUTO MARGINS HAVE TO GO THERE: the review rule centres the card
      with `margin: 26px auto 18px`, and in a grid cell auto margins centre it
      in the COLUMN while the kit panel above it is start-aligned — measured
      at 2000px: column 1326, panel x 147, card x 170, a 23px step between two
      1280px boxes. Zero side margins put its left edge on the panel's, and
      being the same width its right edge follows. Below 1500 the panel
      stacks in a flex column and the two already share an edge (1400: both
      x 77, 1246 wide), so nothing is scoped there.
   2. Paper. The kit screen's native Print Quote (legacy-core.js:5258) would
      now print the card; the downloaded PDF never sees it because the node
      sits outside the #wKitPanel that downloadKitPdf clones. */
@media screen and (min-width: 1500px) {
  body[data-thermii-screen="water-kit"] #wCadDrawingCard {
    grid-column: 1;
    margin-left: 0;
    margin-right: 0;
  }
}
@media print {
  #wCadDrawingCard { display: none !important; }
}

/* THE DOWNLOAD QUOTE POPUP (Loz, 2026-09-17: "shall we have it come up as a
   popup (matching our current popup styles) that has an icon at the top and
   then a message with jumping/loading dots … then a green tick appears once
   it's downloaded?"). The behaviour is js/thermii-download-popup.js; this is
   only the dress, and it is deliberately thin because the popup IS the skin's
   modal — scrim, card, shadow, radius, icon disc, title and sub all come from
   `.thermii-skin-modal-*`, exactly as the "How does Thermii work?" popup does.

   EVERY OVERRIDE IS A COMPOUND SELECTOR, AND THAT IS FORCED BY LINK ORDER.
   css/thermii-skin.css is the NINTH stylesheet and this file is the fourth, so
   a rule of equal specificity here LOSES to the shell however it is written —
   the tie that the small print and the Documents hover have both been bitten by.
   `.thermii-skin-modal.thermii-dl-modal` is (0,3,1) against the shell's (0,2,1)
   and wins on specificity rather than on position. Anything that is purely this
   popup's own — the dots, the state discs — needs no such help.

   IT IS NEVER ON PAPER. The scrim is hidden in print below, and the PDF clone
   cannot contain it: html2canvas renders #thermiiPdfRoot alone. */
@media screen {
  body.thermii-skin .thermii-skin-modal.thermii-dl-modal {
    max-width: 420px;
    padding: 40px 36px 30px;
  }

  /* The working state keeps the shell's lilac disc. The two settled states
     take the app's own colours — and the tick is #4f7d00, NOT the #74b700 the
     kit screen paints its ticks with, because this one sits on a pale green
     ground rather than on white: measured, #74b700 is 2.24:1 there against
     WCAG 1.4.11's 3:1 floor for a non-text graphic, where #4f7d00 is 4.47:1.
     Same hue, the legible lightness the text lines already use. */
  body.thermii-skin .thermii-dl-scrim[data-thermii-dl="ready"] .thermii-dl-disc {
    background: #eef7e3;
    color: #4f7d00;
  }

  /* The failure disc borrows the hydraulics warning's ink (#a06000, 4.53:1
     here) rather than the engine's brand orange, which is 3.11:1 on white and
     worse on a tint. */
  body.thermii-skin .thermii-dl-scrim[data-thermii-dl="failed"] .thermii-dl-disc {
    background: #fdf1e3;
    color: #a06000;
  }

  body.thermii-skin .thermii-skin-modal-sub.thermii-dl-sub {
    margin-bottom: 6px;
  }

  /* ── THE JUMPING DOTS ──────────────────────────────────────────────────
     TRANSFORM ONLY, and this is the one declaration the whole feature turns
     on. html2canvas rasterises the quote on the MAIN THREAD for ~6 seconds, so
     an animation of any layout property — or anything driven from JS — would
     freeze solid for exactly as long as the customer is watching it. A
     transform animation is composited and keeps running through the block.

     AND THEY REST VISIBLE. css/thermii.css:743 collapses every animation to a
     single 0.01ms iteration under prefers-reduced-motion, so an
     opacity-from-zero animation would leave those users three invisible dots
     under a "Please wait" that never moves. At rest this is a plain ellipsis;
     the animation only lifts it. */
  body.thermii-skin .thermii-dl-dots {
    display: inline-flex;
    gap: 4px;
    margin-left: 5px;
    vertical-align: baseline;
  }

  body.thermii-skin .thermii-dl-dot {
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: currentColor;
    animation: thermii-dl-jump 1.05s ease-in-out infinite;
  }

  body.thermii-skin .thermii-dl-dot:nth-child(2) { animation-delay: 0.14s; }
  body.thermii-skin .thermii-dl-dot:nth-child(3) { animation-delay: 0.28s; }

  /* The live region is the title and sub themselves; it must not become a
     layout box of its own or the shell's own spacing shifts. */
  body.thermii-skin .thermii-dl-live {
    display: contents;
  }
}

@keyframes thermii-dl-jump {
  0%, 60%, 100% { transform: translateY(0); }
  30% { transform: translateY(-4px); }
}

@media print {
  .thermii-dl-scrim { display: none !important; }
}


/* ── The "add product" bars: variant B + a solid chip (Loz, 2026-09-21) ──────
   js/thermii-add-buttons.js chipifies #wOptionalToolsBtn and #wExtraProductsBtn,
   adding `.thermii-addbar`. This left-aligns them, gives the label an AA-readable
   orange (~5:1, up from #ff5b00's ~3:1), and a real hover lift; the chip is the
   solid orange disc from variant C. Both bars are hidden on paper (the tools body
   and the extra-products wrap are print-hidden / stripped), so no print rule. */
body.thermii-skin .thermii-addbar {
  justify-content: flex-start !important;
  gap: 12px !important;
  color: #c2410c !important;
  transition: background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease !important;
}
body.thermii-skin .thermii-addbar:hover {
  background: rgba(255, 91, 0, 0.12) !important;
  transform: translateY(-1px);
  box-shadow: 0 3px 10px rgba(255, 91, 0, 0.11);
}
body.thermii-skin .thermii-addchip {
  position: relative;
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #ff5b00;
}
/* the +/- is DRAWN as two white bars, so it is geometrically centred in every
   browser — a font glyph sat high, and differently per browser (Safari vs pane) */
body.thermii-skin .thermii-addchip::before,
body.thermii-skin .thermii-addchip.is-plus::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: #fff;
  border-radius: 1px;
}
body.thermii-skin .thermii-addchip::before {   /* horizontal bar — + and − */
  width: 11px;
  height: 2px;
  transform: translate(-50%, -50%);
}
body.thermii-skin .thermii-addchip.is-plus::after {  /* vertical bar — + only */
  width: 2px;
  height: 11px;
  transform: translate(-50%, -50%);
}


/* The disclaimer's "contact our technical team" link (Loz, 2026-09-21) — the
   brand purple + underline so it reads as a link in the quiet grey copy, and
   carries into the PDF clone (which renders in screen media). #6c4bd8 is ~5.3:1
   on the disclaimer ground. */
body.thermii-skin .kb-disclaimer a {
  color: #6c4bd8;
  text-decoration: underline;
}
body.thermii-skin .kb-disclaimer a:hover {
  text-decoration-thickness: 2px;
}

/* A single manifold's "Recommended flow rate" repeats the room card / System
   Totals figure, so it is hidden on screen (Loz, 2026-09-24). The class is set
   by js/thermii-room-numbers.js only when the quote has ONE manifold; two or
   more keep their own figures. Paper keeps it: the print rule below is
   re-emitted into the PDF clone as #thermiiPdfRoot .thermii-mflow-dup, (1,1,0)
   with !important, over this screen rule's (0,1,0). */
@media screen {
  .thermii-mflow-dup { display: none; }
}
@media print {
  .thermii-mflow-dup { display: inline !important; }
}

@media print {
  /* the electric groups stack on paper as the flat list did, each after the
     first with its air and hairline */
  .thermii-room-grid.is-spec-only .thermii-spec { display: block !important; padding: 0 !important; }
  .thermii-room-grid.is-spec-only .thermii-spec-g { display: block !important; padding: 0 !important; border: 0 !important; }
  .thermii-room-grid.is-spec-only .thermii-spec-g + .thermii-spec-g .thermii-spec-h {
    margin-top: 22px;
    padding-top: 20px;
    border-top: 1px solid var(--journey-border, #ececf4);
  }
  /* the electric rule the screen hides (above) stays on paper, where its height
     is part of the page-break model */
  #kitItems div[style*="height:1px;background:#c8c8c8"] { display: block !important; }
  #kitItems .wkit-heat-panel { margin-top: 24px !important; }
  #kitItems .wkit-rooms-col > .thermii-kit-eyebrow-heat { margin-bottom: 10px !important; }
}

/* ── THE PROJECT SUMMARY, IN THE CARD'S OWN HEADLINE (Loz, 2026-09-24) ────────
   js/thermii-project-quote.js marks the total block `is-project` on a mixed
   project and inserts .thermii-project-sum: a line per system, the VAT the
   whole figure includes, and "PROJECT TOTAL:" in the card's purple figure. The
   lines it replaces — the Items/VAT pair, the engine's caption, its total and
   its VAT note — are hidden, never removed: other code reads the engine's
   total. Same type as the lines it replaces, so a mixed card reads as the
   same card, only about the whole project. */
.kit-total-block.is-project .tks-lines,
.kit-total-block.is-project .kit-total-lbl,
.kit-total-block.is-project #wKitTotal,
.kit-total-block.is-project #kitTotal,
.kit-total-block.is-project .tks-vat-note {
  display: none !important;
}
.thermii-project-sum { text-align: left; }
.thermii-project-sum-item + .thermii-project-sum-item { margin-top: 10px; }
.thermii-project-sum-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-family: 'Poppins', sans-serif;
  font-size: 14.5px;
  color: #5b6480;
}
.thermii-project-sum-sys { display: inline-flex; align-items: center; gap: 8px; }
.thermii-project-sum-sys .thermii-drawer-sys { display: inline-block; width: 13px; height: 13px; background-color: currentColor; }
.thermii-project-sum-fig { color: #1f2a44; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.thermii-project-sum-meta { margin: 2px 0 0 21px; font-family: 'Poppins', sans-serif; font-size: 12px; color: #8b93a4; }
.thermii-project-sum-rule { height: 1px; margin: 14px 0 10px; background: #e4e6ef; }
.thermii-project-sum-lbl {
  margin-top: 18px;
  font-family: 'Poppins', sans-serif;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: #6b7280;
}
.thermii-project-sum-total {
  margin: 4px 0 10px;
  font-family: 'Poppins', sans-serif;
  font-size: 47px;                 /* the card's own figure: 47px/600 purple, measured */
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -.01em;
  color: var(--thermii-purple, #7138ed);
  font-variant-numeric: tabular-nums;
}
.thermii-project-sum-note { font-family: 'Poppins', sans-serif; font-size: 12.5px; line-height: 1.5; color: #6b7280; }

/* ── A MIXED PROJECT'S KIT PAGE SHOWS EVERY ROOM (Loz, 2026-09-25) ──────────
   js/thermii-project-rooms.js moves the other journey's room cards and heat
   rows onto this page. OUTSIDE @media screen on purpose: the hide must hold on
   paper too, or native print would show the engine's one-system totals AND the
   project copy. Nothing here sets a height the page-break model depends on
   beyond what the moved cards already carry. */
.thermii-pr-hide { display: none !important; }
/* a borrowed card KEEPS its Edit / Duplicate (Loz, 2026-09-28): a press is
   routed to its own journey by js/thermii-project-rooms.js onBorrowedEdit */
.thermii-pr-on .thermii-drawer-sys {
  display: inline-block; flex: 0 0 13px; width: 13px; height: 13px;
  background-color: currentColor;
}
.thermii-pr-h { margin: 6px 0 0; }
.thermii-pr-h:first-of-type { margin-top: 0; }
.thermii-pr-note {
  order: 1; margin-left: auto; margin-right: 12px;
  font-size: 12.5px; font-weight: 300; color: #6b7280; white-space: nowrap;
}
/* the heat rows carry their system, drawn before the room name */
.thermii-pr-row .wkit-heat-name::before {
  content: ""; display: inline-block; width: 12px; height: 12px;
  margin-right: 7px; vertical-align: -1px; background-color: currentColor;
}
.thermii-pr-row.is-water .wkit-heat-name::before {
  color: #149de8;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2012%2012%27%3E%3Cpath%20d%3D%27M6%20.9C6%20.9%202.2%205%202.2%207.5a3.8%203.8%200%200%200%207.6%200C9.8%205%206%20.9%206%20.9Z%27%20fill%3D%27%23000%27%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2012%2012%27%3E%3Cpath%20d%3D%27M6%20.9C6%20.9%202.2%205%202.2%207.5a3.8%203.8%200%200%200%207.6%200C9.8%205%206%20.9%206%20.9Z%27%20fill%3D%27%23000%27%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
}
.thermii-pr-row.is-electric .wkit-heat-name::before {
  color: #e8a020;
  -webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2012%2012%27%3E%3Cpath%20d%3D%27M7.1.7%202.3%206.8h2.9L4.7%2011.3%209.9%205.1H6.7Z%27%20fill%3D%27%23000%27%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2012%2012%27%3E%3Cpath%20d%3D%27M7.1.7%202.3%206.8h2.9L4.7%2011.3%209.9%205.1H6.7Z%27%20fill%3D%27%23000%27%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
}
/* the totals column's Water + Electric split, under the project figure */
.thermii-pr-split {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px;
  margin: 4px 0 2px; font-size: 13px; font-weight: 400; color: #5b6480;
  font-variant-numeric: tabular-nums;
}
.thermii-pr-part { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.thermii-pr-plus { color: #8b93a4; }

/* ── THE CLOSED HEAT CHECK SAYS HOW IT CAME OUT (Loz, 2026-09-28) ───────────
   js/thermii-heat-status.js. The green pill is the amber top-up pill's mirror
   — same size, weight and place — in the heat panel's own greens (#4f7d00 ink,
   the all-clear tint, the #74b700 tick). The two totals beside it show only
   while the panel is CLOSED (open, the totals column below says them in full)
   and only where the header has room: on wide screens the header is one
   unwrapping row, so below a 1200px panel the figures step aside rather than
   squeeze the year picker; at 1100px and down the header wraps anyway, and
   they simply follow the pill. The faint hover says the whole bar opens it. */
.wkit-heat-panel { container-type: inline-size; container-name: heatpanel; }
.thermii-heat-okpill {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  margin-left: 14px; padding: 3px 10px; border-radius: 20px;
  border: 1px solid #cfe7a8; background: #f0f9eb; color: #4f7d00;
  font-size: 11px; font-weight: 500; white-space: nowrap; vertical-align: middle;
}
.thermii-heat-okpill svg { flex: none; color: #74b700; }
.thermii-heat-hdrfigs {
  margin-left: 14px; font-size: 13px; font-weight: 300; color: #5b6480;
  white-space: nowrap; vertical-align: middle; font-variant-numeric: tabular-nums;
}
.thermii-heat-hdrfigs b { font-weight: 600; color: #222846; }
@media screen and (max-width: 700px) {
  .thermii-heat-hdrfigs-w { display: none; }
  /* the pill starts its own row under the title here, so it lines up with the
     title rather than carrying the desktop 14px; the bare figures sit on the
     SAME row at 12px, so a green<->amber flip keeps the header's height */
  .thermii-heat-okpill { margin-left: 0; padding: 3px 7px; }
  .thermii-heat-hdrfigs { margin-left: 0; font-size: 12px; }   /* the header's 12px gap spaces it */
}
.wkit-heat-hdr[aria-expanded="true"] .thermii-heat-hdrfigs { display: none; }
@media screen and (min-width: 1101px) {
  /* one unwrapping row here: the title must not shrink into two lines to make
     room (measured at 1500px, the narrowest panel beside the sidebar: 270px of
     title squeezed to 230 and wrapped, 69px header -> 86) */
  .wkit-heat-title { flex-shrink: 0; white-space: nowrap; }
  @container heatpanel (max-width: 1199.98px) {
    .thermii-heat-hdrfigs { display: none; }
  }
  /* and under ~1000px of panel, with a pill in the row (green or amber), the
     "Property built" label steps aside — the three years say what they are */
  @container heatpanel (max-width: 999.98px) {
    .wkit-heat-hdr:has(.thermii-heat-okpill, .wkit-heat-hdr-warn) .thermii-heat-picker-l { display: none; }
  }
}
.wkit-heat-panel:has(.wkit-heat-hdr[aria-expanded="false"]:hover) { background: #fafaff !important; }
.wkit-heat-hdr[aria-expanded="false"] { cursor: pointer; }
/* paper keeps the header it had: the PDF forces this panel open and prints
   the all-clear line in full, and a new pill in a clone rendered at 760px
   could wrap the header and move a page break */
@media print {
  .thermii-heat-okpill, .thermii-heat-hdrfigs { display: none !important; }
}

/* ── THE KIT BAR'S COUNT NO LONGER RUNS UNDER THE ARROW ON A PHONE ──────────
   (Loz, 2026-09-28.) The engine sets the name nowrap and the count
   flex-shrink:0 inline (legacy-core.js:4579-4581 water, :1175-1178 electric),
   so at 390px "Manifold Assembly" (157px) + "6 items" (52px) needed 217px of
   a 172px label box and ran under the collapse arrow; "Controls &
   Thermostats" (194px) overflowed on its own, in BOTH kits. On a phone:
     - every water bar puts its count UNDER the name, so the stack is uniform
       (electric bars already wrap inline: room name, area badge, count);
     - a name longer than the box wraps, in both kits;
     - 6px of air before the arrow's ring (it was 3px on "Screed Components").
   Screen only, and <=700px: the PDF clone renders at 760px, so paper — whose
   page breaks measure these rows — cannot see it. */
@media screen and (max-width: 700px) {
  #wKitItems .wkit-cat-hdr > span:first-child {
    flex-direction: column;
    align-items: flex-start !important;
    gap: 1px !important;
  }
  #wKitItems .wkit-cat-hdr > span:first-child,
  #kitItems .wkit-cat-hdr > span:first-child {
    padding-right: 6px;
  }
  #wKitItems .wkit-cat-hdr > span:first-child > span:first-child,
  #kitItems .wkit-cat-hdr > span:first-child > span:first-child {
    white-space: normal !important;
    line-height: 1.25;
  }
}

/* ── ADD-ROOM BUTTONS TAKE THE COLOUR OF THE SYSTEM THEY ADD (Loz, 2026-09-28) ─
   The pair was coloured by POSITION — the page's own "+ Add Another Room"
   green, the door to the other system magenta — which is right on the water
   pages and backwards on the electric ones, where the green button added an
   electric room and the magenta one a water room. Green = water, magenta =
   electric everywhere else (the drawer's + Add Room, the system marks), so on
   the ELECTRIC panel (kit page and Your Selections) the two swap colours;
   order and size are untouched, the water panel is unchanged.
   !important because the engine's own onmouseover/onmouseout write the green
   wash inline on the add button, and the kit screen strips its inline style
   only once per render. */
@media screen {
  #electricPanel [onclick="eAddAnotherRoom()"],
  #electricPanel [onclick="eAddAnotherRoomFromSummary()"] {
    border-color: #ba39b5 !important;
    color: #ba39b5 !important;
  }
  #electricPanel [onclick="eAddAnotherRoom()"]:hover,
  #electricPanel [onclick="eAddAnotherRoomFromSummary()"]:hover,
  #electricPanel [onclick="eAddAnotherRoom()"]:focus-visible,
  #electricPanel [onclick="eAddAnotherRoomFromSummary()"]:focus-visible {
    background: #f7ebf6 !important;
  }
  #electricPanel .thermii-other-door-btn {
    border-color: var(--journey-green, #74b700);
    color: var(--journey-green, #74b700);
  }
  #electricPanel .thermii-other-door-btn:hover,
  #electricPanel .thermii-other-door-btn:focus-visible {
    background: #f0f7e6;
    color: var(--journey-green, #74b700);
  }
}
