/* TrimOut app chrome.
 *
 * HISTORY, CORRECTED AT THE CLAIM (24d, 2026-08-24): this file began as a
 * verbatim copy of site/trimout-app-screens.html's style block, under a
 * nothing-redesigned law that kept three agents' markup and one stylesheet
 * from drifting. Rex then ruled on the LOOK itself ("It feels like I'm
 * filling in a Word document... I want it to look clean and attractive
 * rather than cold and standard") and ratified the paint-job direction:
 * SAME STRUCTURE, NEW SKIN. So the copy law is dead and the CLASS
 * CONTRACT is the whole law now:
 *
 *   card  btn  btn g  btn sm  btn w  field  row  spread  pill p-ok p-warn
 *   p-bad p-mute  note  note w  note b  drop  steps  kpi  q  opts  opt  lic
 *   dots  narrow  top  tabs  who  av
 *
 * EVERY SELECTOR AND CLASS NAME IN THIS FILE IS FROZEN; only values may
 * change. A class renamed here is a screen that silently loses its
 * styling, and the screen agent who wrote the markup will be looking for
 * the bug in his own template. New rules go at the bottom under their own
 * comment, as they always did. tools-side: the 24d deploy note carries a
 * scripted before/after selector diff proving nothing was removed.
 *
 * THE SKIN (24d, the ratified mock): slate ink and porcelain neutrals, a
 * copper accent (the trade's own material) for primary actions and the
 * current step, green kept as the one color of progress, Bricolage
 * Grotesque for headings and Instrument Sans for body, hairline borders
 * traded for soft shadows on cards. Fonts load from Google Fonts in
 * base.html; every face declares a full system fallback stack, so the
 * 401 gate and an offline box render fine without the fetch.
 *
 * ---------------------------------------------------------------------------
 * THE SKIN AGAIN (DEC-123, 2026-08-29, Rex on the TrimOut Look canvas): "Use
 * the new website template we created." It is THE FIELD BOOK, and it lands
 * here as values only, which is what makes it a skin rather than a rebuild:
 * every selector, every class name and every rule below is the one that was
 * here before the change.
 *
 *   paper #f7f1e3 for the page, #fdf8ee for cards, #efe6d2 for panels
 *   ink #2b2620, muted #77705f, dashed dividers #d9cdb2
 *   green #3f7d51 for done, which is the same job green already had
 *   Lora for headings, Nunito Sans for body, IBM Plex Mono for figures
 *
 * BUTTONS ARE DEEP TEAL #3d7068 AND NEVER ORANGE. Rex rejected orange
 * buttons twice, including the accent dot on the Field Book board itself, so
 * `--accent` - which is what `.btn` paints with - is teal. TERRACOTTA
 * #c24d2f SURVIVES AS A SMALL TEXT ACCENT AND NOWHERE ELSE: it is `--said`,
 * it names the line the punch list is asking him about, and it appears in no
 * `background` declaration in this file. `tests/test_29f_the_field_book.py`
 * holds both halves of that as a lint.
 *
 * ---------------------------------------------------------------------------
 * THE SKIN AGAIN, AGAIN (Order 2026-09-02b, THE DOORS AND THE PAINT, ruling
 * 4): "The app should follow the same color and flow as the main website."
 * DEC-123's Field Book paper is retired - it lasted one order - and the
 * values below are lifted from site/index.html's own :root, the same way
 * the Field Book's were lifted from the TrimOut Look canvas. TEAL STAYS:
 * `--accent`/`--blue` were already #3d7068, so the never-orange rule and
 * every test that pins it carry forward untouched. What actually moves:
 *
 *   white page #ffffff, cool-white alternate #f5f8f7 (was cream #f7f1e3/
 *   #fdf8ee/#efe6d2, all three collapsed to one pair), ink #17211f (was
 *   warm black #2b2620), line/rule #e2e8e6 (was dashed tan #d9cdb2),
 *   muted #6f7b79, body copy #46514f
 *   Manrope 700/800 for headings (was Lora), Source Sans 3 for body (was
 *   Nunito Sans), IBM Plex Mono unchanged for figures
 *   buttons keep their 48px height but take the site's 10px radius, not a
 *   pill; the secondary button's ink border is 1.5px, matching the site
 *   cards keep 14px radius and the site's own soft shadow
 *   warn #8a6d1f and bad #9c3b22 are unchanged - already not orange, so
 *   there was nothing to fix
 *
 * `--said` (terracotta #c24d2f) is UNCHANGED AND STILL TEXT-ONLY: the site
 * has no equivalent of a "this is the line we are asking about" accent, so
 * there is no site value to adopt in its place, and the alternative
 * (folding it into `--teal-ink`) would erase the one signal a dealer has for
 * which punch-list line is open. It keeps its own custom property and its
 * own lint in test_29f, both untouched by this order.
 * ------------------------------------------------------------------------- */

  :root{
    --ink:#17211f; --blue:#3b42ff; --blue-d:#1d73e8; --muted:#636b8a;
    --rule:#e5e7f2; --bg:#ffffff; --tint:#f7f8fc; --mid:#414a5e;
    --accent:#3b42ff;
  --mark-block:#1A5FA8; --accent-d:#1d73e8; --accent-bg:#eef0ff;
    --ok:#2f7d4f; --okbg:#e3f1e7; --warn:#8a6d1f; --warnbg:#fbf3df;
    --bad:#9c3b22; --badbg:#f6e6df;
    --shadow:0 6px 24px rgba(23,33,31,.06);
    /* the third paper, for panels and rails. Same value as --tint now that
       the site's paint has only one alternate to the white page; the name
       stays separate so no rule below this line changes shape. */
    --panel:#f7f8fc;
    /* the ONE place terracotta is allowed: small text, never a control. */
    --said:#c24d2f;
  }
  *{box-sizing:border-box;margin:0;padding:0}
  body{background:var(--tint);color:var(--ink);
    font:17px/1.6 "Source Sans 3",Verdana,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif}
  h1{font:800 32px/1.15 Manrope,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    letter-spacing:-.02em;margin-bottom:6px}
  h2{font:700 21px/1.25 Manrope,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    letter-spacing:-.02em;margin-bottom:4px}
  h3{font:700 16px/1.3 Manrope,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    letter-spacing:-.02em;margin-bottom:5px}
  p{color:var(--mid)}
  .sub{color:var(--muted);font-size:14.5px;margin-bottom:22px}
  a{color:var(--blue)}
  code{font:12.5px "IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;background:var(--tint);
    padding:1px 5px;border-radius:3px;border:1px solid var(--rule)}


  /* app chrome. Order 2026-09-02b: the header matches the site's - white,
     sticky, a 1px bottom line, 64px tall - not the Field Book's panel-tinted
     bar with a 2px rule. */
  .top{background:var(--bg);border-bottom:1px solid var(--rule);box-shadow:none;
    position:sticky;top:0;z-index:60}
  .top .in{max-width:1080px;margin:0 auto;padding:0 24px;height:64px;display:flex;
    align-items:center;gap:22px}
  .brand{display:flex;align-items:center;gap:10px;
    font:800 22px/1 Manrope,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    letter-spacing:-.02em;text-decoration:none;color:var(--ink)}
  .tabs{display:flex;gap:20px;margin-left:14px}
  .tabs a{color:var(--muted);text-decoration:none;font-size:15px;font-weight:700;
    padding:20px 0;border-bottom:3px solid transparent}
  .tabs a.cur{color:var(--ink);border-bottom-color:var(--accent)}
  .who{margin-left:auto;display:flex;align-items:center;gap:9px;font-size:13.5px;color:var(--mid)}
  .av{width:29px;height:29px;border-radius:50%;background:var(--ink);color:var(--bg);
    display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:680}
  main{max-width:1080px;margin:0 auto;padding:28px 24px 80px}
  .narrow{max-width:460px;margin:0 auto;padding:56px 24px 80px}

  /* bits. Order 2026-09-02b: the card border drops to the site's 1px line
     and the shadow is the site's own soft one (set on --shadow above); the
     radius was already 14px and needed no change. */
  .card{background:var(--bg);border:1px solid var(--rule);border-radius:14px;
    padding:24px 26px;margin-bottom:16px;box-shadow:var(--shadow)}
  .card.pad0{padding:0;overflow:hidden}
  /* A row's actions sheet must escape the card; :has flips the clip only
     while a menu is open, so the rounded table corners keep their trim
     the rest of the time (found on the 08-12 browser pass: the sheet
     rendered clipped to a sliver). */
  .card.pad0:has(.menu[open]){overflow:visible}
  /* Buttons keep their 48px-tall touch target but take the site's 10px
     radius, not a pill; `.btn.sm` overrides the height back down since the
     site never scaled its small buttons up. */
  .btn{display:inline-flex;align-items:center;justify-content:center;
    height:48px;background:var(--accent);color:var(--bg);border:0;
    border-radius:10px;padding:0 26px;font:inherit;font-size:15px;
    font-weight:700;cursor:pointer;text-decoration:none}
  .btn:hover{background:var(--accent-d)}
  .btn.g{background:var(--bg);color:var(--ink);border:1.5px solid var(--ink)}
  .btn.g:hover{background:var(--tint)}
  .btn.sm{height:36px;padding:0 16px;font-size:13px}
  .btn.w{width:100%;text-align:center}
  .btn[disabled]{opacity:.45;cursor:not-allowed}
  .field{margin-bottom:14px}
  .field label{display:block;font-size:12.5px;font-weight:640;color:var(--mid);margin-bottom:5px}
  .field input,.field select,.field textarea{width:100%;font:inherit;font-size:15px;
    padding:10px 12px;border:1.5px solid var(--rule);border-radius:9px;background:var(--bg);color:var(--ink)}
  .field input:focus,.field select:focus{outline:0;border-color:var(--accent)}
  .row{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
  .spread{display:flex;justify-content:space-between;align-items:flex-start;gap:16px}
  table{width:100%;border-collapse:collapse;font-size:13.5px}
  th,td{text-align:left;padding:10px 12px;border-bottom:1.5px dashed var(--rule);vertical-align:middle}
  th{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);
    font-weight:700;background:var(--tint)}
  tr:last-child td{border-bottom:0}
  td input,td select{width:100%;font:inherit;font-size:13.5px;padding:5px 8px;
    border:1.5px solid transparent;border-radius:5px;background:transparent;color:var(--ink)}
  td input:hover,td select:hover{border-color:var(--rule);background:var(--bg)}
  td input:focus,td select:focus{outline:0;border-color:var(--accent);background:var(--bg)}
  td input.edited{background:var(--accent-bg);border-color:var(--accent);font-weight:600}
  .pill{display:inline-block;font-size:11px;font-weight:700;padding:2px 8px;border-radius:999px;
    letter-spacing:.02em;white-space:nowrap}
  .p-ok{background:var(--okbg);color:var(--ok)}
  .p-warn{background:var(--warnbg);color:var(--warn)}
  .p-bad{background:var(--badbg);color:var(--bad)}
  .p-mute{background:var(--tint);color:var(--muted)}
  .note{background:var(--tint);border-left:3px solid var(--blue);padding:12px 16px;
    border-radius:0 9px 9px 0;font-size:14px;margin:14px 0;color:var(--mid)}
  .note b{color:var(--ink)}
  .note.w{background:var(--warnbg);border-left-color:var(--warn)}
  .note.b{background:var(--badbg);border-left-color:var(--bad)}
  .drop{border:2px dashed var(--rule);border-radius:12px;padding:32px;text-align:center;
    background:var(--tint);color:var(--muted);font-size:14px}
  .steps{display:flex;gap:0;margin-bottom:22px;background:#fff;border:1px solid var(--rule);
    border-radius:9px;overflow:hidden}
  .steps div,.steps a{flex:1;padding:11px 14px;font-size:12.5px;font-weight:600;color:var(--muted);
    border-right:1px solid var(--rule);display:flex;gap:8px;align-items:center;text-decoration:none}
  .steps div:last-child,.steps a:last-child{border-right:0}
  .steps div.done,.steps a.done{color:var(--ok);background:#FAFDFB}
  .steps a:hover{background:var(--tint);color:var(--ink)}
  .steps div.cur{color:var(--ink);background:var(--accent-bg);box-shadow:inset 0 -2px 0 var(--accent)}
  .steps i{font-style:normal;width:18px;height:18px;border-radius:50%;background:var(--rule);
    color:var(--muted);font-size:10.5px;display:flex;align-items:center;justify-content:center;
    font-weight:700}
  .steps .done i{background:var(--ok);color:#fff}
  .steps .cur i{background:var(--accent);color:#fff}
  /* 22i item 2: the progress bar. It sits ABOVE the steps because the steps
     are the thing it measures, and it is deliberately thin - Rex ruled a
     coarse step bar with the facts ratio inside the Questions step, not a
     dashboard. `width` is the CLAMPED number; the true one never leaves
     run.json. */
  .prog{height:6px;background:var(--rule);border-radius:99px;overflow:hidden;
        margin-bottom:10px}
  .prog .fill{height:100%;background:var(--accent);border-radius:99px;
              transition:width .35s ease}
  .prog .fill.done{background:var(--ok)}
  /* The ratio inside one step. Muted and small: it is the inside of a step,
     never the outer number, and it must not read as the headline. */
  .steps .facts{display:block;margin-top:5px;font-size:11px;font-weight:500;
                color:var(--muted);line-height:1.35}
  .steps .fbar{display:block;height:3px;background:var(--rule);
               border-radius:99px;overflow:hidden;margin-bottom:3px}
  .steps .fbar span{display:block;height:100%;background:var(--muted);
                    border-radius:99px}
  .menu{position:relative;display:inline-block}
  .menu>summary{list-style:none;cursor:pointer;padding:4px 10px;border-radius:7px;
    font-weight:700;color:var(--muted);user-select:none}
  .menu>summary::-webkit-details-marker{display:none}
  .menu>summary:hover{background:var(--tint);color:var(--ink)}
  .menu[open]>summary{background:var(--tint)}
  .menu .sheet{position:absolute;right:0;top:100%;z-index:30;background:var(--bg);
    border:1.5px solid var(--rule);border-radius:12px;box-shadow:0 10px 28px rgba(43,38,32,.18);
    min-width:210px;padding:6px;white-space:normal;text-align:left}
  .menu .sheet .rename input{display:block}
  .menu .sheet form{margin:0}
  .menu .sheet button,.menu .sheet a{display:block;width:100%;text-align:left;background:none;
    border:0;padding:8px 12px;font-size:13.5px;border-radius:6px;cursor:pointer;color:var(--ink);
    text-decoration:none}
  .menu .sheet button:hover,.menu .sheet a:hover{background:var(--tint)}
  .menu .sheet button.danger{color:var(--bad)}
  /* 16f item 10a: the rename FIELDS live behind the rename CONTROL. The
     summary is styled as one more menu row so the closed menu reads as a
     list of actions rather than as a list with a form sitting open in
     it; the fields keep the padded, ruled-off block they had. */
  .menu .sheet details.rename{margin-top:4px;border-top:1px solid var(--rule)}
  .menu .sheet details.rename > summary{display:block;padding:8px 12px;font-size:13.5px;
    border-radius:6px;cursor:pointer;color:var(--ink);list-style:none}
  .menu .sheet details.rename > summary::-webkit-details-marker{display:none}
  .menu .sheet details.rename > summary:hover{background:var(--tint)}
  .menu .sheet .rename form{padding:0 12px 8px}
  .menu .sheet .rename input{width:100%;border:1px solid var(--rule);border-radius:7px;
    padding:7px 9px;font-size:13px;margin-bottom:6px}
  .kpi{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:18px}
  .kpi div{background:var(--bg);border:1.5px solid var(--rule);border-radius:12px;
    padding:15px 17px;box-shadow:var(--shadow)}
  .kpi.five{grid-template-columns:repeat(5,1fr)}
  .kpi b{display:block;font:700 23px/1.2 "IBM Plex Mono",ui-monospace,Menlo,
    Consolas,monospace;letter-spacing:-.01em}
  .kpi span{font-size:12px;color:var(--muted)}
  .kpi a{display:block;color:inherit;text-decoration:none}
  .kpi a:hover b{color:var(--total)}
  .kpi a:hover span{text-decoration:underline}
  .q{border:1.5px solid var(--rule);border-radius:14px;margin-bottom:14px;background:var(--bg);
    box-shadow:var(--shadow)}
  .q .h{padding:15px 18px;border-bottom:1px solid var(--rule)}
  .q .b{padding:15px 18px}
  .q .occ{font-size:12.5px;color:var(--muted);margin-top:9px;line-height:1.7}
  .opts{display:flex;gap:8px;flex-wrap:wrap;margin-top:11px}
  .opt{border:1.5px solid var(--rule);border-radius:9px;padding:9px 14px;font-size:13.5px;
    cursor:pointer;background:var(--bg)}
  .opt:hover{border-color:var(--accent)}
  .opt.sel{border-color:var(--accent);background:var(--accent-bg);font-weight:620;color:var(--accent-d)}
  .lic{display:flex;justify-content:space-between;align-items:center;padding:12px 0;
    border-bottom:1px solid var(--rule);font-size:14px}
  .lic:last-child{border-bottom:0}
  .dots{font:14px "IBM Plex Mono",ui-monospace,monospace;color:var(--muted);letter-spacing:.14em}
  @media(max-width:820px){.kpi,.kpi.five{grid-template-columns:1fr 1fr}
    .tabs{display:none}}

/* ---------------------------------------------------------------------------
 * BELOW THIS LINE IS NOT FROM THE PROTOTYPE. Two class names, both for one
 * thing the prototype has no state for.
 *
 * 08-07c item 5: Rex viewing a dealer's job "should still be recorded, with a
 * banner on screen while it is active, so it can never be mistaken for the
 * dealer's own action in a log". chrome() never drew that banner because the
 * prototype has no open-as-dealer mode to draw it in.
 *
 * THE BANNER HAS TO STAY ON SCREEN, not just be at the top of it. The failure
 * this exists to prevent is an admin who has forgotten he is acting as
 * somebody else and takes an action believing it is his own, and that is a
 * mistake made three screens down a page rather than at the top of one. So
 * .chrome wraps the banner and the header together and it is the thing that
 * sticks; .top keeps its own sticky rule from the lifted block, which inside
 * an already-stuck parent has nothing left to do and does it harmlessly.
 * ------------------------------------------------------------------------- */
  .chrome{position:sticky;top:0;z-index:70}
  .acting{background:var(--warnbg);border-bottom:1px solid var(--warn);
    color:var(--warn);font-size:13px;font-weight:600}
  .acting .in{max-width:1080px;margin:0 auto;padding:9px 24px;display:flex;
    gap:12px;align-items:center;flex-wrap:wrap}
  .acting form{margin-left:auto}
  .acting .btn.sm{padding:4px 10px}

/* ---------------------------------------------------------------------------
 * 08-14o: the initials in the header became a menu (Account, Dealer
 * Configuration, Admin for the real role, Sign out). The .menu/.sheet
 * machinery is the jobs table's, reused; these rules only stop the avatar
 * from picking up the three-dots summary's padding and grey hover, mark
 * the menu item for the screen the dealer is on, and give the sign-out
 * form the same top rule the rename form has, so the destructive-adjacent
 * action reads as its own group.
 * ------------------------------------------------------------------------- */
  .who .menu>summary{padding:0;border-radius:50%;line-height:0}
  .who .menu>summary:hover,.who .menu[open]>summary{background:none}
  .who .menu>summary:hover .av{background:var(--accent-d)}
  .who .menu .sheet{margin-top:8px}
  .menu .sheet a.cur{color:var(--accent-d);font-weight:650}
  .menu .sheet form.signout{border-top:1px solid var(--rule);margin-top:4px;
    padding-top:4px}

/* The lifted block hides .tabs under 821px, which was harmless while the
 * header was Jobs/Configuration/Admin and the create form lived on the
 * list. 08-14o made the tabs the dealer's ONLY doors (New Project,
 * Change Orders, My Projects) and moved the create form behind the first
 * of them, so a phone with one project would have had no way to start
 * another. Three short labels fit where the old row did not; the company
 * name is what gives way. */
  @media(max-width:820px){
    .tabs{display:flex;gap:12px;margin-left:4px}
    .tabs a{font-size:12.5px}
    .top .in{gap:12px;padding:0 14px}
    .who .co{display:none}
  }

/* ---------------------------------------------------------------------------
 * 24d gave the wordmark a CSS square, pure decoration with no template
 * change. Order 2026-09-02b ruling 5 brings back THE SETTLED MARK ITSELF
 * (four corner brackets around a block) as an inline SVG in _chrome.html,
 * left of the wordmark - the thing Rex noticed missing ("we settled on that
 * logo a long time ago and somewhere it disappeared"), which a CSS square
 * was never going to be mistaken for. The square rule is retired; the SVG
 * carries `aria-hidden` and paints in `currentColor`, so this one rule is
 * all the CSS the mark needs. */
  .brand svg{height:22px;width:22px;flex:none;color:var(--ink)}

/* ---------------------------------------------------------------------------
 * 24f, THE GUIDED BUILD (Option C phase one). The stage frame around the
 * shared question-card macro: one decision per screen, scaled up. New
 * classes only, at the bottom, per this file's own rule.
 * ------------------------------------------------------------------------- */
  .stage{max-width:640px;margin:10px auto 0;text-align:center}
  .stage-rail{display:flex;gap:14px;align-items:baseline;justify-content:center;
    flex-wrap:wrap;font-size:13px;color:var(--muted);margin-bottom:18px}
  .stage-rail a{color:var(--muted);font-size:12.5px}
  .stage-landed{color:var(--ok);font-weight:600}
  .stage .hello{font-size:14px;color:var(--said);font-weight:700;margin-bottom:10px}
  .stage-q{font:800 27px/1.2 Manrope,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    letter-spacing:-.02em}
  .stage-why{font-size:15px;color:var(--mid);margin-top:8px}
  .stage-card{text-align:left;margin-top:6px}
  .stage-card .q{box-shadow:var(--shadow)}
  .stage-card .q .h h3{font:700 20px/1.3 Manrope,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    letter-spacing:-.02em}
  .stage-card .q .b{font-size:15px}
  .stage-card .q .b label{font-size:15px}
  .stage-card select,.stage-card input[type=text],.stage-card input:not([type]){
    font-size:15px}
  .stage-nav{display:flex;justify-content:space-between;align-items:center;
    margin-top:20px}
  .stage-actions{display:flex;gap:16px;align-items:center}
  .stage-back{font-size:14px;color:var(--muted);text-decoration:none}
  .stage-back:hover{color:var(--ink)}
  .stage-skip{font-size:14px;color:var(--muted)}
  .stage-count{font-size:12.5px;color:var(--muted);margin-top:12px}

/* ---------------------------------------------------------------------------
 * 24g (C2/C3): the documents journey strip and the finish moment. New
 * classes only, at the bottom, per this file's own rule.
 * ------------------------------------------------------------------------- */
  .docs-lede{max-width:66ch;font-size:15.5px;color:var(--mid);margin-bottom:12px}
  .docs-status{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:20px}
  .docs-status .pill{font-size:12px;padding:5px 12px}
  .finish{max-width:640px;margin:12px auto 0;text-align:center}
  .finish h2{font:800 31px/1.15 Manrope,-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
    letter-spacing:-.02em}
  .finish .sub2{font-size:15px;color:var(--mid);margin-top:8px}
  .dl-tiles{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin:22px 0 6px;
    text-align:left}
  @media(max-width:640px){.dl-tiles{grid-template-columns:1fr}}
  .dl-tile{background:var(--bg);border-radius:14px;padding:20px 22px;
    box-shadow:var(--shadow);display:flex;flex-direction:column;gap:8px}
  .dl-tile b{font-size:16px}
  .dl-tile span{font-size:13px;color:var(--mid)}
  .dl-tile .btn{align-self:flex-start;margin-top:4px}

  /* 24h B5: THE BUILD TIMELINE. Rex: "there is still no timeline for the
     build." The bar rides every data-busy form; the sentence stays and
     the bar moves under it. It is perceived progress, not measurement -
     the press does not report percentages - so it eases toward full and
     holds short of it until the redirect lands. */
  .busybar{margin-top:12px;height:6px;border-radius:3px;
    background:var(--rule);overflow:hidden}
  .busybar i{display:block;height:100%;width:0;border-radius:3px;
    background:var(--accent);
    animation:busyfill 75s cubic-bezier(.15,.6,.3,1) forwards}
  @keyframes busyfill{0%{width:0}10%{width:34%}30%{width:62%}
    60%{width:82%}100%{width:94%}}
  @media (prefers-reduced-motion: reduce){
    .busybar i{animation:none;width:50%;opacity:.6}}

  /* 24i A1: THE DOCUMENTS PAGE STOPS READING LIKE A DOCUMENT. Four
     intake cards on a grid, each wearing its own status; drop zones
     that look like drop zones; the equipment decision as option tiles.
     (Replaces the 24g lede + status strip, whose pills moved into the
     card heads; .docs-lede/.docs-status stay for any old markup.) */
  .docs-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px;
    align-items:start;margin-bottom:20px}
  @media (max-width:920px){.docs-grid{grid-template-columns:1fr}}
  .docs-grid>.card{margin:0}
  .wt-card{margin-top:22px}
  .docs-grid .wt-card{margin-top:0}
  /* 29c: the record control, in the Field Book skin. THE BUTTON IS THE
     PRODUCT'S OWN TEAL `.btn` and paints nothing of its own, which is
     DEC-123's rule and the reason there is no colour on the controls here.
     The one colour this block introduces is the RECORDING DOT, and it is a
     conventional red on purpose: red-while-recording is the one convention
     every recorder in the world already taught the dealer, and Rex named it
     ("a conventional red recording indicator dot"). It is 10px across, it
     appears only while a recording is running, and it is on no control and
     in no button, so the never-orange rule is untouched by it. */
  .rec{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
  /* THE `hidden` ATTRIBUTE HAS TO OUTRANK `.btn`, and without this rule it
     does not: the browser's own `[hidden]{display:none}` is weaker than a
     class selector, so `.btn`'s `display:inline-block` put Pause, Stop and
     a ticking 00:00 on the screen of a page where nothing was recording.
     Found on the first eyeball of this control, not in a test: markup can
     say hidden and a stylesheet can still show it. */
  .rec [hidden]{display:none}
  .rec-live{display:inline-flex;gap:8px;align-items:center;
    font:600 15px/1 "IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
    color:var(--ink)}
  .rec-dot{width:10px;height:10px;border-radius:50%;background:#c0392b;
    display:inline-block;animation:recbeat 1.4s ease-in-out infinite}
  /* Paused reads as paused without a second control to look at: the dot
     stops beating and goes quiet, the clock holds its figure. */
  .rec-live.held .rec-dot{animation:none;opacity:.35}
  .rec-say{font-size:13px;color:var(--muted);flex:1 1 220px}
  @keyframes recbeat{0%,100%{opacity:1}50%{opacity:.25}}
  @media (prefers-reduced-motion:reduce){.rec-dot{animation:none}}
  .card-head{display:flex;align-items:baseline;justify-content:space-between;
    gap:10px;margin-bottom:10px}
  .card-head h2{margin-bottom:0}
  .card.doc .drop{padding:22px 16px;border-color:var(--rule);
    transition:border-color .15s ease, background .15s ease}
  .card.doc .drop:hover{border-color:var(--accent);background:var(--accent-bg)}
  .opt-tile{display:flex;gap:10px;align-items:center;border:1.5px solid
    var(--rule);border-radius:10px;padding:11px 14px;font-size:13.5px;
    margin-bottom:8px;cursor:pointer;transition:border-color .15s ease,
    background .15s ease}
  .opt-tile:hover{border-color:var(--accent)}
  .opt-tile:has(input:checked){border-color:var(--accent);
    background:var(--accent-bg)}
  .opt-tile input[type=radio]{width:auto;flex:none}

/* ---------------------------------------------------------------------------
 * DEC-123 (Rex, 2026-08-29): THE PUNCH LIST IS THE NAVIGATION.
 *
 * The five-across strip became the eight-stage chain drawn the way the Field
 * Book board draws a punch list: one line each, a box that gets ticked, the
 * current line highlighted, later lines saying waiting. New class names under
 * their own comment at the bottom, which is this file's own rule; `.steps`
 * keeps its rules untouched because the class contract above is frozen.
 * ------------------------------------------------------------------------- */
  .punch{list-style:none;margin:0 0 22px;padding:0;background:var(--bg);
    border:1.5px solid var(--rule);border-radius:14px;box-shadow:var(--shadow);
    overflow:hidden}
  .punch li{border-bottom:1.5px dashed var(--rule)}
  .punch li:last-child{border-bottom:0}
  .punch li>a,.punch li>span{display:flex;align-items:center;gap:14px;
    padding:13px 18px;text-decoration:none;color:var(--ink);flex-wrap:wrap}
  .punch li>a:hover{background:var(--tint)}
  .punch .box{flex:none;width:24px;height:24px;border:2px solid var(--ink);
    border-radius:6px;font-style:normal;font-size:14px;font-weight:700;
    display:flex;align-items:center;justify-content:center;line-height:1}
  .punch b{font-size:15px;font-weight:700}
  .punch em{font-style:normal;font-size:13px;color:var(--muted);flex:1;
    min-width:140px}
  .punch u{text-decoration:none;font-size:12px;font-weight:700;
    letter-spacing:.04em;color:var(--muted)}
  .punch .done .box{background:var(--ink);color:var(--bg)}
  .punch .done u{color:var(--ok)}
  /* the board highlights the line it is asking about in the palest paper
     and names it in terracotta. That word is the ONE terracotta on the
     product: small text, and not a control. */
  .punch .cur{background:var(--warnbg)}
  .punch .cur b{font-weight:800}
  .punch .cur u{color:var(--said)}
  .punch .wait>span{opacity:.6}
  /* the facts ratio rides inside its own line, the same shape it had inside
     the questions step of the strip */
  .punch .facts{flex-basis:100%;font-size:11px;font-weight:500;
    color:var(--muted);line-height:1.35;padding-left:38px}
  .punch .fbar{display:block;height:3px;background:var(--rule);
    border-radius:99px;overflow:hidden;margin-bottom:3px;max-width:220px}
  .punch .fbar span{display:block;height:100%;background:var(--muted);
    border-radius:99px}
  @media(max-width:640px){.punch em{display:none}}

/* ---------------------------------------------------------------------------
 * 09-03a ruling 4 (Rex, 2026-09-03): THE NUMBERED STEP RAIL.
 *
 * "There should be step one through however many steps to complete it."
 * `.steps` keeps every rule it has (the class contract is frozen); these are
 * the rail's own wrapper, its count line and the one instruction under it,
 * new names at the bottom under their own comment, which is this file's rule.
 *
 * IT STACKS ON A PHONE. Five instructions side by side at 500px wide are five
 * unreadable columns, and this rail exists to be read.
 * ------------------------------------------------------------------------- */
  .rail{margin:0 0 22px}
  .rail .steps{margin-bottom:8px}
  .rail-count{margin:0 0 8px;font-size:12px;font-weight:700;
    letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
  .rail-say{margin:0;font-size:13.5px;color:var(--muted)}
  @media(max-width:640px){
    .rail .steps{display:block}
    .rail .steps div{border-right:0;border-bottom:1px solid var(--rule)}
    .rail .steps div:last-child{border-bottom:0}
  }

/* ---------------------------------------------------------------------------
 * 09-03a ruling 5 (Rex, 2026-09-03): THE UPLOAD BAR, BESIDE THE BUTTON.
 *
 * "there should be a status bar showing the progress of the upload. If
 * there's not, the dealer will think his webpage froze and refresh."
 * The element ships hidden; trimout.js reveals it when bytes start moving.
 * ------------------------------------------------------------------------- */
  .upbar{display:inline-flex;align-items:center;gap:8px;margin-left:10px;
    vertical-align:middle}
  /* HIDDEN MEANS HIDDEN. `display:inline-flex` on a class beats the sheet's
     own `[hidden]` rule by source order, so the bar sat on every upload form
     at zero percent, which reads as something already stuck. */
  .upbar[hidden]{display:none}
  .upbar progress{width:150px;height:8px;border:0;border-radius:99px;
    background:var(--rule);appearance:none;-webkit-appearance:none}
  .upbar progress::-webkit-progress-bar{background:var(--rule);
    border-radius:99px}
  .upbar progress::-webkit-progress-value{background:var(--accent);
    border-radius:99px}
  .upbar progress::-moz-progress-bar{background:var(--accent);
    border-radius:99px}
  .upbar-pct{font-size:12.5px;font-weight:700;color:var(--muted);
    font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
    min-width:38px}

/* ---------------------------------------------------------------------------
 * ITEM 3 OF THE 2026-09-08 PLAN: the question pattern on a page of its own.
 *
 * Four rules, new class names at the bottom, which is this file's own
 * standing rule. Everything else the tree draws is `.q`, `.opt`, `.opt-tile`
 * and `.pill`, which this product already had.
 *
 * THE PROGRESS LINE IS STICKY ON THE SETUP PAGE AND NOT ON THE QUESTIONS
 * PAGE, and that is deliberate rather than an oversight: the questions page
 * renders its pill inside the card that introduces the questions, where it
 * reads as part of a sentence, and moving it would have changed a page this
 * strand pinned byte for byte. `sticky` is a parameter on the macro.
 * ------------------------------------------------------------------------- */
  .qbar{position:sticky;top:0;z-index:5;padding:10px 0;background:var(--bg);
    border-bottom:1px solid var(--rule);margin-bottom:6px}
  .qprog{font-size:12.5px;padding:5px 12px}
  /* THE ANSWERED CARD. A `<details>` and not a script: "reopens in place, no
     reload" has to be true with JavaScript off as well as on, which is the
     same rule the follow-up cards follow. */
  .qans > summary{cursor:pointer;list-style:none;display:flex;gap:10px;
    align-items:center;flex-wrap:wrap}
  .qans > summary::-webkit-details-marker{display:none}
  .qchange{font-size:12.5px;color:var(--accent-ink);font-weight:620}
  .qans[open] > summary .qchange{color:var(--muted)}

/* ---------------------------------------------------------------------------
 * THE PHONE LAYER. Rex, 2026-09-08: "Phone one-at-a-time, desktop long
 * page: yes."
 *
 * TWO RULES, AND THE SCRIPT PUTS THE CLASSES ON. Every card is in the HTML
 * the server rendered; on a narrow screen `trimout.js` marks the ones that
 * are not the current step. `qstep-off` and NOT the `hidden` attribute,
 * because `hidden` is the TREE's word for "this branch is not open" and is
 * what the progress line counts. A wizard reaching for the same attribute
 * would tell a dealer he had one question left because ten were off-screen.
 *
 * IT IS INERT ON A DESKTOP AND WITH NO SCRIPT. Nothing here fires unless the
 * script has added the class, so the long page is what a browser that never
 * ran the file shows, which is the whole no-JavaScript rule this pattern
 * follows.
 * ------------------------------------------------------------------------- */
  .qstep-off{display:none}
  .qnav{display:flex;justify-content:space-between;align-items:center;
    gap:12px;margin-top:16px}
  .qnav button[disabled]{opacity:.45;cursor:default}
  .qnav[hidden]{display:none}
/* AND A BUTTON INSIDE IT THAT SAYS `hidden` IS HIDDEN (2026-09-18). The rule
   above exists because the browser's own `[hidden]{display:none}` is weaker
   than a class selector, and `.rec [hidden]` up at line 430 carries the same
   lesson with the same note: "markup can say hidden and a stylesheet can still
   show it." The NAV got the rule and the three buttons in it never did, so
   `nav.back.hidden` on the first card and `nav.next.hidden` at the end of the
   line both did nothing at all. Invisible while the glyph was flex-shrunk to
   0px; visible the moment that was fixed, as a back arrow on card one that
   steps nowhere and a forward arrow sitting beside the finishing button. */
  .qnav [hidden]{display:none}
/* THE TWO ARROWS (Rex, 2026-09-16): "an image of an arrow pointing forwards
   and an arrow pointing backwards". A 44px press with 24px of drawing in it,
   which is the tap target the rest of this stylesheet uses. The count sits
   between them, because that is the line the two presses move. */
  .qarrow{width:44px;height:44px;min-width:44px;padding:0;flex:0 0 auto;
    display:inline-flex;align-items:center;justify-content:center}
  .qarrow svg{width:24px;height:24px;min-width:24px;flex:none;display:block}  /* min-width + flex:none (2026-09-18): inside the flex button the svg was
     shrunk to 0px wide, so the arrow stroked in ink and painted nothing -
     Rex's blank white boxes. Proven live by injecting this rule. */
  .qnav-count{margin:0;flex:1 1 auto;text-align:center}
  /* THE STATUS BAR BETWEEN THE ARROWS (Rex, 2026-09-18): "just have a status
     bar that is slowly completing." It takes the seat the count used to, so
     it fills the space between the two presses and reads as the line they
     move. */
  .qnav-bar{flex:1 1 auto;margin:0}

/* ---------------------------------------------------------------------------
 * RULING 136 (Rex, 2026-09-09): "so many words on the page. there should be
 * hardly any descriptions at all. very simple one liners."
 *
 * THE `why` CONTROL IS THE VALVE THE SWEEP NEEDED. An explanation that is
 * genuinely load-bearing folds in here instead of being deleted: collapsed by
 * default, one word to open it, the paragraph in place beside the control it
 * is about. A `<details>` and NOT a script, which is this product's standing
 * rule: a control that only exists after a script runs does not exist at all.
 *
 * IT IS DELIBERATELY PLAIN. No box, no tint, no icon, no border. Rex has
 * rejected busy screens twice; a valve that decorated itself would put back
 * in ink what the sweep took out in words. It is a small teal word and
 * nothing else until it is opened.
 * ------------------------------------------------------------------------- */
  .why{margin:6px 0 0}
  .why > summary{display:inline-block;list-style:none;cursor:pointer;
    font-size:12.5px;font-weight:620;color:var(--accent-ink);
    border-bottom:1px dotted var(--accent-ink);line-height:1.2}
  .why > summary::-webkit-details-marker{display:none}
  .why > summary:hover{color:var(--total);border-bottom-color:var(--total)}
  .why > p{margin:8px 0 0;font-size:13px;color:var(--muted);max-width:56ch}

/* ---------------------------------------------------------------------------
 * GLASS, 2026-09-09. Rex approved the renderings on the TrimOut redesign
 * canvas ("Glass"), so this is the fourth skin and, like the three before
 * it, IT IS VALUES ONLY ABOVE THIS LINE: not one selector or class name in
 * the frozen block was renamed, and every rule below either re-declares a
 * custom property or restyles a class the contract already names. New class
 * names appear only for the two dropdowns and the theme switch, which are
 * markup this file had no state for.
 *
 * THE TOKENS, from the boards:
 *
 *   dark    ground #0f1412, two soft glows (teal #5aa39a, blue #1A5FA8),
 *           panels rgba(255,255,255,.06) with a 1px rgba(255,255,255,.12)
 *           line and an 18px backdrop blur, 22px radii, accent #5aa39a,
 *           totals #8fe0d2, ink #e9f0ed, muted #93a29d, the mark in white
 *   light   ground #f6f7f5, the same two glows at low opacity, panels white
 *           at 70 per cent with a 1px rgba(23,33,31,.10) line, accent
 *           #3d7068 with #2f5a54 for hover and for a total, ink #17211f,
 *           muted #6f7b79, the mark in ink
 *
 * Outfit for titles, IBM Plex Sans for body, IBM Plex Mono unchanged for
 * figures. Every face keeps a full fallback stack, so the 401 gate and an
 * offline box render without the fetch, which is base.html's standing rule.
 *
 * DARK IS THE DEFAULT AND LIGHT IS THE FALLBACK. `:root` with no attribute
 * takes the light values on purpose: a page rendered outside the signed-in
 * shell has no stored answer to read, and unreadable dark text on white is
 * the one failure that cannot be recovered from on screen.
 *
 * NO PRINT RULE IS ADDED OR CHANGED. Documents stay light; the only
 * `@media print` block in the app is check_report.html's own and this file
 * has none, so a themed screen cannot reach a printed page.
 * ------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------
 * THE PAINT AGAIN, 2026-09-15: INDIGO. Rex ruled the marketing site's new
 * paint tonight - light is "indigo on white", dark is "glass, indigo
 * atmosphere" - and asked the question this file has answered four times
 * already: "obviously the app will follow the main page right?" Yes. So this
 * is the fifth skin and, like the four before it, IT IS VALUES ONLY: not one
 * selector or class name changed, the frosted panels, the 18px blur, the
 * 22px radii and the 48px buttons are the ones GLASS drew, the faces are
 * still Outfit / IBM Plex Sans / IBM Plex Mono, dark is still the default
 * and the switch is still in the initials menu. Only the colour moved.
 *
 *   light   ground #f7f8fc, panels white at 70 per cent on a
 *           rgba(23,33,31,.10) line, accent #3b42ff with #1d73e8 for hover,
 *           a figure #1d3fb8, chips and selected fills #eef0ff, ink #17211f
 *   dark    ground #0e1130, the same frosted panel on a
 *           rgba(255,255,255,.13) line, accent #4c53ff with #6b71ff for
 *           hover, a figure #a7abff, ink #f1f3ff, the header pill at
 *           rgba(255,255,255,.04)
 *
 * TWO VALUES ARE NOT THE ONES HE TYPED, both for contrast and both said out
 * loud rather than quietly:
 *
 *   `--ground` in light is #f7f8fc and not #ffffff. A white-at-70-per-cent
 *   panel on a pure white page is not a panel: the glass this skin is
 *   named for disappears and every card becomes an outline. He allowed for
 *   it ("if the pure white kills the glass panels' contrast, use #f7f8fc").
 *
 *   `--muted` is #636b8a and not #6f7b79. The old green-grey reads 4.39:1
 *   on white, under the 4.5 a dealer needs, and it was the one token in the
 *   sheet still carrying the teal skin's hue. #636b8a is 5.25:1 on the
 *   page, 4.95:1 on the ground and 4.64:1 on a chip.
 *
 * `--glow-teal` KEEPS ITS NAME AND LOSES ITS COLOUR. It is the left-hand
 * glow, it is now indigo, and the name is the one `body::before` and
 * `tests/test_0909_glass_theme.py` both read: renaming it would be a
 * selector change in a values-only order, which this file has never made.
 *
 * TEAL SURVIVES IN THE COMMENTS ABOVE AND NOWHERE ELSE. The history block
 * is the record of why every value in this file is what it is, and
 * test_29f's own rule is that the prose is left alone and the rules are
 * what get checked.
 * ------------------------------------------------------------------------- */

  :root,
  :root[data-theme="light"]{
    --ink:#17211f; --mid:#414a5e; --muted:#636b8a;
    --bg:#ffffff; --ground:#f7f8fc; --tint:#eef0ff; --panel:#eef0ff;
    --rule:rgba(23,33,31,.10);
    --accent:#3b42ff; --accent-d:#1d73e8; --accent-bg:rgba(59,66,255,.12);
    --accent-ink:#3b42ff;
    --blue:#3b42ff; --blue-d:#1d73e8;
    --total:#1d3fb8;
    --glass:rgba(255,255,255,.70);
    --glass-soft:rgba(255,255,255,.55);
    --glass-line:rgba(23,33,31,.10);
    /* `--line` IS `--glass-line` UNDER THE SITE'S NAME, and it is declared
       because seven hairlines in the app ask for it: `_proposal_check.html`,
       `_your_houses.html`, `_change_draft.html`, `defaults.html`, and the two
       self-contained proposal partials all write `border-top:1px solid
       var(--line)`. They were written against site/index.html's token names,
       this sheet has never had that name, and an undefined var makes the
       whole shorthand invalid: the border was not a faint line, it was no
       line at all. One alias is cheaper than seven edits and keeps those
       partials portable. */
    --line:rgba(23,33,31,.10);
    --glass-shadow:0 18px 46px rgba(23,33,31,.10);
    --glass-inset:inset 0 1px 0 rgba(255,255,255,.70);
    --shadow:0 18px 46px rgba(23,33,31,.10);
    --pill-on:rgba(59,66,255,.08);
    --sheet:rgba(255,255,255,.94);
    --header:rgba(255,255,255,.70);
    --btn-face:linear-gradient(180deg,#4a51ff,#3b42ff);
    --btn-ink:#ffffff;
    --btn-glow:0 10px 26px rgba(59,66,255,.26);
    --brand-ink:#17211f;
    --glow-teal:rgba(59,66,255,.12);
    --glow-blue:rgba(29,115,232,.10);
    --ok:#2f7d4f; --okbg:rgba(47,125,79,.12);
    --warn:#8a6d1f; --warnbg:rgba(138,109,31,.12);
    --bad:#9c3b22; --badbg:rgba(156,59,34,.12);
    --said:#c24d2f;
    --radius:22px;
    --font-title:Outfit,"Trebuchet MS",-apple-system,BlinkMacSystemFont,
      "Segoe UI",Helvetica,Arial,sans-serif;
    --font-body:"IBM Plex Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",
      Helvetica,Arial,sans-serif;
  }
  :root[data-theme="dark"]{
    --ink:#f1f3ff; --mid:#cdd3f5; --muted:#b8bde0;
    --bg:#161a3c; --ground:#0e1130; --tint:rgba(255,255,255,.05);
    --panel:rgba(255,255,255,.05);
    --rule:rgba(255,255,255,.13);
    --accent:#4c53ff; --accent-d:#6b71ff; --accent-bg:rgba(76,83,255,.16);
    --accent-ink:#8e94ff;
    --blue:#4c53ff; --blue-d:#6b71ff;
    --total:#a7abff;
    --glass:rgba(255,255,255,.06);
    --glass-soft:rgba(255,255,255,.05);
    --glass-line:rgba(255,255,255,.13);
    --line:rgba(255,255,255,.13);
    --glass-shadow:0 18px 46px rgba(0,0,0,.45);
    --glass-inset:inset 0 1px 0 rgba(255,255,255,.10);
    --shadow:0 18px 46px rgba(0,0,0,.45);
    --pill-on:rgba(255,255,255,.09);
    --sheet:rgba(20,24,60,.88);
    --header:rgba(255,255,255,.04);
    --btn-face:linear-gradient(180deg,#5d64ff,#4c53ff);
    --btn-ink:#ffffff;
    --btn-glow:0 10px 26px rgba(76,83,255,.38);
    --brand-ink:#ffffff;
    --glow-teal:rgba(59,66,255,.35);
    --glow-blue:rgba(29,115,232,.28);
    /* GREEN AND NOT THE OLD MINT (2026-09-15). Done has been green in
       every skin; #7fd3c6 was a teal-green chosen to sit beside a teal
       accent, and on the indigo ground it was the last surface in the app
       that still read as teal. */
    --ok:#62d996; --okbg:rgba(98,217,150,.14);
    --warn:#e0c273; --warnbg:rgba(224,194,115,.14);
    --bad:#e79b84; --badbg:rgba(231,155,132,.14);
    --said:#e79b84;
  }

  /* THE GROUND AND THE TWO GLOWS. The ground is painted on `html` and the
     body is transparent, which is what lets the two glows sit at z-index -1
     and still be seen. They are FIXED rather than absolute so a long page
     does not drag them off screen: the boards draw one glow at the top left
     and one at the top right of the VIEWPORT, and a scrolled page that lost
     them would be a different paint job halfway down. `pointer-events:none`
     because they are paint and must never eat a click. */
  html{background:var(--ground);color-scheme:light}
  :root[data-theme="dark"]{color-scheme:dark}
  body{background:transparent;color:var(--ink);font-family:var(--font-body)}
  body::before,body::after{content:"";position:fixed;pointer-events:none;
    z-index:-1;top:0;left:0;right:0;height:520px}
  body::before{background:radial-gradient(900px 420px at 18% -8%,
    var(--glow-teal),transparent 70%)}
  body::after{background:radial-gradient(760px 380px at 86% 4%,
    var(--glow-blue),transparent 70%)}
  h1,h2,h3,.stage-q,.finish h2,.stage-card .q .h h3{font-family:var(--font-title)}
  h1,h2,h3{color:var(--ink)}
  h1{font-weight:700}
  h2,h3{font-weight:600}

  /* THE FLOATING PILL. `.top` keeps its sticky rule and gives up its bar:
     the paint moves to `.top .in`, which is the pill, so nothing that
     imports the chrome macro has to change. */
  .top{background:transparent;border-bottom:0;box-shadow:none;
    padding:16px 24px 0}
  .top .in{max-width:1180px;height:64px;padding:0 14px 0 20px;
    border-radius:999px;background:var(--header,var(--glass));
    border:1px solid var(--glass-line);
    -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
    box-shadow:var(--glass-shadow),var(--glass-inset);gap:10px}
  .brand{font-family:var(--font-title);font-weight:700;font-size:20px;
    letter-spacing:-.01em;color:var(--brand-ink)}
  .brand svg{height:20px;width:20px;color:var(--brand-ink)}
  .tabs{gap:6px;margin-left:18px}
  .tabs a{color:var(--muted);height:44px;padding:0 16px;border-radius:12px;
    border-bottom:0;font-size:14.5px;font-weight:600;display:flex;
    align-items:center}
  .tabs a:hover{color:var(--ink);background:var(--pill-on)}
  .tabs a.cur{color:var(--ink);background:var(--pill-on);border-bottom:0}
  .who{gap:12px;font-size:13px;color:var(--muted)}
  .av{width:34px;height:34px;border-radius:50%;
    background:rgba(255,255,255,.12);border:1px solid var(--glass-line);
    color:var(--brand-ink);font-size:12px;font-weight:600;letter-spacing:.03em}
  :root,:root[data-theme="light"]{--av-face:rgba(59,66,255,.10)}
  .av{background:var(--av-face,rgba(255,255,255,.12))}
  :root[data-theme="dark"] .av{background:rgba(255,255,255,.12)}
  main{max-width:1180px}

  /* FROSTED PANELS. One rule reaches every card on every screen because
     `.card` is the contract's own name for a panel. */
  .card,.kpi div,.dl-tile,.q,.punch,.steps{background:var(--glass);
    border:1px solid var(--glass-line);border-radius:var(--radius);
    -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
    box-shadow:var(--glass-shadow),var(--glass-inset)}
  .q .h{border-bottom:1px solid var(--glass-line)}
  .steps{overflow:hidden}
  .steps div,.steps a{border-right:1px solid var(--glass-line);
    color:var(--muted)}
  .steps div.done,.steps a.done{color:var(--ok);background:transparent}
  .steps div.cur{color:var(--ink);background:var(--accent-bg);
    box-shadow:inset 0 -2px 0 var(--accent)}
  .steps a:hover{background:var(--pill-on);color:var(--ink)}
  .steps i{background:var(--pill-on);color:var(--muted)}
  .steps .done i{background:var(--ok);color:var(--ground)}
  .steps .cur i{background:var(--accent);color:var(--ground)}

  /* CONTROLS. The primary button is the boards' teal gradient; the
     secondary is a glass panel with a brighter line, which is how the
     boards draw "Back". */
  .btn{background:var(--btn-face);color:var(--btn-ink);border:0;
    border-radius:14px;height:48px;padding:0 26px;font-weight:600;
    box-shadow:var(--btn-glow)}
  .btn:hover{background:var(--accent-d);color:var(--btn-ink)}
  .btn.g{background:var(--glass);color:var(--ink);
    border:1px solid var(--glass-line);
    -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
    box-shadow:var(--glass-shadow),var(--glass-inset)}
  .btn.g:hover{background:var(--pill-on)}
  .btn.sm{height:36px;padding:0 16px;font-size:13px;border-radius:11px}
  .field label{color:var(--muted)}
  .field input,.field select,.field textarea,td input,td select{
    background:var(--glass-soft);border:1px solid var(--glass-line);
    border-radius:12px;color:var(--ink)}
  .field input:focus,.field select:focus,.field textarea:focus,
  td input:focus,td select:focus{border-color:var(--accent);
    background:var(--glass)}
  td input:hover,td select:hover{border-color:var(--glass-line);
    background:var(--glass-soft)}
  td input.edited{background:var(--accent-bg);border-color:var(--accent)}
  th,td{border-bottom:1px solid var(--glass-line)}
  th{background:transparent;color:var(--muted)}
  code{background:var(--glass-soft);border:1px solid var(--glass-line);
    color:var(--ink)}

/* THE ACCENT AS A WORD. `--accent` is a FILL - the button, the ring around a
   chosen tile, the line under the current step - and against the dark ground
   #4c53ff does all three well while reading only 3.49:1 as text, under the
   4.5 a dealer standing in a mechanical room needs. So every rule below that
   paints a WORD with the accent reads `--accent-ink` instead (#8e94ff in
   dark, 6.86:1; in light it IS the accent, so nothing there moves), and the
   hover on such a word takes the figure colour, which passes in both. */
  a{color:var(--accent-ink)}
  a:hover{color:var(--total)}

  /* CHIPS, PILLS AND THE TILE THAT IS A CHIP. `.opt` and `.opt-tile` are
     the boards' answer tiles: a 14px radius, a glass face, and the chosen
     one ringed in the accent with its own glow. */
  .opt,.opt-tile{background:var(--glass);border:1px solid var(--glass-line);
    border-radius:14px;color:var(--ink);
    -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}
  .opt:hover,.opt-tile:hover{border-color:var(--accent)}
  .opt.sel,.opt-tile:has(input:checked){border-color:var(--accent);
    background:var(--accent-bg);color:var(--ink);
    box-shadow:0 0 0 1px var(--accent-bg),0 14px 36px var(--accent-bg)}
  .pill{border-radius:999px}
  .p-mute{background:var(--pill-on);color:var(--muted)}
  .note{background:var(--glass-soft);border-left:3px solid var(--accent);
    border-radius:0 12px 12px 0;color:var(--mid)}
  .drop{border:2px dashed var(--glass-line);border-radius:18px;
    background:var(--glass-soft);color:var(--muted)}
  .card.doc .drop{border-color:var(--glass-line)}
  .card.doc .drop:hover{border-color:var(--accent);background:var(--accent-bg)}

  /* PROGRESS. Every bar in the app: the thin job bar, the build timeline,
     the facts ratio inside a step and the upload bar. */
  .prog,.busybar,.steps .fbar,.punch .fbar{background:var(--pill-on)}
  .prog .fill,.busybar i,.steps .fbar span,.punch .fbar span{
    background:var(--accent)}
  .prog .fill{box-shadow:0 0 16px var(--accent-bg)}
  .prog .fill.done{background:var(--ok)}
  .upbar progress::-webkit-progress-bar{background:var(--pill-on)}
  .upbar progress::-webkit-progress-value{background:var(--accent)}
  .upbar progress::-moz-progress-bar{background:var(--accent)}

  /* THE SHEET behind the initials and behind a row's actions. */
  .menu .sheet{background:var(--sheet);border:1px solid var(--glass-line);
    border-radius:18px;padding:6px;min-width:264px;
    -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
    box-shadow:var(--glass-shadow),var(--glass-inset)}
  .menu .sheet button,.menu .sheet a{color:var(--ink);min-height:44px;
    display:flex;align-items:center;padding:0 12px;border-radius:12px;
    font-size:14.5px}
  .menu .sheet button:hover,.menu .sheet a:hover{background:var(--pill-on)}
  .menu .sheet a.cur{background:var(--accent-bg);color:var(--total);
    font-weight:600}
  .menu .sheet form.signout{border-top:1px solid var(--glass-line)}
  .menu>summary{color:var(--muted)}
  .menu>summary:hover{background:var(--pill-on);color:var(--ink)}
  .menu[open]>summary{background:var(--pill-on)}
  .who .menu>summary:hover .av{background:var(--accent-bg);
    border-color:var(--accent)}

  /* FIGURES. A total is the one number the boards paint. */
  .kpi b{color:var(--total)}
  .total,.money-total{color:var(--total)}
  .qbar{background:transparent}
  .rail-say,.stage-count,.stage-skip,.stage-back,.dots{color:var(--muted)}
  .acting{background:var(--warnbg);border-bottom:1px solid var(--warn);
    color:var(--warn)}

/* ---------------------------------------------------------------------------
 * THE TWO DROPDOWNS AND THE THEME SWITCH, 2026-09-09. The only new class
 * names this skin adds, because they are markup the app did not have.
 *
 * A DROPDOWN IS A `<details>`, never a script. This product's standing rule
 * is that a control which only exists after a script runs does not exist at
 * all, and the top bar is the one control a dealer cannot do without.
 * ------------------------------------------------------------------------- */
  .tabs .nav{position:relative}
  .tabs .nav>summary{list-style:none;cursor:pointer;user-select:none;
    display:flex;align-items:center;gap:7px;height:44px;padding:0 16px;
    border-radius:12px;font-size:14.5px;font-weight:600;color:var(--muted)}
  .tabs .nav>summary::-webkit-details-marker{display:none}
  .tabs .nav>summary:hover{color:var(--ink);background:var(--pill-on)}
  .tabs .nav[open]>summary,.tabs .nav.cur>summary{color:var(--ink);
    background:var(--pill-on)}
  .tabs .nav>summary svg{width:14px;height:14px;flex:none;display:block}
  .tabs .nav .sheet{position:absolute;left:0;top:100%;margin-top:8px;
    z-index:30;background:var(--sheet);border:1px solid var(--glass-line);
    border-radius:18px;padding:6px;min-width:264px;
    -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
    box-shadow:var(--glass-shadow),var(--glass-inset);
    display:flex;flex-direction:column;gap:2px}
  .tabs .nav .sheet a{display:flex;align-items:center;min-height:44px;
    padding:0 12px;border-radius:12px;font-size:14.5px;color:var(--ink);
    text-decoration:none}
  .tabs .nav .sheet a:hover{background:var(--pill-on)}
  .tabs .nav .sheet a.cur{background:var(--accent-bg);color:var(--total);
    font-weight:600}

  /* THE DARK / LIGHT SWITCH, first in the initials menu. Two submit buttons
     in one form: a segmented control that works with the script switched
     off, because the answer is stored on the server and not in the browser. */
  .themeswitch{display:flex;gap:4px;padding:4px;border-radius:14px;
    background:var(--glass-soft);border:1px solid var(--glass-line);
    margin:0 0 4px}
  .themeswitch button{flex:1;display:flex;align-items:center;
    justify-content:center;height:44px;border:0;border-radius:12px;
    background:none;font:inherit;font-size:13.5px;font-weight:600;
    color:var(--muted);cursor:pointer}
  .themeswitch button:hover{color:var(--ink)}
  .themeswitch button[aria-pressed="true"]{color:var(--ink);
    background:var(--accent-bg);border:1px solid var(--accent);
    box-shadow:0 0 0 1px var(--accent-bg),0 10px 24px var(--accent-bg)}

  /* THE ACCOUNT ROWS. The board draws the page as a label, a value and a
     Change: one panel, hairline rows, nothing else. */
  .arow{display:flex;gap:16px;align-items:center;padding:14px 0;
    min-height:44px;border-bottom:1px solid var(--glass-line)}
  .arow:last-child{border-bottom:0}
  .arow>.k{width:180px;flex:none;font-size:12.5px;font-weight:600;
    color:var(--muted)}
  .arow>.v{flex:1;min-width:0;font-size:15px;color:var(--ink)}
  .arow>.c{font-size:13px;font-weight:600;color:var(--accent-ink);
    text-decoration:none;flex:none}
  .arow>.c:hover{color:var(--total)}
  .arow-form{padding:0 0 16px}
  /* A ROW WHOSE CHANGE OPENS A FORM IS ONE `<details>`, and its summary IS the
     row, so the closed page reads as a plain list of answers. `list-style` off
     on both engines, or the browser draws its own triangle in the label
     column. */
  .arow-d{border-bottom:1px solid var(--glass-line)}
  .arow-d:last-child{border-bottom:0}
  .arow-d>summary{list-style:none;cursor:pointer;border-bottom:0}
  .arow-d>summary::-webkit-details-marker{display:none}
  .arow-d>summary:hover .c{color:var(--total)}
  .arow-d[open]>summary .c{color:var(--muted)}
  @media(max-width:640px){
    .arow{flex-wrap:wrap;gap:6px}
    .arow>.k{width:100%}
  }

  /* THE PILL BAR ON A PHONE. Four things have to fit across 390 points: the
     mark, the wordmark, two dropdown pills and the initials. The company name
     is what gives way, which is the rule the 08-14o media query already set;
     everything else gives up padding rather than presence, because a door a
     dealer cannot see is a door he does not have. */
  @media(max-width:560px){
    .top{padding:10px 10px 0}
    .top .in{padding:0 8px 0 12px;gap:6px;height:58px}
    .brand{font-size:17px;gap:8px}
    .brand svg{height:18px;width:18px}
    .tabs{margin-left:6px;gap:2px}
    .tabs .nav>summary{padding:0 9px;font-size:13px;gap:5px;height:40px}
    /* THE SHEET SPANS THE PHONE rather than hanging off its right edge: at
       this width a 264px panel opened under the second pill runs past the
       screen, and a menu a thumb cannot reach is a menu that is not there. */
    .tabs .nav .sheet{position:fixed;left:10px;right:10px;top:70px;
      min-width:0}
    .av{width:30px;height:30px}
    main{padding:20px 14px 64px}
  }
  /* AND BELOW 430 THE WORDMARK GIVES WAY TOO. The 08-14o media query already
     set the order of sacrifice - the company name goes first, because the
     doors are what a dealer came for - and this is the next thing down that
     list. The MARK stays, because it is how he knows which app he is in, and
     the word stays in the DOM as the link's accessible name. */
  @media(max-width:430px){
    .brand{font-size:0;gap:0}
    .brand svg{height:20px;width:20px}
    .tabs{margin-left:8px}
  }

  /* ====================================================================
     THE PROJECTS FLOW, 2026-09-09. Rex's NewProject, ProjectSource,
     MyProjects and ProjectQuestions boards.

     APPENDED IN ITS OWN SECTION, exactly as the Glass skin above was, and
     for the same reason: not one selector or class name declared earlier in
     this file is renamed or redefined here, so the frozen class contract
     `tests/test_0909_glass_theme.py` holds is untouched and a merge from
     another strand lands beside this block rather than inside it.

     EVERY VALUE IS A TOKEN. The boards are drawn in the dark palette; the
     tokens above carry both, so these rules are written once and both looks
     come out right.
     ==================================================================== */

  /* The two page widths the boards use. `main` is 1080 by default; New
     project is drawn at 600 and Add a proposal at 1000, so they say so
     rather than every board being re-measured against the default. */
  main.pnarrow{max-width:648px}
  main.pwide{max-width:1048px}

  /* A LIST ROW, WHICH IS THE SHAPE BOTH LISTS SHARE. Name, date, a status
     cell and one figure on the right: My projects prints a room count there
     and Add a proposal prints nothing, and the cells are named for what they
     hold rather than for which list they are on. Hairlines between, none
     under the last, which is `.arow`'s rule one card along. */
  .plist{display:flex;flex-direction:column}
  .prow{display:flex;gap:16px;align-items:center;padding:14px 0;
    min-height:44px;border-bottom:1px solid var(--glass-line)}
  .prow:last-child{border-bottom:0}
  .prow .nm{flex:1;min-width:0;font-size:15px;font-weight:600;color:var(--ink)}
  .prow .nm a{color:var(--ink);text-decoration:none}
  .prow .nm a:hover{color:var(--accent-ink)}
  .prow .dt{width:130px;flex:none;font-size:13.5px;color:var(--muted)}
  .prow .st{width:130px;flex:none}
  .prow .st .fin{font-size:13.5px;font-weight:600;color:var(--muted)}
  .prow .fig{width:110px;flex:none;text-align:right;font-size:14px;
    color:var(--ink)}
  .prow .more{flex:none}

  /* THE PICKABLE ROW on Add a proposal. It is a radio and it looks like a
     row, because the board draws rows: the control is real (a keyboard
     reaches it, a form posts it) and the ring is what says which one is
     chosen. */
  .pickrow{cursor:pointer;margin:0}
  .pickrow input[type=radio]{width:auto;flex:none;accent-color:var(--accent)}
  .pickrow:hover .nm{color:var(--accent-ink)}
  .pickrow:has(input:checked) .nm{color:var(--accent-ink)}

  /* THE COMPACT DROP BOX. One line, one sub-line and the button, in a
     dashed frame, at the height the board draws it. `.drop` above is the
     documents screen's full-width dropzone and is left alone. */
  .dropbox{display:flex;align-items:center;gap:14px;padding:16px 18px;
    min-height:76px;border:1px dashed var(--glass-line);border-radius:18px;
    background:var(--glass-soft);color:var(--muted)}
  .dropsay{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}
  .dropsay b{font-size:14px;font-weight:600;color:var(--ink)}
  .dropsay .sub{font-size:12.5px;margin:0}
  /* The file input rides inside its own label, so the label IS the button
     and there is no second control to style or to explain. */
  .dropbox .btn input[type=file]{position:absolute;width:1px;height:1px;
    opacity:0;pointer-events:none}
  .dropbox .btn{position:relative;flex:none}

  /* TWO TILES SIDE BY SIDE, one under the other on a phone. */
  .ptiles{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
    gap:24px;align-items:start;margin-bottom:20px}
  @media(max-width:820px){
    .ptiles{grid-template-columns:1fr}
  }

  /* THE ESTIMATE BUILDER'S OWN FURNITURE: the bar, and the tiles a question
     is answered with. The bar itself is `.prog`, which this stylesheet has
     drawn since 22i; only the tile grid is new.

     `.qcount` WENT WITH THE NUMBER, 2026-09-18. Rex: "It would be better not
     to list the amount of questions and just have a status bar that is
     slowly completing." No template renders that line on any board now, so
     the rule that dressed it is gone rather than left to dress nothing. */
  .qtiles{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
    gap:12px;margin-top:14px}
  .qtiles .opt-tile{margin-bottom:0;align-items:flex-start;
    padding:16px 18px;font-size:15.5px;font-weight:600}
  @media(max-width:640px){
    .qtiles{grid-template-columns:1fr}
    .prow{flex-wrap:wrap;gap:8px}
    .prow .dt,.prow .st,.prow .fig{width:auto;text-align:left}
  }

  /* THE QUESTIONS BOARD. `.stage` is the guided frame's own block and is
     centred and 640 wide; Rex's ProjectQuestions rendering is left aligned
     and wider, with the title, the count and the bar stacked over one card.
     A modifier rather than an edit, so the walk and every other `.stage`
     screen is untouched. */
  .stage.qboard{max-width:880px;text-align:left;margin:0 auto}
  .stage.qboard .qtitle{font-family:var(--font-title);font-weight:700;
    font-size:32px;line-height:1.15;letter-spacing:-.02em;color:var(--ink);
    margin:0 0 10px}
  .stage.qboard .prog{height:4px;margin-bottom:22px}
  .stage.qboard .stage-card{margin-top:0}
  .stage.qboard .stage-nav{margin-top:22px}
  .stage.qboard .stage-skip{margin-top:18px;font-size:13px}
  .stage.qboard .note{text-align:left}
  /* ===================================================================
     THE DEALER CONFIGURATION BOARDS, 2026-09-09 (Rex's approved
     renderings). New rules at the bottom under their own comment, which
     is this file's own standing rule; not one selector above the frozen
     line was touched.

     THREE SHAPES AND NO MORE: a table of a label and a figure (Rates), a
     line of chips (both Standards pages), and a two-column grid of cards
     (the Dealer configuration landing). Everything else on these pages is
     `card`, `field`, `btn` and `note`, which the contract already holds. */
  .ratetable>.r{display:flex;gap:16px;align-items:center;min-height:44px;
    padding:10px 0;border-bottom:1px solid var(--glass-line)}
  .ratetable>.r:last-child{border-bottom:0}
  .ratetable>.r>label{flex:1;min-width:0;font-size:14.5px;color:var(--ink);
    font-weight:400}
  .ratetable>.r>span{flex:none;display:flex;align-items:center;gap:6px}
  /* The gap count beside a category name (2026-09-12, the ranked hours
     table). Small, grey and never the control: see `setup_rates.html`. */
  .ratetable>.r>i{flex:none;font-size:12.5px;color:var(--muted);
    font-style:normal;white-space:nowrap}
  .ratetable>.r input{width:84px;text-align:right;
    font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
    font-size:14px}
  .ratetable>.r b{font-size:13px;color:var(--muted);font-weight:500;width:10px}

  /* A LINE OF CHIPS. A chip that changes an answer is a `<button>` inside
     the page's one form, so the page works with no script at all; the one
     that is chosen carries `on`. A chip says a MODEL and, where we know
     one, the product's own name after it - ruling 133's "say the model"
     read onto a standing answer. */
  .chipline{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
  .chip{display:inline-flex;align-items:baseline;gap:7px;padding:7px 13px;
    border-radius:999px;border:1px solid var(--glass-line);
    background:var(--glass);color:var(--ink);font-size:12.5px;
    font-family:inherit;cursor:pointer;text-decoration:none;line-height:1.3}
  .chip:hover{border-color:var(--accent)}
  .chip>b{font-family:var(--font-title);font-weight:600;font-size:13px;
    color:var(--ink)}
  .chip>i{font-style:normal;font-size:12.5px;color:var(--muted)}
  .chip.on{border-color:var(--accent);color:var(--accent-ink);
    box-shadow:inset 0 0 0 1px var(--accent)}
  .chip.on>b{color:var(--accent-ink)}
  .chip.flat{cursor:default}
  .chip.flat:hover{border-color:var(--glass-line)}
  .prov{font-size:12px;color:var(--muted)}

  /* THE LINE OVER A LONG PICK LIST (2026-09-15): a search box, the brands
     in that list as chips, and how many of his rows are still showing. It
     renders `hidden` and trimout.js shows it, so a browser with no script
     never sees a search box that cannot search. The chips are the page's
     own `.chip`, so a narrowed brand looks like every other chosen answer.

     44px of height on the search box, because this is filled in on a
     phone standing in a mechanical room. */
  .pickfind{margin-bottom:7px;max-width:360px}
  .pickfind input[type=search]{width:100%;min-height:44px;padding:10px 13px;
    border-radius:12px;border:1px solid var(--glass-line);
    background:var(--glass);color:var(--ink);font-family:inherit;
    font-size:15px;-webkit-appearance:none;appearance:none}
  .pickfind input[type=search]:focus{border-color:var(--accent);outline:none}
  .pickfind input[type=search]::-webkit-search-cancel-button{
    -webkit-appearance:none;appearance:none}
  .pickbrands{margin-top:7px;gap:6px}
  .pickbrands .chip{padding:5px 11px;font-size:12px}
  .pickcount{display:block;margin-top:6px;font-size:12px;color:var(--muted)}
  .pickcount:empty{display:none}
  /* THE WAY BACK TO EVERYTHING, under a box. One line, quiet. */
  .pickwide{display:inline-block;margin-top:8px;font-size:12.5px;
    color:var(--muted)}

  .clshead{display:flex;align-items:center;gap:10px;margin-bottom:10px}
  .clshead>h2{margin:0;flex:1;min-width:0}

  /* THE LANDING'S TWO CARDS. One column on a phone, because two 44px rows
     side by side at 390 are two rows a thumb misses. */
  .cfgcards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
    gap:24px}
  @media(max-width:720px){.cfgcards{grid-template-columns:1fr}}
  /* A CHIP THAT OPENS A LITTLE FORM. `<details>` again, for the top bar's
     own reason: a control that only exists after a script runs does not
     exist. The sheet sits in flow rather than floating, so a phone gets a
     form under the chips instead of a panel off the edge. */
  .dropchip{display:inline-block}
  .dropchip>summary{list-style:none}
  .dropchip>summary::-webkit-details-marker{display:none}
  .dropchip[open]>summary{border-color:var(--accent);color:var(--accent-ink)}
  .dropchip>form{width:100%;padding-top:4px}
  .chip>input{margin:0 2px 0 0;accent-color:var(--accent)}
  /* A ROW ON THE DEALER CONFIGURATION CARDS: a name, whatever it already
     answers, and the arrow. The hairline is on the TOP so the first row
     sits under the card's own heading, which is how the board draws it. */
  .cfgrow{display:flex;align-items:center;gap:10px;min-height:44px;
    border-top:1px solid var(--glass-line);font-size:14.5px;color:var(--ink);
    text-decoration:none}
  .cfgrow:hover{color:var(--accent-ink)}
  /* THE NAME NEVER WRAPS AND THE ANSWER MAY. A row whose label broke across
     two lines to make room for a count is a row that reads as two rows. */
  .cfgrow>span:first-child{flex:none;white-space:nowrap}
  .cfgrow>.v{flex:1;min-width:0;text-align:right;padding:6px 0;
    font-size:12.5px;color:var(--muted)}
  .cfgrow svg{flex:none;display:block}
  /* A FIGURE WITH ITS CURRENCY DRAWN BESIDE IT, never inside the box. */
  .moneybox{display:flex;align-items:center;gap:8px}
  .moneybox>b{font-size:15px;color:var(--muted);font-weight:500}
  .moneybox>input{flex:1;min-width:0;
    font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace}

/* ---------------------------------------------------------------------------
 * THE PROPOSAL FLOW, 2026-09-09. Rex's boards for the five screens a dealer
 * walks to sell a job: New proposal, Record the walk, Putting your proposal
 * together, Questions, the Proposal with its check, Check result and My
 * proposals. VALUES AND NEW NAMES ONLY: not one class in the frozen contract
 * is renamed here, and every rule below either dresses a class the boards
 * drew or adds a name for markup this file had no state for.
 * ------------------------------------------------------------------------- */

  /* THE TWO FIELD ROWS on the New proposal board: City / State / Zip, and
     Phone / Email. One column on a phone, because three boxes across 390
     points is three boxes nobody can type in. */
  .grid3{display:grid;grid-template-columns:2fr 1fr 1fr;gap:12px}
  .grid2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
  @media(max-width:560px){
    .grid3,.grid2{grid-template-columns:1fr}
  }

  /* RECORD THE WALK. The board's page is a title, one primary button, a
     small link and a Hint, so the only new paint is what those last two
     open. Both are `<details>`, which is this product's standing rule for a
     control that has to exist with the script switched off. */
  .upsmall>summary{list-style:none;cursor:pointer;font-size:13.5px;
    color:var(--muted)}
  .upsmall>summary::-webkit-details-marker{display:none}
  .upsmall>summary span{color:var(--accent-ink);font-weight:600}
  .upsmall>summary:hover span{color:var(--total)}
  /* THE DROP BOX IS COMPACT, which is the board's word for it: it opens
     under a link on a page whose subject is the button above it, and a
     32px-padded box would out-shout the thing a dealer came to press. */
  .drop-sm{padding:14px 16px;margin-top:10px;display:flex;gap:10px;
    align-items:center;flex-wrap:wrap;text-align:left}
  .hintbox>summary{list-style:none;cursor:pointer;display:inline-flex;
    align-items:center;justify-content:center;gap:8px;height:44px;
    border-radius:14px;padding:0 16px;font-size:13.5px;font-weight:600;
    background:var(--glass);color:var(--muted);
    border:1px solid var(--glass-line);
    -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
    box-shadow:var(--glass-shadow),var(--glass-inset)}
  .hintbox>summary::-webkit-details-marker{display:none}
  .hintbox>summary:hover{color:var(--ink)}
  .hintbox>summary svg{flex:none;display:block}
  .hintbox .saylist{list-style:none;margin:14px 0 0;padding:0;
    display:flex;flex-direction:column;gap:6px;font-size:13.5px;
    color:var(--muted)}

  /* THE WAIT PAGE'S BAR. INDETERMINATE ON PURPOSE: a percentage would be a
     claim about how far through a recording a reader is, and nothing in this
     product knows that. It travels instead, and it stops travelling for a
     dealer who has asked his machine to stop moving things. */
  .reading-bar{height:6px}
  .reading-bar .fill{width:38%;animation:reading 1.9s ease-in-out infinite}
  @keyframes reading{
    0%{margin-left:0}
    50%{margin-left:62%}
    100%{margin-left:0}
  }
  @media (prefers-reduced-motion:reduce){
    .reading-bar .fill{animation:none;width:38%}
  }

  /* THE FOUR STAGES BESIDE THE BAR, 2026-09-10, and the two small lines the
     walk page and My proposals gained with them. Four pills in a row, the one
     in force lit, so the bar says WHICH of the four things is happening. */
  .wstage{display:flex;gap:6px;flex-wrap:wrap;margin:14px 0 0}
  .wheld{font-size:12.5px;color:var(--muted);margin:14px 0 0}
  .mpgo{font-size:12.5px;margin-left:10px}

  /* THE QUESTIONS BOARD. Rex's `Questions.dc.html` and
     `PhoneQuestions.dc.html`: a title, a thin bar, one card of big tappable
     tiles, then Back and Next.

     `.qcount` IS NOW ONLY FOR A FIXED WIZARD POSITION, 2026-09-18. Rex: "It
     should not be changing how many questions I have left every time I
     select an answer. It would be better not to list the amount of questions
     and just have a status bar that is slowly completing." Every board whose
     denominator MOVES as he answers dropped its line: the questions screen
     on deploy 134, and the check board on the proposal and at `/check` on
     2026-09-18. What still draws this class is two screens whose total is
     fixed before the first answer and never moves - the project builder's
     guided deck (`job_questions_guided.html`, which a proposal never enters)
     and the dealer's proposal-standards pass (`setup_proposal_ask.html`) -
     and those read as "Step 3 of 8", which is not the thing he asked us to
     stop doing. */
  .qcount{font-family:var(--font-title);font-weight:600;font-size:14px;
    color:var(--muted);margin:0 0 12px}
  .qbar-thin{height:4px;margin:0 0 18px}

  /* THE TILES. `.answer` is the class the card macro has always put on an
     answer, so this dresses every answer in the app and adds no name; the
     `.tile` beside it is the two-column grid's own cell. A tile is a LABEL
     wrapping its radio, so the whole of it is the tap target - which is the
     board's word for it and the only part of this that matters on a phone. */
  .tiles{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;
    margin-top:18px}
  .answer.tile{display:flex;flex-direction:column;justify-content:center;
    gap:4px;min-height:84px;padding:16px 18px;margin:0;cursor:pointer;
    border:1px solid var(--glass-line);border-radius:14px;
    background:var(--glass);
    -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
    box-shadow:var(--glass-shadow),var(--glass-inset)}
  .answer.tile:hover{border-color:var(--accent)}
  .answer.tile input{position:absolute;opacity:0;width:0;height:0}
  .answer.tile:has(input:checked){border-color:var(--accent);
    background:var(--accent-bg);
    box-shadow:0 0 0 1px var(--accent-bg),0 14px 36px var(--accent-bg)}
  .tile-name{font-size:15.5px;font-weight:600;color:var(--ink)}
  .answer.tile:has(input:checked) .tile-name{color:var(--total)}
  /* THE ESCAPE BOX under a tile grid wears the same face every other box on
     a Glass screen wears. It was the browser's default, which on this ground
     is a white slot with a blue ring. */
  .tiles+div>input[type=text],.tiles~div>input:not([type]){
    background:var(--glass-soft);border:1px solid var(--glass-line);
    border-radius:12px;color:var(--ink);padding:10px 12px;font-size:14px;
    width:100%;max-width:340px}
  /* NO `:has` IS NOT NO TILE. A browser without it draws the tile and loses
     only the chosen ring, and the radio comes back so the answer is still
     visible and still tappable. */
  @supports not selector(:has(*)){
    .answer.tile input{position:static;opacity:1;width:auto;height:auto}
  }
  @media(max-width:640px){
    .tiles{grid-template-columns:1fr;gap:10px}
    .answer.tile{min-height:64px;padding:12px 16px}
    .tile-name{font-size:15px}
  }

  /* BACK AND NEXT. The board puts Back at the left and Next hard right on a
     desk, and gives Next the rest of the row on a phone. */
  .qnav{margin-top:22px}
  @media(max-width:640px){
    .qnav{gap:12px}
    /* AN ARROW NEVER STRETCHES. It is a 44px press at every width; the rest
       of the row is the count and the skip. The last child only takes the
       room when it is the button that builds. */
    .qnav .btn:last-child:not(.qarrow){flex:1}
  }

  /* THE PROPOSAL BOARD. Rex's `Proposal.dc.html` and `PhoneProposal.dc.html`:
     one card per room, four columns to a row, then a total card. */
  .proom{padding:14px 22px}
  /* SCOPED TO THE ROOM CARD ON THE MERGE, 2026-09-09. `.prow` is also the
     projects flow's list row (name, date, status, figure) one block above,
     and that row is 44 points tall on purpose. A room LINE is a different
     row that happens to share the name, so it is dressed inside `.proom`
     and neither list changes shape. */
  .proom .prow{display:flex;gap:16px;align-items:center;padding:10px 0;
    border-bottom:1px solid var(--glass-line)}
  .proom .prow:last-child{border-bottom:0}
  /* RULING 267: the keypad line wraps, and its grid takes the whole width
     of the row under the product, the count and the money. */
  .proom .prow.pkeyline{flex-wrap:wrap}
  .proom .pkeys{flex:1 1 100%;order:9;min-width:0}
  .pkind{width:150px;flex:none;font-size:13.5px;color:var(--muted)}
  .pwhat{flex:1;min-width:0;font-size:14px;color:var(--ink)}
  .pbuilt{font-size:12px;color:var(--muted);margin-top:2px}
  /* WIDE ENOUGH TO TYPE IN (2026-09-16). The count is a box on every row
     now, and with the script off it has a Save beside it. */
  .pqty{width:112px;flex:none;text-align:right;color:var(--muted);
    font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
    font-size:13px}
  /* WIDE ENOUGH FOR A FIGURE AND A SAVE (2026-09-17). */
  .pprice{width:150px;flex:none;text-align:right;color:var(--muted);
    font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
    font-size:13px}
  .pmoney{width:96px;flex:none;text-align:right;font-weight:600;
    font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
    font-size:13.5px;color:var(--ink)}
  /* CHANGING THE PRODUCT ON A LINE (2026-09-15). A quiet word beside the
     model, not a button competing with it: the row still reads as a line of
     a proposal, and the drop-down is under the word when he wants it. On a
     phone it sits under the model on its own, because the model already
     takes the whole width there. */
  .rowswap{display:inline-flex;align-items:center;gap:10px;margin-left:8px;
    vertical-align:baseline}
  .rowswap>details{display:inline-block}
  .rowswap summary,.rowswap a,.rowswapadd{cursor:pointer;font-size:12.5px;
    color:var(--accent-ink);text-decoration:none}
  .rowswap summary:hover,.rowswap a:hover,.rowswapadd:hover{
    text-decoration:underline}
  .rowswap summary{list-style:none;min-height:24px;line-height:24px}
  .rowswap summary::-webkit-details-marker{display:none}
  .rowswapform{margin:8px 0 4px;max-width:360px}
  .rowswapbar{display:flex;flex-wrap:wrap;align-items:center;gap:10px;
    margin-top:8px}
  .rowswapundo{display:inline}
  .rowswapundo .btn{padding:2px 10px;min-height:24px;font-size:12px}
  .rowswapadd{display:inline-block;margin-top:10px}
  /* DEPLOY 155, FOLLOW-UP: "ADD TO <ROOM>" OPENS WHERE HE IS. `#pick-add`
     is drawn once, at the foot of the page, and `trimout.js` MOVES it into
     the room's own card when he presses the link - so it needs to read as
     part of that card and not a second one nested inside it, `.foldbody>
     .card`'s own trick two rules down for the identical reason. */
  .proom>#pick-add{border:0;background:none;box-shadow:none;
    border-top:1px solid var(--glass-line);margin:10px 0 0;padding:14px 0 0}
  /* THE PRODUCT'S OWN NAME IS THE DROP-DOWN (2026-09-16). Rex: "if I click on
     the device to change it, it should list devices similar to that." It
     still reads as the line of a proposal - same ink, same size - with a
     dotted underline saying it can be opened. */
  .rowname{display:inline}
  .rowname>summary{display:inline;cursor:pointer;list-style:none;
    color:var(--ink);text-decoration:underline dotted;
    text-underline-offset:3px}
  .rowname>summary::-webkit-details-marker{display:none}
  .rowname>summary:hover,a.rowname:hover{color:var(--accent-ink)}
  a.rowname{color:var(--ink);text-decoration:underline dotted;
    text-underline-offset:3px}
  /* HOW MANY, ON THE LINE. The Save is the no-script road and `trimout.js`
     takes it away where it can submit on a change instead. */
  .rowqty{display:inline-flex;align-items:center;gap:6px;
    justify-content:flex-end}
  .rowqty input{width:46px;text-align:right;padding:2px 6px;font-size:13px;
    min-height:24px}
  .rowqty .btn{padding:2px 8px;min-height:24px;font-size:12px}
  .rowqty.saves .btn{display:none}
  /* THE PRICE, ON THE LINE (2026-09-17). The count box one rule up, in
     dollars: same shape, same Save, same no-script road. The "your price"
     mark is quiet on purpose - it says a figure is his without competing
     with the figure. */
  .rowprice{display:inline-flex;align-items:center;gap:6px;
    justify-content:flex-end}
  .rowprice input{width:76px;text-align:right;padding:2px 6px;font-size:13px;
    min-height:24px}
  .rowprice .btn{padding:2px 8px;min-height:24px;font-size:12px}
  .rowprice.saves .btn{display:none}
  .rowpricekeep{display:inline-flex;align-items:center;gap:6px;margin-top:4px;
    justify-content:flex-end}
  .rowpricekeep .btn{padding:2px 8px;min-height:24px;font-size:12px}
  .rowyours{font-size:11.5px;color:var(--muted)}
  /* RENAME AND REMOVE, ON THE LINE, AND THE ROOM NAME AS A CONTROL (Rex,
     2026-09-18: "full editing power over the proposal"). The two words sit
     after the product in the same quiet size as Undo; the name box opens
     under the row like the swap list does; the room heading keeps its size
     and gains the dotted underline every tappable name on this row wears. */
  /* THE RED X (Rex, 2026-09-21): tiny, far right, red, and the only
     control on the row that asks before it acts. */
  .pgone{flex:none;width:28px;text-align:right}
  .x{background:none;border:0;padding:0;width:24px;height:24px;border-radius:50%;
     color:#c62828;font-size:20px;line-height:24px;cursor:pointer;font-family:inherit}
  .x:hover,.x:focus-visible{background:rgba(198,40,40,.12);outline:none}
  .rowswapundo .x{margin-left:6px}
  @media(max-width:640px){.x{width:34px;height:34px;line-height:34px}.pgone{width:38px}}
  /* RULING 201 (2026-09-21): the check page's Remove on an "Added for you"
     line. A link and not a button, posting to the strip's own endpoint. */
  .linkish{background:none;border:0;padding:0;color:#c62828;cursor:pointer;
    font:inherit;font-size:13.5px;text-decoration:underline dotted}
  .linkish:hover,.linkish:focus-visible{text-decoration:underline;outline:none}
  .rowedit{display:inline-flex;align-items:center;gap:10px;margin-left:8px;
    vertical-align:baseline}
  .rowedit>details{display:inline-block}
  .rowedit summary{font-size:12.5px;color:var(--muted)}
  .rowedit .rowremove{display:inline}
  .rowedit .btn,.rowrestore .btn{padding:2px 10px;min-height:24px;
    font-size:12px}
  .rowrename{display:flex;flex-wrap:wrap;align-items:center;gap:6px;
    margin:6px 0 2px}
  .rowrename input[name=name]{width:240px;max-width:100%;padding:4px 8px;
    font-size:13px}
  .rowrename .btn{padding:2px 8px;min-height:24px;font-size:12px}
  .rowrename.saves .rowsave{display:none}
  /* 2026-09-30: his total labor hours, on the total card. */
  .rowlabor{display:flex;flex-wrap:wrap;align-items:center;gap:6px;
    margin:6px 0 2px;font-size:13px}
  .rowlabor input[name=labor_hours]{padding:3px 6px;font-size:13px}
  .rowlabor .muted{color:var(--muted)}
  .rowlabor .btn{padding:2px 8px;min-height:24px;font-size:12px}
  .rowlabor.saves .rowsave{display:none}
  .roomname>summary{font-size:16px;font-weight:600;color:var(--ink)}
  .roomname .rowrename input[name=name]{font-size:14px;font-weight:400}
  .premoved .prow .pwhat{color:var(--muted)}
  /* RULING 201: the "Added for you" strip, the why in small type under the
     product, indigo edge so it reads as ours and not as a room. */
  .pimplied{border-left:3px solid var(--indigo, #4f46e5)}
  .pimplied .pwhy{display:block;font-size:12px;color:var(--muted)}
  .pimplied .rownotmine{display:inline}
  .rowrestore{display:inline}
  /* THE SOURCES IN A ROOM, ON ONE ROW (2026-09-17). A quieter row than a
     product line, because it is a note about the room rather than a thing
     being sold, and the Hidden box sits under the names where he reads
     them. */
  .psources .pwhat{color:var(--muted)}
  .rowhidden{display:flex;flex-wrap:wrap;align-items:center;gap:10px;
    margin-top:6px}
  .rowhidden label{display:inline-flex;align-items:center;gap:6px;
    font-size:12.5px;color:var(--muted);cursor:pointer}
  .rowhidden .btn{padding:2px 8px;min-height:24px;font-size:12px}
  .rowhidden.saves .btn{display:none}
  /* THE SENTENCE, BESIDE THE ROW (2026-09-21). Rex: "it reloads and sends
     me to the top of the page." A live save says what it did next to the
     line it did it to, in the quiet size of every other note on the row,
     and a refusal says so in the page's own problem colour. */
  .rowsaid{display:block;font-size:12.5px;font-weight:400;color:var(--muted);
    margin-top:4px}
  .rowsaid.problem{color:var(--bad,#A32626)}
  /* RULING 297 (2026-09-29): the press he made stays marked until the
     answer lands, and after a refusal, so his choice is still on screen. */
  .btn.chosen{outline:2px solid currentColor;outline-offset:1px}
  /* THE WORD ON A LINE THAT HAS NO PRODUCT YET, inside the name's own
     summary, so the thing he taps and the thing he reads are one control. */
  .rowneeds{font-size:12.5px;color:var(--accent-ink);margin-left:8px}
  /* THE ONE WIDE SHEET. A select sizes itself to its longest option, and the
     longest option on a whole catalogue is a maker, a model, a part number
     and sixty characters of his own description - which runs off the side of
     a phone. It is held to the card, and the option text is what the search
     line above it is for. */
  #pick [data-pick],#pick .pickfind{max-width:420px}
  #pick select,#pick input[type=search],#pick input[data-pick-text]{
    width:100%;max-width:100%}
  #pick select[data-pick-family]{width:auto;max-width:100%}
  @media(max-width:640px){
    /* BESIDE THE MODEL AND NOT UNDER IT. The phone row already stacks the
       model onto its own line, so a word that takes a second line as well
       costs a third of the card for one link. It wraps on its own where the
       model is long. */
    .rowswap{gap:14px;margin-left:10px}
    .rowswapform{max-width:none}
    .rowswap summary,.rowswap a,.rowswapadd{min-height:44px;
      line-height:44px;font-size:14px}
    .rowswapundo .btn{min-height:44px;font-size:14px;padding:0 14px}
    /* A TAP TARGET AND NOT A WORD (2026-09-16). The name is the control on a
       phone too, so it gets the 44 points every other control on this row
       has, and the count box is typed on with a thumb. */
    .rowname>summary,a.rowname{display:inline-block;min-height:44px;
      line-height:1.5;padding:10px 0}
    .rowqty input{width:64px;min-height:44px;font-size:16px}
    .rowqty .btn{min-height:44px;font-size:14px;padding:0 14px}
    .rowedit{gap:14px;margin-left:10px}
    .rowedit summary{min-height:44px;line-height:44px;font-size:14px}
    .rowedit .btn,.rowrestore .btn,.rowrename .btn{min-height:44px;
      font-size:14px;padding:0 14px}
    .rowrename input[name=name]{min-height:44px;font-size:16px}
    .rowprice input{width:92px;min-height:44px;font-size:16px}
    .rowprice .btn,.rowpricekeep .btn{min-height:44px;font-size:14px;
      padding:0 14px}
    .rowhidden label{min-height:44px;font-size:14px}
    .rowhidden .btn{min-height:44px;font-size:14px;padding:0 14px}
  }
  .ptotal{padding:12px 24px;margin-bottom:16px}
  .ptotal .spread{padding:4px 0;align-items:center}
  .ptotal span{font-family:var(--font-title);font-weight:600;font-size:14px;
    color:var(--muted)}
  .ptotal span.big{font-size:16px;color:var(--ink)}
  .ptotal b{font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
    font-weight:600;font-size:14px;color:var(--ink)}
  .ptotal b.total{font-size:26px;color:var(--total)}
  .ptotal-rule{height:6px;border-top:1px solid var(--glass-line)}
  /* ON A PHONE the row stacks: the model leads and the class and the count
     fold under it, which is what `PhoneProposal.dc.html` draws. */
  @media(max-width:640px){
    .proom{padding:18px 20px}
    .proom .prow{flex-wrap:wrap;gap:4px 12px;padding:12px 0;
      align-items:flex-start}
    .pwhat{order:1;flex:1 1 100%;font-size:14px}
    .pkind{order:2;width:auto;font-size:12px}
    .pqty{order:3;width:auto;text-align:left;font-size:12px;flex:none}
    .pprice{order:4;width:auto;text-align:left;font-size:12px;flex:none}
    .pmoney{order:5;margin-left:auto;width:auto}
  }

  /* THE GREEN ONE-LINE ANSWER. "18 lines checked and found right." */
  .okstrip{display:flex;align-items:center;gap:10px;min-height:44px;
    border:1px solid var(--okbg);border-radius:var(--radius);
    background:var(--okbg);padding:12px 18px;color:var(--ok);
    font-size:14.5px}
  .okstrip svg{flex:none;display:block}

  /* MY PROPOSALS. Rex's `MyProposals.dc.html`: one panel of hairline rows -
     the name, the date, a pill and the total - and no Edit button, because
     the name is the link. */
  .mprow{display:flex;gap:16px;align-items:center;padding:16px 0;
    min-height:44px;border-bottom:1px solid var(--glass-line)}
  .mprow:last-child{border-bottom:0}
  .mpname{flex:1;min-width:0;font-size:15px;font-weight:600}
  .mpdate{width:130px;flex:none;font-size:13.5px;color:var(--muted)}
  .mppill{width:110px;flex:none}
  .mptotal{width:110px;flex:none;text-align:right;font-weight:600;
    font-family:"IBM Plex Mono",ui-monospace,Menlo,Consolas,monospace;
    font-size:14px;color:var(--ink)}
  .mpmore{flex:none}
  /* 2026-09-25: the small second line a row's own name and its client both
     get when they say two different things - `_joblabel.html`, the one
     macro every list that picks a job draws its label from. Quiet on
     purpose: font-weight normal, muted, no margin worth noticing. */
  .rowsub{display:block;font-weight:400;font-size:12.5px;color:var(--muted)}
  @media(max-width:640px){
    .mprow{flex-wrap:wrap;gap:6px 12px}
    .mpname{flex:1 1 100%}
    .mpdate,.mppill,.mptotal{width:auto}
    .mptotal{margin-left:auto}
  }

  /* THE NATIVE POPUP, 2026-09-15. Rex, on the Standards page in the dark
     theme on Windows Chrome: he opens "Choose from your catalog" and the
     list is a white sheet of near-white text - every row invisible but the
     highlighted one.

     WHY. The open list of a `<select>` is not drawn by this stylesheet. It
     is browser chrome, and Chromium paints it from the control's OWN
     computed `background-color` and `color`. The glass skin gives every
     field `background:var(--glass-soft)`, which in the dark theme is white
     at five per cent - a colour Chromium cannot composite over a page it is
     not drawing, so it falls back to opaque white - and `color:var(--ink)`,
     which in the dark theme is #e9f0ed. White paper, white ink. The closed
     control was always fine, because there the glass DOES composite over
     the page behind it; only the popup was unreadable, which is why this
     went unseen for six deploys.

     THE FIX IS TWO BELTS. `color-scheme` tells the browser which palette to
     draw its own chrome from, and it is declared per theme rather than left
     to the root's, so a popup is right even where a control is rendered
     outside the themed shell. Then the options carry an EXPLICIT opaque
     background and colour, which is what actually settles Chromium: an
     author colour on the option outranks anything it would infer from the
     control. The values are the theme's own tokens written out, not
     `var()`, because the popup is browser chrome and custom properties are
     not reliably resolved there - the one place in this file where a token
     is spelled as a number on purpose.

     ONE RULE, EVERY SELECT. The picker, the row-swap sheet, the kind
     select, the rates and config pages and every field on every screen are
     one selector here, because they are one stylesheet - a per-page fix
     would be five copies and four of them would go stale. */
  select{color-scheme:light}
  select option,select optgroup{background-color:#ffffff;color:#17211f}
  :root[data-theme="light"] select{color-scheme:light}
  :root[data-theme="light"] select option,
  :root[data-theme="light"] select optgroup{
    background-color:#ffffff;color:#17211f}
  :root[data-theme="dark"] select{color-scheme:dark}
  :root[data-theme="dark"] select option,
  :root[data-theme="dark"] select optgroup{
    background-color:#161a3c;color:#f1f3ff}

  /* CONFIG FIELD GRID ALIGNMENT, 2026-09-15. Rex, iPhone Safari, the Dealer
     configuration page's Lighting section, dark theme: "Watts when the
     column is blank" / "Default module type" / "Default keypad style" sit
     side by side in a `.row` of three `.field`s. Each label wraps to a
     different number of lines at that width, so each field's input lands at
     a different height and the help text under it reads as a narrow ragged
     column. "Default dimmer" / "Default switch" have the same problem.
     "These don't line up right."

     `.row` and `.field` are frozen selectors (see the class-contract note
     at the top of this file) - nothing here renames or removes either.
     This is a later cascade layer, scoped to `.row:has(> .field + .field)`:
     a `.row` with two or more `.field`s SIDE BY SIDE. A `.row` used for
     anything else (a button bar, a pill row, or the rate rows on
     standards.html/setup_rates.html, which pair one `.field` with a plain
     label column, never two `.field`s next to each other) does not match
     and is untouched. Found by grepping every template for `.row` holding
     2+ adjacent `.field`s: this same shape sits in defaults.html (company
     name/technician phone; the Lighting row; light switches we replace),
     _profileform.html, admin.html, pricing.html, products.html,
     setup_project.html and standards_bundles.html - so the fix is one rule
     for the class, not five patches per page.

     WIDE (the default here, like every other rule in this file): the row
     becomes a grid, and each field spans it with `grid-template-rows:
     subgrid` - label, input, help are the SAME three row tracks across
     every field in the group, so the tallest label's height (however many
     lines it wraps to) sets the row for all of them and every input shares
     one baseline. Current Safari (16+, 2022) and Chrome (117+, 2023) both
     render subgrid correctly - checked in both this pass. Older engines
     without subgrid fall through to the `@supports not` block: a fixed
     min-height on the label (roughly two lines at this label's size) keeps
     inputs level in the common case, though it cannot match a label that
     wraps past that estimate the way subgrid does automatically.

     THE SPAN IS EXACTLY THREE AND EVERY FIELD IN SCOPE MUST HAVE EXACTLY
     THREE TOP-LEVEL CHILDREN: label, the input/select/pick, one help
     block. Measured on this pass: a `.field` with a FOURTH child (found in
     standards_bundles.html's pre-wire number fields, which had two
     separate conditional help divs - "from your standard" and a why line,
     both direct children) does not get a fourth row - a bounded subgrid
     has nowhere to put it, and the extra child lands on top of the input
     instead, invisibly, which is worse than the misaligned inputs this
     rule exists to fix. That page's template now wraps its two help lines
     in one div so every field stays at three children; any new field
     added under this CSS must do the same rather than stacking a second
     bare div after the input.

     NARROW (max-width:640px, this file's own established phone
     breakpoint): one column. Each field is full width with its own label,
     input and help text stacked in a natural column - exactly what
     `.field` already does outside a `.row`. Inputs and selects keep their
     48px height from the base `.field input,.field select` rule above;
     nothing here touches that. */
  .row:has(> .field + .field){
    display:grid;
    grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
    grid-template-rows:auto auto 1fr;
    align-items:start;
  }
  .row:has(> .field + .field)>.field{
    display:grid;
    grid-template-rows:subgrid;
    grid-row:span 3;
    margin-bottom:0;
  }
  .row:has(> .field + .field)>.field>*:nth-child(2){align-self:end}
  @supports not (grid-template-rows:subgrid){
    .row:has(> .field + .field)>.field>label{min-height:40px}
  }
  @media(max-width:640px){
    .row:has(> .field + .field){
      display:flex;
      flex-direction:column;
      align-items:stretch;
    }
    .row:has(> .field + .field)>.field{
      display:block;
      width:100%;
    }
    .row:has(> .field + .field)>.field>label{min-height:0}
  }

  /* ============= A FOLDED SECTION OF THE STANDARDS PAGE ==========
   *
   * Rex, 2026-09-16: "Each category should be able to collapse so that I
   * don't have to scroll through all of the different selections that I've
   * already made just to change one device's standard."
   *
   * A `<details>` wearing the card it replaced, which is why the border,
   * the radius and the padding below are the `.card` rule's own numbers:
   * a section that folds must not look like a different kind of thing from
   * the section beside it. See `app/templates/_fold.html`.
   *
   * THE SUMMARY IS A ROW AND NOT A BUTTON. Title left, mark hard right, the
   * whole row a target, and the native marker off - the same three lines
   * every other disclosure in this file already uses. */
  .fold{background:var(--bg);border:1px solid var(--rule);border-radius:14px;
    padding:0;margin-bottom:12px}
  .fold>summary{list-style:none;cursor:pointer;display:flex;
    align-items:center;gap:10px;padding:14px 16px;
    font-family:var(--font-title);font-weight:600;font-size:16px;
    border-radius:14px}
  .fold>summary::-webkit-details-marker{display:none}
  .fold>summary:hover{background:var(--tint)}
  .fold>summary:focus-visible{outline:2px solid var(--accent);
    outline-offset:2px}
  .fold[open]>summary{border-bottom:1px solid var(--rule);
    border-radius:14px 14px 0 0}
  .foldtitle{flex:1;min-width:0}
  /* THE CHEVRON IS DRAWN HERE rather than in the markup: it is decoration
   * and says nothing the element does not already say to a screen reader,
   * unlike the mark beside it, which is the answer. */
  .fold>summary::after{content:"";width:8px;height:8px;flex:none;
    border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
    transform:rotate(45deg) translate(-2px,-2px);transition:transform .12s}
  .fold[open]>summary::after{transform:rotate(-135deg) translate(-2px,-2px)}
  .foldbody{padding:14px 16px 16px}
  /* A CARD INSIDE A FOLD IS THE FOLD. The sections that moved here brought
   * their own `.card` wrapper, and a card in a card is two borders around
   * one thing. */
  .foldbody>.card{border:0;background:none;padding:0;margin:0;box-shadow:none}
  .fmark{flex:none;display:block}
  .fmark.ok{color:var(--ok)}
  .fmark.gap{color:var(--bad)}
  @media(max-width:480px){
    .fold>summary{padding:12px 13px;font-size:15px}
    .foldbody{padding:12px 13px 14px}
  }
/* ---------------------------------------------------------------------------
 * THE SETUP PATH, 2026-09-16. Rex: "there should be prompts leading me to the
 * next part of the set up... a little arrow pointing at the next part", and a
 * green check beside a section he has finished.
 *
 * THE CHECK AND THE ARROW ARE STROKES, not characters: a text tick renders as
 * whatever face the box happens to have, and `--ok` is already green in both
 * paints. Nothing here is a new colour.
 * ------------------------------------------------------------------------- */
  .pathdone{color:var(--ok);display:inline-flex;align-items:center}
  .pathnext{color:var(--accent);flex:none}
  .setuppath .card.at{border-color:var(--accent)}
  .cfgrow.as-row{border-top:1px solid var(--glass-line)}
  .cfgrow.as-row>span:first-child{display:flex;align-items:center;gap:8px}
  .cfgrow.as-row .btn{white-space:nowrap}
  /* THE WAY ON, at the foot of a step page. Right where his thumb already is
     after a Save, and the only control in the strip. */
  .setupnext{max-width:860px;margin:0 auto 34px;padding:0 20px;
    display:flex;align-items:center;justify-content:flex-end;gap:14px}
  .setupnext p{margin:0;font-size:12.5px;color:var(--muted)}
  .setupnext form{margin:0}
  @media(max-width:520px){
    .setupnext{padding:0 16px}
    .setupnext .btn{flex:1;text-align:center}
  }
  /* THE BLANKS, NAMED, inside the friendly reminder (2026-09-16). A short
     list of links, never a count. */
  .remindlist{margin:0 0 14px;padding:0 0 0 18px;font-size:13.5px}
  .remindlist:empty{display:none}
  .remindlist li{margin:2px 0}
  /* CENTRED, like every other modal. `*{margin:0}` at the top of this sheet
     turns off the auto-centring a <dialog> gets for free, so it has been
     opening in the top left corner; it is named here rather than in the
     reset because the reset is the markup contract. */
  dialog[data-remind]{margin:auto}
  /* A NAME ON THE COMPACT PATH IS A DOOR, and it is drawn as the row's own
     text rather than as a blue link: the row already looks like the board's
     rows, and a line of five blue names would read as a list of links
     instead of a path. */
  .cfgrow.as-row>span:first-child a{color:inherit;text-decoration:none}
  .cfgrow.as-row>span:first-child a:hover{color:var(--accent-ink);
    text-decoration:underline}

/* THE NAME PICKER (2026-09-26), trimout.js's `data-namepick`: his own names
   in a list under the box, "Did you mean" chips over it, "Add ... as new"
   last. In the page's flow under the box, not floating, so a phone's
   keyboard can never cover it. */
.namepick{margin-top:4px;border:1px solid var(--rule);border-radius:10px;
  background:var(--bg);max-height:260px;overflow-y:auto;padding:4px}
.namepick-head{font-size:12px;color:var(--muted);margin:6px 6px 4px}
.namepick .chipline{margin:0 4px 6px}
.namepick-row{display:block;width:100%;text-align:left;padding:9px 10px;
  border:0;background:none;color:var(--ink);font:inherit;font-size:14px;
  border-radius:6px;cursor:pointer}
.namepick-row:hover,.namepick-row:focus{background:var(--tint)}
.namepick-new{color:var(--accent)}

/* ---------------------------------------------------------------------------
 * RULING 247 (Rex, 2026-09-26): THE TWO SIDES, NOTICEABLY SEPARATED.
 * "I know it's all under the same Trimout website but it needs to be
 * noticeably separated... no bleed over or confusion."
 *
 * ONE ACCENT PER SIDE, BOTH ALREADY IN THIS SHEET. Proposals wear the
 * indigo every button already wears (`--accent`); Control4 Projects wear
 * the sky blue of the right-hand glow (`--glow-blue`'s own hue, #1d73e8).
 * Nothing new is painted: a side is a dot, a hairline and a soft tint, and
 * the pill of the menu it lives under. The band sits under the pill bar,
 * outside the sticky header, so a phone keeps its height while scrolling.
 * ------------------------------------------------------------------------- */
  :root,:root[data-theme="light"]{--side-proposal:#3b42ff;
    --side-proposal-bg:rgba(59,66,255,.10);--side-project:#1d73e8;
    --side-project-bg:rgba(29,115,232,.12)}
  :root[data-theme="dark"]{--side-proposal:#8e94ff;
    --side-proposal-bg:rgba(76,83,255,.18);--side-project:#5b9df0;
    --side-project-bg:rgba(29,115,232,.24)}
  [data-side="proposal"]{--side:var(--side-proposal);
    --side-bg:var(--side-proposal-bg)}
  [data-side="project"]{--side:var(--side-project);
    --side-bg:var(--side-project-bg)}
  .tabs .nav.cur>summary{box-shadow:inset 0 -2px 0 var(--side)}
  .sideband{max-width:1180px;margin:12px auto 0;padding:0 24px}
  /* CENTRED on a wide screen, because each page sets its own column width
     inline and the band cannot know it; on a phone every column starts at
     the same 14 point gutter, so there it sits flush left with the heading. */
  .sideband .in{display:flex;align-items:center;justify-content:center;
    gap:6px;flex-wrap:wrap}
  .sidename,.sidetab{display:inline-flex;align-items:center;gap:8px;
    min-height:36px;padding:0 14px;border-radius:999px;
    font:600 13px/1 var(--font-title);letter-spacing:.04em;
    text-transform:uppercase;text-decoration:none;color:var(--muted);
    border:1px solid transparent}
  .sidename::before,.sidetab::before{content:"";width:8px;height:8px;
    border-radius:50%;background:var(--side);flex:none}
  .sidename,.sidetab.cur{color:var(--ink);background:var(--side-bg);
    border-color:var(--side)}
  .sidetab:not(.cur){opacity:.85}
  .sidetab:not(.cur)::before{opacity:.55}
  a.sidetab:hover{color:var(--ink);background:var(--pill-on)}
  @media(max-width:560px){
    .sideband{padding:0 14px;margin-top:10px}
    .sideband .in{justify-content:flex-start}
    .sidename,.sidetab{font-size:12px;padding:0 11px;min-height:34px}
  }
  /* Home is his and neither side's, so its rows carry the mark instead of
     the page: a small chip on a Needs you row, a dot on a Pick up heading. */
  .sidechip{display:inline-flex;align-items:center;gap:6px;
    font-size:11.5px;font-weight:600;color:var(--muted);margin-right:6px}
  .sidechip::before,.sidename-sm::before{content:"";width:7px;height:7px;
    border-radius:50%;background:var(--side);flex:none}
  .sidename-sm{display:inline-flex;align-items:center;gap:8px}
  .mpside{flex:none;width:84px}
  @media(max-width:640px){.mpside{width:auto}}

/* 2026-09-28: SAVED, SAID QUIETLY. The line in the corner of a page that
   saves itself, the green check beside a box that landed, the small menu
   under a photo or a description, and the photo shown big. `trimout.js`
   draws all four. */
.savestatus{position:fixed;right:14px;bottom:calc(12px + env(safe-area-inset-bottom));
  z-index:40;pointer-events:none;font-size:12px;line-height:1.3;
  padding:5px 11px;border-radius:99px;background:var(--bg);
  color:var(--muted);border:1px solid var(--rule);box-shadow:var(--shadow);
  max-width:calc(100vw - 28px)}
body.has-savestatus{padding-bottom:52px}
.savestatus.ok::before{content:"";display:inline-block;width:4px;height:8px;
  border:solid var(--ok);border-width:0 2px 2px 0;transform:rotate(45deg);
  margin:0 8px 2px 2px}
.savestatus.bad{color:var(--bad);border-color:var(--bad);opacity:1}
@media (max-width:700px){
  .savestatus{right:auto;left:50%;transform:translateX(-50%);white-space:nowrap}
  .savestatus.bad{white-space:normal;width:max-content}
}
.savetick{position:absolute;width:18px;height:18px;background:var(--ok);
  border-radius:99px;pointer-events:none;opacity:0;transition:opacity .25s ease}
.savetick::after{content:"";position:absolute;left:6px;top:3px;width:4px;
  height:8px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg)}
.savetick.on{opacity:1}
.savetick.gone{opacity:0;transition:opacity .9s ease}
.tapmenu{position:absolute;z-index:60;min-width:170px;padding:6px;
  border-radius:12px;background:var(--bg);
  border:1px solid var(--rule);box-shadow:var(--shadow);display:flex;
  flex-direction:column}
.tapmenu-item{display:block;text-align:left;font:inherit;font-size:15px;
  padding:11px 12px;border:0;border-radius:8px;background:none;
  color:var(--ink);cursor:pointer}
.tapmenu-item:hover,.tapmenu-item:focus{background:var(--pill-on,var(--tint))}
.tapmenu-item.bad{color:var(--bad)}
.photoview{position:fixed;inset:0;z-index:70;background:rgba(0,0,0,.86);
  display:flex;align-items:center;justify-content:center;padding:16px;
  cursor:zoom-out}
.photoview img{max-width:100%;max-height:100%;object-fit:contain;
  border-radius:8px;background:#fff}
.pphoto{position:relative;flex:0 0 auto;scroll-margin-top:110px}
.pphoto-shot{width:96px;height:96px;border-radius:10px;background:#dfe3e1;
  border:0;padding:0;overflow:hidden;display:flex;align-items:center;
  justify-content:center;cursor:pointer;color:#5b6461;font:inherit;
  font-size:12.5px;font-weight:600}
.pphoto-shot img{width:100%;height:100%;object-fit:contain;display:block}
.pphoto-shot.empty{border:1.5px dashed #9aa4a1}
.pphoto-file{position:absolute;width:1px;height:1px;opacity:0;
  overflow:hidden;clip:rect(0 0 0 0)}
.pphoto form .upbar{margin:6px 0 0}
.pphoto .busysaid{display:block;margin:4px 0 0!important;max-width:220px}
