/* MODEL.html's own styles, moved out of the page's <style> block as they
   stood (Movie, 24 Oct: "how can we reduce the MODEL.html size in a nice
   organized way" -- stage 1). Loaded where the block was, after
   shell-bars.css, so the cascade is unchanged. */
  :root {
    color-scheme: dark;

    /* TYPE, DECLARED ONCE ON PURPOSE.
       MODEL.dc.html carries 284 inline `font-family:` declarations and the
       site 391 across seven files, so changing the typeface there is 391
       edits and a find-and-replace through a 22,000-line file. Here it is
       three lines. When the faces are redesigned, this block is the change
       and every later tier inherits it.

       Barlow / Barlow Condensed / DM Mono are all SIL OFL and vendored in
       vendor/fonts/ with the licence beside them -- nothing is fetched
       from a CDN. Each stack keeps a real fallback so the page is legible
       before the woff2 lands, and if a face is ever swapped for one with
       different metrics the fallback is what a reader sees mid-swap. */
    --font-ui:   'Barlow Condensed', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-body: 'Barlow', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-mono: 'DM Mono', ui-monospace, Menlo, Consolas, monospace;

    /* COLOUR IS NOT DECLARED HERE. palette.js writes every --role token onto
       this element at boot, because 92% of MODEL's colour is set from
       JavaScript (canvas fills, inline styles) and only a table both the
       stylesheet and the painters can read reaches all of it. The fallbacks
       below are the night skin, so the page is never unpainted between parse
       and boot -- and never a SECOND definition to keep in step, because
       palette.js overwrites all of them. See RD-DOCUMENTS/SPEC-skins.md. */
    --surface-page: #1d1f20;
    --surface-panel: rgba(20,22,23,0.50);
    --surface-chip: #2a2d2e;
    --edge-panel: #3a3d3f;
    --ink-primary: #e7e5e2;
    --ink-secondary: #b9bcbe;
    --ink-quiet: #8b8f92;
    --accent: #fd0000;
    --accent-mark: #f0b429;
  }
  html, body { margin:0; height:100%; overflow:hidden;
    /* Two properties rather than the `background:` shorthand: its layer list
       parses differently with and without an image, and a skin that later
       defines --surface-page-tex should not have to re-learn that. */
    background-color: var(--surface-page); background-image: var(--surface-page-tex, none);
    font: 13px/1.45 var(--font-body);
    color: var(--ink-primary); }
  #plan { display:block; width:100%; height:100%; cursor:grab; touch-action:none; }
  /* THE DIMENSION POP-UP: HORIZ / VERT / DIAG over the sheet while the tool
     is up (Movie, 17 Oct: "it should be in PROPERTIES area but should also
     pop up somewhere when the dimensions are in use"). */
  /* AT THE FOOT OF THE SHEET, not its head: a string is most often stood off
     above a house, and a pop-up there took the press that placed it. */
  #dim-pop { position:fixed; bottom:72px; left:50%; transform:translateX(-50%); z-index:30;
    display:flex; gap:4px; padding:4px 6px; border-radius:6px;
    background: var(--surface-panel); border:1px solid var(--edge-panel); }
  #dim-pop[hidden] { display:none; }
  /* AUTO DIMS / HAND DIMS ON THE SHEET: two chips beside the north arrow
     that show or hide each kind (Movie, 18 Oct). */
  #dims-show { position:absolute; display:flex; gap:4px; pointer-events:auto; }
  #dims-show[hidden] { display:none; }
  #dims-show .sel-chip { flex:none; white-space:nowrap; padding:5px 7px; }
  #dims-show .sel-chip[hidden] { display:none; }
  /* AUTO DIMS ROWS: four rows a side, ticked on or off. */
  #auto-rows { position:fixed; z-index:40; display:flex; flex-direction:column; gap:4px; padding:8px;
    background:var(--surface-page, #fff); border:1px solid var(--line-soft, #c9c9c9);
    font:600 10px/1.2 'Barlow Condensed', sans-serif; letter-spacing:0.04em; color:var(--ink-primary, #1d1f20); }
  #auto-rows .auto-rows-side { display:flex; gap:10px; align-items:center; }
  #auto-rows .auto-rows-side > span { width:46px; }
  #auto-rows label { display:inline-flex; gap:3px; align-items:center; cursor:pointer; white-space:nowrap; }
  /* THE LENGTH BOX, LIVE: a line in hand (Movie, 12 Oct: "brighten up the
     LENGTH BOX ... make it orange ... around the outside, but leave the inner
     part darker"). The shared sheet draws the glow; the page names its colour,
     the tracking rays' own orange, since shell-bars.css holds only roles. */
  :root { --length-live: #ffa21a; --length-live-glow: rgba(255, 162, 26, 0.75); }
  /* THE LENGTH HINT (Movie, 12 Oct): "once they place their first point ...
     bring up a popup near the LENGTH area with a arrow pointed to it". Under
     the box, pointing up at it, in the box's own orange; a note, not a
     control, so a press through it still lands on the sheet. */
  #length-hint { position:fixed; z-index:8; pointer-events:none; max-width:260px;
    padding:7px 10px; border-radius:6px; border:1.5px solid var(--length-live);
    background-color: var(--surface-panel); color: var(--ink-primary);
    font-family: var(--font-ui); font-size:11px; font-weight:600; letter-spacing:0.05em;
    text-transform:uppercase; text-align:center; line-height:1.35;
    box-shadow: 0 0 9px 1px var(--length-live-glow); transform:translateX(-50%); }
  #length-hint-text { display:block; }
  /* THE ONE PART THAT TAKES A PRESS (Movie: "put a little checkbox where they
     can TURN OFF the popup in the future"). */
  #length-hint-off { display:inline-flex; align-items:center; gap:5px; margin-top:6px;
    pointer-events:auto; cursor:pointer; font-size:9px; color: var(--ink-quiet); }
  #length-hint-off input { margin:0; width:12px; height:12px; cursor:pointer; accent-color: var(--length-live); }
  #length-hint::before { content:''; position:absolute; left:50%; top:-8px; margin-left:-7px;
    border:7px solid transparent; border-top:0; border-bottom:8px solid var(--length-live); }
  #north-dock { position:fixed; z-index:6; pointer-events:none; }
  #north-dock > button { pointer-events:auto; position:absolute; }
  /* NO BOX ON THE ARROW (Movie, 14 Oct: "the NOrth arrow doesn't need a box").
     The hand is still here and still takes the press; what went is the panel
     it used to draw under itself on hover -- the arrow lights up instead, in
     paintNorth, because it is painted on the canvas and CSS cannot reach it. */
  /* THREE MATCHED CHIPS (Movie, 23 Oct, with a mockup): "update the NORTH
     arrow make it look like the other button" and "add a ZOOM TO FILL". The
     arrow's hand, HOUSE turn and ZOOM TO FILL wear one 40px box; the arrow
     is painted on the little canvas inside its own, by the same painter
     that drew it bare on the sheet from 14 Oct to today. */
  #north-dock .dock-chip { display:flex; align-items:center; justify-content:center; width:40px; height:40px;
    padding:0; border:1px solid var(--edge-panel); border-radius:6px; cursor:pointer; color:#ffffff;
    background-color: var(--surface-chip); background-image: var(--surface-chip-tex, none); }
  #north-hit canvas { display:block; width:38px; height:38px; }
  /* GREEN UNDER THE HAND, the site mark's own (14 Oct); the needles glow
     the same green, painted. */
  #north-hit:hover, #north-hit:focus-visible { border-color: var(--draw-site-datum, #6a9a57); }
  #strip-fill:hover, #strip-fill:focus-visible { border-color: var(--accent); color: var(--accent); }
  /* GOLD, AND THE SAME GOLD THE HOUSE MARK WEARS -- draw-origin, read here as
     the CSS twin of the key paintOrigin paints with. The LETTERING is tinted
     and the glow is the colour at strength, which is his "glow from the WHITE
     part like it is being iluminated in that color". */
  #strip-rotate:hover, #strip-rotate:focus-visible {
    border-color: var(--draw-origin, #f0b429);
    color: color-mix(in srgb, var(--draw-origin, #f0b429) 35%, #ffffff 65%);
    filter: drop-shadow(0 0 5px var(--draw-origin, #f0b429))
      drop-shadow(0 0 10px color-mix(in srgb, var(--draw-origin, #f0b429) 60%, transparent)); }
  #plan.dragging { cursor:grabbing; }
  /* A FILE HELD OVER THE SHEET (Movie, 23 Oct): the sheet says it will take
     it, in the accent, inside its own edge. */
  #plan.drop-here { outline:3px dashed var(--accent); outline-offset:-6px; }
  /* Over a grabbable corner. Declared AFTER .dragging so it wins while a
     corner is actually being dragged -- equal specificity, later rule. */
  #plan.over-corner, #plan.over-wall { cursor:move; }

  #hint { position:fixed; right:12px; bottom:var(--readout-bottom); z-index:5; color: var(--ink-quiet);
    font-family: var(--font-ui); font-size:12px; letter-spacing:0.04em;
    text-transform:uppercase; pointer-events:none; }
  /* REGISTRATION (Movie, 28 Oct), seated just over the pan hint in the
     same corner, in the panel's own surface so it reads as furniture. ON
     TOP of the side tabs and the open panel ("that box should cover over
     the side tabs if those are exposed, this one will only be needed on
     site plan but will be important to see"). */
  #registration { position:fixed; right:12px; bottom:calc(var(--readout-bottom) + 20px);
    z-index:7; display:flex; flex-direction:column; gap:4px; padding:6px 8px;
    border:1px solid var(--edge-panel); border-radius:4px;
    background-color: var(--surface-panel); background-image: var(--surface-panel-tex, none);
    font-family: var(--font-ui); font-size:11px; letter-spacing:0.04em; color: var(--ink-primary); }
  #registration[hidden] { display:none; }
  #registration .reg-head { display:flex; align-items:center; gap:4px; }
  #registration label { display:flex; align-items:center; justify-content:space-between; gap:6px; }
  #registration input { width:7.5em; font: inherit; text-align:right; }
  #registration .reg-sites:empty { display:none; }
  #registration .reg-sites { display:flex; flex-direction:column; gap:2px;
    padding:3px 0; border-block:1px solid var(--edge-panel); }
  #registration .reg-sites label { justify-content:flex-start; }
  #registration .reg-sites label span { flex:1; }
  #registration .reg-sites input { width:auto; margin:0; accent-color: var(--accent-mark, var(--accent)); }
  #registration .reg-sites button { font: inherit; font-size:10px; padding:0 5px; }
  #registration label[hidden] { display:none; }
  #registration [data-reg-align] { font: inherit; font-size:10px; letter-spacing:0.04em;
    white-space:normal; max-width:16em; padding:3px 6px; }
  #registration [data-reg-align][hidden] { display:none; }
  :root {
    /* THE RAIL BAND, left behind when the bar metrics went to
       shell-bars.css: only the sidebars read these two, and the bars
       they measure from are now somebody else's to name. */
    --bottom-band: calc(var(--house-h) + 72px);
    --rail-top: calc(var(--strip-h) + 80px); }
  /* PRINTSCREEN borrows the settings corner's own button rules rather than
     inventing a third button shape on the same bar. */
  #printscreen {
    font-family: var(--font-ui); font-size:9px; font-weight:600;
    letter-spacing:0.05em; text-transform:uppercase;
    display:inline-flex; align-items:center; justify-content:center;
    min-width:64px; padding:4px 6px; margin-left:8px; flex:0 0 auto;
    cursor:pointer; border:1px solid var(--edge-panel); border-radius:3px;
    background-color: var(--surface-chip); background-image: var(--surface-chip-tex, none);
    color: var(--ink-primary); }
  #printscreen:hover { border-color: var(--accent); color: var(--accent); }

  /* THE PRESENTATION. Built on the press, printed, and thrown away -- it is
     never part of the page a drafter works on, which is why it is hidden
     outright on screen instead of being styled to sit somewhere.
     WHITE AND BLACK WHATEVER THE SKIN IS. A night-skin page printed in its
     own colours is a sheet of ink; the pictures inside carry the skin they
     were drawn in, and the paper around them does not. */
  #presentation { display:none; }
  /* LANDSCAPE (Movie, 15 Sep). A plan is wider than it is tall and so is the
     tile grid, so portrait paper spends half its height on nothing. The size
     is declared rather than asked for: the browser's own dialog opens on
     landscape, and a drafter who wants portrait can still say so there. */
  @page { size: landscape; }
  @media print {
    /* THE APP IS A FIXED PANE -- html and body are height:100% with overflow
       hidden so the drawing never scrolls. On paper that clips the set to
       the first sheet: pages two and three exist and simply never come out
       of the printer. Paper scrolls, so the pane rules are lifted here. */
    html, body { height:auto; overflow:visible; }
    body > *:not(#presentation) { display:none !important; }
    #presentation { display:block; position:static; background:#fff; color:#000; }
    .pres-page { page-break-after:always; break-after:page;
      display:flex; flex-direction:column; height:100vh; padding:10mm;
      box-sizing:border-box; }
    .pres-page:last-child { page-break-after:auto; break-after:auto; }
    .pres-title { font-family:'Barlow Condensed',system-ui,sans-serif;
      font-size:13pt; font-weight:600; letter-spacing:.06em;
      text-transform:uppercase; margin-bottom:4mm; }
    .pres-art { flex:1; display:flex; align-items:center;
      justify-content:center; min-height:0; }
    .pres-art img { max-width:100%; max-height:100%; object-fit:contain; }
    /* THE TILES PAGE is a grid of whatever the rail is holding, so it grows
       a row when the drawing grows a level rather than being laid out for
       the six seats that exist today. Four across on landscape paper, where
       three left a hand's width of margin down each side. */
    .pres-tiles { display:grid; grid-template-columns:repeat(4, 1fr);
      gap:0.125in; align-content:start; width:100%; }
    /* A FIGURE CARRIES 40px OF BROWSER MARGIN. Narrowing the grid gap alone
       left the tiles 92px apart -- the gap the eye sees is the margin, not
       the grid's. */
    .pres-tile { display:flex; flex-direction:column; align-items:center;
      gap:2mm; margin:0; }
    .pres-tile img { width:100%; border:0.3mm solid #000; }
    .pres-tile figcaption { font-family:'Barlow Condensed',system-ui,sans-serif;
      font-size:8pt; letter-spacing:.05em; text-transform:uppercase; }
    /* NOT TO SCALE SITS ABOVE THE LOGO (Movie, 15 Sep), in the corner, on
       every page -- so the disclaimer travels with the picture even if one
       page is torn off the set. */
    .pres-foot { display:flex; justify-content:flex-end; margin-top:4mm; }
    .pres-mark { display:flex; flex-direction:column; align-items:center;
      gap:1mm; }
    .pres-scale { font-family:'Barlow Condensed',system-ui,sans-serif;
      font-size:9pt; font-weight:700; letter-spacing:.12em; }
    .pres-mark img { height:12mm; }
  }

  /* THE UPPER RIGHT IS A COLUMN, for the reason #lower-left is a row at the
     other end of the sheet: separately fixed elements sharing one inset are a
     layout kept in step by hand, and this corner was one -- the banner placed
     at +34px and the change note stepping to +68px to get out from under it,
     three numbers that had to be re-measured every time any of the three
     changed height. A flex column does that arithmetic itself.

     CLICK-THROUGH, for #readout's reason a few hundred lines up: it lies over
     the sheet, and a drafter drawing under it should reach the drawing. The
     column is wider than its narrowest child, so the transparent air beside a
     short line would take a press if the column did not refuse it. */
  #upper-right { position:fixed; right:12px; top:var(--note-top); z-index:6;
    display:flex; flex-direction:column; align-items:flex-end; gap:8px;
    pointer-events:none; }

  /* THE DRAFTER'S OWN CLOCK. It was asked for in the upper right (Movie, 20
     Sep: "in the upper right under the top banner ... put in there the
     current time and date of where the person is (timezonewise)") and stood
     there until 25 Sep, when he moved it and the visit count together: "move
     the '# VISITS' and the DATE/TIME down below PROJECT / MODEL / REAL ESTATE
     / BONE / CONSTRUCTION / SPECTS / etc. on the lowest row (where it will be
     covered with http:/addressess (won't matter if these 2 items are covered
     from time to time".

     SO IT IS A TENANT OF THE BAR'S FOOT LANE NOW and carries no position of
     its own -- shell-bars.css's #foot-lane places it, on the same row as the
     count it was always meant to rhyme with. What is left here is what the
     line is MADE of, which did not change.

     THE COUNTER'S TYPE, which is what was asked for -- Barlow Condensed at
     10px with the visit count's weight and tracking, so the page's two
     ambient readings are one voice: what the drawing owes nobody, at the
     head, and how many have been here, at the foot. THE INK IS THIS PAGE'S
     TOKEN and not the counter's literal rgba(29,31,32,0.45), which is ink for
     a white page; MODEL wears a night skin by default and palette.js repaints
     --ink-quiet for whichever skin is in force.

     TABULAR FIGURES, #readout's rule for the same reason: a proportional 1 is
     narrower than a 4, so a clock set in them twitches sideways every time the
     minute changes. */
  #clock { font-family: var(--font-ui); font-size:10px; font-weight:600;
    letter-spacing:0.06em; text-transform:uppercase; color: var(--ink-quiet);
    font-variant-numeric:tabular-nums; white-space:nowrap; }
  /* AN EMPTY SLOT TAKES NO ROOM -- the visit counter's slot beside it keeps
     the same rule for the same reason. The clock is empty until it is written
     and stays empty if it never is, and the lane must not hold a line that is
     not there. */
  #clock:empty { display:none; }

  /* RUNG 2's QUIET NOTE. A page holding an unsaved edit re-reads nothing when
     another page writes — throwing away this drafter's work to avoid throwing
     away someone else's is the defect the ruling exists to remove — so the
     only thing left to do is say so. Quiet on purpose: it reports a fact about
     the file, it does not ask for a decision, and it sits under SAVE rather
     than over the drawing. */
  /* Follows SAVE to the left: it is SAVE's note, and it was under the shell
     for the same reason SAVE was. LAST IN THE COLUMN, under the banner that
     can be up beside it -- which is the step-down the stylesheet used to do
     by hand with a second `top`. */
  #elsewhere { font-family: var(--font-ui); font-size:11px; letter-spacing:0.06em;
    text-transform:uppercase; color: var(--ink-quiet);
    background-color: var(--surface-chip); background-image: var(--surface-chip-tex, none);
    border:1px solid var(--edge-panel); border-radius:6px; padding:5px 10px; }
  #elsewhere[hidden] { display:none; }
  /* Borrows #elsewhere's chip wholesale -- same corner, same job: a quiet
     line of fact that is not a dialog and does not need dismissing. The
     filename wraps rather than stretching the chip off the screen. */
  #saved-where { font-family: var(--font-ui); font-size:11px; letter-spacing:0.06em;
    text-transform:uppercase; color: var(--ink-quiet);
    background-color: var(--surface-chip); background-image: var(--surface-chip-tex, none);
    border:1px solid var(--edge-panel); border-radius:6px; padding:5px 10px;
    max-width:260px; text-align:right; }
  #saved-where[hidden] { display:none; }
  #saved-where b { color: var(--ink); font-weight:600; }

  /* RUNG 3's READ-ONLY BANNER. It names the holder because "somebody is
     editing this" is not something a drafter can act on, and it carries the one
     press that resolves it. Sat under SAVE with the change note, so the page has
     one place where it says things about the file rather than three. */
  /* THE ONE THING IN THIS COLUMN THAT IS PRESSED, so it is the one that takes
     the pointer back off #upper-right. */
  #lease { display:flex; gap:10px; align-items:center; pointer-events:auto;
    font-family: var(--font-ui); font-size:11px; letter-spacing:0.06em;
    text-transform:uppercase; color: var(--ink-primary);
    background-color: var(--surface-chip); background-image: var(--surface-chip-tex, none);
    border:1px solid var(--accent); border-radius:6px; padding:5px 10px; }
  #lease[hidden] { display:none; }
  #take-over { font:inherit; letter-spacing:inherit; text-transform:inherit;
    color: var(--accent); background:none; border:1px solid var(--accent);
    border-radius:4px; padding:3px 8px; cursor:pointer; }

  /* THE DRIVE-THRU SIGN. It rises from the foot and covers the bone in the
     middle of the strip -- z-index 7, the page's modal layer, over
     the strip's 6. Off screen rather than hidden while shut so the rise is a
     movement the drafter can follow; `visibility` goes with it so nothing
     under the foot takes a stray press.

     THE BOARD IS ONE IMAGE and every zone on it is a percentage of that
     image, measured off assets/drivethru-menu-board.png (2000x1550). The
     art moves, the zones move with it, and nothing here hardcodes a pixel.
     The same arrangement as gruff-drivethru.js, for the same reason: the
     board is a PICTURE, and only the controls painted on top of it take a
     press. */
  /* BIGGER, BECAUSE MOVIE COULD NOT READ IT (16 Sep: "make the stuff in the
     drivethru menu bigger its hard to see"), and then bigger again on 17 Sep
     with a new sign drawn for it: "i'd like this one so the stuff inside can
     be bigger". The shelf went from a sixth of the picture's height to two
     fifths of it, which is what let the cards below grow.

     RE-MEASURED, NOT RESCALED. The art changed shape as well as size, so
     every figure here was read off the new file's own pixels: the screen's
     black runs x 427..948 and y 175..510, the shelf's x 87..932 and y
     536..1150, the post's disc y 1217..1333 centred on x 509.5. Nothing was
     carried over from the square board by proportion. */
  /* TWO BOARDS, TWO SHAPES, ONE RULE. The shape is a variable the skin sets
     rather than a second copy of this block.

     AND THE HEIGHT IS A CONSTRAINT, not an outcome. On the portrait sign this
     was the binding term -- 720px of width on a 0.68 board is 1059px of
     height, taller than the laptop it is drawn on. On the landscape one it is
     slack and the WIDTH binds, which is why that width went up: see the note
     on the screen below. The term stays because it is the rule, not the
     answer, and the next board may be tall again. */
  #drivethru { position:fixed; left:50%; bottom:0; z-index:7;
    --dt-aspect:1.29; --dt-headroom:16px;
    --dt-w:min(860px, calc(100vw - 24px),
      calc((100vh - var(--dt-headroom)) * var(--dt-aspect)));
    width:var(--dt-w);
    transform:translate(-50%, 0);
    transition:transform 220ms ease-out, visibility 220ms;
    pointer-events:none; }
  #drivethru[data-shut] { transform:translate(-50%, 104%); visibility:hidden; }
  #dt-frame { position:relative; width:100%; aspect-ratio:var(--dt-aspect); }
  #dt-board { position:absolute; inset:0; width:100%; height:100%;
    display:block; pointer-events:none; }
  /* Gruff's screen: what he is saying, and the one line under it that says
     what the press will do. Live so a drafter on a reader hears the order
     move without watching the board. */
  /* ── RE-MEASURED FOR THE LANDSCAPE BOARD ───────────────────────────────
     Movie, 20 Sep: "the text is coming off the top board so i increased the
     text area for the dog", and then a second pass -- "i should make more
     space for him". What he sent back is not the old sign made taller, it is
     a different SHAPE: 2000x1550 landscape where the old was 1020x1500
     portrait. So every figure below was read off the new file's own pixels
     rather than carried over by proportion, the same way the 17 Sep art was.

       the dog's band   x 444..1912   y  124.. 458
       the shelf        x 104..1896   y  486..1198
       the bone's disc  x 943..1053   y 1267..1374

     AND THE OVERFLOW IS FIXED BY WIDTH, NOT BY HEIGHT, which is worth saying
     because the band is SHORTER than the one it replaces. At a 1366 sheet the
     old screen was 261x168 and the three lines in it wrapped to eight; this
     one is 626x144 and they wrap to four. A band a third taller would not
     have held the old wrapping either -- the line that was falling off the
     bottom was falling off because seventeen characters is not a sentence. */
  #dt-screen { position:absolute; left:22.2%; top:8%; width:73.4%; height:21.548%;
    box-sizing:border-box; padding:1.6% 2.4%; pointer-events:none;
    display:flex; flex-direction:column; justify-content:center; gap:6px;
    font-family: var(--font-mono); color:#ff4033;
    text-shadow:0 0 10px rgba(255,64,51,0.45); letter-spacing:0.03em;
    line-height:1.35; overflow:hidden; }
  /* SCALED TO THE BOX IT HAS, not to the one it used to have. The square
     board gave the screen 363x240 at a 1366-wide sheet and 22px sat in it;
     the new art's screen is 261x168 there, and 22px in it wraps "BUNGALOW --
     WHICH ONE?" onto three lines. 22 * 261/363 is about 16, which is what
     these ceilings are -- the same type at the same size relative to the
     screen it is printed on. */
  #dt-line { font-size:clamp(11px, 1.9vw, 17px); font-weight:600; }
  #dt-note { font-size:clamp(9px, 1.4vw, 14px); opacity:0.7; }
  /* SMALLER THAN THE NOTE AND DIMMER, at every width -- the ceilings and the
     floors both step down, so there is no sheet size where this line grows
     into the one above it. The screen is pointer-events:none so that the
     board behind it stays a picture; this line takes its press back, and it
     is the only thing in there that does. */
  #dt-project { pointer-events:auto; margin-top:2px;
    font-size:clamp(7px, 1vw, 10px); opacity:0.55; letter-spacing:0.06em;
    color:inherit; text-decoration:none; line-height:1.25; }
  #dt-project:hover { opacity:0.95; text-decoration:underline; }
  /* THE THIRD BUTTON (Movie, 20 Oct), dressed like the two banners so the
     three read as one set: build, trace, or go and set the numbers. */
  .dt-project-go { pointer-events:auto; align-self:stretch; appearance:none; cursor:pointer;
    border:2px solid #fff; border-radius:5px; background:#d7262f; color:#fff; padding:4px 6px;
    margin-top:4px; font-family: var(--font-ui); font-weight:800; letter-spacing:0.06em;
    text-transform:uppercase; font-size:clamp(9px, 1.25vw, 14px); line-height:1.1;
    box-shadow:0 2px 6px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.25); }
  .dt-project-go:hover, .dt-project-go:focus-visible { background:#ff4033; }
  .dt-project-go[hidden] { display:none; }
  /* THE SHELF COPY IS ROUGH'S: the ruff sign says it on its screen. On the
     sheet it is the row under the banners, the same width they span. */
  html:not([data-theme="rough"]) #dt-project-go-shelf { display:none; }
  html[data-theme="rough"] #dt-project-go-shelf { position:absolute; left:6%; width:88%;
    top:89.5%; height:8.2%; margin:0; padding:0 1.5%; }
  /* ── THE BANNERS, ONE ROW UNDER THE SHELF, EITHER SIDE OF THE PRESS ─────
     Movie, 16 Oct: "those 2 banners and arrows (red and white) to pop up
     down below". On the sign the row straddles the bone's post (the bone
     stands at 50%, 81.7%, 5.5% wide); the gap in the middle is the bone's
     width plus air, so the arrows meet it and nothing stands on it. The row
     itself takes no press -- only the two banners do -- which is the rule
     the whole board keeps. */
  #dt-banners { position:absolute; left:6%; width:88%; top:80.3%; height:8.2%;
    display:flex; align-items:center; justify-content:space-between; gap:1%;
    pointer-events:none; }
  #dt-banners[hidden] { display:none; }
  .dt-banner { pointer-events:auto; flex:0 1 31%; height:100%; box-sizing:border-box;
    appearance:none; border:2px solid #fff; border-radius:5px;
    background:#d7262f; color:#fff; cursor:pointer; padding:0 1.5%;
    font-family: var(--font-ui); font-weight:800; letter-spacing:0.06em;
    font-size:clamp(9px, 1.25vw, 15px); line-height:1.1; text-transform:uppercase;
    box-shadow:0 2px 6px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.25); }
  .dt-banner:hover, .dt-banner:focus-visible { background:#ff4033; }
  .dt-banner[hidden], .dt-arrow[hidden] { display:none; }
  .dt-arrow { flex:0 0 10%; height:70%; display:block; pointer-events:auto;
    appearance:none; background:none; border:none; padding:0; cursor:pointer; }
  .dt-arrow svg { width:100%; height:100%; display:block; }
  .dt-arrow:hover path, .dt-arrow:focus-visible path { stroke:#ffd9d6; }
  .dt-arrow path { fill:none; stroke:#fff; stroke-width:5; stroke-linecap:round; stroke-linejoin:round; }
  .dt-arrow .dt-arrow-shade { stroke:#d7262f; stroke-width:10; }
  .dt-banner-gap { flex:0 0 9%; }
  /* THE NUMBER ON THE SIGN'S PRESS, seated for THIS press's artwork rather
     than the foot's: the sign's disc is drawn larger, and Movie placed the
     number by eye on each (16 Oct): on the bone "lowered down because the
     BONE is in the middle", on the house "centered up down in the blue
     button area in the house" -- "middle". */
  #dt-bone-balance { position:absolute; left:0; right:0; top:80%;
    transform:translateY(-50%); text-align:center; pointer-events:none;
    font-family: var(--font-ui); font-weight:700; color:#fff;
    text-shadow:0 1px 2px rgba(29,31,32,0.55); letter-spacing:0.02em;
    line-height:1; }
  #dt-bone-balance:empty { display:none; }
  /* The tile strip: the house types, on the board's own shelf. */
  #dt-tiles { position:absolute; left:5.9%; top:32.5%; width:88.2%; height:44.1%;
    box-sizing:border-box; display:flex; flex-direction:column;
    justify-content:center; gap:4px; pointer-events:none; }
  /* ONLY THE CARDS TAKE A PRESS, not the shelf they stand on. This read
     `#dt-tiles > *`, and `> *` is #build-bar -- a flex COLUMN, not a control,
     and one that spans the shelf corner to corner. So the gaps between cards,
     and the strip of shelf either side of them, were taken by a container and
     never reached the sheet underneath.

     THE RULE IS THE ONE THE BOARD'S OWN COMMENT STATES two blocks up: the
     board is a PICTURE, and only the controls painted on top of it take a
     press. pointer-events inherits, so the container passes the press through
     and the buttons and fields take it back. model-drivethru.spec.js pins
     every zone of it, the way gruff-drivethru.spec.js pins the other board's.

     WHAT THIS DOES NOT FIX, so nobody looks for it here: a corner pressed onto
     a CARD. A card is a control and swallowing that press is correct. The
     deeper shelf on Movie's 18 Sep art grew the cards, which put more of the
     sheet behind a control, and model-html-outline -- which traces a square
     with the board up -- started failing on CI when the camera framed the
     drawing small. That belongs to the spec, and clearOfTheShelf there is
     where it is answered. */
  #dt-tiles > * { pointer-events:none; }
  #dt-tiles button, #dt-tiles input { pointer-events:auto; }
  /* THE DISC IS A SMALLER SHARE OF A WIDER BOARD -- 110px of 2000 where it
     was 116 of 1020 -- so the percentage halves while the button on screen
     stays the size the art draws it. Centred on x 998 of 2000, which is half
     a percent off centre and not worth a number of its own. */
  #dt-bone { position:absolute; left:50%; top:81.7%; width:5.5%;
    transform:translateX(-50%); aspect-ratio:1 / 1; border:none; padding:0;
    border-radius:50%; background:transparent; cursor:pointer;
    pointer-events:auto;
    /* THE SHADOW THE ART USED TO CAST, cast by the press itself now (Movie,
       20 Sep: "put the little shadow around the bone button on the bottom so
       it looks more 3d"). The painted disc left the board with its shadow,
       and a disc lying flat on the post is a sticker -- the shadow was the
       whole of what made it a dome.

       MEASURED OFF THE OLD ARTWORK, not dialled in by eye. On the 2000px
       board the painted disc was 123px across, and the halo around it
       reached 0px left, 0px right, 0px above and 10px BELOW -- which is why
       Movie's word for it is "on the bottom" and why there is no x offset
       here. Down the disc's centre line it ran 57 levels under the post's
       143 grey about 4px below the edge and was gone by 10px: an offset of
       4, a blur of 6, and 57/143 of black.

       AS FRACTIONS OF --dt-w, BECAUSE THE SIGN IS NOT ONE SIZE. Every figure
       on this board is a percentage of the picture and this is the same rule
       one layer down: 4/2000 and 6/2000 are the two numbers the art was
       measured in. Fixed pixels would be right at the 860px sign and wrong
       at every other width -- a slab on a phone, a hairline on a wall.

       ON THE IMAGE RATHER THAN THE BUTTON: drop-shadow traces the PNG's
       alpha, which is the disc. The button's own box is a square with
       transparent corners, so a box-shadow would cast a square.

       AND IT IS A VARIABLE BECAUSE `filter` DOES NOT STACK. The hover
       brightens the disc, and a bare `filter:brightness()` there would
       REPLACE this rather than add to it -- the press would drop its shadow
       at the exact moment the drafter points at it. */
    --bone-lift: drop-shadow(0 calc(var(--dt-w) * 0.002)
      calc(var(--dt-w) * 0.003) rgba(0,0,0,0.40)); }
  /* CONTAIN, BECAUSE THE ARTWORK IS NO LONGER SQUARE. The post's box is a
     1:1 circle and the bone filled it; a house is taller than it is wide and
     a stretched one reads as a different building. */
  #dt-bone img { width:100%; height:100%; display:block; object-fit:contain;
    filter:var(--bone-lift); }
  #dt-bone:hover img { filter:var(--bone-lift) brightness(1.12); }
  #dt-close { position:absolute; right:5.5%; top:9%; pointer-events:auto;
    font-family: var(--font-ui); font-size:11px; font-weight:700;
    letter-spacing:0.08em; cursor:pointer; color:#f2efe9;
    background:rgba(0,0,0,0.45); border:1px solid rgba(242,239,233,0.45);
    border-radius:4px; padding:3px 8px; }
  #dt-close:hover { border-color:#ff4033; color:#ff4033; }

  /* ROUGH'S BOARD IS A SHEET, NOT A SIGN. Movie, 17 Sep: "the blueprint will
     replace the drivethru", "in the ROUGH version it isn't going to show the
     dog", "no dog will ask questions they will just see the selections".

     THE ZONES ARE RE-MEASURED, NOT NUDGED, which is what the block above
     demands of anyone who replaces the art. assets/draft-board.png is a
     1536x1024 sheet reduced to 896x597, and its blue rule runs from 2.539% to
     97.266% across and 3.809% to 95.996% down -- measured off the file's own
     pixels, not read off a ruler. Every zone below sits inside that rule with
     room to spare, so a sheet re-scanned at another size moves the rule and
     the zones with it.

     THE SIGN'S FURNITURE IS GONE RATHER THAN RESTYLED. A sign has a portrait
     bezel, an LED screen and a post; a sheet of paper has none of them, so
     the screen is not shown here at all. What is lost with it is one line --
     signNote's "press the bone and I'll build it" -- and with the outline
     button retired (see `round`) that line has one wording in practice. The
     selections and the press are what is left, which is the whole of what was
     asked for.

     THE TILES NEED NO SKIN OF THEIR OWN. They were already "the board's own
     blueprint cards: paper, blue ink, a hard edge" for a sign that had no
     paper on it. On actual paper they are simply at home, which is why this
     block sets geometry and one button's colours and nothing else. */
  /* IT RIDES ABOVE THE BAR RATHER THAN OVER IT. Movie, 17 Sep: "don't put the
     house on the blueprint, just bring the blueprint up high enough so the
     house button on the model area can be seen still" -- "i mean the blue
     house button don't put it on the blueprint".

     So the sheet has no press on it at all. The sign covers the bone and
     carries its own copy of it on the post; the sheet leaves the bone
     standing in the bar underneath, and that one press is both the way up to
     the board and the way to build what you picked off it. Which is also
     which button he meant by the one that lights while the blueprint shows.

     THE LIFT IS `--house-h` AND A GAP, and --house-h is a min-height, so a
     foot bar whose row wrapped would be taller than this clears. The spec
     asserts the bone's rectangle is clear of the sheet's rather than
     asserting this number, so a wrap fails there instead of on a squint. */
  /* AND IT SITS IN THE MIDDLE OF THE SHEET, not just above the bar (Movie,
     18 Sep: "can you move the blueprint to closer to middle (top
     bottomwise)").

     CENTRED, OR CLEAR OF THE BAR, WHICHEVER IS LOWER. Centring alone is a
     rule that works on the screen it was written on: on a short sheet half
     the board is taller than the room above the foot, and the centred sheet
     lands back on the bone it was lifted off. `max` says the real rule --
     put it in the middle, and never below the bar -- rather than leaving the
     two in tension. The height it needs for that is the width it took
     divided by its own aspect, which is why the width became a variable. */
  /* AND THE SHEET COMES IN A TENTH, clear of the press below it. Movie, 24
     Sep: "can you make the 'BLUEPRINT' backdrop about 90% that size so there
     will be a little space between the 'BLUEPRINT' and the HOUSE BUTTON (and
     put some padding in so it doesn't cover the HOUSE button (always leave a
     few pixels gap at least but more prefered".

     THE SCALE GOES ON THE WIDTH and nothing else moves: the frame takes its
     height from the aspect and every tile is a percentage of the frame, so
     one number brings the whole sheet in and its contents keep their
     proportions.

     AND THE GAP GOES FROM 12px TO 28px, which is the figure the headroom
     above already reserves for this press -- so the space kept for the house
     and the space left under the sheet are one number rather than two that
     drift. Twelve pixels was "not covering it"; this is the clearance he
     asked for. */
  html[data-theme="rough"] #drivethru { --dt-aspect:1.5;
    --dt-sheet-scale:0.9;
    --dt-headroom:calc(var(--house-h) + 28px);
    --dt-w:calc(min(860px, calc(100vw - 24px),
      calc((100vh - var(--dt-headroom)) * var(--dt-aspect)))
      * var(--dt-sheet-scale));
    bottom:max(calc(var(--house-h) + 28px),
      calc(50vh - var(--dt-w) / var(--dt-aspect) / 2)); }
  html[data-theme="rough"] #dt-screen { display:none; }
  /* THE HOUSE PRESS IN THE MIDDLE OF THE ROW (Movie, 16 Oct: "on the ROUGH -
     drivethru version can you do it similar to the change on the Ruff
     version", drawn as [ DRAW HOUSE ] -> (house) -> [ DRAW HOUSE OUTLINE ]).
     The sheet has no bone to point at, so the sign's own press -- the same
     button, the same handler, wearing the house under this skin -- stands
     where the arrows meet, and the shelf gives up the bottom of the sheet
     for the row. */
  html[data-theme="rough"] #dt-bone { display:none; top:70%; width:9.5%;
    border-radius:8px; }
  /* ONLY WITH THE ROW: the sheet has no post for a press to stand on, so the
     house press rises and goes with the banners it stands between -- "pop
     up down below once the user selects the house type". */
  html[data-theme="rough"] #dt-banners:not([hidden]) ~ #dt-bone { display:block; }
  html[data-theme="rough"] #dt-tiles { left:6%; top:13%; width:88%; height:56%; }
  html[data-theme="rough"] #dt-banners { top:72%; height:9%; }
  html[data-theme="rough"] .dt-banner-gap { flex:0 0 12%; }
  html[data-theme="rough"] #dt-bone-balance { top:62%; }
  /* BLUE ON PAPER (Movie, 16 Oct: "the colors on the HOUSE / ROUGH version
     should be BLUE not RED. Red is RUFF version"): the sheet's own ink, the
     house press's blue, with the paper's cream for the lettering and the
     arrows. */
  html[data-theme="rough"] .dt-banner { background:#1f3f6b; border-color:#f1e9d2; color:#f1e9d2;
    box-shadow:0 2px 5px rgba(31,63,107,0.35), inset 0 1px 0 rgba(255,255,255,0.18); }
  html[data-theme="rough"] .dt-banner:hover, html[data-theme="rough"] .dt-banner:focus-visible { background:#2c5a99; }
  html[data-theme="rough"] .dt-arrow path { stroke:#f1e9d2; }
  html[data-theme="rough"] .dt-arrow .dt-arrow-shade { stroke:#1f3f6b; }
  html[data-theme="rough"] .dt-arrow:hover path, html[data-theme="rough"] .dt-arrow:focus-visible path { stroke:#fff; }
  /* Inside the rule, and in the sheet's own ink. The sign's CLOSE is light
     lettering on a dark plate because it sits on a dark sign; the same plate
     on paper reads as a sticker. */
  html[data-theme="rough"] #dt-close { right:4.6%; top:6.4%; color:#1f3f6b;
    background:rgba(241,233,210,0.72); border-color:#1f3f6b; }
  html[data-theme="rough"] #dt-close:hover { border-color:#8f2a1d; color:#8f2a1d; }

  /* THE BUILD BAR, now the sign's tile shelf rather than a row on the foot. */
  #build-bar { flex:0 1 auto;
    display:flex; flex-direction:column; align-items:stretch; gap:4px; }
  #build-bar[hidden] { display:none; }
  #build-families, #build-entries { display:flex; gap:4px;
    justify-content:center; flex-wrap:wrap; }
  /* THE FIRST SCREEN KEEPS ITS WIDER GAP and nothing else. It used to take a
     taller card as well; with one size everywhere that override is gone, and
     what is left is the space between the three that Movie asked for when
     they were alone on the shelf. `:has()` rather than a class because the
     condition is already in the DOM -- #build-entries carries `hidden`
     exactly when no family is open, set in one place. */
  #build-bar:has(#build-entries[hidden]) #build-families {
    gap:clamp(8px, calc(var(--dt-w) * 0.022), 24px); }
  /* THE BONE ASKS FOR THE PRESS once a complete order is on the board. The
     glow is the one the lit bone already wears; the pulse is what makes it a
     request rather than a state. Stilled for anyone who has asked for less
     motion -- the glow stays, so nothing is only announced by movement. */
  @keyframes dt-bone-ask {
    0%, 100% { filter:var(--bone-lift) brightness(1.15)
      drop-shadow(0 0 4px rgba(255,64,51,0.55)); }
    50%      { filter:var(--bone-lift) brightness(1.35)
      drop-shadow(0 0 13px rgba(255,64,51,0.95)); }
  }
  #dt-bone[data-ready] img { animation:dt-bone-ask 1.4s ease-in-out infinite; }
  @media (prefers-reduced-motion: reduce) {
    #dt-bone[data-ready] img { animation:none;
      filter:var(--bone-lift) brightness(1.3) drop-shadow(0 0 9px rgba(255,64,51,0.8)); }
  }
  #build-entries[hidden] { display:none; }
  /* TILES WITH NO CARD SHARE ONE CARD'S SLOT, stacked (Movie, 18 Oct: ON
     GRADE / ONE STOREY and ON GRADE / 2 STOREY side by side wrapped the row
     and spilled the board). Each takes half the square, its words wrapped. */
  #build-entries .dt-stack, #build-families .dt-stack { display:flex; flex-direction:column; gap:4px;
    width:clamp(48px, calc(var(--dt-w) * 0.14), 134px); }
  #build-entries .dt-stack button, #build-families .dt-stack button { flex:1 1 0; white-space:normal;
    line-height:1.15; padding:3px 4px; }
  /* The tiles are the board's own blueprint cards: paper, blue ink, a hard
     edge. They are not palette tokens because the board is one picture in
     one light, and a night-skin chip on it would read as a hole. */
  #build-bar button { font-family: var(--font-ui); font-size:clamp(10px, 1.5vw, 14px);
    font-weight:700; letter-spacing:0.04em; text-transform:uppercase;
    cursor:pointer; color:#1f3f6b; padding:5px 10px; border-radius:2px;
    border:1px solid #1f3f6b; background:#f1e9d2; }
  #build-bar button:hover { background:#fff8e6; }
  /* A CARD IS THE WHOLE BUTTON. Movie drew one per house type, lettering and
     all, so the tile wearing one carries no text of its own -- the label
     stays in the DOM for the reader and the check, clipped out of sight,
     because a printed name under a printed name reads as a stutter. */
  #build-bar button.card { padding:0; border:none; background:none;
    line-height:0; }
  /* TWO ROWS HAVE TO FIT THE SHELF. A family and its open submenu are both
     on the board at once, so the card is sized so that pair clears the
     black strip -- a tile that spills past the frame is an order the
     drafter cannot reach.

     MEASURED AGAINST THE BOARD, NOT THE VIEWPORT. This was 8vw, which is the
     wrong ruler by one step: the shelf a card has to fit inside is a fraction
     of --dt-w, and --dt-w is a min() of three terms of which the viewport is
     only one. On the portrait sign the two happened to track each other. On
     the landscape one they came apart -- the board grew and the cards did
     not, and three tiles sat in the middle of a shelf twice their height.

     THE CEILING IS THE SHELF'S OWN ARITHMETIC. The sign's shelf is 44.1% of
     a board 1/1.29 as tall as it is wide, so at the 860px cap it is 294px and
     two rows plus their gap must live in that: 2h + 4 <= 294, h <= 145. 120
     leaves room for the label row the entries carry and is still a third
     bigger than the 86 it replaces -- Movie, 16 Sep, and still standing:
     "make the stuff in the drivethru menu bigger its hard to see". */
  /* SQUARE, AND ONE SIZE FOR EVERY CARD (Movie, 24 Sep): "these should all be
     the same size (a little bigger than the bottom ones) the top ones are a
     bit too big and they would look better matching sizes i think".

     THAT SUPERSEDES THE 150/110 EARLIER THE SAME EVENING, which gave the
     family row half again the entries', and it is the second time this pair
     of numbers has moved -- both times after he looked at the board rather
     than at a description of it. One rule now, so there is nowhere for a
     second size to hide.

     A LITTLE BIGGER THAN THE BOTTOM ROW WAS, which is what he asked for:
     0.1265 of the board became 0.14, so the entries grow from 111px to 122
     at his window and the families come down from 150 to meet them.

     THE PAIR STILL HAS TO CLEAR THE SHELF. It is 44.1% of a board 1/1.29 as
     tall as it is wide -- 294px at the 860px cap -- and a family row and its
     open submenu share it: two rows at the 134 ceiling is 268 and a gap,
     inside 294 with room. Equal rows make that arithmetic simpler than it
     has been all evening. */
  /* THE CARD IS A SQUARE BOX, and the picture fits inside it. `width:auto`
     off a fixed height let the ART decide the tile's shape, so a card that
     was not square made its own row uneven -- 1storey.png is 512x562 and
     rendered 111x122 beside its 122x122 neighbours. The board's own rule is
     square cards, one size a row, and it is checked in
     model-drivethru.spec.js.
     `contain` RATHER THAN `cover` because the alternative is cropping
     somebody's drawing to fit a grid: a square card fills this box exactly
     and is untouched, and a card that is not square pillarboxes instead of
     losing its edges. A square export still looks better and is the thing to
     ask for; this makes the board honest either way. */
  #build-bar button.card img { display:block;
    height:clamp(48px, calc(var(--dt-w) * 0.14), 134px);
    width:clamp(48px, calc(var(--dt-w) * 0.14), 134px);
    object-fit:contain;
    border:1px solid transparent; border-radius:2px; }
  #build-bar button.card:hover img { border-color:#8f2a1d; }
  #build-bar button.card.open img, #build-bar button.card.chosen img {
    border-color:#8f2a1d; box-shadow:0 0 0 2px rgba(143,42,29,0.45); }
  #build-bar button.card .said { position:absolute; width:1px; height:1px;
    margin:-1px; padding:0; overflow:hidden; clip:rect(0 0 0 0);
    white-space:nowrap; border:0; }
  /* THE SIZE ROW sits under the entry chips, on the same shelf, because it
     is the same question continued: which garage. The fields wear the
     board's paper and ink rather than the browser's, or they read as a
     form dropped on a picture. */
  #build-sizes { display:flex; gap:4px; justify-content:center;
    align-items:center; flex-wrap:wrap; }
  #build-sizes[hidden] { display:none; }
  #size-stock { display:flex; gap:4px; justify-content:center; flex-wrap:wrap; }
  #size-custom { display:flex; gap:3px; align-items:center;
    font-family:var(--font-ui); font-size:clamp(10px, 1.5vw, 14px);
    font-weight:700; letter-spacing:0.04em; color:#1f3f6b; }
  #size-custom[hidden] { display:none; }
  #size-custom input { width:4.2em; font-family:var(--font-ui);
    font-size:clamp(10px, 1.5vw, 14px); font-weight:700; text-align:center;
    color:#1f3f6b; background:#f1e9d2; border:1px solid #1f3f6b;
    border-radius:2px; padding:3px 4px; }
  #size-custom input:focus { outline:2px solid #8f2a1d; outline-offset:1px; }
  /* The family whose submenu is open, and the type the drawing carries, both
     read as the page's one attention colour -- the same signal the wall tool
     uses when it is armed. */
  #build-bar button.open, #build-bar button.chosen {
    color:#8f2a1d; border-color:#8f2a1d; box-shadow:inset 0 0 0 1px #8f2a1d; }

  /* THE PLACEHOLDER MOVED DOWN ONE LEVEL when the column became three panes.
     `#tool-slot:empty` can never match again -- the slot always holds its
     three pane divs -- so the rule would have gone on passing every check
     while saying nothing, which is the failure this file keeps finding. It
     is the panes that can be empty now, and only the two that hold keys: an
     empty tool pane means the roster did not load. */
  [data-left-pane="drafting"]:empty::after,
  [data-left-pane="build"]:empty::after { content:'TOOLS'; display:block;
    padding:6px 2px; font-family: var(--font-ui); font-size:9px;
    letter-spacing:0.1em; color: var(--ink-quiet); opacity:0.5; }
  /* AND PROPERTIES SAYS WHY IT IS BLANK. With nothing selected the box is
     hidden, so before this the tab opened onto an empty panel that looked
     broken rather than idle -- the same thing the LEVELS panel's "NO
     SECTIONS" line was written for. `:empty` cannot do this one: `#props-box`
     is still a child while it is hidden, so the line is toggled in script
     beside the box it stands in for. */
  [data-props-empty] { display:block; padding:6px 2px;
    font-family: var(--font-ui); font-size:9px; letter-spacing:0.1em;
    color: var(--ink-quiet); opacity:0.5; }
  [data-props-empty][hidden] { display:none; }
  /* 5px AND 3px, not 10 and 5: MOVE and STRETCH (Movie, 20 Oct) put a
     fourth row in DRAW / EDIT, and the rail with PROPERTIES up has to fit
     at 1366x768 without a fold. The spacing gave the 19px, no text did. */
  [data-tool-group] + [data-tool-group] { margin-top:5px; }
  [data-tool-group-name] { font-family: var(--font-ui); font-size:9px;
    font-weight:600; letter-spacing:0.1em; text-transform:uppercase;
    color: var(--ink-quiet); margin-bottom:3px; }
  /* ── THE ASSEMBLED ONES, UP BESIDE THE TITLE ────────────────────────────
     Movie, 14 Oct: "bring those ASSEMBLED units for BATH and KITCHEN up
     beside the title (to the right)", and for the colour, "give them GOLD or
     whatever the BUILD/ PROPERTIES beside it is using" / "match the BUILT and
     PROPERTIES outline 'currently GOLD'".

     THAT GOLD IS `--accent`, which is what a selected rail tab wears
     (.rail-tab[aria-selected="true"] in shell-bars.css). Taking the token
     rather than the colour is what makes it answer his other question before
     he asks it -- he had wondered about "Red and Blue depending on Ruuf or
     Rough", and the token is already exactly that: gold on RUFF, blue on
     ROUGH, in both NIGHT and DAY.

     A ROW OF ITS OWN, laid along the title: the group's name takes what it
     needs and the assembled chips ride in the space left over, so a group
     with none of them looks exactly as it did. */
  .tool-group-head { display:flex; align-items:center; gap:6px; margin-bottom:5px; }
  .tool-group-head [data-tool-group-name] { margin-bottom:0; flex:0 0 auto; }
  .tool-group-head .sel-row { margin:0; flex:1 1 auto; }
  /* ASSEMBLED, NOT A PIECE. A whole kitchen or washroom goes down in one
     press where everything under it is a single cabinet or fixture, and that
     is the difference the colour is carrying. */
  .sel-chip.assembled { color: var(--accent); border-color: var(--accent); }
  .sel-chip.assembled:hover { border-color: var(--accent); }
  .tool-keys { display:grid; grid-template-columns:repeat(4, 40px); gap:4px; }
  /* THE BAR UNDER THE TOP BAR, FLUSH LEFT (Movie, 20 Oct: "the SELECT
     ASSEMBLY TRIM DIMENSIONS - new buttons overlap the tab - we should push
     those so they are right underneath the top bar and could also be pushed
     fully to the left (i might add more buttons to it"). It stood at
     rail-top - 48px, which put its foot on the tabs' head. Now 4px under
     the 44px bar, on the page's left edge, and its keys run in ONE ROW that
     grows to the right: a fifth key goes beside the fourth, never into a
     second row that would reach the tabs again. */
  #select-bar { position:fixed; z-index:30; left:0;
    top: calc(var(--strip-h, 44px) + 4px); padding:3px;
    border:1px solid var(--edge-panel); border-left:none; border-radius:0 3px 3px 0;
    background: var(--surface-panel); }
  #select-bar .tool-keys { grid-template-columns:none; grid-auto-flow:column; grid-auto-columns:40px; }
  #select-bar:empty, #left-rail[hidden] + #select-bar { display:none; }
  /* PICKING GLOWS (Movie, 19 Oct: "they can see it glowing when its
     selected"): SELECT while it is picking a kind, and the one kind picked --
     a glow, so it never reads as a tool armed to draw. */
  .tool-key.tool-key[data-picking], .tool-key.tool-key[data-picked], .sel-chip.sel-chip[data-picked],
  .tool-key.tool-key[data-stretch-glow] {
    border-color: var(--pick-glow, #5fd4ff); color: var(--pick-glow, #5fd4ff);
    box-shadow: 0 0 6px 1px var(--pick-glow, #5fd4ff), inset 0 0 0 1px var(--pick-glow, #5fd4ff); }
  .tool-key { position:relative; width:40px; height:40px; padding:0;
    display:block; cursor:pointer; border-radius:3px;
    border:1px solid var(--edge-panel);
    background-color: var(--surface-chip);
    background-image: var(--surface-chip-tex, none);
    color: var(--ink-quiet); font-family: var(--font-ui); }
  .tool-key:hover { border-color: var(--accent); color: var(--accent); }
  /* ARMED IS THE ACCENT, the same signal every armed control on the page
     uses, because a drafter should not have to learn that the tool column
     means something different from the rest of the chrome. */
  .tool-key[aria-pressed="true"] { border-color: var(--accent);
    color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
  /* AN UNAVAILABLE TOOL LOOKS DOWN, and it has to look down MEASURABLY. An
     opacity of 0.9 would satisfy "the rule exists" while looking identical on
     screen, and a `disabled` attribute with no rule behind it at all is the
     broken state that looks exactly like the working one -- so
     tests/model-tool-boards.spec.js reads the COMPUTED opacity, not the
     attribute. Three signals, none of them the accent: the key is dimmed, it
     loses the hover promise, and the cursor says it will not take the press. */
  .tool-key:disabled, .tool-key:disabled:hover { opacity:0.35; cursor:not-allowed;
    border-color: var(--edge-panel); color: var(--ink-quiet); box-shadow:none; }
  /* Fixed to the viewport and out of the way of hit-testing: a label that
     ate the pointer would break the very gesture it is reporting on. */
  #draw-len { position:fixed; z-index:40; pointer-events:none;
    font-family: var(--font-mono); font-size:12px; font-weight:600;
    padding:2px 6px; border-radius:3px; white-space:nowrap;
    background: var(--surface-panel); color: var(--ink-primary);
    border:1px solid var(--edge-panel); }
  [data-tool-letter] { position:absolute; top:2px; left:4px; font-size:11px;
    font-weight:600; line-height:1; }
  /* The five bare faces keep the same box: a key whose letter box collapses
     would sit differently from its neighbours and read as a different kind of
     key, which is a thing about the tool that is not true. */
  [data-tool-letter]:empty::after { content:'\200b'; }
  /* THE FACE IS THE LETTER AND A DRAWING (Movie, 27 Oct: "don't put text in
     the box except the letter, just an icon and command letter"). The name
     stays in the key for a reader, out of sight; the tooltip says it whole. */
  [data-tool-name] { position:absolute; width:1px; height:1px; overflow:hidden;
    clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; }
  [data-tool-icon] { position:absolute; left:50%; top:55%; transform:translate(-50%, -50%);
    width:22px; height:22px; line-height:0; pointer-events:none; }
  [data-tool-icon] svg { display:block; }
  /* SELECTION and OBJECT TYPE. Chips wrap rather than scroll: ALL LEVELS and
     FENESTRATION are long words in a 120px panel, and a row that scrolls
     sideways hides the chip a drafter is looking for. */
  /* THE COLUMN IS AS WIDE AS THE KEYPAD AND NO WIDER. Measured, because the
     first version was not: `flex:1 1 auto` chips plus the words FENESTRATION
     and ALL LEVELS pushed the panel to 451px, against the keypad's 172. A
     panel two and a half times the width it needs is drawing area taken for
     nothing, and it is the same occlusion cost Skipper measured and cut on the
     right. Fixing the slot's width makes the chips wrap inside it instead of
     stretching it. */
  #tool-slot { width:172px; }
  .sel-row { display:flex; flex-wrap:wrap; gap:3px; }
  .sel-chip { flex:0 1 auto; min-width:0; overflow-wrap:anywhere;
    padding:5px 4px; cursor:pointer;
    border:1px solid var(--edge-panel); border-radius:2px;
    background-color: var(--surface-chip);
    background-image: var(--surface-chip-tex, none);
    color: var(--ink-quiet); font-family: var(--font-ui); font-size:9px;
    font-weight:600; letter-spacing:0.04em; line-height:1; }
  .sel-chip:hover { border-color: var(--accent); color: var(--accent); }
  /* A TYPED DIMENSION, wearing the panel's own furniture rather than the
     browser's: the chips beside it set the border, the ink and the face, and
     a field that looked like neither would read as a hole in the panel. It is
     a text box, so it takes the READING font -- a dimension is meant to be
     read back, and the chips' 9px caps are a label voice, not a value one. */
  .prop-field { width:100%; box-sizing:border-box; padding:4px 5px;
    border:1px solid var(--edge-panel); border-radius:2px;
    background: var(--surface-field, var(--surface-chip));
    color: var(--ink); font-family: var(--font-ui); font-size:11px;
    letter-spacing:0.02em; line-height:1.2; }
  .prop-field:focus { outline:none; border-color: var(--accent); }
  /* §6's other two surfaces go down the same way the keys do, and the same
     0.35 so one number governs "unavailable" across the whole column. Read as
     computed opacity by the spec, never as the attribute. */
  .sel-chip:disabled, .sel-chip:disabled:hover { opacity:0.35; cursor:not-allowed;
    border-color: var(--edge-panel); color: var(--ink-quiet); box-shadow:none; }
  .sel-chip[aria-pressed="true"] { border-color: var(--accent);
    color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
  .sel-help { margin-top:5px; font-family: var(--font-ui); font-size:9px;
    line-height:1.25; color: var(--ink-quiet); }
  /* The old page's warning red (#b04050, :23622), and it means one thing: a
     filter is engaged. Not "the cursor is over something excluded". */
  .sel-help[data-engaged] { color:#b04050; }
  /* ASSEMBLY and UNGROUP run the panel's width -- they are commands, not a
     choice between siblings, and a full-width button is what says so. */
  .asm-wide { display:block; width:100%; margin-bottom:3px; }
  /* AN AUTHOR `display` BEATS THE BROWSER'S `[hidden]`, which is a rule of the
     cascade rather than a quirk: [hidden]{display:none} lives in the UA
     stylesheet and any author display wins it. Without this line UNGROUP is
     visible whenever it has been rendered, hidden attribute and all -- it
     showed with nothing selected, which invites a press that cannot do
     anything. Caught by the spec, not by looking. */
  .asm-wide[hidden] { display:none; }
  .asm-wide:disabled { opacity:0.42; cursor:not-allowed;
    border-color: var(--edge-panel); color: var(--ink-quiet); }
  .asm-wide:disabled:hover { border-color: var(--edge-panel);
    color: var(--ink-quiet); }
  [data-assembly-dialog] { margin-top:6px; padding-top:6px;
    border-top:1px solid var(--edge-panel); }
  [data-assembly-dialog][hidden] { display:none; }
  [data-assembly-name] { box-sizing:border-box; width:100%; margin-bottom:4px;
    padding:5px 4px; border:1px solid var(--edge-panel); border-radius:2px;
    background: var(--surface-chip); color: var(--ink);
    font-family: var(--font-ui); font-size:10px; letter-spacing:0.04em;
    text-transform:uppercase; }

  /* THE LEVELS / LAYERS PANEL. Movie: "i will also need the LEVELS / LAYERS
     menu too", and "the level layers should be a tab on the right side like
     in the DC files". It is a third pane in the right-edge group rather than
     a second rotated tab on the same edge -- Devin's ruling, and he withdrew
     the "must not bury the rail" line with it: reachable means one click, not
     simultaneously visible. Two tabs on one edge is how the chrome-on-chrome
     collisions of #389 happened.

     Carried from MODEL.dc.html:1850-1955 by SHAPE, not by stylesheet: a card
     per level with its layer rows inside, the active one lit. */
  /* NO SECOND SCROLLER, AND NO 42vh (Movie, 15 Sep: "take a look at the DC
     version that is the best version"). MODEL.dc.html's rail scrolls AS ONE
     (:1840, `overflow-y:auto` on the rail itself) so ELEVATIONS, LEVELS with
     their layer sets, SECTIONS and the BONEYARD read as one list. Capping
     this pane at 42vh put a short window inside a tall panel: the levels
     scrolled away inside it while the rail below them stood empty. */
  #levels-panel { display:flex; flex-direction:column; gap:6px; }
  #levels-panel::-webkit-scrollbar { display:none; }
  /* ONE PANE SHOWS AT A TIME, and `hidden` is not enough on its own: both
     panes set `display` in their own rules, which beats the attribute's UA
     style. */
  #right-rail #levels-panel[hidden],
  #right-rail #view-rail[hidden],
  #right-rail #notes-panel[hidden],
  #right-rail #history-panel[hidden] { display:none; }
  /* UNDO / REDO (Movie, 20 Oct): the third tab on the right edge, under
     NOTES / CALC, and its pane -- the session's versions, newest at the top,
     the one on the sheet marked. Seated under the notes tab by the same
     hand that seats that tab under LEVELS / LAYERS. */
  #history-tab { right:0; top: calc(var(--rail-top) + 64vh + 16px);
    writing-mode:vertical-lr; max-height:32vh; white-space:nowrap;
    border-radius:4px 0 0 4px; }
  #history-panel { display:flex; flex-direction:column; height:100%; min-height:0;
    font-family: var(--font-ui); }
  .hp-head { display:flex; align-items:center; gap:6px; padding:5px 6px;
    font-size:10px; font-weight:700; letter-spacing:0.12em;
    border-bottom:1px solid rgba(128, 128, 128, 0.35); }
  .hp-title { flex:1; }
  .hp-count { font-size:8px; font-weight:600; letter-spacing:0.04em; opacity:0.75; white-space:nowrap; }
  .hp-list { flex:1; min-height:0; overflow:auto; }
  .hp-row { display:flex; gap:8px; align-items:baseline; padding:4px 8px;
    font-size:10px; letter-spacing:0.04em; cursor:pointer;
    border-left:3px solid transparent; }
  .hp-row:hover, .hp-row:focus-visible { background: rgba(128, 128, 128, 0.18); outline:none; }
  .hp-row[aria-selected="true"] { border-left-color: currentColor; font-weight:700; }
  .hp-row[data-side="redo"] { opacity:0.55; }
  .hp-when { font-variant-numeric: tabular-nums; opacity:0.8; white-space:nowrap; }
  .hp-what { flex:1; }
  /* THE NOTES PAD (Movie, 16 Oct): "a 'notepad' that can open up as a tab
     under the LEVELS/LAYERS tab ... that blue sticky pad look and put the
     text dark grey or black that says NOTES ... when it opens up make it a
     notepad area about the same width that the LEVELS / LAYERS opens up to
     ... a page that can scroll down ... but only about 5 pages in length".
     The DZDOCU pad he sent is pale sky blue with a darker gum strip along
     its top; RUFF's pad is the yellow one. A sticky note is paper, so it
     keeps its colour on the night sheet too. */
  :root { --pad: #d6e9f8; --pad-gum: #a9cdea; --pad-ink: #2b2f33;
    --pad-rule: rgba(43, 47, 51, 0.10); }
  html[data-theme="ruff"] { --pad: #fbeea0; --pad-gum: #efd65c; }
  /* ONE LINE, LIKE THE TAB ABOVE IT (Movie, 20 Oct: "i'd like the NOTES /
     CALC tab to be wider (taller in this position) so the words all fit on
     one line like the LEVELS / LAYER one above"). 14vh was 107px at a 768
     window and NOTES / CALC runs to about 125px, so the label broke into
     two columns. The same 32vh cap #right-tab has; the tab's height is its
     text's, so the cap only stops it on a short window. */
  #notes-tab { right:0; top: calc(var(--rail-top) + 32vh + 8px);
    border:1px solid var(--pad-gum); border-right:none;
    border-radius:4px 0 0 4px; writing-mode:vertical-lr; max-height:32vh;
    white-space:nowrap;
    background: linear-gradient(to right, var(--pad-gum) 0 6px, var(--pad) 6px);
    color: var(--pad-ink); }
  #notes-tab:hover, #notes-tab[aria-selected="true"] { color:#000;
    border-color: var(--pad-ink); }
  #right-rail[data-pane-showing="notes"] { overflow:hidden; }
  #notes-panel { position:relative; display:flex; flex-direction:column; height:100%;
    background: var(--pad); border:1px solid var(--pad-gum);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25); }
  .np-head { display:flex; align-items:center; gap:6px; padding:5px 6px;
    background: var(--pad-gum); color: var(--pad-ink);
    font-family: var(--font-ui); font-size:10px; font-weight:700;
    letter-spacing:0.12em; }
  .np-title { flex:1; }
  .np-count { font-weight:600; font-size:8px; letter-spacing:0.04em; opacity:0.75; }
  .np-count[data-full] { opacity:1; color:#8a1c1c; }
  .np-clear { font-family: var(--font-ui); font-size:9px; font-weight:700;
    letter-spacing:0.08em; padding:2px 6px; cursor:pointer;
    background: var(--pad); color: var(--pad-ink); border:1px solid var(--pad-ink);
    border-radius:2px; }
  .np-clear:hover { background:#fff; }
  .np-text { flex:1; min-height:0; width:100%; box-sizing:border-box;
    resize:none; border:0; outline:none; margin:0; padding:4px 8px;
    overflow-y:auto; color: var(--pad-ink); font-family: var(--font-ui);
    font-size:12px; line-height:18px;
    background: repeating-linear-gradient(to bottom, transparent 0 17px,
      var(--pad-rule) 17px 18px) local, var(--pad); }
  .np-text::placeholder { color: var(--pad-ink); opacity:0.45; }
  /* The grab spots: a tab of the gum colour on the left edge and on the top
     and bottom, each with a strip of reach wider than it looks. */
  .np-grip { position:absolute; z-index:2; touch-action:none; }
  .np-grip::after { content:''; position:absolute; background: var(--pad-ink); opacity:0.35;
    border-radius:2px; }
  .np-grip:hover::after { opacity:0.7; }
  .np-grip[data-notes-grip="left"] { left:-5px; top:0; bottom:0; width:10px; cursor:ew-resize; }
  .np-grip[data-notes-grip="left"]::after { left:3px; top:calc(50% - 18px); width:4px; height:36px; }
  .np-grip[data-notes-grip="top"], .np-grip[data-notes-grip="bottom"] { left:0; right:0; height:10px; cursor:ns-resize; }
  .np-grip[data-notes-grip="top"] { top:-5px; }
  .np-grip[data-notes-grip="bottom"] { bottom:-5px; }
  .np-grip[data-notes-grip="top"]::after, .np-grip[data-notes-grip="bottom"]::after {
    top:3px; left:calc(50% - 18px); width:36px; height:4px; }
  /* NOTES | CALC, on the gum strip. */
  .np-tabs { display:flex; gap:2px; }
  .np-tab { font-family: var(--font-ui); font-size:10px; font-weight:700; letter-spacing:0.12em;
    padding:1px 6px; cursor:pointer; background:transparent; color: var(--pad-ink);
    border:1px solid transparent; border-radius:2px; opacity:0.6; }
  .np-tab[aria-selected="true"] { opacity:1; border-color: var(--pad-ink); background: var(--pad); }
  .calc { flex:1; min-height:0; display:flex; flex-direction:column; gap:4px; padding:6px;
    color: var(--pad-ink); font-family: var(--font-ui); }
  .calc[hidden] { display:none; }
  .calc-tape { flex:1; min-height:40px; overflow-y:auto; font-size:11px; line-height:16px;
    background: repeating-linear-gradient(to bottom, transparent 0 15px, var(--pad-rule) 15px 16px) local; }
  .calc-row { display:flex; gap:6px; cursor:pointer; padding:0 2px; }
  .calc-row:hover { background: rgba(255,255,255,0.45); }
  .calc-row .ex { flex:1; opacity:0.7; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .calc-row .an { font-weight:700; white-space:nowrap; }
  .calc-line { width:100%; box-sizing:border-box; font-family: var(--font-ui); font-size:13px;
    padding:4px 6px; border:1px solid var(--pad-ink); border-radius:2px; background:#fff; color:#111; }
  .calc-answer { min-height:34px; font-size:16px; font-weight:700; text-align:right; }
  .calc-answer small { display:block; font-size:10px; font-weight:600; opacity:0.7; }
  .calc-answer[data-bad] { font-size:11px; color:#8a1c1c; }
  .calc-keys { display:grid; grid-template-columns:repeat(5, 1fr); gap:3px; }
  .calc-keys button { font-family: var(--font-ui); font-size:12px; font-weight:700; padding:6px 0;
    cursor:pointer; background:#fff; color: var(--pad-ink); border:1px solid var(--pad-gum); border-radius:3px; }
  .calc-keys button:hover { border-color: var(--pad-ink); }
  .calc-keys .op { background: var(--pad-gum); }
  .calc-keys .unit { font-size:10px; letter-spacing:0.06em; }
  .calc-keys .wide { font-size:9px; letter-spacing:0.06em; }
  .calc-keys .eq { background: var(--pad-ink); color: var(--pad); }
  .lv-head { display:flex; align-items:center; gap:6px; }
  .lv-head-title { flex:1; font-family: var(--font-ui); font-size:9px;
    letter-spacing:0.1em; text-transform:uppercase; color: var(--ink-quiet); }
  .lv-datum { font-family: var(--font-ui); font-size:9px; letter-spacing:0.04em;
    color: var(--ink-quiet); opacity:0.8; }
  .lv-card { display:flex; gap:0; border:1px solid var(--rule); background:transparent; }
  .lv-bar { width:4px; flex:0 0 4px; }
  .lv-body { flex:1; min-width:0; display:flex; flex-direction:column; }
  .lv-name { font-family: var(--font-ui); font-size:10px; letter-spacing:0.06em;
    text-align:left; padding:4px 6px; background:none; border:0; color: var(--ink);
    cursor:pointer; text-transform:uppercase; }
  .lv-card[data-active] .lv-name { font-weight:700; }
  .lv-layers { display:flex; flex-direction:column; }
  .lv-layer { font-family: var(--font-ui); font-size:9px; letter-spacing:0.04em;
    text-align:left; padding:3px 6px 3px 12px; background:none; border:0;
    color: var(--ink-quiet); cursor:pointer; }
  .lv-layer[data-active] { background: var(--accent); color: var(--on-accent);
    font-weight:700; }
  .lv-card-head { display:flex; align-items:center; }
  .lv-card-head .lv-name { flex:1; }
  /* A half floor's row says where its picture is: the preview rail seats no
     card of its own for it any more, and silence there reads as missing
     rather than folded. */
  .lv-rides { font-family: var(--font-ui); font-size:8px; letter-spacing:0.04em;
    text-transform:uppercase; color: var(--ink-quiet); opacity:0.75;
    padding:0 6px 3px 12px; }
  .lv-add, .lv-del { font-family: var(--font-ui); background:none; cursor:pointer;
    color: var(--ink-quiet); }
  .lv-card[data-boneyard] { flex-wrap:wrap; }
  .lv-card[data-boneyard] > .lv-head { flex:1 1 100%; }
  .lv-open-suite { flex:1 1 100%; display:flex; justify-content:flex-end; padding:0 0 4px; }
  .lv-add { border:1px solid var(--rule); font-size:9px; font-weight:700;
    letter-spacing:0.06em; padding:2px 5px; }
  .lv-del { border:0; font-size:12px; line-height:1; padding:2px 6px; }
  .lv-del:hover, .lv-add:hover { color: var(--ink); }
  .lv-empty { font-family: var(--font-ui); font-size:9px; letter-spacing:0.04em;
    color: var(--ink-quiet); opacity:0.7; padding:3px 6px; }
  .lv-layer[disabled] { opacity:0.35; cursor:default; }
  .props-title { font-family: var(--font-ui); font-size:9px; font-weight:600;
    letter-spacing:0.1em; text-transform:uppercase; color: var(--ink-quiet);
    margin-bottom:6px; }
  /* THE PROPERTIES BOX, under the tool column in the left rail. The rule the
     slot used to carry is now the BOX's -- the divider belongs above the
     fold button, or a folded box would sit on the tools with no line between
     them. */
  /* AND IT CANNOT WIDEN THE RAIL. #left-rail sets min-width and no max
     (shell-bars.css:901), so the rail takes the width of its widest child --
     which made a panel's longest line into a measurement of how much of the
     PLAN the rail covers. A two-line note in the BEAM panel took the rail to
     470px and swallowed the presses model-structure-place.spec.js uses to
     draw a beam by hand: both its beam tests failed with `beams 0/0`, no page
     error, nothing on screen to explain it.
     The cap is the tool column's own width, so the box wraps inside the rail
     the tools already established instead of setting a new one. */
  #props-box { margin-top:10px; padding-top:10px; max-width:180px;
    border-top:1px solid var(--edge-panel); }
  #props-box[hidden] { display:none; }
  .props-fold { display:block; width:100%; padding:0 0 6px; border:0;
    background:none; cursor:pointer; text-align:left;
    font-family: var(--font-ui); font-size:9px; font-weight:600;
    letter-spacing:0.1em; text-transform:uppercase; color: var(--ink-quiet); }
  .props-fold:hover { color: var(--ink); }
  /* The fold's arrow is the button's own, so the word never moves. */
  .props-fold::after { content:' \25B4'; opacity:0.7; }
  [data-folded] > .props-fold::after { content:' \25BE'; }
  #props-slot { font-family: var(--font-ui); font-size:10px;
    color: var(--ink-quiet); }
  #props-slot[hidden] { display:none; }
  [data-folded] > #props-slot { display:none; }

  /* THE SEATING CHART: six cut views, all six always shown. The shape is
     MODEL.dc.html's rail (:188-200) and the PAIRING is its own -- E1|E3 over
     E2|E4, then the two section seats side by side (:7266, :7281).

     NOT its colours, though. That rail is #fff cards with #b04060 accents
     baked in, because that page has one light skin. This page has a night
     one, and the same literals inside drawCutMarks2D punched a white disc
     into the night drawing -- tests/model-html-cuts.spec.js's header records
     it. Every colour here is a palette token, so the rail follows the skin. */
  /* The CONTAINER takes no clicks, the seats do -- MODEL.dc.html's own split
     (.view-thumb carries `pointer-events:auto`, so its wrapper does not).
     Without it the 6px gutters between seats swallow taps meant for the
     drawing underneath. */
  /* TWO COLUMNS, AND THE SEATS ARE SMALL ON PURPOSE. Two because this rail is
     going to hold the plan cards as well -- ROOF and SITE, then plan/floor for
     each level, the way MODEL.dc.html pairs them across its two columns
     (:7268-7279). Six seats is three rows; the full chart is closer to seven,
     so the rail grows DOWNWARD and the seat has to be small enough that it
     still fits when it does.
     Measured on repro-garage-house rather than eyeballed: at 118px the rail
     hid 9.6% of the drawing, at 84px it hides 5.5%, for the same six views.
     A single tall column was worse than either (10.9%) -- fit() puts the house
     against the right edge, so a column runs straight down the work. */
  /* A TENANT OF THE RIGHT PANEL, not a corner overlay. It was fixed at
     right:12px/top:80px and hid 5.5% of the drawing whatever the drafter was
     doing; inside the panel it hides nothing when the panel is shut. */
  #view-rail { display:grid; grid-template-columns:repeat(2, 84px); gap:6px;
    /* No max-height of its own any more: the panel it sits in scrolls, so the
       plan cards can double the row count without the rail needing a second
       scroller inside the first. */ }
  #view-rail[hidden] { display:none; }
  .seat { pointer-events:auto;
    width:84px; padding:0; text-align:left; cursor:pointer; overflow:hidden;
    background-color: var(--surface-panel); background-image: var(--surface-panel-tex, none);
    border:1px solid var(--edge-panel); border-radius:4px; }
  .seat:hover { border-color: var(--accent); }
  .seat.active { border-color: var(--accent); box-shadow:0 0 0 1px var(--accent); }
  /* A SEAT WITH NOTHING BEHIND IT IS A CHAIR, NOT A BUTTON: disabled rather
     than merely unstyled, so it leaves the tab order and a screen reader's
     list of things to press. An empty SECTION seat is the exception -- Movie,
     11 Oct: "same +CUT as down here, but put it in the middle of the layout
     preview" -- it is the + CUT press, its mark centred on the blank screen. */
  .seat { position:relative; }
  .seat-cut { position:absolute; left:0; right:0; top:0; height:54px;
    display:flex; align-items:center; justify-content:center; pointer-events:none; }
  .seat-cut[hidden] { display:none; }
  .seat-cut span { font-family: var(--font-ui); font-size:9px; font-weight:700;
    letter-spacing:0.06em; padding:2px 5px; border:1px solid var(--rule);
    color: var(--ink-quiet); }
  .seat:hover .seat-cut span { color: var(--ink); border-color: var(--accent); }
  /* WHOSE THE FAINT WALLS ARE. A seat that has folded its half floor in
     draws it lighter, and lighter alone does not say "this is the 1.5
     floor" -- so the storey number sits in the corner of the picture.
     Over the canvas rather than in the label, which is one nowrap line
     that would ellipsis away the tag before the name. */
  .seat-fold { position:absolute; right:2px; top:40px; pointer-events:none;
    font-family: var(--font-ui); font-size:7px; font-weight:700;
    letter-spacing:0.04em; padding:0 2px; border-radius:2px;
    background: var(--surface-panel); color: var(--ink-quiet);
    border:1px solid var(--edge-panel); }
  .seat-fold[hidden] { display:none; }
  .seat.active .seat-fold { color: var(--accent); border-color: var(--accent); }
  /* BG: this layer drawn faded behind the plan in view (Movie, 21 Oct). Top
     right of the picture, and it takes its own clicks. */
  .seat-bg { position:absolute; right:2px; top:2px; pointer-events:auto; cursor:pointer;
    font-family: var(--font-ui); font-size:7px; font-weight:700;
    letter-spacing:0.04em; padding:0 3px; border-radius:2px;
    background: var(--surface-panel); color: var(--ink-quiet);
    border:1px solid var(--edge-panel); }
  .seat-bg[hidden] { display:none; }
  .seat-bg[data-lit] { color: var(--surface-page); background: var(--accent); border-color: var(--accent); }
  .seat[disabled] { cursor:default; }
  .seat[disabled]:hover { border-color: var(--edge-panel); }
  .seat canvas { display:block; width:82px; height:54px; }
  .seat[disabled] canvas { opacity:0.25; }
  /* THE SHELF PREVIEWS, CENTRED ABOVE THE BONE (Movie, 23 Oct): "little
     square preview windows showing the SHELF items. if only one make it
     centered above the bone, if more are added continue to center them
     above the bone" -- and "make those SHELF squares retractable into a
     SHELFS tab that will be positioned just above the bone when closed
     (centered)". Mounted inside #dt-bar, whose left is the sheet's axis,
     and the transform makes the axis the strip's MIDDLE, however many
     squares it holds. THE TAB SITS INSIDE THE BAR'S OWN TOP BAND, not over
     the sheet: the strip's foot is one tab's height (14px, clear of the
     bone's top) below the bar's top edge, so retracted it covers nothing a
     press on the sheet wants
     (typed-drag's line at the sheet's foot landed on it, measured), and
     opened the squares rise above the bar. */
  #shelf-strip { position:absolute; left:0; bottom:calc(100% - 14px); transform:translateX(-50%);
    display:flex; flex-direction:column; align-items:center; gap:4px;
    pointer-events:auto; }
  #shelf-strip[hidden] { display:none; }
  #shelf-previews { display:flex; align-items:flex-end; gap:6px; }
  #shelf-previews[hidden] { display:none; }
  .shelf-preview { width:72px; padding:0; cursor:pointer; overflow:hidden; text-align:center;
    background-color: var(--surface-panel); background-image: var(--surface-panel-tex, none);
    border:1px solid var(--edge-panel); border-radius:4px; }
  .shelf-preview canvas { display:block; width:70px; height:70px; }
  .shelf-preview .shelf-preview-name { display:block; padding:2px 3px 3px; font-size:9px;
    letter-spacing:.04em; text-transform:uppercase; color: var(--ink-secondary, var(--ink-primary));
    border-top:1px solid var(--edge-panel); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .shelf-preview:hover { border-color: var(--accent); }
  .shelf-preview[data-active] { border-color: var(--accent); box-shadow:0 0 0 1px var(--accent); }
  #shelf-tab { height:14px; box-sizing:border-box; padding:0 8px; font-size:8px; letter-spacing:.08em;
    cursor:pointer; line-height:12px;
    color: var(--ink-primary);
    background-color: var(--surface-chip); background-image: var(--surface-chip-tex, none);
    border:1px solid var(--edge-panel); border-radius:6px 6px 0 0; }
  #shelf-tab:hover { color: var(--accent); border-color: var(--accent); }
  /* OBJ / PROP: the SHELF tab turned upside down, hanging under the top bar
     over the right rail, and the window it drops (Movie, 24 Oct). */
  #objprop-tab { position:fixed; z-index:45; top:var(--strip-h, 44px); height:16px; box-sizing:border-box;
    padding:0 10px; font-size:8px; letter-spacing:.08em; line-height:14px; cursor:pointer;
    color: var(--ink-primary);
    background-color: var(--surface-chip); background-image: var(--surface-chip-tex, none);
    border:1px solid var(--edge-panel); border-top:none; border-radius:0 0 6px 6px; }
  #objprop-tab:hover, #objprop-tab[aria-expanded="true"] { color: var(--accent); border-color: var(--accent); }
  #objprop-tab[hidden], #objprop[hidden] { display:none; }
  #objprop { position:fixed; z-index:45; width:300px; box-sizing:border-box; padding:6px;
    max-height:calc(100vh - var(--strip-h, 44px) - 80px); overflow:auto;
    /* SOLID: the rail's cards and the dock must not show through it. */
    background-color: var(--surface-page, #111);
    background-image: linear-gradient(var(--surface-panel), var(--surface-panel));
    border:1px solid var(--accent); border-radius:0 0 4px 4px;
    font-family: var(--font-ui); }
  #objprop .objprop-title { font-size:10px; font-weight:600; letter-spacing:.08em; color: var(--ink-primary);
    text-transform:uppercase; padding:0 2px 5px; }
  #objprop .objprop-views { display:grid; grid-template-columns:1fr 1fr; gap:4px; }
  #objprop .objprop-views[hidden] { display:none; }
  #objprop figure { margin:0; border:1px solid var(--edge-panel); border-radius:3px; }
  #objprop canvas { display:block; width:100%; height:110px; }
  #objprop figcaption { font-size:8px; letter-spacing:.08em; color: var(--ink-quiet);
    padding:2px 4px; border-top:1px solid var(--edge-panel); }
  #objprop .objprop-props { padding-top:6px; }
  /* A STAIR TAKES THE TWO TOP BOXES, its PLAN and its SECTION. */
  #objprop[data-objprop-kind="stair"] figure:nth-child(n+3) { display:none; }
  #objprop[data-objprop-kind="stair"] canvas { height:140px; }
  #objprop [data-stair-risers-said] { font-size:10px; align-self:center; padding:0 6px; color: var(--ink-primary); }
  #objprop [data-stair-width] { width:72px; }
  /* OPEN SUITE AND SAVE AS SUITE EITHER SIDE OF THE TAB (Movie, 23 Oct),
     the tab's own size, and only while the sheet shows a shelf. Equal
     widths, so the tab between them stays centred on the bone. */
  #shelf-tab-row { display:flex; align-items:flex-end; gap:6px; }
  .shelf-tab-side { height:14px; width:88px; box-sizing:border-box; padding:0 4px; font-size:8px;
    letter-spacing:.06em; cursor:pointer; line-height:12px; white-space:nowrap;
    color: var(--ink-primary);
    background-color: var(--surface-chip); background-image: var(--surface-chip-tex, none);
    border:1px solid var(--edge-panel); border-radius:6px 6px 0 0; }
  .shelf-tab-side:hover { color: var(--accent); border-color: var(--accent); }
  .shelf-tab-side[hidden] { display:none; }
  .seat-label { padding:3px 4px 4px; border-top:1px solid var(--edge-panel);
    font-family: var(--font-ui); font-size:8px; font-weight:600;
    letter-spacing:0.04em; text-transform:uppercase; color: var(--ink-quiet);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .seat.active .seat-label { color: var(--accent); }

  /* The empty and error states say what to do, because a black screen with
     nothing on it is indistinguishable from a page that failed to boot. */
  /* #promote's OWN RULES CAME OUT WITH THE CARD (Movie, 15 Oct): the board
     promotes itself now and asks nothing. The cards below kept the shell it
     set -- a modal with two ways out, one look for one kind of thing -- and
     `.promote-card` / `.promote-row` still carry its name because that is
     where the shape was first written down. [hidden] is spelled out on every
     one of them: the author display would otherwise beat the UA rule, which
     is the bug the UNGROUP button had. */
  /* THE SAME SHELL for the bone's second press -- and it is a SEPARATE
     element from #bone-choice above, deliberately. That one is the TOY
     board's "break the bone here, or move this wall?", a different bone
     entirely: reusing its id because both are about a bone and both offer
     two answers is exactly the collision DEFINITIONS.md exists to stop. */
  #bone-choice, #build-choice, #garage-lesson, #corner-join, #garage-align, #trace-ask, #hologram-ask, #trace-insert,
  #footprint-ask, #room-corner-ask, #stair-fit { position:fixed; inset:0; z-index:7; display:flex;
    align-items:center; justify-content:center; padding:24px;
    background:rgba(0,0,0,0.35); }
  #bone-choice[hidden], #build-choice[hidden], #garage-lesson[hidden], #corner-join[hidden], #garage-align[hidden],
  #trace-ask[hidden], #hologram-ask[hidden], #trace-insert[hidden], #footprint-ask[hidden], #room-corner-ask[hidden], #stair-fit[hidden] { display:none; }
  /* A button the pull puts out of reach is HIDDEN, not greyed -- see the
     card's own note. The row is flex, so this has to beat its display. */
  #footprint-ask button[hidden] { display:none !important; }
  /* THE BEAM QUESTION HOLDS NOTHING: no backdrop, over the top of the sheet. */
  #beam-ask { position:fixed; top:64px; left:50%; transform:translateX(-50%); z-index:6; }
  #beam-ask[hidden] { display:none; }
  #brush-warn { position:fixed; top:64px; left:50%; transform:translateX(-50%); z-index:6; }
  #shelf-suite, #shelf-close { position:fixed; top:64px; left:50%; transform:translateX(-50%); z-index:6; }
  #shelf-suite[hidden], #shelf-close[hidden] { display:none; }
  /* THE X THAT CLOSES A SHELF (Movie, 23 Oct): top right of its square
     above the bone, and at the right end of its row in the LEVELS pane. A
     span, not a button -- it sits inside the square's and the row's own
     buttons -- drawn by CSS so the square's name reads as the name alone. */
  .shelf-close-x { position:absolute; top:1px; right:1px; width:12px; height:12px; line-height:11px;
    font-size:12px; text-align:center; border-radius:2px; cursor:pointer;
    color: var(--ink-quiet); background-color: var(--surface-panel); }
  .shelf-close-x::before { content:'\00d7'; }
  .shelf-close-x:hover { color: var(--accent); }
  .shelf-preview { position:relative; }
  .lv-layer[data-shelf] { position:relative; padding-right:20px; }
  .lv-layer[data-shelf] .shelf-close-x { top:2px; right:5px; background:none; }
  .lv-layer[data-active] .shelf-close-x { color: var(--on-accent); }
  /* BESIDE THE ROOF KEY, over the BUILD panel, not over the plan the ROOF
     tool's own edge flip is pressed on. */
  #roof-pick { position:fixed; top:240px; left:30px; max-width:300px; z-index:6; }
  #roof-pick[hidden] { display:none; }
  #stair-pick { position:fixed; top:240px; left:30px; max-width:300px; z-index:6; }
  #stair-pick[hidden] { display:none; }
  #stair-pick button[aria-pressed="true"] { background: var(--accent); color: var(--on-accent); }
  #stair-pick [data-stair-turns][hidden] { display:none; }
  #gable-ask { position:fixed; top:240px; left:30px; max-width:300px; z-index:6; }
  #gable-ask[hidden] { display:none; }
  .gable-ask-row { display:flex; align-items:center; justify-content:space-between; gap:6px; margin:4px 0; font-size:10px; }
  .gable-ask-row input { width:72px; }
  #brush-warn[hidden] { display:none; }
  #brush-warn .promote-card { border-color: var(--ink-warn); box-shadow: 0 0 14px var(--ink-warn); }
  #brush-warn b { color: var(--ink-warn); }
  /* THE TRACE CARD: what the file is, the scale it was drawn at, and the
     two-point CALIBRATE on its own preview. */
  .trace-card [hidden] { display:none !important; }
  /* The level's tracing images in the ask card, each with MOVE and DELETE. */
  .trace-list { margin:0 0 12px; }
  .trace-row { display:flex; align-items:center; gap:6px; padding:3px 0;
    border-bottom:1px solid var(--edge-panel); font-family: var(--font-ui); font-size:12px;
    color: var(--ink-primary); }
  .trace-row span { flex:1; overflow-wrap:anywhere; }
  /* A HOLOGRAM'S ROW WRAPS: its name on a line of its own, the verbs under
     it -- squeezed into one row the name stood a letter wide ("S U I T E"). */
  .trace-row[data-hologram-row] { flex-wrap:wrap; }
  .trace-row[data-hologram-row] [data-hologram-name] { flex-basis:100%; }
  /* ONE CARD PER FILE, in its colour; a numbered row per copy (Movie, 20 Oct). */
  .hologram-file { border-left:4px solid var(--holo-ink); padding-left:6px; margin:6px 0; }
  .hologram-file-head [data-hologram-name] { flex:1 1 auto; color: var(--holo-ink); }
  .hologram-file-head [data-hologram-swatch] { width:12px; height:12px; border-radius:2px;
    background: var(--holo-ink); flex:0 0 auto; }
  .hologram-file-head [data-hologram-copies] { opacity:0.7; }
  .hologram-copy [data-hologram-number] { min-width:2.2em; font-weight:600; color: var(--holo-ink); }
  .hologram-copy { cursor:pointer; border-radius:3px; }
  .hologram-copy:hover, .hologram-copy[data-lit] { box-shadow: 0 0 0 1px var(--holo-ink), 0 0 8px var(--holo-ink); }
  /* A LIST, AND THE BUTTONS FOR THE ONE PICKED (Movie, 21 Oct: "can it just
     LIST the items and show all the button for only the one the user has
     selected so it won't be as tall, and this menu could also go to 2 or 3
     columns and be wider too"). */
  .hologram-copy [data-hologram-where] { opacity:0.75; }
  .hologram-file-head [data-hologram-copies], .hologram-tools [data-hologram-floor] { flex:0 0 auto; white-space:nowrap; align-self:center; }
  .hologram-inside { padding-left:14px; cursor:pointer; }
  .hologram-inside input { margin:0 6px 0 0; }
  .hologram-tools { display:flex; flex-wrap:wrap; gap:6px; flex-basis:100%; padding:4px 0 2px; }
  #hologram-ask .promote-card { max-width:min(1100px, 94vw); width:min(1100px, 94vw); }
  #hologram-ask [data-hologram-list] { display:grid; gap:8px 14px;
    grid-template-columns:repeat(auto-fill, minmax(300px, 1fr)); max-height:62vh; overflow:auto; }
  #hologram-ask [data-hologram-list] .hologram-show { grid-column:1 / -1; }
  #hologram-ask .hologram-file { margin:0; }
  .trace-row button { cursor:pointer; padding:3px 8px; border-radius:3px; border:1px solid var(--edge-panel);
    background: var(--surface-chip); color: var(--ink-quiet); font-family: var(--font-ui);
    font-size:10px; font-weight:600; letter-spacing:0.04em; }
  .trace-row button:hover { border-color: var(--accent); color: var(--accent); }
  .trace-row [data-trace-delete]:hover { border-color:#b04050; color:#b04050; }
  .trace-row [data-hologram-delete]:hover { border-color:#b04050; color:#b04050; }
  .trace-row span[data-hologram-name] { color: rgb(82,151,218); }
  .hologram-head { margin: 4px 0 4px; }
  /* A DXF's layers under its row: lit while showing, struck through while off. */
  [data-trace-layers] { margin: 0 0 8px; }
  [data-trace-layers] button { cursor:pointer; padding:2px 7px; border-radius:3px; border:1px solid var(--edge-panel);
    background: var(--surface-chip); color: var(--ink-primary); font-family: var(--font-ui);
    font-size:10px; font-weight:600; letter-spacing:0.04em; }
  [data-trace-layers] button[aria-pressed="false"] { opacity: 0.5; text-decoration: line-through; color: var(--ink-quiet); }
  [data-trace-layers] button:hover { border-color: var(--accent); }
  .hologram-show button[aria-pressed="true"] { border-color: rgb(82,151,218); color: rgb(82,151,218); }
  .hologram-show button[aria-pressed="false"] { opacity: 0.55; text-decoration: line-through; }
  /* THE HOLOGRAMS' LAYERS (Movie, 26 Oct): which set, OWN SET, the switches. */
  .hologram-layers { display:flex; flex-wrap:wrap; align-items:center; gap:6px; padding:6px 0;
    border-top:1px solid var(--edge-panel); border-bottom:1px solid var(--edge-panel); }
  .hologram-layers select { padding:3px 6px; border-radius:3px; border:1px solid var(--edge-panel);
    background: var(--surface-chip); color: var(--ink-primary); font-family: var(--font-ui);
    font-size:10px; font-weight:600; letter-spacing:0.04em; }
  .hologram-own-set { font-family: var(--font-ui); font-size:10px; font-weight:600; letter-spacing:0.04em;
    color: var(--ink-quiet); display:inline-flex; align-items:center; gap:4px; cursor:pointer; }
  .hologram-layer-buttons { display:flex; flex-wrap:wrap; gap:4px; flex-basis:100%; }
  .hologram-layers button:disabled { opacity:0.35; cursor:default; }
  .trace-card { max-width:30em; width:100%; max-height:calc(100vh - 60px); overflow-y:auto; }
  .trace-card input[type=text] { width:100%; box-sizing:border-box; margin-top:4px; padding:4px 8px;
    border:1px solid var(--edge-panel); border-radius:3px; background: var(--surface-chip);
    color: var(--ink-primary); font-family: var(--font-ui); font-size:12px; }
  .trace-label { font-family: var(--font-ui); font-size:10px; font-weight:600; letter-spacing:0.06em;
    color: var(--ink-quiet); margin-top:10px; }
  .trace-status { font-family: var(--font-ui); font-size:12px; color: var(--ink-quiet); }
  .trace-preview { position:relative; margin-top:6px; }
  .trace-preview img { display:block; width:100%; max-height:42vh; object-fit:contain;
    border:1px solid var(--edge-panel); background:#fff; cursor:crosshair; }
  .trace-mark { position:absolute; width:10px; height:10px; margin:-5px 0 0 -5px; border-radius:50%;
    border:2px solid #b04050; background:rgba(176,64,80,0.25); pointer-events:none; }
  .trace-verdict { margin-top:8px; font-family: var(--font-ui); font-size:11px; font-weight:600;
    letter-spacing:0.05em; color: var(--accent); }
  .trace-pages, .trace-cal-row { display:flex; align-items:center; gap:6px; margin-top:8px; }
  .trace-pages .trace-label, .trace-cal-row .trace-label { margin-top:0; }
  .trace-cal-row input[type=text] { width:7em; margin-top:0; }
  .trace-chips { display:flex; flex-wrap:wrap; gap:4px; margin-top:4px; }
  .trace-card button { cursor:pointer; padding:4px 9px; border-radius:3px; border:1px solid var(--edge-panel);
    background: var(--surface-chip); color: var(--ink-quiet); font-family: var(--font-ui);
    font-size:11px; font-weight:600; letter-spacing:0.04em; }
  .trace-card button[aria-pressed="true"], [data-trace-go] { border-color: var(--accent) !important; color: var(--accent) !important; }
  .trace-wide { width:100%; margin-top:10px; }
  .trace-card [data-trace-card-layers] button[aria-pressed="false"] { opacity:0.5; text-decoration:line-through; }
  [data-trace-editable] { border-color: var(--accent) !important; color: var(--accent) !important; }
  /* A DXF's words, to copy out: selectable text, a COPY on each. */
  .trace-text { margin-top:8px; }
  .trace-text summary { cursor:pointer; font-family: var(--font-ui); font-size:10px; font-weight:600;
    letter-spacing:0.06em; color: var(--ink-quiet); }
  .trace-text-list { max-height:9em; overflow-y:auto; margin-top:4px; }
  .trace-text-row { display:flex; align-items:center; gap:6px; padding:2px 0; font-family: var(--font-ui);
    font-size:12px; color: var(--ink-primary); }
  .trace-text-row span { flex:1; user-select:text; overflow-wrap:anywhere; }
  .trace-text-row button { cursor:pointer; padding:1px 6px; border-radius:3px; border:1px solid var(--edge-panel);
    background: var(--surface-chip); color: var(--ink-quiet); font-family: var(--font-ui); font-size:9px;
    font-weight:600; letter-spacing:0.04em; }
  .trace-hint { margin-top:6px; font-family: var(--font-ui); font-size:11px; color: var(--ink-quiet); }
  .trace-err { min-height:0; font-family: var(--font-ui); font-size:10px; color:#b04050; margin-top:3px; }
  .trace-actions { margin-top:14px; }
  /* PROFESSOR GRUFF'S GARAGE LESSON, the old page's card (MODEL.dc.html
     garageCallout) in this page's shell: who is talking, the rule, and the
     right-vs-wrong sketch. */
  .gruff-head { display:flex; gap:12px; align-items:flex-start; }
  .gruff-head svg { flex-shrink:0; }
  .gruff-name { font-family: var(--font-ui); font-size:13px; font-weight:700;
    letter-spacing:0.08em; text-transform:uppercase; color: var(--ink-primary); }
  .gruff-sketches { display:flex; gap:14px; justify-content:center; margin:4px 0 12px; }
  .gruff-off { display:flex; align-items:center; gap:6px; margin-top:10px;
    font-family: var(--font-ui); font-size:11px; color: var(--ink-quiet); cursor:pointer; }
  /* The queued house, named on its own line above the two answers -- Movie,
     19 Sep: "(and should show what type of house they are currently going to
     build - (1 storey bungalow, 2 storey over garage, Modified Bilevel
     etc.)". A choice between two verbs with no subject is not a choice. */
  [data-build-choice-house] { font-weight:700; letter-spacing:0.06em; }
  /* IT FOLLOWS THE CURSOR AND TAKES NO PRESSES. pointer-events:none is not
     decoration here -- a label under the pointer that could be clicked would
     eat the very click it is describing. */
  #hover-name { position:fixed; z-index:8; pointer-events:none;
    padding:2px 6px; border:1px solid var(--edge-panel); border-radius:3px;
    background-color: var(--surface-panel);
    background-image: var(--surface-panel-tex, none);
    color: var(--ink-primary); font-family: var(--font-ui); font-size:11px;
    letter-spacing:0.03em; white-space:nowrap;
    box-shadow:0 2px 8px rgba(0,0,0,0.3); }
  #hover-name[hidden] { display:none; }
  .promote-card { max-width:26em; padding:16px 18px; border-radius:4px;
    border:1px solid var(--edge-panel);
    background-color: var(--surface-panel);
    background-image: var(--surface-panel-tex, none); }
  .promote-card p { margin:0 0 12px; font-family: var(--font-ui);
    font-size:13px; line-height:1.4; color: var(--ink-primary); }
  /* THE LEASE NOTE UNDER THE THREE ANSWERS. Dressed as #lease is -- same
     size, same letter-spacing, same uppercase, same accent press -- because
     it is the same sentence, and a drafter who reads both should recognise
     the second as the first rather than wonder what else is wrong.
     `[hidden]` needs the explicit display:none here: display:flex outranks
     the attribute's own UA rule. */
  .promote-card [data-guard-lease] { display:flex; gap:10px;
    align-items:center; justify-content:center; margin:12px 0 0;
    font-size:11px; letter-spacing:0.06em; text-transform:uppercase;
    color: var(--ink-primary); }
  .promote-card [data-guard-lease][hidden] { display:none; }
  .promote-card [data-guard-take-over] { font:inherit;
    letter-spacing:inherit; text-transform:inherit; color: var(--accent);
    background:none; border:1px solid var(--accent); border-radius:4px;
    padding:3px 8px; cursor:pointer; }
  .promote-row { display:flex; gap:8px; }
  .promote-row button { flex:1; padding:7px 10px; cursor:pointer;
    border-radius:3px; border:1px solid var(--edge-panel);
    background-color: var(--surface-chip);
    background-image: var(--surface-chip-tex, none);
    color: var(--ink-quiet); font-family: var(--font-ui); font-size:11px;
    font-weight:600; letter-spacing:0.04em; }
  .promote-row button:hover { border-color: var(--accent); color: var(--accent); }
  [data-promote-go], [data-room-pick-build] { border-color: var(--accent); color: var(--accent); }
  /* HOW FAR FORWARD THE ROOM OVER THE GARAGE COMES (Movie, 10 Oct). Not a
     dialog: the plan stays live behind it, so the bold line is seen moving
     as the feet are clicked. */
  #room-pick { position:fixed; left:50%; top:64px; transform:translateX(-50%);
    z-index:6; }
  #room-pick[hidden] { display:none; }
  #room-pick .promote-card { max-width:34em; }
  #room-pick .promote-row + .promote-row { margin-top:8px; }
  #room-pick [aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }
  /* HOW BIG THE LOT IS (Movie, 13 Oct). #room-pick's card and its reason:
     not a dialog, because the AUTOSITE rectangle lands on the plan behind
     it and the drafter should watch it land. The tiles wrap, since there
     are six of them and a seventh that opens two fields. */
  #lot-pick { position:fixed; left:50%; top:64px; transform:translateX(-50%);
    z-index:6; }
  #lot-pick[hidden] { display:none; }
  #lot-pick .promote-card { max-width:44em; }
  #lot-pick .promote-row + .promote-row { margin-top:8px; }
  /* TWO COLUMNS (Movie, 14 Oct): the sizes on the left, the four commands on
     the right. The left side grows with its rows and the right holds a
     column width, so the commands keep one line each however long a lot's
     description runs. */
  #lot-pick .lot-body { display:flex; gap:14px; align-items:flex-start;
    margin-top:8px; }
  #lot-pick .lot-left { flex:1 1 auto; min-width:0; }
  #lot-pick .lot-commands { flex:0 0 11.5em; display:flex; flex-direction:column;
    gap:6px; }
  #lot-pick .lot-commands button { padding:7px 10px; cursor:pointer;
    border-radius:3px; border:1px solid var(--edge-panel);
    background-color: var(--surface-chip);
    background-image: var(--surface-chip-tex, none);
    color: var(--ink-quiet); font-family: var(--font-ui); font-size:11px;
    font-weight:600; letter-spacing:0.04em; white-space:nowrap; }
  #lot-pick .lot-commands button:hover { border-color: var(--accent); color: var(--accent); }
  /* THE VERB IS THE ONE THAT DRAWS, so it wears the accent the other three
     do not -- the same rule [data-promote-go] follows on every other card. */
  #lot-pick [data-lot-pick-draw] { border-color: var(--accent); color: var(--accent); }
  /* THE WAY THROUGH, AND IT LOOKS LIKE ONE. Wide as the card, filled rather
     than outlined, and in the accent -- which is this page's red -- so it is
     the thing a drafter sees before he has read a word of the ladder above
     it. The four commands beside the sizes keep their outline: they are
     choices, and this is the one that needs no choosing. */
  #lot-pick .lot-ok { display:block; width:100%; margin-top:10px;
    padding:10px 14px; border:1px solid var(--accent); border-radius:6px;
    background-color: var(--accent); color: var(--accent-ink);
    font-family: var(--font-ui); font-size:15px; font-weight:700;
    letter-spacing:0.1em; text-transform:uppercase; cursor:pointer; }
  #lot-pick .lot-ok:hover { filter:brightness(1.1); }
  /* NOTHING FITS, NOTHING TO TAKE: with no lot on the ladder big enough for
     the building there is no best fit to press OK for, and a button that
     answered a question it cannot answer would be worse than one that waits. */
  #lot-pick .lot-ok:disabled { opacity:0.45; cursor:default; filter:none; }
  /* AND THE BUTTON IT WILL PRESS GLOWS WHILE IT CAN (Movie, 16 Oct:
     "highlight the 'DRAW AUTOSITE' button so the user knows when they press
     OK that is what will happen" ... "make it glow").

     SO OK IS NOT MAGIC. The one risk in a button that answers for you is the
     drafter not knowing what it answered; the glow names the verb before he
     presses, and the autosite line keeps saying which SIZE. The pulse is
     what makes it a pointer rather than a state -- the drive-thru bone's own
     trick -- and it is STILLED for anyone who asked for less motion, with
     the glow left on, so nothing is announced only by movement. */
  @keyframes lot-ok-points {
    0%, 100% { box-shadow: 0 0 4px 0 var(--accent); }
    50%      { box-shadow: 0 0 12px 2px var(--accent); }
  }
  #lot-pick [data-lot-pick-draw].lot-ok-target {
    animation: lot-ok-points 1.4s ease-in-out infinite; }
  @media (prefers-reduced-motion: reduce) {
    #lot-pick [data-lot-pick-draw].lot-ok-target {
      animation:none; box-shadow: 0 0 9px 1px var(--accent); }
  }
  #lot-pick [data-lot-pick-draw]:disabled { opacity:0.45; cursor:default;
    border-color: var(--edge-panel); color: var(--ink-quiet); }
  /* ONE COLUMN, SMALLEST FIRST (Movie, 14 Oct): "in the list of site sizes
     can you list them all in 1 column (wide columns allow all the text on one
     line) many rows to allow all the selections from smallest to biggest
     last". Six wrapped chips made a reader hunt for the size he wanted among
     three stacked scraps of text; a column of full-width rows is a list, and
     a list is read top to bottom. THE ORDER IS lot-sizes.js's OWN and is
     ascending by construction -- the harness holds that, so this page never
     sorts a table it does not own. */
  #lot-pick .lot-tiles { display:flex; flex-direction:column; gap:4px; }
  #lot-pick .lot-tiles button { display:flex; align-items:baseline; gap:8px;
    width:100%; text-align:left; padding:5px 9px; cursor:pointer; border-radius:3px;
    border:1px solid var(--edge-panel);
    background-color: var(--surface-chip);
    background-image: var(--surface-chip-tex, none);
    color: var(--ink-quiet); font-family: var(--font-ui); }
  #lot-pick .lot-tiles button:hover { border-color: var(--accent); color: var(--accent); }
  #lot-pick [aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }
  /* THE SIZE IS THE NAME and the rest is support, so the dimensions are the
     only part at full strength. ONE LINE each: the size holds a column width
     so the tier letters line up down the list, the character is pushed right
     by its own auto margin, and nothing wraps. */
  #lot-pick .lot-size { font-size:12px; font-weight:700; letter-spacing:0.04em;
    min-width:7.2em; white-space:nowrap; }
  #lot-pick .lot-tier { font-size:9px; letter-spacing:0.1em; opacity:0.75;
    min-width:3em; }
  #lot-pick .lot-area { font-size:9px; opacity:0.75; white-space:nowrap; }
  #lot-pick .lot-character { font-size:9px; opacity:0.75; margin-left:auto;
    white-space:nowrap; }
  /* TOO SMALL FOR WHAT IS DRAWN: faded and unpickable (Movie, 14 Oct: "fade
     out the 'too small' sizes"). Still on the list, so the ladder reads as a
     ladder and a drafter can see what his own building has ruled out. */
  #lot-pick .lot-tiles button.lot-small { opacity:0.4; cursor:default; }
  #lot-pick .lot-tiles button.lot-small:hover { border-color: var(--edge-panel);
    color: var(--ink-quiet); }
  /* AND THE FIRST ONE THAT FITS IS THE BRIGHT ONE ("make the most
     appropriate size brighter"): full-strength ink and the accent edge, so
     the eye lands on it before it has read a single number. */
  #lot-pick .lot-tiles button.lot-best { border-color: var(--accent);
    color: var(--ink-primary); }
  #lot-pick .lot-tiles button.lot-best .lot-size { color: var(--accent); }
  #lot-pick .lot-tiles button.lot-best .lot-tier,
  #lot-pick .lot-tiles button.lot-best .lot-area,
  #lot-pick .lot-tiles button.lot-best .lot-character { opacity:1; }
  #lot-pick .lot-custom { display:flex; gap:4px; align-items:center; margin-top:8px;
    font-family: var(--font-ui); font-size:11px; color: var(--ink-quiet); }
  #lot-pick .lot-custom[hidden] { display:none; }
  #lot-pick .lot-custom input { width:4.6em; font-family: var(--font-ui); font-size:12px;
    padding:4px 5px; border-radius:3px; border:1px solid var(--edge-panel);
    background-color: var(--surface-panel); color: var(--ink-primary); }
  /* THE SENTENCE ABOUT ODD SHAPES, quieter than the tiles and above the
     verb -- it is what to do after pressing, not a choice. */
  #lot-pick .lot-note { margin-top:10px; font-family: var(--font-ui); font-size:10px;
    line-height:1.45; color: var(--ink-quiet); }
  /* THE NOTICE IS A MESSAGE, NOT A DOOR. It covers the whole sheet to be
     read, and while it did so with pointer events it also swallowed every
     press aimed at the bars behind it -- including NEW and OPEN, the two
     controls the "no saved drawing" notice is asking the drafter to use. */
  #notice { position:fixed; inset:0; z-index:6; display:none;
    align-items:center; justify-content:center; padding:24px; text-align:center;
    pointer-events:none; }
  #notice.show { display:flex; }
  #notice div { max-width:34em; pointer-events:auto; }
  #notice h1 { font-family: var(--font-ui); font-size:19px; font-weight:600;
    letter-spacing:0.02em; margin:0 0 8px; color: var(--accent); }
  #notice p { margin:0 0 6px; color: var(--ink-secondary); }
  #notice code { font-family: var(--font-mono); font-size:0.92em;
    color: var(--ink-primary); padding:1px 5px; border-radius:3px;
    background-color: var(--surface-chip); background-image: var(--surface-chip-tex, none); }
