:root {
    --freeman-navy: #001f5f;
    /* Navy used as *text* (headings, price totals). Same as navy in light mode;
       lightened in dark mode so it stays legible on a dark background (A1). */
    --navy-ink: #001f5f;
    --freeman-cyan: #00bdf6;
    --freeman-cyan-dark: #009cc6;
    --ink: #1a1a1a;
    --ink-soft: #4a4a4a;
    --muted: #777;
    --line: #e3e3e6;
    --bg: #f6f7f9;
    --card: #ffffff;
    --accent: #00bdf6;
    --good: #42bb37;   /* Freeman Green (primary palette) */
    --warn: #fb471f;   /* Freeman Orange (primary palette) */
    --bad: #fb471f;    /* Freeman Orange (primary palette) */
    --shadow: 0 1px 2px rgba(15,23,42,.06), 0 4px 12px rgba(15,23,42,.06);
    --radius: 10px;
    --radius-sm: 6px;
  }
  * { box-sizing: border-box; }
  html, body {
    margin: 0; padding: 0; background: var(--bg);
    color: var(--ink);
    color-scheme: light dark;
    font-family: 'Nunito Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-size: 14px;
    line-height: 1.45;
  }
  header.brand {
    background: #000;
    color: #fff;
    border-bottom: 4px solid var(--freeman-cyan);
    padding: 14px 24px;
    /* auto 1fr auto sizes the side tracks to their actual content (the New
       Design button, the icon row) and gives the leftover space to the centre
       track — so the logo/title sit centred in the GAP between those two
       groups, not on the header's outer edges. With 1fr auto 1fr the centre
       block centres on the full header instead, which reads as off-centre
       whenever the icon row (wide) and New Design (narrow) are so unequal. */
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 16px;
    position: sticky;
    top: 0;
    z-index: 100;
  }
  /* New Design lives here rather than in .actions, so every header BUTTON rule
     below is written against :is(.actions, .hdr-left). Miss that and the button
     falls all the way back to the UA default (grey, 2px outset border) because
     nothing else in the file styles a bare <button>. */
  header.brand .hdr-left { justify-self: start; display: flex; align-items: center; gap: 8px; }
  header.brand .hdr-center {
    justify-self: center;
    display: flex;
    align-items: center;
    gap: 24px;
    min-width: 0;
  }
  header.brand .app-title {
    font-size: clamp(16px, 1.9vw, 26px);
    font-weight: 600;
    white-space: nowrap;
    letter-spacing: -0.01em;
    /* The centre is the track that gives way when the header runs out of room,
       so the title has to be able to shrink rather than force an overflow. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  header.brand .logo-wrapper {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 12px;
  }
  header.brand .logo-text {
    font-size: 26px;
    font-weight: 800;
    line-height: 1.1;
  }
  header.brand .logo-tag {
    font-size: 15px;
    color: var(--freeman-cyan);
  }
  header.brand h1 {
    flex: 1;
    font-size: clamp(18px, 2.5vw, 26px);
    margin: 0;
    font-weight: 600;
    text-align: center;
  }
  header.brand .actions {
    justify-self: end;
    display: flex;
    /* Now that these are all icons the whole row is narrow enough to keep on
       one line, and wrapping it looked broken — a single stray avatar dropping
       to a second row. Keeping it nowrap also gives the grid track a real
       min-content width, so the CENTRE block is what yields when the window
       gets tight (its title ellipsises) rather than the controls wrapping or
       being pushed off-screen, which is what the old flex header did. */
    flex-wrap: nowrap;
    align-items: center;
    gap: 8px;
    justify-content: end;
  }
  header.brand :is(.actions, .hdr-left) button {
    background: transparent;
    color: #fff;
    border: 1px solid #555;
    padding: 7px 12px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: 13px;
  }
  header.brand :is(.actions, .hdr-left) button:hover { background: #333; }
  header.brand :is(.actions, .hdr-left) button.primary {
    background: var(--freeman-cyan);
    border-color: var(--freeman-cyan);
    color: #000;
    font-weight: 600;
  }
  /* Pricing / Design segmented toggle: two buttons that read as one control. */
  header.brand .actions .mode-toggle {
    display: inline-flex;
    border: 1px solid #555;
    border-radius: 20px;
    overflow: hidden;
  }
  header.brand .actions .mode-toggle button {
    border: none;
    border-radius: 0;
    margin: 0;
  }
  header.brand .actions .mode-toggle button + button {
    border-left: 1px solid #555;
  }
  header.brand .actions .mode-toggle button.active {
    background: var(--freeman-cyan, #00bdf6);
    color: #000;
    font-weight: 600;
  }
  /* Compact icon buttons (Save / Share / Feedback). */
  header.brand .actions button.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    width: 36px;
    height: 36px;
    border-radius: 50%;
  }
  header.brand .actions button.icon-btn svg { display: block; }
  /* The two side-column widths are custom properties rather than literals so
     that collapsing a column can drop it to a narrow rail (--col-l/--col-r ->
     max-content) and hand its width to the centre. See the rail rules further
     down. Every override of these columns must go through the properties, not a
     literal, or folding stops working in that mode. */
  main.layout {
    display: grid;
    --col-l: 300px;
    --col-r: 290px;
    grid-template-columns: var(--col-l) 1fr var(--col-r);
    gap: 8px;
    padding: 8px;
    max-width: 1920px;
    margin: 0 auto;
  }
  @media (max-width: 1380px) {
    main.layout { --col-l: 280px; --col-r: 270px; }
  }
  @media (max-width: 1180px) {
    main.layout { --col-l: 300px; grid-template-columns: var(--col-l) 1fr; }
    aside.picklist { grid-column: 1 / -1; }
  }
  @media (max-width: 900px) {
    main.layout { grid-template-columns: 1fr; }
  }
  .card {
    background: var(--card);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 16px;
  }
  .card h2 {
    font-size: 13px;
    text-transform: none;
    letter-spacing: .8px;
    color: var(--ink-soft);
    margin: 0 0 12px 0;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--line);
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  .card h2 .badge {
    background: var(--freeman-cyan);
    color: #000;
    font-size: 10px;
    padding: 2px 6px;
    border-radius: 4px;
    letter-spacing: .5px;
    text-transform: none;
  }
  .card h3 {
    font-size: 11px;
    text-transform: none;
    letter-spacing: .6px;
    color: var(--muted);
    margin: 12px 0 8px 0;
  }
  label.field { display: block; margin-bottom: 10px; }
  label.field > span {
    display: block;
    font-size: 11px;
    color: var(--ink);
    font-weight: 700;
    text-transform: none;
    letter-spacing: .5px;
    margin-bottom: 4px;
  }
  input[type="number"], input[type="text"], select {
    width: 100%;
    padding: 8px 10px;
    border: 2px solid var(--freeman-cyan);
    border-radius: var(--radius-sm);
    background: #f0f8ff;
    font-size: 14px;
    color: var(--ink);
    font-weight: 600;
    text-align: center;
  }
  select {
    text-align: left;
  }
  input[type="number"]:focus, input[type="text"]:focus, select:focus {
    outline: 2px solid var(--freeman-navy);
    outline-offset: -1px;
    background: #e6f5ff;
    border-color: var(--freeman-navy);
  }
  .row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  .dim-row { align-items: flex-end; }
  .dim-row label.field { margin-bottom: 0; }
  .dim-row label.field > span {
    min-height: auto;
    display: block;
    font-size: 11px;
    font-weight: 700;
    text-transform: none;
    color: var(--ink);
    letter-spacing: .4px;
    margin-bottom: 2px;
  }
  .row3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }
  .unit-toggle {
    display: flex;
    background: transparent;
    border-radius: 0;
    padding: 0;
    gap: 8px;
    margin-bottom: 0;
    flex-wrap: nowrap;
  }
  .unit-toggle button {
    flex: 0 1 auto;
    border: none;
    background: transparent;
    padding: 5px 10px;
    font-size: 12px;
    border-radius: 0;
    cursor: pointer;
    color: var(--muted);
    font-weight: 500;
    text-transform: none;
  }
  .unit-toggle button.active {
    background: var(--freeman-navy);
    color: #fff;
    box-shadow: none;
    border-radius: 4px;
    padding: 5px 10px;
  }
  .pills {
    display: flex;
    gap: 6px;
    margin-bottom: 12px;
  }
  .pills button {
    flex: 1;
    padding: 7px 6px;
    border: 1px solid var(--line);
    background: #fff;
    border-radius: 999px;
    cursor: pointer;
    font-size: 12px;
    color: var(--ink-soft);
  }
  .pills button.active {
    background: var(--freeman-navy);
    color: #fff;
    border-color: var(--freeman-navy);
  }
  /* Shape/curve pills that the selected tile model cannot physically do are
     disabled (see cxTileBend) rather than hidden, so the user can see the
     option exists but understands this product can't do it. */
  .pills button:disabled {
    opacity: .4;
    cursor: not-allowed;
    background: repeating-linear-gradient(-45deg, #f4f6f9, #f4f6f9 4px, #eaedf2 4px, #eaedf2 8px);
  }

  /* ---- Config radio groups ------------------------------------------------
     The processor-config dropdowns are rendered as radio pills to save vertical
     space. The <select> elements are UNTOUCHED and still the model — they are
     only visually hidden.
     TO REVERT TO DROPDOWNS: delete the `cfg-native` class from those selects in
     index.html and stop calling syncCfgRadios() (or just delete `.cfg-native`'s
     rules here). No option lists, values, listeners or dependent logic were
     changed. */
  .cfg-native { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
  .cfg-radio { display: flex; flex-wrap: wrap; gap: 5px; margin: 0 0 10px;
    justify-content: flex-start; }
  .cfg-cell { min-width: 0; }
  .cfg-radio button {
    flex: 0 1 auto;
    padding: 5px 9px;
    border: 1px solid var(--freeman-cyan);
    border-radius: 999px;
    background: transparent;
    color: var(--ink);
    font-size: 12px;
    font-weight: 600;
    line-height: 1.25;
    cursor: pointer;
    text-align: left;
  }
  .cfg-radio button:hover:not(:disabled):not(.active) { background: rgba(0,189,246,.14); }
  .cfg-radio button.active {
    background: var(--freeman-navy);
    border-color: var(--freeman-navy);
    color: #fff;
  }
  .cfg-radio button:disabled { opacity: .4; cursor: not-allowed; }
  .cfg-radio button:focus-visible { outline: 2px solid var(--freeman-navy); outline-offset: 1px; }
  /* The label above each group is the <label class="field"> caption, so the
     hidden select's own margin shouldn't leave a gap. */
  label.field:has(> .cfg-native) { margin-bottom: 4px; }

  /* ---- Collapsible cards -------------------------------------------------
     Click (or tap) a card heading to fold it away. The rail-left / rail-right
     body classes are set by updateRailClasses() but not styled yet — the
     narrow-rail treatment for collapsed side columns lands separately. */
  @media screen {
    .is-collapsible > h2 {
      cursor: pointer;
      user-select: none;
      -webkit-user-select: none;
      /* Kills the ~300ms tap delay and double-tap-to-zoom on the heading, so a
         phone/tablet tap toggles immediately. */
      touch-action: manipulation;
      -webkit-tap-highlight-color: transparent;
      min-height: 34px;
      gap: 8px;
    }
    .is-collapsible > h2:hover { color: var(--ink); }
    .is-collapsible > h2:focus-visible {
      outline: 2px solid var(--freeman-navy);
      outline-offset: 3px;
      border-radius: 4px;
    }
    /* Fold icon drawn as a masked pseudo-element: headings whose text is
       rewritten at runtime (#tileInfoCard) would lose an injected child node,
       and a mask still picks up `color` so hover + dark mode need no extra
       rules. Expanded shows "collapse", collapsed shows "expand". */
    .is-collapsible > h2::after {
      content: '';
      flex: 0 0 auto;
      width: 18px;
      height: 18px;
      margin-left: auto;
      /* Invisible padding box keeps the icon a comfortable touch target even
         though the whole heading is tappable. */
      box-sizing: content-box;
      padding: 8px 11px;
      color: var(--muted);
      background-color: currentColor;
      --card-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 4.5V19.5'/%3E%3Cpath d='M21 12H9.5'/%3E%3Cpath d='M14 7L9 12l5 5'/%3E%3C/svg%3E");
      -webkit-mask: var(--card-fold-icon) center / 18px 18px no-repeat;
      mask: var(--card-fold-icon) center / 18px 18px no-repeat;
    }
    .collapsed > h2::after { --card-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19.5 4.5V19.5'/%3E%3Cpath d='M3 9.5h7V5.5l6.5 6.5L10 18.5v-4H3z'/%3E%3C/svg%3E"); }
    .is-collapsible > h2:hover::after { color: var(--ink); }
    /* html2canvas can't render CSS mask, so the chevron above rasterizes as a
       solid gray square in exported package images (see elementToCanvas()) —
       hide it for the duration of any html2canvas capture. */
    body.html2canvas-capturing .is-collapsible > h2::after { display: none !important; }
    /* Right-hand column: control on the LEFT of the heading, arrows mirrored so
       they point the way the panel folds — outward to the right, giving the
       centre visuals the width. `order: -1` moves the pseudo-element ahead of
       the heading's anonymous text item. No auto margin on either side: the
       title sits directly after the control, left-justified like every other
       card heading, rather than being pushed to the far right of the column. */
    aside.picklist.is-collapsible > h2::after {
      order: -1;
      margin-left: 0;
      margin-right: 0;
      --card-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 4.5V19.5'/%3E%3Cpath d='M3 12h11.5'/%3E%3Cpath d='M10 7l5 5-5 5'/%3E%3C/svg%3E");
    }
    /* Centre column: full-width cards fold vertically, so the pair is rotated
       90 degrees — bar on top, arrow up to collapse and down to expand.
       Descendant selector so cards nested in #complexCard inherit it. */
    section#centerCol .card.is-collapsible > h2::after { --card-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='rotate(90 12 12)'%3E%3Cpath d='M5 4.5V19.5'/%3E%3Cpath d='M21 12H9.5'/%3E%3Cpath d='M14 7L9 12l5 5'/%3E%3C/g%3E%3C/svg%3E"); }
    section#centerCol .card.collapsed > h2::after { --card-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='rotate(90 12 12)'%3E%3Cpath d='M19.5 4.5V19.5'/%3E%3Cpath d='M3 9.5h7V5.5l6.5 6.5L10 18.5v-4H3z'/%3E%3C/g%3E%3C/svg%3E"); }
    /* Tile Information: despite living in the LEFT sidebar (#inputs), it folds
       vertically like a centre-column card (collapsing it to a narrow rail
       alongside "Wall Design" made no sense — it's already inside that rail),
       so give it the same rotated up/down chevron instead of the left-column
       default. */
    #tileInfoCard.is-collapsible > h2::after { --card-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='rotate(90 12 12)'%3E%3Cpath d='M5 4.5V19.5'/%3E%3Cpath d='M21 12H9.5'/%3E%3Cpath d='M14 7L9 12l5 5'/%3E%3C/g%3E%3C/svg%3E"); }
    #tileInfoCard.collapsed > h2::after { --card-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='rotate(90 12 12)'%3E%3Cpath d='M19.5 4.5V19.5'/%3E%3Cpath d='M3 9.5h7V5.5l6.5 6.5L10 18.5v-4H3z'/%3E%3C/g%3E%3C/svg%3E"); }
    aside.picklist.collapsed > h2::after { --card-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4.5 4.5V19.5'/%3E%3Cpath d='M21 9.5h-7V5.5L7.5 12 14 18.5v-4h7z'/%3E%3C/svg%3E"); }
    /* Card-style fold: keep only the h2 heading. Scoped away from
       .mini-collapsible, whose heading is an h3 — a bare `.collapsed` selector
       here would hide that h3 too, collapsing those sections to an empty box
       with no heading left to click, so they could never be rolled back down.
       The .mini-collapsible.collapsed rule below owns that case instead. */
    .collapsed:not(.mini-collapsible) > *:not(h2) { display: none !important; }
    /* Roll up/down collapsible for the four "Commonly Added Items" sections
       (Single Wall's Details pane + the Comparison/Multiple Wall/Complex left
       panels). Lightweight sibling of .is-collapsible above — reuses the same
       masked-chevron technique but works on a plain wrapper div with an h2 OR
       h3 heading (these sections aren't standalone .card elements, so they
       don't go through initCollapsibleCards()). */
    .mini-collapsible > h2, .mini-collapsible > h3 {
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: space-between;
      user-select: none;
      -webkit-user-select: none;
      touch-action: manipulation;
      -webkit-tap-highlight-color: transparent;
    }
    .mini-collapsible > h2:hover, .mini-collapsible > h3:hover { color: var(--ink); }
    .mini-collapsible > h2:focus-visible, .mini-collapsible > h3:focus-visible {
      outline: 2px solid var(--freeman-navy);
      outline-offset: 3px;
      border-radius: 4px;
    }
    .mini-collapsible > h2::after, .mini-collapsible > h3::after {
      content: '';
      flex: 0 0 auto;
      width: 16px;
      height: 16px;
      margin-left: auto;
      box-sizing: content-box;
      padding: 6px 8px;
      color: var(--muted);
      background-color: currentColor;
      --mini-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='rotate(90 12 12)'%3E%3Cpath d='M5 4.5V19.5'/%3E%3Cpath d='M21 12H9.5'/%3E%3Cpath d='M14 7L9 12l5 5'/%3E%3C/g%3E%3C/svg%3E");
      -webkit-mask: var(--mini-fold-icon) center / 16px 16px no-repeat;
      mask: var(--mini-fold-icon) center / 16px 16px no-repeat;
    }
    .mini-collapsible.collapsed > h2::after, .mini-collapsible.collapsed > h3::after {
      --mini-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='rotate(90 12 12)'%3E%3Cpath d='M19.5 4.5V19.5'/%3E%3Cpath d='M3 9.5h7V5.5l6.5 6.5L10 18.5v-4H3z'/%3E%3C/g%3E%3C/svg%3E");
    }
    .mini-collapsible > h2:hover::after, .mini-collapsible > h3:hover::after { color: var(--ink); }
    .mini-collapsible.collapsed > *:not(h2):not(h3) { display: none !important; }
    .mini-collapsible.collapsed { padding-bottom: 0; }
    .mini-collapsible.collapsed > h2, .mini-collapsible.collapsed > h3 { margin-bottom: 0; }
    /* Tighten the fold: the heading's bottom rule would otherwise float above
       a strip of empty padding. */
    .card.collapsed { padding-bottom: 8px; }
    .collapsed > h2 { margin-bottom: 0; padding-bottom: 0; border-bottom: none; }
    /* The aside is not a .card, so `.card h2` never reaches its own heading.
       `order: -1` pins it above advance-mode's flex `order` on
       #exportPackageSection (the aside becomes a flex column there). It needs to
       be a flex container itself, or the ::after chevron's own `order` and the
       rail layout below have nothing to act on. */
    aside.picklist > h2 { order: -1; margin: 0 4px 8px; display: flex; align-items: center; }

    /* ---- Collapsed side columns become narrow rails ----------------------
       The label turns on its side so the centre column gets the width. The
       rail-left / rail-right body classes and .mw-layout.mw-rail are set by
       updateRailClasses(). */
    body.rail-left main.layout { --col-l: max-content; }
    body.rail-right main.layout { --col-r: max-content; }
    #inputs.collapsed > h2,
    .mw-left.collapsed > h2,
    aside.picklist.collapsed > h2 {
      writing-mode: vertical-rl;
      /* vertical-rl reads top-to-bottom; 180deg puts it bottom-to-top, the
         convention for a vertical panel label. */
      transform: rotate(180deg);
      align-items: center;
      justify-content: flex-start;
      /* Room between the icon and the rotated label. */
      gap: 10px;
      padding: 4px 0;
      margin: 0 auto;
      white-space: nowrap;
      /* In a vertical writing mode `height` IS the inline size, and for a
         block-level heading it resolves to the LABEL's length alone — the icon
         then falls outside the rail and looks unclickable. height:auto /
         max-content / fit-content all do this; a min-height is what actually
         reserves the room (longer labels just grow past it). */
      min-height: 132px;
    }
    /* The badge ("LED tiles only") would sit sideways and double the rail width
       for no benefit. */
    #inputs.collapsed > h2 .badge,
    .mw-left.collapsed > h2 .badge,
    aside.picklist.collapsed > h2 .badge { display: none; }
    /* Keep the fold arrow horizontal inside the rotated heading. */
    #inputs.collapsed > h2::after,
    .mw-left.collapsed > h2::after,
    aside.picklist.collapsed > h2::after {
      writing-mode: horizontal-tb;
      transform: rotate(180deg);
      /* NOT `margin: auto` — an auto margin adds nothing to the intrinsic size,
         so the heading sizes to the label only and the icon renders outside the
         rail entirely (invisible, and looks unclickable). */
      margin: 0;
      padding: 6px 4px;
      flex: 0 0 auto;
    }
    /* #r2PickCard / #exportPackageSection are pinned visible by
       `body.mode-complex #r2PickCard { display: block !important }` (1,2,0),
       which outranks the generic fold rule — so the fold has to name them.
       Two IDs (2,1,0) wins. Without this the pane rails WIDER, not narrower,
       because max-content still measures these children. */
    #cxConfigPanel.collapsed > #r2PickCard,
    #cxConfigPanel.collapsed > #exportPackageSection { display: none !important; }
    /* Collapsed rail cards shouldn't keep the old column's padding. */
    #inputs.collapsed,
    aside.picklist.collapsed { padding-left: 6px; padding-right: 6px; }
  }
  @media screen and (max-width: 900px) {
    /* Single-column phone layout: a rail would just be a full-width strip. */
    body.rail-left main.layout,
    body.rail-right main.layout { --col-l: auto; --col-r: auto; }
    /* Design Mode empties this column — every card inside it is hidden and the
       controls move into the Wall Editor's left pane — so without this the fold
       heading is left stranded at the bottom of the page as a bar with nothing
       under it. Comparison and Multiple Wall hide the whole aside, so only
       mode-complex needs this. */
    body.mode-complex aside.picklist > h2 { display: none !important; }
    /* The vertical "rail" treatment above (writing-mode: vertical-rl,
       min-height: 132px) is for a narrow SIDE column that keeps its full
       height next to the main content — it makes no sense once the layout is
       one full-width column, where it instead reserved a tall, mostly-empty
       box for a sideways label (Adam, 2026-09-04). Reset the heading back to
       a normal short horizontal bar here, same as any other collapsed card. */
    #inputs.collapsed > h2,
    .mw-left.collapsed > h2,
    aside.picklist.collapsed > h2 {
      writing-mode: horizontal-tb;
      transform: none;
      min-height: 44px;
      justify-content: flex-start;
      padding: 0;
    }
    #inputs.collapsed > h2 .badge,
    .mw-left.collapsed > h2 .badge,
    aside.picklist.collapsed > h2 .badge { display: inline; }
    #inputs.collapsed > h2::after,
    .mw-left.collapsed > h2::after,
    aside.picklist.collapsed > h2::after {
      writing-mode: horizontal-tb;
      transform: none;
      margin-left: auto;
      order: 0;
      /* aside.picklist's rail-specific icon (an arrow pointing INTO a rail on
         the right) reads backwards once it's a plain stacked bar — swap back
         to the same generic expand glyph every other collapsed card uses. */
      --card-fold-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19.5 4.5V19.5'/%3E%3Cpath d='M3 9.5h7V5.5l6.5 6.5L10 18.5v-4H3z'/%3E%3C/svg%3E");
    }
    #inputs.collapsed,
    aside.picklist.collapsed { padding-left: 16px; padding-right: 16px; }
  }
  @media screen and (max-width: 900px) {
    /* The heading needs a bigger tap target on a phone. */
    .is-collapsible > h2 { min-height: 44px; font-size: 14px; }
  }

  /* Pixel Map overlay + colour pills: own full-width row inside .tb-left, so
     they sit under the Center label field rather than off on the right. */
  .pm-pill-row {
    flex: 1 0 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
    margin-top: 2px;
  }
  /* Colour group pushed to the far right of its row; the Show group stays left.
     `margin-left: auto` rather than space-between so the two groups don't drift
     apart when the row wraps. */
  .pm-pill-row .pm-color-pills { margin-left: auto !important; }

  /* Topology pill (Data / Power Wiring toolbars) + its popover. */
  .topo-pill { display: inline-flex; align-items: center; gap: 7px; }
  .topo-pill .topo-pill-glyph { display: inline-flex; width: 26px; height: 20px; }
  .topo-pill .topo-pill-glyph svg { width: 26px; height: 20px; display: block; }
  /* The glyph inside the pill is a preview, not a control: mute the run and let
     the arrow carry the brand colour. */
  .topo-pill .tg-run { stroke: #9aa8bd; }
  .topo-pill .tg-arrow { stroke: var(--freeman-navy); }
  .topo-pill .tg-dot { fill: #f0a92b; }
  .topo-pop {
    position: absolute; z-index: 1200; width: 320px; max-width: calc(100vw - 24px);
    background: var(--card); border: 1px solid var(--line); border-radius: 10px;
    box-shadow: 0 10px 30px rgba(15,23,42,.22); padding: 10px 12px 12px;
  }
  .topo-pop-head {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    font-size: 12px; font-weight: 700; color: var(--ink-soft);
    margin-bottom: 8px; padding-bottom: 6px; border-bottom: 1px solid var(--line);
  }
  .topo-pop-head button {
    border: none; background: none; color: var(--muted); font-size: 18px;
    line-height: 1; cursor: pointer; padding: 0 2px;
  }
  .topo-pop-head button:hover { color: var(--ink); }
  /* The <select> keeps its screen-reader label but the visible caption is the
     popover header now. */
  .topo-pop .topo-native-label { display: none; }
  .topo-pop .hint { margin-top: 8px !important; }
  @media print { .topo-pop { display: none !important; } }

  /* ---- Wiring topology picker -------------------------------------------
     Replaces a 16-option <select> with a 4x4 glyph grid. The native select is
     still the model (see the markup) so CB3 gating and state sync are
     unchanged; it is visually hidden here rather than removed. */
  .topo-native { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
  .topo-field { margin-bottom: 6px; }
  .topo-grid { display: grid; grid-template-columns: 1fr 1fr;
    border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
  .topo-quad { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding: 8px; }
  /* Dividers between the four quadrants, matching the reference layout. */
  .topo-quad:nth-child(2), .topo-quad:nth-child(4) { border-left: 1px solid var(--line); }
  .topo-quad:nth-child(3), .topo-quad:nth-child(4) { border-top: 1px solid var(--line); }
  .topo-btn { display: flex; align-items: center; justify-content: center; padding: 4px;
    border: 2px solid transparent; border-radius: 6px; background: transparent;
    cursor: pointer; transition: background .12s, border-color .12s; }
  .topo-btn:hover:not(:disabled) { border-color: var(--freeman-cyan); }
  .topo-btn:focus-visible { outline: 2px solid var(--freeman-navy); outline-offset: 1px; }
  .topo-btn.active { background: var(--freeman-cyan); border-color: var(--freeman-cyan); }
  .topo-btn:disabled { opacity: .28; cursor: not-allowed; }
  .topo-btn svg { display: block; width: 44px; height: 36px; }
  /* Glyph strokes: the run is neutral, the arrow reads as signal direction and
     the start dot is the amber "begin here" marker. On the active (cyan) tile
     everything flips to navy so it stays legible against the fill. */
  .topo-btn .tg-run { stroke: #9aa8bd; }
  .topo-btn .tg-arrow { stroke: var(--freeman-navy); fill: none; }
  .topo-btn .tg-dot { fill: #f0a92b; }
  .topo-btn.active .tg-run,
  .topo-btn.active .tg-arrow { stroke: var(--freeman-navy); }
  .topo-btn.active .tg-dot { fill: var(--freeman-navy); }

  .export-pills button {
    flex: none;
    width: 100%;
    padding: 9px 16px;
    font-weight: 700;
    font-size: 13px;
    border: none;
    background: var(--freeman-navy);
    color: #fff;
    transition: background .12s;
    box-sizing: border-box;
  }
  .export-pills button:hover {
    background: #001845;
  }
  .export-pills button:disabled,
  #exportPackageSection button:disabled {
    opacity: .55;
    cursor: progress;
  }
  /* Shared style for solid-navy action pills (LED Designer Tools row) —
     same size/shape/color family as .export-pills so the two adjacent
     button groups read as one consistent system. Auto-width by design
     (buttons hug their own label instead of filling equal grid cells). */
  .tool-pill {
    width: 100%;
    box-sizing: border-box;
    white-space: normal;
    text-align: center;
    padding: 9px 16px;
    border: none;
    border-radius: 999px;
    background: var(--freeman-navy);
    color: #fff;
    font-weight: 700;
    font-size: 13px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    transition: background .12s;
  }
  .tool-pill:hover { background: #001845; }
  #roundBlock { display: none; }
  #roundBlock.show { display: block; }
  #cmpModePills button {
    flex: auto;
    padding: 8px 12px;
    border: none;
    background: transparent;
    border-radius: 8px;
    font-size: 13px;
    color: var(--ink);
    font-weight: 500;
    cursor: pointer;
    border-bottom: 2px solid transparent;
  }
  #cmpModePills button.active {
    background: var(--freeman-navy);
    color: #fff;
    border-color: var(--freeman-navy);
  }
  .mode-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-bottom: 12px;
  }
  .mode-toggle #printPdfBtn { flex: 0 0 auto; }
  /* Plain text tabs, not a pill group: no track behind the row and no filled
     capsule on the active one. The current tab is marked with colour, weight
     and an underline instead, so it still reads as the selected one. */
  .mode-toggle .seg {
    display: inline-flex;
    background: none;
    border-radius: 0;
    padding: 0;
    gap: 4px;
  }
  .mode-toggle .seg button {
    border: none;
    background: transparent;
    padding: 6px 18px;
    border-radius: 0;
    cursor: pointer;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .3px;
    color: var(--ink-soft);
    border-bottom: 2px solid transparent;
  }
  .mode-toggle .seg button:hover:not(.active) { color: var(--ink); }
  .mode-toggle .seg button.active {
    background: none;
    color: var(--freeman-navy);
    border-bottom-color: var(--freeman-navy);
  }
  /* Advance mode toggle now lives in the header actions row (top-left,
     where Print / PDF used to sit) — same shape/size as its neighbors,
     just recolored cyan like .primary when active. */
  header.brand .actions button#advanceModeToggle.active {
    background: var(--freeman-cyan, #00bdf6);
    border-color: var(--freeman-cyan, #00bdf6);
    color: #000;
    font-weight: 600;
  }

  /* ---- Advance mode gating (Single Wall Configuration) ----
     Simple mode (Advance OFF) shows only the essentials: tile + size +
     Wall Visual + pricing. Advance mode reveals the Data / Power wiring and
     Pixel Map tabs, the Processor Configuration + wiring topology panel, and
     the Export Package + R2 Pick List cards. */
  body:not(.advance-mode) #viewTabs button[data-view="data"],
  body:not(.advance-mode) #viewTabs button[data-view="power"],
  body:not(.advance-mode) #viewTabs button[data-view="pixel"] { display: none !important; }
  body:not(.advance-mode) #configCard { display: none !important; }
  body:not(.advance-mode) #exportPackageSection,
  body:not(.advance-mode) #r2PickCard { display: none !important; }
  /* Tile Information is the at-a-glance simple-mode card; Advance mode hides it. */
  body.advance-mode #tileInfoCard { display: none !important; }
  /* Advance mode: push the Export Package card to the bottom of the sidebar.
     The sidebar is a plain block stack, so switch it to a flex column here and
     use order to float Export Package last (all other cards keep order 0). */
  body.advance-mode aside.picklist { display: flex; flex-direction: column; }
  body.advance-mode aside.picklist > #exportPackageSection { order: 1; }
  .view-tabs button {
    flex: 1;
    font-size: 13px;
    padding: 8px 6px;
    font-weight: 600;
  }
  .view-panel { display: none; }
  .view-panel.active { display: block; }
  .kpis {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin-top: 8px;
  }
  .kpi {
    background: #fafbfc;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
  }
  .kpi .label { font-size: 10px; text-transform: none; color: var(--muted); letter-spacing: .5px; }
  .kpi .value { font-size: 18px; font-weight: 600; margin-top: 2px; color: var(--ink); }
  .kpi .unit  { font-size: 11px; color: var(--muted); }
  .kpi.accent .value { color: var(--freeman-cyan); }
  .kpi.full { grid-column: span 2; }
  /* Wall Summary: match the approved screenshot — dark values, gray sub-text. */
  #summaryCard .kpi .value,
  #summaryCard .kpi.accent .value { color: var(--ink); }
  .visual-wrap {
    background: #0e1424;
    border-radius: var(--radius);
    padding: 14px;
    min-height: 480px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    position: relative;
  }
  .visual-wrap svg { width: 100%; height: auto; max-height: 520px; }
  /* The wall's on-screen size is governed by the RENDERED HEIGHT, not the width:
     renderVisual lays the wall into ~312 of its 460 canvas units (the rest is
     reserved for the flown truss, scale figure and dimension lines), so the wall
     is always ~0.68 x whatever height the SVG box gets. Widening the container
     alone therefore does nothing. Folding BOTH side columns to rails is an
     explicit "give the diagram the room" gesture, so the cap goes up to match —
     paired with the container-derived viewBox in renderVisual, that turns the
     reclaimed ~500px into a genuinely larger wall rather than empty margin. */
  body.rail-left.rail-right .visual-wrap svg { max-height: 760px; }
  /* Wall Editor: inline "set custom tile-column angle" popover (Advance Mode).
     Opens on clicking any seam dot / tile band on the curvature plan view —
     no armed edit mode needed. Positioned absolutely inside #cxPlanWrap
     (which already has position:relative via .visual-wrap). */
  .cx-angle-popover {
    position: absolute; z-index: 20; width: 168px;
    background: #101a2e; border: 1px solid #2a3852; border-radius: 8px;
    padding: 10px; box-shadow: 0 6px 20px rgba(0,0,0,.35);
    font-size: 12px; color: #bfe9ff;
  }
  body.diagrams-light .cx-angle-popover { background: #ffffff; border-color: #cdd7e2; color: #16314f; box-shadow: 0 6px 20px rgba(0,0,0,.15); }
  .cx-angle-popover-title { font-weight: 700; margin-bottom: 6px; }
  .cx-angle-popover-row { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
  .cx-angle-popover-row input {
    width: 84px; padding: 5px 7px; border-radius: 6px; border: 1px solid #2a3852;
    background: #0e1424; color: #eaf6ff; font-size: 13px;
  }
  body.diagrams-light .cx-angle-popover-row input { background: #f6f9fc; border-color: #d6dde6; color: #16314f; }
  .cx-angle-popover-btns { display: flex; flex-wrap: wrap; gap: 6px; }
  .cx-angle-popover-btns button {
    padding: 5px 9px; border-radius: 999px; border: none; background: #00bdf6;
    color: #001f5f; font-weight: 700; font-size: 11px; cursor: pointer;
  }
  .cx-angle-popover-btns button.ghost { background: transparent; border: 1px solid #3a4a66; color: #9fb2c9; }
  body.diagrams-light .cx-angle-popover-btns button.ghost { border-color: #cdd7e2; color: #5a6b7f; }
  /* Custom wiring editor toolbar */
  .custom-wiring-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 10px; padding: 10px; background: #f6f9fc; border: 1px solid #d6dde6; border-radius: 8px; }
  .custom-wiring-bar .cwb-label { font-size: 11px; text-transform: uppercase; letter-spacing: .5px; color: #5b6675; font-weight: 700; }
  .cwb-chips { display: flex; flex-wrap: wrap; gap: 6px; }
  .cwb-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 9px; border-radius: 999px; border: 2px solid transparent; background: #fff; cursor: pointer; font-size: 12px; font-weight: 600; color: #1a2433; line-height: 1.2; }
  .cwb-chip .sw { width: 12px; height: 12px; border-radius: 3px; flex: 0 0 auto; }
  .cwb-chip.active { border-color: #1a2433; box-shadow: 0 0 0 1px #1a2433 inset; }
  .cwb-chip.over { color: #c62828; }
  .cwb-chip .cnt { font-weight: 400; color: #5b6675; }
  .cwb-chip.over .cnt { color: #c62828; font-weight: 700; }
  .cwb-add { padding: 4px 10px; border-radius: 999px; border: 1px dashed #b8c2cf; background: #fff; cursor: pointer; font-size: 12px; font-weight: 700; color: #16314f; }
  .cwb-btn { padding: 4px 10px; border-radius: 6px; border: 1px solid #d6dde6; background: #fff; cursor: pointer; font-size: 12px; font-weight: 600; color: #1a2433; }
  .cwb-spacer { flex: 1 1 auto; }
  .cwb-warn { font-size: 12px; font-weight: 700; color: #c62828; }
  .custom-wiring-bar .cwb-cap { font-size: 11px; color: #5b6675; }
  svg .cw-cell { cursor: crosshair; }
  /* Enable Custom Wiring controls */
  .cw-enable-btn { display: inline-flex; align-items: center; gap: 8px; width: 100%; justify-content: center; padding: 9px 12px; border-radius: 8px; border: 1px solid #b8c2cf; background: #fff; color: #16314f; font-size: 13px; font-weight: 700; cursor: pointer; transition: background .12s, border-color .12s; }
  .cw-enable-btn:hover { border-color: #00bdf6; }
  .cw-enable-btn .cw-dot { width: 10px; height: 10px; border-radius: 50%; background: #b8c2cf; flex: 0 0 auto; }
  .cw-enable-btn.active { background: #00bdf6; border-color: #00bdf6; color: #001f5f; }
  .cw-enable-btn.active .cw-dot { background: #001f5f; }
  .cw-tab-btn.active { background: var(--freeman-navy); border-color: var(--freeman-navy); color: #fff; }
  /* Light diagram mode: the SVG fills its own white background, so the wrapper
     must be white too (no dark/blue box around the wall). Meta text goes dark. */
  body.diagrams-light .visual-wrap { background: #ffffff; }
  body.diagrams-light .visual-meta { color: #1a1a1a; }
  .visual-meta {
    color: #ffffff;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 16px;
    width: 100%;
    margin-top: 10px;
    font-size: 14px;
    font-weight: 500;
  }
  /* Per-port load readout line, below the main meta line. */
  .visual-meta.visual-meta-load {
    margin-top: 6px;
    font-size: 12px;
    font-weight: 500;
    opacity: 0.92;
  }
  .visual-meta.visual-meta-load > div { width: 100%; }
  .view-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 8px;
    min-height: 28px;
  }
  .view-toolbar .tb-left,
  .view-toolbar .tb-right {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
  }
  /* Three-zone toolbar: equal-flex left/right push the center group to the
     true middle of the window. flex-wrap above lets each zone's buttons drop
     to a second line instead of forcing the whole page to overflow
     horizontally when the window is too narrow to fit them on one line. */
  .view-toolbar .tb-left   { flex: 1 1 auto; min-width: 0; justify-content: flex-start; }
  .view-toolbar .tb-center { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; justify-content: center; }
  .view-toolbar .tb-right  { flex: 1 1 auto; min-width: 0; justify-content: flex-end; }
  .view-toolbar .tb-label {
    font-size: 10px;
    color: var(--muted);
    letter-spacing: .5px;
  }
  .icon-btn {
    background: var(--freeman-cyan);
    color: #000;
    border: none;
    padding: 7px 12px;
    border-radius: 999px;
    font-size: 12px;
    cursor: pointer;
    font-weight: 600;
  }
  .icon-btn:hover { background: var(--freeman-cyan-dark); }
  .icon-btn.ghost {
    background: transparent;
    color: var(--ink-soft);
    border: 1px solid var(--line);
  }
  .icon-btn.ghost:hover { background: #f1f2f5; }
  /* Uniform toolbar toggle buttons (Processor image / Port lines / Dark mode):
     same pill shape as Simple Wiring / Max Port Capacity — rounded, white, navy
     when active. Fixed width so all three match; single-line centered text. */
  .tb-toggle {
    width: 124px;
    height: 34px;
    padding: 0 10px;
    border: 1px solid var(--line);
    background: #fff;
    color: var(--ink-soft);
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    white-space: nowrap;
    line-height: 1.1;
    box-sizing: border-box;
  }
  .tb-toggle:hover { background: #f1f2f5; }
  .tb-toggle.active {
    background: var(--freeman-navy);
    color: #fff;
    border-color: var(--freeman-navy);
  }
  .tb-toggle:disabled { opacity: 0.5; cursor: not-allowed; }
  .spec-table { width: 100%; border-collapse: collapse; margin-top: 8px; }
  .spec-table th, .spec-table td {
    text-align: left;
    padding: 6px 8px;
    font-size: 12px;
    border-bottom: 1px solid var(--line);
  }
  .spec-table th { color: var(--muted); font-weight: 500; text-transform: none; letter-spacing: .4px; font-size: 10px; }
  .spec-table td.right { text-align: right; }
  .hint { font-size: 11px; color: var(--muted); margin-top: 4px; }
  .right { text-align: right; }
  .grand-total {
    margin-top: 14px;
    padding: 12px;
    background: #fafbfc;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
  }
  .grand-total .gt-row {
    display: flex;
    justify-content: space-between;
    font-size: 13px;
    margin: 4px 0;
    color: var(--ink-soft);
  }
  .grand-total .gt-row.total {
    border-top: 1px solid var(--line);
    padding-top: 8px;
    margin-top: 8px;
    font-weight: 700;
    color: var(--ink);
    font-size: 16px;
  }
  .gt-warn { color: var(--bad) !important; }
  /* LED Design Documentation form fields */
  .ldlbl { display:block; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.5px; color:#5b6675; margin-bottom:4px; }
  .ldin { width:100%; padding:8px 10px; border:1px solid #c7d0db; border-radius:6px; font-size:13px; box-sizing:border-box; }
  .picklist-section { margin-bottom: 14px; }
  .picklist-section h4 {
    font-size: 12px;
    text-transform: none;
    letter-spacing: .5px;
    color: var(--ink-soft);
    margin: 12px 0 6px 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: 4px;
    border-bottom: 1px dashed var(--line);
  }
  .picklist-section h4 .cat-total { color: var(--muted); font-size: 11px; font-weight: 500; }
  .pick-item {
    display: grid;
    grid-template-columns: 1fr 50px 80px;
    gap: 8px;
    padding: 5px 0;
    font-size: 13px;
    align-items: baseline;
  }
  .pick-item .name { color: var(--ink); }
  .pick-item .qty  { text-align: right; color: var(--ink-soft); }
  .pick-item .price { text-align: right; color: var(--ink-soft); font-size: 12px;}
  .pick-item.small  { font-size: 12px; color: var(--muted); }
  /* 3D View tab (src/js/95-view3d.js). The stage height is set from its
     width in JS (16:9-ish) so the canvas never distorts. */
  .v3d-toolbar .tb-center { flex-wrap: wrap; }
  .v3d-stage {
    position: relative; width: 100%; min-height: 260px; border-radius: 8px;
    overflow: hidden; background: #1c2230;
  }
  .v3d-canvas { display: block; touch-action: none; cursor: grab; }
  .v3d-canvas:active { cursor: grabbing; }
  .v3d-msg {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    color: #c9d2e3; font-size: 14px; text-align: center; padding: 20px; pointer-events: none;
  }
  .v3d-hint {
    position: absolute; left: 10px; bottom: 8px; max-width: 48%; font-size: 11px; color: rgba(220,228,240,.75);
    pointer-events: none; text-shadow: 0 1px 2px rgba(0,0,0,.6);
  }
  .v3d-upload-ctl .tb-left { gap: 8px; }
  .v3d-trim { display: inline-flex; align-items: center; gap: 5px; margin-left: 12px; }
  .v3d-trim-in {
    width: 64px; padding: 4px 6px; font: inherit; font-size: 12px; text-align: center;
    border: 1px solid var(--line); border-radius: 6px; background: var(--card); color: var(--ink);
  }
  .v3d-trim-in:disabled { opacity: .6; cursor: not-allowed; }
  .v3d-zoom { width: 150px; accent-color: var(--freeman-cyan); vertical-align: middle; }
  .v3d-zoom-val { min-width: 38px; }
  .v3d-disclaimer {
    position: absolute; right: 10px; bottom: 8px; max-width: 46%; text-align: right;
    font-size: 11px; line-height: 1.35; color: rgba(220,228,240,.8);
    pointer-events: none; text-shadow: 0 1px 2px rgba(0,0,0,.6);
  }
  .v3d-meta { display: flex; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
  .v3d-meta-r { color: var(--muted); font-size: 11px; }
  .v3d-offscreen { position: absolute; left: -10000px; top: 0; width: 800px; height: 460px; overflow: hidden; }
  /* Print / Save PDF from Single Wall always prints the 2D Wall Visual, even
     when the 3D View tab is the one on screen. */
  body.print-visual-mode [data-view-panel="3d"] { display: none !important; }
  body.print-visual-mode [data-view-panel="visual"] { display: block !important; }
  /* Pixel Map tile-ID numbering style dropdown (next to the IDs pill). */
  .pm-id-style {
    align-self: center; height: 28px; padding: 2px 6px; font: inherit; font-size: 12px;
    border: 1px solid var(--line); border-radius: 6px; background: var(--card); color: var(--ink); cursor: pointer;
  }
  @media print { .pm-id-style { display: none !important; } }
  /* Pixel Map custom colour: small wheel icon beside the preset pills, and its popup. */
  .pills .pm-wheel-btn {
    width: 22px; height: 22px; min-width: 0; min-height: 0; padding: 0; flex: 0 0 auto;
    border-radius: 50%; border: 1px solid var(--line); cursor: pointer; align-self: center;
    background: radial-gradient(circle, #fff 0 18%, transparent 62%),
                conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
  }
  .pills .pm-wheel-btn:hover { transform: scale(1.1); }
  .pm-wheel-pop {
    display: none; position: absolute; z-index: 3000; padding: 12px; width: 236px;
    background: #2b2d33; border: 1px solid #44474f; border-radius: 8px;
    box-shadow: 0 10px 28px rgba(0,0,0,.35); color: #e7e9ee; font-size: 12px;
  }
  .pm-wheel-row { display: flex; align-items: center; gap: 12px; }
  .pm-wheel-wrap { position: relative; width: 180px; height: 180px; }
  .pm-wheel-wrap canvas { display: block; width: 180px; height: 180px; cursor: crosshair; touch-action: none; }
  .pm-wheel-dot {
    position: absolute; width: 12px; height: 12px; margin: -6px 0 0 -6px; pointer-events: none;
    border: 2px solid #fff; border-radius: 50%; box-shadow: 0 0 0 1px #000;
  }
  .pm-wheel-val { writing-mode: vertical-lr; direction: rtl; width: 18px; height: 180px; accent-color: #fff; }
  .pm-wheel-foot { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
  .pm-wheel-swatch { width: 26px; height: 26px; border-radius: 4px; border: 1px solid #666; flex: 0 0 auto; }
  .pm-wheel-hex {
    flex: 1; min-width: 0; padding: 4px 6px; font: inherit; text-transform: uppercase;
    background: #1d1f24; color: #fff; border: 1px solid #555; border-radius: 4px;
  }
  .pm-wheel-done {
    padding: 4px 10px; font: inherit; font-weight: 700; cursor: pointer;
    background: var(--freeman-cyan); color: #000; border: none; border-radius: 999px;
  }
  @media print { .pm-wheel-pop { display: none !important; } }
  /* Pricing Mode "exceeds standard 4K" red flag (over4kWarning()). */
  .warn-4k {
    margin-top: 10px; padding: 8px 10px; border-radius: 6px;
    background: #fff4f0; border: 1px solid #f3c2b3; color: #b3401b;
    font-size: 12px; font-weight: 600; line-height: 1.4;
  }
  .legend {
    display: flex;
    gap: 14px;
    flex-wrap: wrap;
    margin-top: 10px;
    font-size: 11px;
    color: var(--ink-soft);
  }
  .legend .sw {
    display: inline-block;
    width: 12px; height: 12px;
    border-radius: 2px;
    vertical-align: middle;
    margin-right: 5px;
  }
  body.print-visual-mode header,
  body.print-visual-mode main.layout > section > .card:not(#visualCard):not(#summaryCard),
  body.print-visual-mode main.layout > section:first-child,
  body.print-visual-mode main.layout > aside,
  body.print-visual-mode footer,
  body.print-visual-mode .mode-toggle,
  body.print-visual-mode #viewTabs { display: none !important; }
  /* The aspect-ratio pills + color-key bar above the wall visual: only
     meaningful in the printout when an aspect ratio is actually selected
     (state.aspect !== 'none'), in which case handlePrint() adds
     print-show-aspect so the overlay drawn on the wall visual has its
     pill/key context; otherwise there's nothing to key, so hide the bar. */
  body.print-visual-mode:not(.print-show-aspect) .view-toolbar,
  body.print-visual-mode:not(.print-show-aspect) .legend { display: none !important; }
  /* The generic @media print block further down hides `.card h2`, `.legend`,
     and `.pills` unconditionally (for every OTHER print flow — comparison,
     multiwall, etc.) using !important, which beats these same-media rules by
     specificity alone. Re-assert the visual-mode title/aspect bar here with
     matching !important and higher specificity so it wins regardless of
     source order. */
  @media print {
    body.print-visual-mode .card > h2.print-only-title { display: flex !important; }
    body.print-visual-mode.print-show-aspect .view-toolbar,
    body.print-visual-mode.print-show-aspect .legend,
    body.print-visual-mode.print-show-aspect #aspectPills { display: flex !important; }
  }
  #comparisonCard,
  #multiwallCard,
  #complexCard { display: none !important; }

  /* Single Wall Configuration: hide the Pick List card (kept for other modes). */
  body.mode-simple #picklistCard { display: none !important; }

  body.mode-comparison #visualCard,
  body.mode-comparison #summaryCard,
  body.mode-comparison #picklistCard,
  body.mode-comparison #multiwallCard,
  body.mode-comparison #complexCard { display: none !important; }
  body.mode-comparison #inputs { display: block !important; grid-column: auto !important; }
  body.mode-comparison #inputs > div:first-child { display: none; }
  body.mode-comparison #centerCol { grid-column: 2/3 !important; }
  body.mode-comparison #comparisonCard { display: block !important; }
  /* var(--col-l), not a literal, so folding the left column still lets the
     comparison grid take the width even though this declaration is !important. */
  body.mode-comparison main.layout { grid-template-columns: var(--col-l) 1fr !important; }
  body.mode-comparison aside.picklist { display: none !important; }

  /* Multiple Wall: center canvas card + right sidebar (Estimated Total +
     Processor Configuration), matching the pricing tool layout. The shared
     diagram card and the left input column are hidden here. */
  body.mode-multiwall #summaryCard,
  body.mode-multiwall #picklistCard,
  body.mode-multiwall #configCard,
  body.mode-multiwall #inputs,
  body.mode-multiwall #comparisonCard,
  body.mode-multiwall #complexCard,
  body.mode-multiwall #visualCard { display: none !important; }
  body.mode-multiwall #multiwallCard { display: block !important; }
  body.mode-multiwall #centerCol { grid-column: 1 / -1 !important; }
  body.mode-multiwall aside.picklist { display: none !important; }
  body.mode-multiwall main.layout { grid-template-columns: 1fr !important; }

  /* Project-wide export from Multiple Wall mode needs #visualCard/#summaryCard
     laid out (non-zero size) so html2canvas/svgToCanvas can capture them per
     wall, but they must not visibly flash on screen. Extra class = higher
     specificity than the display:none rule above, so this wins regardless of
     source order; positioning off-screen keeps it out of view while still
     giving it real layout dimensions. Toggled by _mwBuildAllProjectCanvases(). */
  body.mode-multiwall.mw-exporting #visualCard,
  body.mode-multiwall.mw-exporting #summaryCard {
    display: block !important;
    position: absolute !important;
    left: -99999px !important;
    top: 0 !important;
  }

  /* Complex: full-width editable canvas card (the click-to-remove tile grid
     lives here now). Left inputs and right sidebar hidden. */
  body.mode-complex #visualCard,
  body.mode-complex #summaryCard,
  body.mode-complex #picklistCard,
  body.mode-complex #inputs,
  body.mode-complex #comparisonCard,
  body.mode-complex #multiwallCard { display: none !important; }
  body.mode-complex #complexCard { display: block !important; }
  /* Project-wide Pick List & Export: only relevant to a whole multi-wall
     project, so show it only in Multiple Wall + Advance Mode (mode-complex
     reached from the multiwall base, i.e. NOT adv-single, which is Advance
     Mode entered from Single Wall) — hidden in Multiwall's plain Simple view
     and everywhere else. */
  #mwPackageBlock { display: none !important; }
  body.mode-complex.advance-mode:not(.adv-single) #mwPackageBlock { display: block !important; }
  body.mode-complex main.layout { grid-template-columns: 1fr !important; }
  /* The right sidebar is hidden outright in Complex mode. It used to be shown
     as a full-width row below the canvas card so the Export Package + R2 Pick
     List cards inside it stayed reachable — but both of those (and the
     Processor Configuration card) are relocated into #cxConfigPanel at boot,
     so nothing inside this column is visible here any more. What was left was
     an empty strip containing only the "Details" heading and a fold control
     with nothing to fold.
     The `> .card` rule is kept as a backstop in case a card is ever added back
     to this column without being given a Complex-mode home. */
  body.mode-complex aside.picklist { display: none !important; }
  body.mode-complex aside.picklist > .card { display: none !important; }
  body.mode-complex #exportPackageSection,
  body.mode-complex #r2PickCard { display: block !important; }

  /* Advance Mode entered from the Single Wall page: it's a single wall, so hide
     the multi-wall Area/Wall tab row and the canvas/wall management bars. */
  body.adv-single #cxCanvasTabs,
  body.adv-single #cxCanvasBar,
  body.adv-single #cxWallBar { display: none !important; }
  /* A single wall has no "Full Canvas vs This Wall" distinction — hide the Scope
     toggle on the Data / Power / Pixel Map tabs when editing a single wall. */
  body.adv-single .cx-scope { display: none !important; }
  /* A single wall has no position relative to other walls — hide Position X/Y. */
  body.adv-single #cxPosRow { display: none !important; }
  .gc-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.55);
    z-index: 1000;
    padding: 24px;
    overflow-y: auto;
  }
  .gc-overlay.open { display: flex; align-items: flex-start; justify-content: center; }
  .gc-modal {
    background: var(--card);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 22px 24px 26px;
    width: 100%;
    max-width: 1000px;
    margin: auto;
  }
  .gc-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
  }
  .gc-modal-head h2 {
    margin: 0;
    font-size: 18px;
    color: var(--navy-ink);
  }
  .gc-close {
    background: none;
    border: none;
    font-size: 26px;
    line-height: 1;
    color: var(--muted);
    cursor: pointer;
    padding: 0 4px;
  }
  .gc-close:hover { color: var(--ink); }
  .gc-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 14px;
  }
  .gc-box {
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 14px;
    background: var(--bg);
  }
  .gc-box h3 {
    margin: 0 0 10px;
    font-size: 13px;
    color: var(--navy-ink);
    border-bottom: 2px solid var(--freeman-cyan);
    padding-bottom: 6px;
  }
  .gc-box label {
    display: block;
    font-size: 11px;
    color: var(--ink-soft);
    margin-bottom: 8px;
  }
  .gc-box input {
    display: block;
    width: 100%;
    margin-top: 3px;
    padding: 6px 8px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-size: 13px;
    box-sizing: border-box;
  }
  .gc-box input:focus { outline: none; border-color: var(--freeman-cyan); }
  .gc-out {
    margin-top: 8px;
    padding: 8px 10px;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-weight: 700;
    color: var(--freeman-navy);
    font-size: 13px;
    min-height: 18px;
    white-space: pre-line;
  }
  .tc-modal {
    display: flex;
    flex-direction: column;
    transition: max-width 0.15s ease, max-height 0.15s ease;
  }
  .tc-modal.tc-size-compact { max-width: 640px; max-height: 70vh; }
  .tc-modal.tc-size-expanded { max-width: 96vw; max-height: 90vh; }
  .tc-modal.tc-size-full {
    max-width: 100vw;
    width: 100vw;
    max-height: 100vh;
    height: 100vh;
    margin: 0;
    border-radius: 0;
  }
  .gc-overlay:has(.tc-size-full) { padding: 0; }
  .tc-size-btn {
    background: none;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-size: 13px;
    line-height: 1;
    padding: 5px 8px;
    cursor: pointer;
    color: var(--ink-soft);
  }
  .tc-size-btn:hover { border-color: var(--freeman-cyan, #00b3d6); color: var(--navy-ink); }
  #tcMatrixWrap { overflow: auto; flex: 1; }
  .tc-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 14px;
    margin: 0 0 16px;
  }
  .tc-controls label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 11px;
    color: var(--ink-soft);
    font-weight: 600;
  }
  .tc-controls select, .tc-controls input {
    padding: 6px 8px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-size: 13px;
  }
  .tc-controls input[type="number"] { width: 90px; }
  #tcMatrixWrap table {
    border-collapse: collapse;
    font-size: 10px;
    table-layout: fixed;
  }
  #tcMatrixWrap th, #tcMatrixWrap td {
    border: 1px solid var(--line);
    text-align: center;
    vertical-align: middle;
    padding: 2px;
    box-sizing: border-box;
    overflow: hidden;
    white-space: normal;
    word-break: break-word;
  }
  #tcMatrixWrap th {
    background: var(--bg);
    color: var(--navy-ink);
    font-weight: 700;
  }
  #tcMatrixWrap td.tc-corner {
    background: var(--bg);
    font-weight: 700;
    color: var(--ink-soft);
  }
  .cmp-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 14px;
  }
  .cmp-toolbar.generic-mode {
    justify-content: center;
  }
  .cmp-tb-group { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .cmp-tb-label { font-size: 10px; color: var(--muted); letter-spacing: .5px; text-transform: none; }
  .cmp-discount { display: flex; align-items: center; gap: 8px; }
  .cmp-discount input { width: 70px; }
  .cmp-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 16px;
  }
  .cmp-card {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 16px;
    background: #fff;
    display: flex;
    flex-direction: column;
    position: relative;
  }
  /* Tile-card select checkbox + "copy as image" floating bar */
  .cmp-card.cmp-card-selected {
    border-color: var(--freeman-cyan);
    box-shadow: 0 0 0 2px rgba(0,189,246,.35);
  }
  /* Row holding the "Select" checkbox and, opposite it, the "Beam Product
     Info" link (only for tiles with a Beam Dynamics catalog URL on file —
     see TILE_BEAM_URL). */
  .cmp-card-toprow {
    display: flex; align-items: center; justify-content: space-between;
    gap: 8px; margin-bottom: 8px;
  }
  .cmp-select {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 11px; font-weight: 600; color: var(--muted);
    cursor: pointer; user-select: none;
  }
  .cmp-select input { width: 15px; height: 15px; cursor: pointer; accent-color: var(--freeman-navy); margin: 0; }
  .cmp-beam-link {
    font-size: 11px; padding: 4px 10px; text-decoration: none; white-space: nowrap;
  }
  #comparisonCard { position: relative; }
  /* Light mode: the Price Comparison "Select" checkbox and Commonly Added
     Items checkboxes as a crisp white box with a black outline, instead of
     the OS-native unchecked style (a nondescript grey square that doesn't
     match the rest of the UI's navy/white palette). Dark mode keeps the
     native/accent-color look, which already reads fine there. */
  html:not([data-theme="dark"]) .cmp-select input,
  html:not([data-theme="dark"]) input.common-item-cb {
    appearance: none; -webkit-appearance: none;
    border: 1.5px solid #000; border-radius: 3px;
    background: #fff; position: relative;
  }
  html:not([data-theme="dark"]) .cmp-select input:checked,
  html:not([data-theme="dark"]) input.common-item-cb:checked {
    background: var(--freeman-navy); border-color: var(--freeman-navy);
  }
  html:not([data-theme="dark"]) .cmp-select input:checked::after,
  html:not([data-theme="dark"]) input.common-item-cb:checked::after {
    content: ''; position: absolute; left: 4px; top: 1px;
    width: 4px; height: 8px; border: solid #fff; border-width: 0 2px 2px 0;
    transform: rotate(45deg);
  }
  html:not([data-theme="dark"]) input.common-item-cb { width: 13px; height: 13px; }
  html:not([data-theme="dark"]) input.common-item-cb:checked::after { left: 3px; top: 0; width: 3.5px; height: 7px; }
  /* Print / Save PDF: when one or more tiles are checked via .cmp-select-cb,
     only those cards print — everything else in the grid is pulled out of
     layout for the duration of the print pass (see handlePrint's 'comparison'
     branch). No checkboxes checked → nothing gets this class → all
     currently-filtered cards print, same as before. */
  .cmp-card.cmp-print-excluded { display: none !important; }
  .cmp-copy-bar {
    position: absolute; top: 44px; right: 18px; z-index: 40;
    display: flex; align-items: center; gap: 10px;
  }
  .cmp-copy-count { font-size: 12px; font-weight: 600; color: var(--ink-soft); }
  .cmp-copy-btn {
    background: var(--freeman-cyan); color: #001f5f;
    border: none; border-radius: 8px; padding: 7px 16px;
    font-size: 13px; font-weight: 700; cursor: pointer;
  }
  .cmp-copy-btn:hover { filter: brightness(1.06); }
  .cmp-copy-btn:disabled { opacity: .6; cursor: default; }
  .cmp-copy-clear {
    background: #fff; color: var(--ink-soft); border: 1px solid var(--line);
    border-radius: 8px; padding: 6px 12px; font-size: 12px; cursor: pointer;
  }
  .cmp-copy-clear:hover { border-color: var(--freeman-navy); color: var(--freeman-navy); }
  .cmp-copy-status { font-size: 12px; color: var(--muted); }
  @media print {
    .cmp-card-toprow, .cmp-copy-bar { display: none !important; }
  }
  .cmp-card .cmp-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 12px;
  }
  .cmp-card .cmp-title { font-size: 14px; font-weight: 700; color: var(--ink); line-height: 1.2; }
  .cmp-card .cmp-sub { font-size: 11px; color: var(--muted); margin-top: 2px; }
  .cmp-pitch-badge {
    background: var(--freeman-navy);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    padding: 4px 8px;
    border-radius: 4px;
    white-space: nowrap;
  }
  .cmp-status-badge {
    display: inline-block;
    font-size: 9px;
    letter-spacing: .5px;
    text-transform: none;
    padding: 3px 6px;
    border-radius: 3px;
    margin-top: 6px;
    font-weight: 600;
  }
  .cmp-status-badge.owned { background: var(--good); color: #fff; }
  .cmp-status-badge.rental { background: var(--warn); color: #fff; }
  .cmp-price-pill {
    border-radius: 8px;
    padding: 8px 12px;
    text-align: left;
    margin: 8px 0;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    flex-wrap: wrap;
  }
  .cmp-price-pill .pp-label { font-size: 11px; text-transform: none; letter-spacing: .5px; opacity: .85; }
  .cmp-price-pill .pp-amount { display: flex; align-items: baseline; gap: 6px; white-space: nowrap; }
  .cmp-price-pill .pp-value { font-size: 16px; font-weight: 700; }
  .cmp-price-pill .pp-detail { font-size: 11px; opacity: .75; }
  .cmp-added-items { margin: 4px 0 8px; color: var(--muted); }
  .cmp-added-items .pp-detail { font-size: 11px; opacity: .85; padding: 1px 0; }
  .cmp-price-list { background: #e7f6ec; color: #000; }
  .cmp-price-disc { background: #e0f7ff; color: #000; }
  .cmp-price-disc.discount-good { background: #e0f7ff; color: #000; }
  .cmp-price-disc.discount-bad { background: #ffe4dc; color: #fb471f; }
  .cmp-tint-box { background: #fafbfc; }
  .cmp-summary { border-top: 1px solid var(--line); padding-top: 10px; margin-top: 10px; }
  .cmp-summary .cmp-row {
    display: flex; justify-content: space-between; gap: 8px;
    font-size: 12px; padding: 4px 0; color: var(--ink-soft);
  }
  .cmp-summary .cmp-row .k { color: var(--muted); font-size: 11px; }
  .cmp-summary .cmp-row .v { text-align: right; font-weight: 500; }
  .cmp-specs { border-top: 1px solid var(--line); padding-top: 10px; margin-top: 10px; }
  .cmp-specs-title { font-size: 12px; font-weight: 700; color: var(--ink); margin-bottom: 8px; }
  .cmp-spec-grid { display: grid; grid-template-columns: 1fr; gap: 8px; }
  .cmp-spec-box {
    border: 1px solid var(--line); border-radius: 6px; background: #fafbfc;
    padding: 8px 10px;
  }
  .cmp-spec-box-title { font-size: 11px; font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .4px; margin-bottom: 4px; }
  .cmp-specs .cmp-spec-line { font-size: 12px; color: var(--ink-soft); padding: 2px 0; }
  .cmp-specs .cmp-spec-or { color: var(--muted); font-style: italic; font-size: 11px; padding: 0; }
  #tileSpecs .cmp-specs { border-top: none; padding-top: 0; margin-top: 4px; }
  #tileSpecs .cmp-spec-line {
    font-size: 13px; color: var(--ink); padding: 5px 0 5px 18px; position: relative;
  }
  #tileSpecs .cmp-spec-line::before {
    content: '•'; position: absolute; left: 2px; color: var(--freeman-navy); font-weight: 700;
  }
  #tileSpecs .cmp-spec-line:not(:last-child) { border-bottom: 1px solid var(--line); }
  .pills.compact button {
    padding: 4px 9px;
    font-size: 11px;
  }
  /* Wall Editor shape + remove-tiles pills: equal-size flex items that wrap to
     their own full-width row once the container is too narrow to fit two. */
  .cx-shape-pills button {
    flex: 1 1 160px;
  }
  /* Power Wiring toolbar: 120V/208V and the gear pill sit next to the
     Simple/Max Port Capacity/Custom Wiring (and Full Canvas/This Wall) pills,
     whose longer labels wrap to two lines and end up physically taller. Force
     the same box size here so every pill in the toolbar reads as one family. */
  #powerSourcePills button, #cxPowerSourcePills button,
  #procGearPowerPills button, #cxProcGearPowerPills button {
    min-width: 96px;
    min-height: 35px;
  }
  /* Simple Wiring / Max Port Capacity / Custom Wiring pills, and the Primary /
     Redundant path pills next to them: "Max Port Capacity" is the longest
     label and otherwise forces that one pill wider (and taller, once it
     wraps) than its siblings. Match the .tb-toggle box size (Proc image /
     Port lines, further right in the same toolbar row) so every pill in the
     row — wiring mode, path, and toggles — reads as one uniform-size family. */
  #wiringModePills button, #powerWiringModePills button,
  #cxWiringModePills button, #cxPowerWiringModePills button,
  #procSplitPills button, #cxProcSplitPills button,
  #wiringViewPills button, #cxWiringViewPills button {
    min-width: 124px;
    min-height: 34px;
  }
  .print-only-title { display: none; }
  body.print-visual-mode .print-only-title { display: flex; font-weight: 700; margin-bottom: 12px; }
  /* `.card h2` (0,1,1) sets display:flex for the fold chevron and outranks
     `.print-only-title` (0,1,0), which leaked #visualCard's print heading onto
     the screen — a second "Wall Visual" directly above the tab bar that already
     says it. These two put it back, scoped to an h2 inside a card so the
     Multiple Wall #mwPrintHeader (a div, hidden by its own inline style) is
     untouched. #visualCard is also excluded from folding now (NO_FOLD_CARDS):
     that heading was doubling as its fold control, and a fold control you
     cannot see is the same trap that made a rolled-up Commonly Added Items
     unrecoverable. */
  .card > h2.print-only-title { display: none; }
  body.print-visual-mode .card > h2.print-only-title { display: flex; }

  /* Print Comparison Mode - only shows comparison card */
  body.print-comparison-mode { background: #fff; }
  body.print-comparison-mode * { margin: 0 !important; padding: 0 !important; }
  body.print-comparison-mode header,
  body.print-comparison-mode .brand,
  body.print-comparison-mode footer,
  body.print-comparison-mode .mode-toggle,
  body.print-comparison-mode #inputs,
  body.print-comparison-mode section#inputs,
  body.print-comparison-mode aside { display: none !important; }
  body.print-comparison-mode main.layout { display: block !important; margin: 0 !important; padding: 0 !important; grid-template-columns: 1fr !important; gap: 0 !important; }
  body.print-comparison-mode main.layout > section:not(:has(#comparisonCard)) { display: none !important; }
  body.print-comparison-mode #comparisonCard { display: block !important; grid-column: 1/-1; margin: 0 !important; padding: 0 !important; border: none !important; box-shadow: none !important; }
  body.print-comparison-mode #comparisonCard h2 { display: none !important; }
  /* While measuring (screen) the comparison card uses the same compact 3-up
     layout as the printed page, so the measured height matches what prints. */
  body.print-comparison-mode .cmp-controls-row,
  body.print-comparison-mode .cmp-tb-group,
  body.print-comparison-mode .cmp-tb-label,
  body.print-comparison-mode .cmp-toolbar,
  body.print-comparison-mode .cmp-discount,
  body.print-comparison-mode .pills,
  body.print-comparison-mode #comparisonCard > div:first-child,
  body.print-comparison-mode .cmp-copy-bar,
  body.print-comparison-mode .cmp-card-toprow,
  body.print-comparison-mode [data-cmpmode],
  body.print-comparison-mode [data-cmppitch] { display: none !important; }
  body.print-comparison-mode .cmp-grid {
    display: grid !important;
    grid-template-columns: repeat(3, 1fr) !important;
    gap: 10px !important;
    width: 100% !important;
  }
  /* Measure at the true printable page width with full readable fonts. The
     card fills the page width; we only scale DOWN later if it's too tall. */
  body.print-comparison-mode #comparisonCard {
    position: absolute !important;
    top: 0 !important; left: 0 !important;
    width: var(--cmp-page-w, 730px) !important;
    background: #fff !important;
    transform: scale(var(--cmp-print-scale, 1));
    transform-origin: top center;
    z-index: 9999 !important;
  }

  /* ---- Multiple Wall Configuration ---- */
  .mw-canvas-tabs { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-bottom: 14px; border-bottom: 1px solid var(--line); padding-bottom: 12px; }
  .mw-canvas-tabs button { border: 1px solid var(--line); background: #fff; color: var(--ink-soft); padding: 7px 16px; border-radius: 999px; cursor: pointer; font-size: 13px; font-weight: 600; }
  .mw-canvas-tabs button.active { background: var(--freeman-navy); color: #fff; border-color: var(--freeman-navy); }
  .mw-canvas-tabs button.mw-addcanvas { background: transparent; color: var(--freeman-cyan-dark); border-style: dashed; }
  .mw-wallbar { display: flex; gap: 10px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 14px; }
  .mw-wallbar .field { flex: 0 0 auto; }
  .mw-wallbar .field input, .mw-wallbar .field select { min-width: 130px; }
  .mw-esttotal { border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; background: var(--card, #fff); margin-bottom: 14px; }
  .mw-esttotal-row { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
  .mw-esttotal-title { font-size: 13px; font-weight: 700; color: var(--ink); display: flex; align-items: center; justify-content: center; gap: 8px; flex-wrap: wrap; }
  .mw-esttotal-col { display: flex; flex-direction: column; align-items: center; gap: 2px; }
  .mw-esttotal-val { font-size: 20px; font-weight: 700; color: var(--ink); line-height: 1.1; }
  .mw-esttotal-sub { font-size: 11px; color: var(--muted); margin-top: 2px; }
  .mw-esttotal-prices { display: flex; justify-content: center; gap: 20px; flex-wrap: wrap; }

  /* Left = all data entry, Right = large canvas (canvas gets ~3/4+ of width). */
  /* --mw-col-l so folding the config pane can drop it to a rail (see
     updateRailClasses -> .mw-rail) and hand the width to the canvas. */
  .mw-layout { display: grid; --mw-col-l: 360px; grid-template-columns: var(--mw-col-l) minmax(0, 3fr); gap: 16px; align-items: start; }
  .mw-layout.mw-rail { --mw-col-l: max-content; }
  .mw-left h3 { margin: 14px 0 8px; }
  .mw-left .mw-wallbar { display: block; margin-bottom: 10px; }
  .mw-left .mw-wallbar .field { display: block; margin: 0 0 8px; }
  .mw-left .mw-wallbar .field input, .mw-left .mw-wallbar .field select { width: 100%; min-width: 0; }
  .mw-left .mw-wallbar .mw-btn-row { display: flex; gap: 6px; flex-wrap: wrap; }
  .mw-center .visual-wrap { background: #0e1424; min-height: 600px; }
  .mw-center .visual-wrap svg { max-height: 680px; }
  /* Data/Power/Pixel/Ground Support each render their own top visual
     (.view-panel), which should sit ABOVE the Wall Editor's shape/curve
     controls (#cxShapeCard) and the curvature plan/ISO view
     (#cxPlanSection) — but on the Wall Editor tab itself, the plain wall
     visual (#cxTopVisualBlock) takes that same top slot. #cxCanvasTabs and
     #cxViewTabs stay before all of this (default order 0). See
     cxUpdateEditorVisibility() for the show/hide logic. */
  .mw-center { display: flex; flex-direction: column; }
  #cxTopVisualBlock, .mw-center > .view-panel { order: 1; }
  #cxShapeCard { order: 2; }
  #cxPlanSection { order: 3; }
  body.diagrams-light .mw-center .visual-wrap { background: #ffffff; }
  .mw-canvas-svg-tile { cursor: pointer; }
  @media (max-width: 1180px) { .mw-layout { --mw-col-l: 320px; grid-template-columns: var(--mw-col-l) minmax(0, 2.5fr); } }
  @media (max-width: 900px) { .mw-layout { grid-template-columns: 1fr; } .mw-layout.mw-rail { --mw-col-l: auto; } }

  /* Advance Mode (Advanced Editing / #complexCard): match Simple Mode's
     left-column width (300px, 280px under 1380px) instead of the wider
     360px/320px used by Multiple Wall Configuration's #multiwallCard. */
  #complexCard .mw-layout { --mw-col-l: 300px; grid-template-columns: var(--mw-col-l) minmax(0, 3fr); }
  #complexCard .mw-layout.mw-rail { --mw-col-l: max-content; }
  @media (max-width: 1380px) { #complexCard .mw-layout { --mw-col-l: 280px; } }
  @media (max-width: 1180px) { #complexCard .mw-layout { --mw-col-l: 280px; grid-template-columns: var(--mw-col-l) minmax(0, 2.5fr); } }

  /* Wall Summary spec table (matches the printable sheet). */
  .mw-sum-table { width: 100%; border-collapse: collapse; font-size: 13px; }
  .mw-sum-table th, .mw-sum-table td { padding: 7px 10px; border-bottom: 1px solid var(--line); white-space: nowrap; }
  .mw-sum-table th { text-transform: uppercase; letter-spacing: .5px; font-size: 11px; color: var(--muted); font-weight: 600; text-align: left; }
  .mw-sum-table td.r, .mw-sum-table th.r { text-align: right; }
  .mw-sum-table .mw-pow { font-variant-numeric: tabular-nums; }
  .mw-sum-table .mw-sum-canvas td { background: #eef4fb; color: var(--freeman-navy); font-weight: 700; text-transform: uppercase; letter-spacing: .5px; font-size: 11px; }
  .mw-sum-table .mw-sum-sub td { font-style: italic; color: var(--ink-soft); background: #fafbfc; }
  .mw-sum-table .mw-sum-total td { font-weight: 700; border-top: 2px solid var(--ink); border-bottom: none; }
  .mw-sum-table td.mw-sum-inc { width: 1%; padding-right: 2px; }
  .mw-sum-table tr.mw-sum-excluded td { color: #b7bdc6; text-decoration: line-through; }
  .mw-sum-table tr.mw-sum-excluded td.mw-sum-inc { text-decoration: none; }
  .mw-sum-table tr.mw-sum-common td { white-space: normal; color: var(--muted); font-size: 11.5px; padding-top: 0; padding-bottom: 8px; }
  body.diagrams-light .mw-sum-table .mw-sum-canvas td { background: #eef4fb; }

  /* Print / Save-as-PDF: show the project print header (selected Area +
     project wall count), the per-area rollup (Area info + project grand
     total), and the full multi-area Wall Summary table — everything else in
     Multiple Wall Configuration (canvas visual, wall/area editors, etc.) is
     hidden for this printout. */
  @media print {
    body.print-mw-summary > *:not(main):not(.print-copyright),
    body.print-mw-summary main.layout > *:not(section),
    body.print-mw-summary main.layout > section > .card:not(#multiwallCard) { display: none !important; }
    body.print-mw-summary #multiwallCard > *:not(#mwPrintHeader):not(#mwRollup):not(#mwSummaryBlock):not(#mwGrandTotal) { display: none !important; }
    body.print-mw-summary #multiwallCard { border: none; box-shadow: none; }
    body.print-mw-summary #mwPrintHeader { display: flex !important; }
    body.print-mw-summary #mwRollup { border-top: none; padding-top: 0; }
    body.print-mw-summary #mwSummaryBlock { border-top: 1px solid var(--line); margin-top: 18px; padding-top: 16px; }
    body.print-mw-summary #mwSummaryBlock button,
    body.print-mw-summary #mwSummaryBlock .field { display: none !important; }
    body.print-mw-summary #mwGrandTotal { margin-top: 18px; padding-top: 14px; }
    /* Print / Save-as-PDF for Design/Config Mode's Project Pricing block
       (#cxSummaryBlock, inside #complexCard) — same idea as print-mw-summary
       above, just scoped to #complexCard instead of #multiwallCard. */
    body.print-cx-summary > *:not(main):not(.print-copyright),
    body.print-cx-summary main.layout > *:not(section),
    body.print-cx-summary main.layout > section > .card:not(#complexCard) { display: none !important; }
    body.print-cx-summary #complexCard > *:not(#cxSummaryBlock) { display: none !important; }
    body.print-cx-summary #complexCard { border: none; box-shadow: none; }
    body.print-cx-summary #cxSummaryBlock button,
    body.print-cx-summary #cxSummaryBlock .field { display: none !important; }
    body.print-mw-summary .print-copyright,
    body.print-cx-summary .print-copyright {
      display: block !important;
      position: fixed;
      bottom: 4px;
      left: 0;
      right: 0;
      text-align: center;
      color: #999 !important;
      font-size: 9px !important;
    }
    /* Landscape page is wider but the table's overflow-x:auto wrapper would
       otherwise clip it — let the table lay out at full width and shrink
       the type/padding just enough to fit one page wide without wrapping
       any cell's text. */
    body.print-mw-summary #mwSummaryTable { overflow-x: visible !important; }
    body.print-mw-summary .mw-sum-table { font-size: 10px; }
    body.print-mw-summary .mw-sum-table th,
    body.print-mw-summary .mw-sum-table td { padding: 4px 6px; }
    body.print-mw-summary .mw-sum-table th { font-size: 9px; }
    body.print-mw-summary .mw-sum-table .mw-sum-canvas td { font-size: 9px; }
    body.print-mw-summary .mw-sum-table tr.mw-sum-common td { font-size: 9px; }
    /* Include/exclude checkboxes are a screen-only editing control — the
       printout is a final handoff sheet, not something to interact with. */
    body.print-mw-summary .mw-sum-table .mw-sum-inc { display: none !important; }
    /* Unchecked walls (struck through on screen) are excluded from every
       total on the sheet — printing them anyway is confusing on a handoff
       document, so drop the rows entirely instead of just showing strikethrough. */
    body.print-mw-summary .mw-sum-table tr.mw-sum-excluded { display: none !important; }
  }

  /* ============================================================
     MOBILE / SMALL SCREEN (phones, narrow windows)
     Layout already collapses to one column at 900px; these rules
     make it comfortable and tappable on a phone. Screen-only.
     ============================================================ */
  @media screen and (max-width: 760px) {
    body { font-size: 15px; }

    /* Header: stack logo + title, let the action buttons wrap and
       scroll horizontally instead of squeezing. Un-stick it here (desktop
       keeps the sticky/locked header) — pinned permanently, this header's
       logo+title+button rows eat too much of a phone's short viewport;
       letting it scroll away with the page gives that space back. */
    /* Phone: drop the three-track grid. There is no room to centre anything
       against the full width, so the header stacks — New Design and the logo
       share row one, the title takes row two, and the icon row scrolls. */
    header.brand {
      display: flex;
      flex-wrap: wrap;
      gap: 8px 12px;
      padding: 10px 14px;
      position: static;
    }
    header.brand .hdr-center {
      order: 2;
      flex-basis: 100%;
      justify-content: flex-start;
      gap: 10px;
    }
    header.brand .app-title { font-size: 16px; }
    header.brand h1 {
      order: 3;
      flex-basis: 100%;
      text-align: left;
      font-size: 16px;
    }
    header.brand .actions {
      display: flex;
      width: 100%;
      order: 4;
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      padding-bottom: 2px;
      /* MUST be flex-start here. The desktop rule right-aligns this row, and in
         a nowrap flex container that overflows, `end` alignment pushes the
         overflow off the START edge — where scrolling cannot reach it. On a
         phone that silently hid the first two mode icons at negative x. */
      justify-content: flex-start;
    }
    /* A scrolling row would clip a tooltip hanging below it, and hover is not
       a thing on touch anyway — the aria-label still names each control. */
    header.brand .actions .hdr-tip::after { display: none; }
    header.brand :is(.actions, .hdr-left) button {
      flex: 0 0 auto;
      padding: 9px 12px;
      font-size: 13px;
      white-space: nowrap;
    }

    /* Single column, edge-to-edge with small gutters. */
    main.layout { padding: 8px; gap: 10px; }

    /* Mode tabs: let them wrap to two rows and shrink padding. */
    .mode-toggle .seg {
      flex-wrap: wrap;
      justify-content: center;
      width: 100%;
    }
    .mode-toggle .seg button {
      padding: 9px 14px;
      font-size: 12px;
      flex: 1 1 auto;
    }

    /* Inputs: 16px stops iOS Safari from auto-zooming on focus.
       Bump padding so fields are easy to tap. */
    input[type="number"], input[type="text"], select {
      font-size: 16px;
      padding: 10px 12px;
    }

    /* Bigger tap targets for the pill / toggle / tab rows. */
    .pills { flex-wrap: wrap; }
    .pills button { padding: 9px 12px; font-size: 13px; }
    .unit-toggle { flex-wrap: wrap; }
    .unit-toggle button { padding: 8px 12px; font-size: 13px; }
    .view-tabs button { padding: 11px 6px; font-size: 13px; }
    .icon-btn { padding: 8px 12px; font-size: 12px; }

    /* Visual area: cap height to the viewport so the wall + controls
       fit without a long scroll, and ease the min-height. */
    .visual-wrap { min-height: 0; padding: 10px; }
    .visual-wrap svg { max-height: 60vh; }
    .visual-meta { font-size: 12px; flex-wrap: wrap; gap: 4px 12px; }

    /* Two-up KPI grid stays, but let single full ones breathe. */
    .kpi .value { font-size: 16px; }

    /* Modals: near full-width, scrollable. */
    .gc-modal, .gc-overlay > div { max-width: 96vw !important; }
    .gc-grid { grid-template-columns: 1fr !important; }

    /* Pick list rows: keep columns but tighten. */
    .pick-item { font-size: 12px; gap: 6px; }
  }

  /* Extra-narrow phones (<=420px): drop the four-up comparison grid
     and any 3-column input rows to a single column. */
  @media screen and (max-width: 420px) {
    .row3 { grid-template-columns: 1fr 1fr; }
    header.brand .logo-text { font-size: 20px; }
    .mode-toggle .seg button { padding: 8px 10px; font-size: 11px; }
  }

  .print-copyright { display: none; }
  @page { size: letter portrait; margin: 0.5in; }
  @media print {
    /* Force the light palette regardless of the on-screen theme — a printout
       should always be a white background with dark, readable text (genuine
       color like price pills, brand navy, and colored badges stays intact;
       this only overrides the dark-MODE surface/text colors). The
       `html[data-theme="dark"]` variable block earlier in this file isn't
       itself media-scoped, so without this a printout taken while the app is
       in dark mode kept the dark card/panel backgrounds and light-on-dark
       text — unreadable and ink-heavy on paper. Same values as the
       JPEG/PDF export's force-light-export block. */
    html, html[data-theme="dark"] {
      --ink: #1a1a1a !important;
      --ink-soft: #4a4a4a !important;
      --muted: #777 !important;
      --line: #e3e3e6 !important;
      --bg: #f6f7f9 !important;
      --card: #ffffff !important;
      --navy-ink: #001f5f !important;
    }
    /* Hardcoded (non-variable) dark-mode surfaces that would otherwise still
       leak a dark background into the printout even with the reset above. */
    .cmp-spec-box, .cmp-tint-box, .mw-sum-table .mw-sum-sub td { background: #fafbfc !important; }
    .grand-total { background: #fafbfc !important; }
    .kpi { background: #f6f7f9 !important; }
    .cmp-copy-clear { background: #fff !important; }
    * { margin: 0 !important; padding: 0 !important; }
    body { background: #fff !important; margin: 0 !important; padding: 0 !important; line-height: 1.2; }
    html { margin: 0 !important; padding: 0 !important; }
    header,
    .brand,
    footer,
    .mode-toggle,
    main.layout > section:not(:has(#comparisonCard)),
    main.layout > aside { display: none !important; }
    .print-copyright {
      display: block !important;
      position: fixed;
      bottom: 4px;
      left: 0;
      right: 0;
      text-align: center;
      color: #999 !important;
      font-size: 9px !important;
    }
    main.layout { display: block !important; margin: 0 !important; padding: 0 !important; }
    #inputs { display: none !important; }
    #centerCol { display: block !important; width: 100% !important; max-width: none !important; margin: 0 !important; padding: 0 !important; }
    .card { box-shadow: none !important; border: none !important; break-inside: avoid; page-break-inside: avoid; margin: 0 !important; padding: 0 !important; }
    .card h2 { display: none !important; }
    /* Print the wall visual in light mode: white background, no color box
       around it, and no invert filter (the SVG is re-rendered light for print). */
    .visual-wrap { background: #fff !important; border: none !important; padding: 8px; }
    .visual-wrap svg { filter: none !important; }
    .visual-meta { color: #000 !important; }
    /* Hide the color key (LED tile, wall outline, aspect-ratio) when printing. */
    .legend { display: none !important; }
    #comparisonCard {
      grid-column: 1/-1; margin: 0 auto !important; padding: 0 !important;
      /* Fixed printable width, scaled uniformly (down only) to fit height. */
      width: var(--cmp-page-w, 730px) !important;
      transform: scale(var(--cmp-print-scale, 1));
      transform-origin: top center;
    }
    #comparisonCard h2 { display: none !important; }
    #comparisonCard > div:first-child { display: none !important; }
    .cmp-controls-row { display: none !important; }
    .cmp-tb-group, .cmp-tb-label { display: none !important; }
    .cmp-toolbar { display: none !important; }
    .cmp-discount { display: none !important; }
    .pills { display: none !important; }
    .cmp-copy-bar { display: none !important; }
    .cmp-card-toprow { display: none !important; }
    [data-cmpmode] { display: none !important; }
    [data-cmppitch] { display: none !important; }
    .cmp-grid {
      display: grid;
      grid-template-columns: repeat(3, 1fr) !important;
      gap: 8px !important;
      break-inside: avoid;
      margin: 0 !important;
      padding: 0 !important;
      width: 100% !important;
    }
    /* Cards keep their normal on-screen sizing so they stay legible; the
       whole card is scaled uniformly via --cmp-print-scale to fit one page. */
    .cmp-card {
      border: 1px solid #ccc !important;
      /* Restore inner padding (the global "* {padding:0}" reset above strips
         it, which was clipping text against the card edge). */
      padding: 12px !important;
      page-break-inside: avoid;
      break-inside: avoid;
      box-sizing: border-box;
      overflow: hidden;
      -webkit-print-color-adjust: exact;
      print-color-adjust: exact;
    }
    /* Keep a little breathing room inside the rows so values aren't cut. */
    .cmp-card .cmp-summary { padding-top: 8px !important; margin-top: 8px !important; }
    .cmp-card .cmp-summary .cmp-row { padding: 3px 0 !important; }
    .cmp-card .cmp-price-pill { padding: 8px 10px !important; }
    /* Slightly wider gap so adjacent cards don't crowd. */
    .cmp-grid { gap: 12px !important; padding: 2px !important; }
  }

  /* Processor image legend under the Data wiring diagram (Option 3). */
  .proc-img-legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; align-items: flex-start; }
  .proc-img-card { display: flex; flex-direction: column; align-items: center; gap: 4px; max-width: 360px; flex: 1 1 280px; }
  .proc-img-card img { width: 100%; height: auto; border-radius: 6px; border: 1px solid rgba(15,23,42,0.18); background: #fff; }
  .proc-img-cap { font-size: 12px; font-weight: 700; opacity: 0.85; text-align: center; }

  /* =====================================================================
     DARK MODE — auto-follows the OS / browser setting (prefers-color-scheme).
     Scoped to `screen` so every print / PDF output keeps the light palette.
     Brand navy stays dark where it is a *fill* (white text sits on it); navy
     used as *text* switches to --navy-ink (a lighter blue) so it stays
     legible on a dark background. Placed last so it overrides the base rules.
     ===================================================================== */
  /* Manual theme override: an inline <head> script sets html[data-theme] to the
     stored choice, or the OS preference on first visit. The toggle button flips
     it. (Replaces the former prefers-color-scheme media query so users can pick.) */
  html[data-theme="dark"] {
      --ink: #e8edf6;
      --ink-soft: #b7c0d0;
      --muted: #8b94a6;
      --line: #2b3446;
      --bg: #0e1424;
      --card: #171f33;
      --navy-ink: #6f9bec;
      --shadow: 0 1px 2px rgba(0,0,0,.55), 0 4px 14px rgba(0,0,0,.55);
  }
  /* Text inputs / selects: dark field instead of the light-blue fill. */
  html[data-theme="dark"] input[type="number"],
  html[data-theme="dark"] input[type="text"],
  html[data-theme="dark"] select { background: #10192e; }
  html[data-theme="dark"] input[type="number"]:focus,
  html[data-theme="dark"] input[type="text"]:focus,
  html[data-theme="dark"] select:focus { background: #16233d; }
  /* Controls that hardcode a light surface + variable text. */
  html[data-theme="dark"] .pills button,
  html[data-theme="dark"] .tb-toggle,
  html[data-theme="dark"] .mw-canvas-tabs button { background: var(--card); color: var(--ink-soft); }
  html[data-theme="dark"] .mode-toggle .seg button.active { color: var(--freeman-cyan); border-bottom-color: var(--freeman-cyan); }
  html[data-theme="dark"] .pills button:disabled {
    background: repeating-linear-gradient(-45deg, #1a2238, #1a2238 4px, #141b2e 4px, #141b2e 8px);
  }
  /* Active pill fill (--freeman-navy, #001f5f) barely reads against the dark
     theme's own navy surfaces — same problem .cfg-radio's active state had.
     Use the bright cyan/navy-text combo instead so the selected pill (e.g.
     Aspect Ratio) is actually visible. */
  html[data-theme="dark"] .pills button.active {
    background: var(--freeman-cyan);
    border-color: var(--freeman-cyan);
    color: #001f5f;
  }
  /* Solid-fill action pills (Export Package, LED Designer Tools, icon-btn)
     have no border in light mode because the white page gives them enough
     edge contrast on their own. Against the dark theme's navy page that
     edge disappears, so give them the same outline every other pill has. */
  html[data-theme="dark"] .export-pills button,
  html[data-theme="dark"] .tool-pill,
  html[data-theme="dark"] .icon-btn:not(.ghost) {
    border: 1px solid var(--line);
  }
  /* Wall Summary "AREA X" section header row: was a light-blue (#eef4fb)
     background meant to sit on a white page — on the dark theme that reads
     as a near-white bar. Give it a dark surface with cyan text instead. */
  html[data-theme="dark"] .mw-sum-table .mw-sum-canvas td {
    background: #1a2438;
    color: var(--freeman-cyan);
  }
  /* Hover surfaces (were light #f1f2f5). */
  html[data-theme="dark"] .tb-toggle:hover,
  html[data-theme="dark"] .icon-btn.ghost:hover,
  html[data-theme="dark"] .mw-canvas-tabs button:hover { background: #1f2942; }
  /* Subtle off-white panels (#fafbfc). */
  html[data-theme="dark"] .grand-total,
  html[data-theme="dark"] .mw-sum-table .mw-sum-sub td,
  html[data-theme="dark"] .cmp-tint-box,
  html[data-theme="dark"] .cmp-spec-box { background: #131b2d; }
  /* Summary KPI tiles — slightly lifted surface so they read as tiles. */
  html[data-theme="dark"] .kpi { background: #1e2740; }
  /* Price Comparison product cards (were #fff). The coloured price pills
     inside them stay light on purpose — they read as callout chips. */
  html[data-theme="dark"] .cmp-card,
  html[data-theme="dark"] .cmp-copy-clear { background: var(--card); }
  /* Config radio pills: cyan fill reads better than navy-on-dark, and the
     active label flips to navy text for contrast. */
  html[data-theme="dark"] .cfg-radio button { color: var(--ink); }
  html[data-theme="dark"] .cfg-radio button.active {
    background: var(--freeman-cyan);
    border-color: var(--freeman-cyan);
    color: #001f5f;
  }
  /* Topology picker: lift the neutral run stroke and use the bright navy-ink
     for arrows. The active tile stays a cyan fill with dark strokes. */
  html[data-theme="dark"] .topo-btn .tg-run { stroke: #6b7789; }
  html[data-theme="dark"] .topo-btn .tg-arrow { stroke: var(--navy-ink); }
  /* Same specificity as the two rules above but later in the file, so the
     active tile has to restate itself or it loses its navy strokes and sits
     grey-on-cyan. */
  html[data-theme="dark"] .topo-btn.active .tg-run,
  html[data-theme="dark"] .topo-btn.active .tg-arrow { stroke: var(--freeman-navy); }
  html[data-theme="dark"] .topo-btn:hover:not(:disabled) { background: #1f2942; }
  html[data-theme="dark"] .topo-pill .tg-run { stroke: #6b7789; }
  html[data-theme="dark"] .topo-pill .tg-arrow { stroke: var(--navy-ink); }

  /* =====================================================================
     FORCE-LIGHT EXPORT — applied to <html> only while capturing an export
     (Export Package JPEGs/complete package, Print/Save Package as PDF, LED
     Design Documentation). Unlike the print stylesheet above (which only
     applies to an actual browser print), these captures snapshot the live
     on-screen DOM/SVGs via canvas, so a dark OS/browser theme would otherwise
     bake a dark background into the exported image. Every declaration here
     mirrors the light (non-dark) values with !important so it wins over the
     `prefers-color-scheme: dark` block above regardless of the OS setting.
     ===================================================================== */
  html.force-light-export {
    --ink: #1a1a1a !important;
    --ink-soft: #4a4a4a !important;
    --muted: #777 !important;
    --line: #e3e3e6 !important;
    --bg: #f6f7f9 !important;
    --card: #ffffff !important;
    --navy-ink: #001f5f !important;
    --shadow: 0 1px 2px rgba(15,23,42,.06), 0 4px 12px rgba(15,23,42,.06) !important;
  }
  html.force-light-export input[type="number"],
  html.force-light-export input[type="text"],
  html.force-light-export select { background: #fff !important; }
  html.force-light-export .pills button,
  html.force-light-export .tb-toggle,
  html.force-light-export .mw-canvas-tabs button { background: #f1f2f5 !important; color: #4a4a4a !important; }
  html.force-light-export .mode-toggle .seg { background: none !important; }
  html.force-light-export .grand-total,
  html.force-light-export .mw-sum-table .mw-sum-sub td { background: #fafbfc !important; }
  html.force-light-export .kpi { background: #f6f7f9 !important; }
  html.force-light-export .cmp-card,
  html.force-light-export .cmp-copy-clear { background: #fff !important; }
  html.force-light-export body { background: #f6f7f9 !important; }
  /* Ask-the-Calculator chatbot widget (v1, rule-based, offline) */
  #askBotBubble { position:fixed; bottom:10px; right:20px; width:66px; height:66px; border-radius:50%;
    border:1px solid var(--line-strong); background:var(--accent,#0b5cab); color:#fff; font-size:14px; font-weight:bold;
    cursor:pointer; box-shadow:0 3px 10px rgba(0,0,0,.3); z-index:2300; }
  #askBotBubble:hover { opacity:.9; }
  @media print {
    #askBotBubble, .askbot-panel { display: none !important; }
  }
  /* Design & Config mode hides the Ask bubble by default (it can crowd the
     Wall Editor canvas) — the "Ask assistant" pill in that toolbar adds
     .show-askbot back to reveal it. Other modes are unaffected. */
  body.mode-complex:not(.show-askbot) #askBotBubble,
  body.mode-complex:not(.show-askbot) .askbot-panel { display: none !important; }
  .askbot-panel { position:fixed; bottom:84px; right:20px; width:360px; max-width:calc(100vw - 32px); max-height:72vh;
    background:var(--card); border:1px solid var(--line-strong); border-radius:8px; box-shadow:0 8px 24px rgba(0,0,0,.3);
    z-index:2300; display:flex; flex-direction:column; overflow:hidden; }
  .askbot-head { background:var(--accent,#0b5cab); color:#fff; padding:10px 12px; font-weight:bold; font-size:13px;
    display:flex; justify-content:space-between; align-items:center; }
  .askbot-head-btns { display:flex; align-items:center; gap:10px; }
  .askbot-head button { background:none; border:none; color:#fff; font-size:18px; cursor:pointer; line-height:1; }
  .askbot-head-btns button[aria-label="Clear conversation"] { font-size:12px; font-weight:normal; opacity:.85; }
  .askbot-head-btns button[aria-label="Clear conversation"]:hover { opacity:1; text-decoration:underline; }
  .askbot-log { flex:1; overflow-y:auto; padding:10px; font-size:13px; background:var(--bg); color:var(--ink); min-height:120px; }
  .askbot-msg { margin-bottom:10px; }
  .askbot-msg.user { text-align:right; }
  .askbot-msg.user .askbot-bubble { background:var(--accent,#0b5cab); color:#fff; }
  .askbot-bubble { display:inline-block; padding:8px 10px; border-radius:8px; background:var(--card); color:var(--ink); border:1px solid var(--line);
    max-width:90%; text-align:left; white-space:pre-line; }
  .askbot-suggest { display:inline-block; margin:3px 4px 0 0; padding:4px 8px; border:1px solid var(--line-strong);
    border-radius:12px; background:var(--card); color:var(--ink); font-size:12px; cursor:pointer; }
  .askbot-suggest:hover { background:rgba(0,189,246,.12); }
  .askbot-suggest.cat { background:var(--accent,#0b5cab); color:#fff; border-color:var(--accent,#0b5cab); font-weight:600; }
  .askbot-suggest.cat:hover { opacity:.9; }
  .askbot-suggest.home { background:transparent; color:var(--accent,#0b5cab); border-style:dashed; }
  .askbot-form { display:flex; border-top:1px solid var(--line); padding:8px; gap:6px; background:var(--card); }
  .askbot-form input { flex:1; border:1px solid var(--line); border-radius:4px; padding:6px 8px; font-size:13px; background:var(--card); color:var(--ink); }
  .askbot-form button { border:1px solid var(--line-strong); background:var(--card); color:var(--ink); border-radius:4px; padding:6px 12px; font-size:13px; cursor:pointer; }
  @media (max-width: 640px) {
    .askbot-panel { right:12px; left:12px; width:auto; bottom:70px; }
    #askBotBubble { right:16px; }
  }

  /* =====================================================================
     Streamlined toolbar: labeled buttons with icons, mode buttons styled
     like the primary "New Design" pill, plus icon-only utility buttons,
     theme toggle and the user account menu. Added 2026-07-30.
     ===================================================================== */
  header.brand :is(.actions, .hdr-left) button.btn-ico {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    white-space: nowrap;
  }
  header.brand :is(.actions, .hdr-left) button.btn-ico svg { flex: 0 0 auto; }

  /* Pricing / Design read like the primary New Design button. The active mode
     is a solid cyan pill; the inactive one is an outline so the current mode
     is always obvious. */
  header.brand .actions button.mode-btn {
    background: transparent;
    border: 1px solid var(--freeman-cyan);
    color: var(--freeman-cyan);
    font-weight: 600;
  }
  header.brand .actions button.mode-btn.active {
    background: var(--freeman-cyan);
    border-color: var(--freeman-cyan);
    color: #000;
  }
  header.brand .actions button.mode-btn:hover { background: rgba(0,189,246,.18); }
  header.brand .actions button.mode-btn.active:hover { background: var(--freeman-cyan-dark); }
  /* The three mode pills sit together as one segment. */
  header.brand .actions #modeSeg { display: inline-flex; gap: 8px; }

  /* ---- Icon-only header controls + hover labels -------------------------
     The mode segment and the tool buttons are icons; their text still lives in
     the markup (a <span> for the label, aria-label for assistive tech) but is
     hidden here and surfaced on hover through .hdr-tip instead. */
  header.brand .actions button.hdr-ico {
    width: 36px;
    height: 36px;
    padding: 0;
    border-radius: 50%;
    justify-content: center;
    gap: 0;
  }
  header.brand .actions button.hdr-ico > span { display: none; }
  header.brand .actions button.hdr-ico svg { flex: 0 0 auto; }

  /* Hover/focus label. Uses data-tip rather than the native title attribute so
     there is exactly ONE tooltip — a title= would pop up alongside this one. */
  .hdr-tip { position: relative; }
  .hdr-tip::after {
    content: attr(data-tip);
    position: absolute;
    top: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    background: #10151f;
    color: #fff;
    border: 1px solid #2c3444;
    border-radius: 6px;
    padding: 5px 9px;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity .12s ease;
    z-index: 300;
  }
  .hdr-tip:hover::after,
  .hdr-tip:focus-visible::after { opacity: 1; }

  /* Laptop widths: the centre block can only stay truly centred while BOTH 1fr
     tracks are at least as wide as the icon row, so tightening the icons keeps
     symmetric centring down to roughly 1250px instead of ~1380px. Below that
     the centre still yields rather than the controls wrapping. */
  @media screen and (max-width: 1400px) {
    header.brand { gap: 12px; padding: 12px 16px; }
    header.brand .actions { gap: 6px; }
    header.brand .actions #modeSeg { gap: 6px; }
    header.brand .actions button.hdr-ico,
    header.brand .actions button.icon-btn { width: 32px; height: 32px; }
    header.brand .hdr-center { gap: 12px; }
  }
  /* The right-most icons would push their label off the viewport edge, so the
     last few anchor their tooltip to the right instead of centring it. */
  header.brand .actions > .user-menu .hdr-tip::after,
  header.brand .actions > .hdr-tip:nth-last-child(-n+3)::after {
    left: auto;
    right: 0;
    transform: none;
  }
  /* Diagonal strike through the $ icon whenever pricing is hidden — in any mode,
     including a Pricing-Mode blackout toggled from the icon itself. */
  #priceModeBtn .price-slash { opacity: 0; }
  #priceModeBtn.price-hidden .price-slash { opacity: 1; }

  /* ---- Configuration Mode: hide every pricing surface --------------------
     Driven by `body.hide-pricing` (set in setToolMode / togglePriceIcon). Whole
     cards and rows are removed rather than blanked, so the layout reads as "this
     build has no pricing" instead of a column of em-dashes. fmt$() separately
     returns a placeholder, which covers anything reaching an export table. */
  body.hide-pricing #estTotalCard,
  body.hide-pricing #posterRateCard,
  body.hide-pricing #mwEstTotal,
  body.hide-pricing #mwGrandTotal,
  body.hide-pricing #grandTotalRow,
  body.hide-pricing .grand-total,
  body.hide-pricing .mw-esttotal,
  body.hide-pricing .cmp-price-pill,
  body.hide-pricing .cmp-added-items,
  body.hide-pricing .pick-item .price,
  body.hide-pricing .pick-head .price,
  body.hide-pricing #discountApprovalNote,
  body.hide-pricing #cxDiscountApprovalNote,
  body.hide-pricing #cmpDiscountApprovalNote,
  body.hide-pricing #mwEstDiscountApprovalNote { display: none !important; }

  /* Thin divider between the primary actions and the icon utilities. */
  header.brand .actions .actions-sep {
    width: 1px;
    align-self: stretch;
    margin: 2px 2px;
    background: #555;
  }

  /* Theme toggle: the icon is injected by applyColorTheme() from THEME_ICONS
     (auto / light / dark), so there is no CSS icon swap any more. */
  #themeToggleBtn svg { display: block; }

  /* User account menu (top-right). */
  header.brand .actions .user-menu { position: relative; }
  header.brand .actions button.user-avatar {
    background: var(--freeman-cyan);
    border-color: var(--freeman-cyan);
    color: #001f5f;
  }
  header.brand .actions button.user-avatar:hover { background: var(--freeman-cyan-dark); }
  .user-dropdown {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    min-width: 232px;
    background: var(--card);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 6px;
    z-index: 1100;
  }
  .user-dropdown[hidden] { display: none; }
  .user-dropdown .ud-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px 10px;
    border-bottom: 1px solid var(--line);
    margin-bottom: 6px;
  }
  .user-dropdown .ud-avatar {
    width: 38px; height: 38px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(0,189,246,.15); color: var(--freeman-cyan); flex: 0 0 auto;
  }
  .user-dropdown .ud-name { font-weight: 700; font-size: 14px; color: var(--ink); }
  .user-dropdown .ud-email { font-size: 12px; color: var(--muted); }
  /* Scoped under header.brand .actions so these beat the generic
     `header.brand .actions button` styling (which would otherwise give the
     menu buttons a border + white text, invisible on the light menu). */
  header.brand .actions .user-dropdown .ud-item {
    display: block; width: 100%; text-align: left;
    background: none; border: none; border-radius: var(--radius-sm);
    padding: 9px 10px; font: inherit; font-size: 13px; color: var(--ink);
    cursor: pointer; text-decoration: none;
  }
  header.brand .actions .user-dropdown .ud-item:hover { background: rgba(0,189,246,.12); }
  .user-dropdown .ud-sep { height: 1px; background: var(--line); margin: 6px 4px; }
  header.brand .actions .user-dropdown .ud-danger { color: var(--warn); }

  /* =====================================================================
     Generic in-page modal (Scoping Questions) + blurred backdrop shared
     with the Generic Calculator overlay so the app frame recedes behind it.
     ===================================================================== */
  .gc-overlay.open,
  .app-overlay.open {
    backdrop-filter: blur(5px);
    -webkit-backdrop-filter: blur(5px);
  }
  .app-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.55);
    z-index: 1000;
    padding: 24px;
    overflow-y: auto;
  }
  .app-overlay.open { display: flex; align-items: flex-start; justify-content: center; }
  .app-modal {
    background: var(--card);
    color: var(--ink);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    width: 100%;
    max-width: 900px;
    margin: auto;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 48px);
  }
  .app-modal-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--line);
  }
  .app-modal-head h2 { margin: 0; font-size: 18px; color: var(--navy-ink); }
  .app-modal-actions { display: flex; align-items: center; gap: 10px; }
  .app-modal-actions .am-btn {
    background: var(--freeman-cyan); color: #001f5f; border: none;
    border-radius: 999px; padding: 7px 16px; font-weight: 700; font-size: 13px; cursor: pointer;
  }
  .app-modal-actions .am-btn:hover { background: var(--freeman-cyan-dark); }
  .app-modal-actions .am-close {
    background: none; border: none; font-size: 26px; line-height: 1;
    color: var(--muted); cursor: pointer; padding: 0 4px;
  }
  .app-modal-actions .am-close:hover { color: var(--ink); }
  .app-modal-body { padding: 18px 22px 24px; overflow-y: auto; }

  /* Scoping Questions form (rendered into .app-modal-body). */
  #scopingModalBody h2 {
    font-size: 13px; color: #fff; background: var(--freeman-navy);
    padding: 6px 10px; border-radius: 4px; margin: 16px 0 10px;
  }
  #scopingModalBody .q { margin-bottom: 12px; }
  #scopingModalBody .qtext { font-weight: 700; margin-bottom: 5px; color: var(--navy-ink); }
  #scopingModalBody .opts { display: flex; flex-wrap: wrap; gap: 6px 18px; }
  #scopingModalBody .opt { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
  #scopingModalBody .opt input[type="checkbox"] { width: 14px; height: 14px; margin: 0; accent-color: #fff; }
  #scopingModalBody .fillinput,
  #scopingModalBody .other {
    width: 100%; font-family: inherit; font-size: 13px; color: var(--ink);
    background: var(--card); border: 1px solid var(--line); border-radius: 6px; padding: 6px 8px;
  }
  #scopingModalBody .fillinput:focus,
  #scopingModalBody .other:focus { outline: none; border-color: var(--freeman-cyan); box-shadow: 0 0 0 2px rgba(0,189,246,.2); }
  #scopingModalBody textarea.fillinput { resize: vertical; }
  #scopingModalBody .other { margin-top: 6px; }
  #scopingModalBody .entry { display: block; font-size: 12px; color: var(--muted); margin-bottom: 6px; }
  #scopingModalBody .entry .fillinput { margin-top: 4px; }
  #scopingModalBody .entry + .opts::before { content: "or select:"; font-size: 12px; color: var(--muted); margin-right: 4px; align-self: center; }
  #scopingModalBody .note { font-size: 12px; color: var(--muted); margin-top: 18px; border-top: 1px solid var(--line); padding-top: 8px; }

  /* Collaborative shared-design banner (shown by 90-app.js while editing a share). */
  #shareBanner {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    flex-wrap: wrap; padding: 8px 14px; font-size: 13px;
    background: rgba(0,189,246,.12); color: var(--ink);
    border-bottom: 1px solid var(--freeman-cyan, #00bdf6);
  }
  #shareBanner .share-banner-actions { display: flex; gap: 8px; flex: 0 0 auto; }
  #shareBanner button {
    border: 1px solid var(--freeman-cyan, #00bdf6); background: var(--freeman-cyan, #00bdf6);
    color: #001f5f; border-radius: 999px; padding: 5px 14px; font: inherit; font-size: 12px;
    font-weight: 700; cursor: pointer; white-space: nowrap;
  }
  #shareBanner button#shareRevertBtn { background: transparent; color: var(--freeman-cyan, #00bdf6); }
  #shareBanner button:hover { opacity: .9; }
  @media print { #shareBanner { display: none !important; } }

/* Aspect-ratio box pin grid: 3x3 dots after the ratio pills. */
.aspect-pos { display: inline-grid; grid-template-columns: repeat(3, 8px); gap: 2px; margin-left: 8px; padding: 2px; border: 1px solid var(--line, #d0d7e2); border-radius: 4px; vertical-align: middle; }
.aspect-pos button { width: 8px; height: 8px; padding: 0; border: 1px solid #b8c2d1; border-radius: 2px; background: transparent; cursor: pointer; }
.aspect-pos button:hover { border-color: #ffb500; }
.aspect-pos button.active { background: #ffb500; border-color: #ffb500; }
