@import url("/shared_pieces.css");
  :root{ --bg:#f4f2ee; --panel:#fff; --ink:#1c1a17; --dim:#6b6459; --line:#dcd7cd; --accent:#3a7d6e;
    /* A COLOR SWATCH IS ONE SIZE EVERYWHERE. It was written out five times -- #settings,
       #elpanel, #site-more, .fxonpiece and #acctpanel -- and the fifth copy drifted, which is
       how the account page came to carry swatches several times the size of the ones on the
       site and settings pages. A number copied five times is a design decision living in code.
       One place now; change it here and every panel follows. */
    --swatch-w:38px; --swatch-h:26px;
    /* ONE STATIC DOCK POSITION FOR THE NON-DESIGN PANELS (#sitepanel/#acctpanel) — was four
       hand-copied `position:fixed;top:92px;right:12px` blocks, a THE ONE LAW violation (structure
       duplicated as code instead of read from one place). Nothing writes these two at runtime any more
       (2026-08-20: only the piece inspector needs to move — see --inspectorDock* below); they stay at
       these defaults always, which is exactly the floating-over-the-canvas position Katri ruled is fine
       for a panel that carries no controls for the piece she's looking at. */
    --panelDockTop:92px; --panelDockRight:12px; --panelDockW:min(94vw,420px);
    /* THE SELECTED-PIECE INSPECTOR ONLY (Katri, 2026-08-20): #sitepanel/#acctpanel hold no controls for
       the piece she's looking at, so they stay on --panelDockRight/--panelDockW above, floating over the
       canvas same as always. #settings is a DESIGN panel she uses WHILE looking at the piece, so it gets
       its own dedicated position, written by window._dockPanels (editor.js) — never a second position
       calculation, just a second pair of properties so the two groups can move independently. Defaults
       here cover the moment before boot. */
    --inspectorDockTop:92px; --inspectorDockRight:12px; --inspectorDockW:min(94vw,420px); }

  /* BUTTON BASELINE. Katri, item 8 on the tablet list, 2026-09-03: "all UI buttons need to be
     what the program uses. not whatever these things are ... the switch for the button look
     should be all panes." Every panel had its own selector giving its own buttons their own look
     -- fifty-odd of them, no shared floor. This is the floor: any button that doesn't match a
     more specific rule gets the program's own look. Specific rules already in this file win by
     cascade; anything new elsewhere inherits sensible defaults without asking.
     Padding/size/radius/border/font all come from the tokens, so a build's accent walks through. */
  button { font: inherit; font-size: 12px; line-height: 1.35; padding: 5px 10px; border-radius: 7px; border: 1px solid var(--line); background: var(--bg); color: var(--ink); cursor: pointer }
  button:hover:not(:disabled) { border-color: var(--accent) }
  button:disabled { opacity: .5; cursor: not-allowed }
  button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px }

  /* THREE SHARED PATTERNS. Katri, item 8 on the tablet list: "the switch for the button look
     should be all panes." These are the shapes buttons take when they mean the same thing across
     the panel: `.primary` is the accent-solid used for the one action per row that COMMITS
     (Save Room, + new character, Log in). `.mini` is the compact roll-up-your-sleeves inline
     control ("Upload", "Clear", "New Blank"). `.clr` is the little clear/x affordance that
     rescinds a setting. Every hardcoded inline copy of one of these three shapes across the
     builder should reach for its class instead. */
  button.primary { background: var(--accent); color: #fff; border-color: var(--accent); font-weight: 600; padding: 7px 14px }
  button.primary:hover:not(:disabled) { filter: brightness(1.08) }
  /* THE OUTLINED PRIMARY -- an accent-bordered call to action that isn't the one solid green
     button on the panel. Used where the same commitment weight would drown the row it sits on. */
  button.primaryoutline { border-color: var(--accent); color: var(--accent); font-weight: 600 }
  button.primaryoutline:hover:not(:disabled) { background: color-mix(in oklab, var(--accent) 10%, transparent) }
  button.primaryoutlinebig { font-size: 14px; padding: 8px 18px; border-radius: 9px }
  /* AN ACCENT-INK button carries a save intent (Save, Make, Publish) without a filled ground.
     font-size and the display:none stay inline where the surrounding row expects them. */
  button.accentink { color: var(--accent); font-weight: 600 }
  /* THE BUILDDOCS HEADER STRIP -- one size for its buttons and its download/PDF/tab links, so a
     shared rule replaces four inline font-size overrides. */
  #bdhd button, #bdhd a { font-size: 13px }
  #bdhd #bd-sub { font-size: 12px }
  /* THE APP-VERSION PANEL wears the same label+input shape on every row -- one rule replaces the
     inline flex/gap/font-size restatements every label used to carry. */
  #ss-appversion label { display: flex; gap: 8px; font-size: 13px; align-items: center; margin: 0 0 6px }
  #ss-appversion label > span { flex: 1 }
  #ss-appversion input[type=text] { flex: 1.4; font: inherit; font-size: 12px; padding: 5px 7px;
    border: 1px solid var(--line); border-radius: 7px; background: var(--bg); color: var(--ink) }
  /* THE MODEL PANEL wears its own row shape: tighter gap, smaller ink, longer inputs. One rule
     replaces the four labels and five inputs that used to spell out this shape inline. */
  #ss-personas label { display: flex; gap: 6px; align-items: center; font-size: 12px; margin-bottom: 4px }
  #ss-personas label > span { flex: 1 }
  #ss-personas input[type=text] { flex: 1.8; font: inherit; font-size: 11px; padding: 5px 7px;
    border: 1px solid var(--line); border-radius: 7px; background: var(--bg); color: var(--ink) }
  #ss-personas label input[type=text] + input[type=text] { flex: 1 }
  #ss-personas input[type=number] { width: 60px; font: inherit; font-size: 12px; padding: 5px 7px;
    border: 1px solid var(--line); border-radius: 7px; background: var(--bg); color: var(--ink) }
  #ss-personas p { font-size: 11px; color: var(--dim); margin-bottom: 8px }
  #ss-personas p:last-of-type { margin-top: 6px }
  /* THE SAME COMPACT ROW SHAPE runs across the ss-* settings sections that ship with the
     builder chrome. Applied by-panel so a section can still opt out with its own selector
     rather than by re-adding inline styles. */
  #ss-charceiling label { display: flex; gap: 6px; align-items: center; font-size: 12px; margin-top: 8px }
  #ss-charceiling label > span { flex: 1 }
  #ss-charceiling input[type=number] { width: 60px; font: inherit; font-size: 12px; padding: 5px 7px;
    border: 1px solid var(--line); border-radius: 7px; background: var(--bg); color: var(--ink) }
  #ss-charceiling p { font-size: 11px; color: var(--dim); margin-top: 4px; margin-bottom: 8px }
  #ss-memberediting > div:first-of-type { font-size: 12px; margin-top: 10px; color: var(--dim) }
  #memberexpose { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 5px }
  #ss-roles p { font-size: 11px; color: var(--dim); margin-bottom: 6px }
  /* THE SETTINGS PANELS EXPLAIN THEMSELVES IN SMALL DIM PROSE, and every one of those
     paragraphs was carrying the same font/color inline. A shared class name reads its
     intent; per-instance margins (some 4px, some 8px) stay on the element inline. */
  .hint { font-size: 11px; color: var(--dim); line-height: 1.5 }
  p.hint { margin: 0 0 8px }
  button.mini { font-size: 11px; padding: 3px 9px; border-radius: 6px }
  button.clr { font-size: 11px; padding: 2px 8px; border-radius: 6px; background: transparent; color: var(--dim); border-color: transparent }
  button.clr:hover:not(:disabled) { color: #c0392b; border-color: #c0392b; background: var(--bg) }
  @media (prefers-color-scheme: dark){ :root{ --bg:#141310; --panel:#1d1b17; --ink:#ece7dc; --dim:#8f887a; --line:#312d26; --accent:#7ec8b4; } }
  /* body.fx-inplace = this stylesheet mounted on a MEMBER's live page (editor.js chrome floats over their
     own served render — see factory.py's _fxInplaceStyle). The live page has its own box model, background
     and layout already; these two rules are the builder's own page reset/shell and must never touch it. */
  /* :where() so the guard costs ZERO specificity — `body:not(.fx-inplace) *` scores (0,1,1) and silently
     OUTRANKS every element-selector rule below it, which zeroed `header{padding:9px 14px}` in the BUILDER
     (measured: header padding computed 0px; #pagebar survived only because an id beats (0,1,1)). Wrapped
     in :where(), this matches the original bare `*` at (0,0,0) and the cascade is unchanged. */
  :where(body:not(.fx-inplace)) *{box-sizing:border-box;margin:0;padding:0}
  html,body{height:100%}
  /* isolation:isolate gives body its own stacking context so the page-background layers (#pagebg z-index:-2,
     #bgvideo z-index:-1) paint ABOVE body's own theme background and below the content — without it those
     negative-z layers bubble to the root context and are hidden behind body's opaque theme bg, so the page
     background the member sets never showed in edit/preview mode. */
  body:where(:not(.fx-inplace)){background:var(--bg);color:var(--ink);font:16px/1.55 -apple-system,"Segoe UI",Roboto,sans-serif;display:flex;flex-direction:column;isolation:isolate}
  #bgvideo{position:fixed;inset:0;width:100%;height:100%;object-fit:cover;z-index:-1}
  /* THE HEADER WRAPS RATHER THAN RUNNING OFF THE EDGE (2026-09-03). At a tablet's 768px the bar
     was a non-wrapping flex row, so the last control -- Preview -- sat at left:778 on a 768px
     viewport: entirely OFF SCREEN and unreachable, and it dragged the whole page 80px sideways,
     which on a touch screen means the interface can be swiped off-centre. `flex-wrap` only
     engages when the row cannot fit, so nothing changes at desktop width.
     NOTE the coupling: --inspectorDockTop is a hardcoded 92px and nothing measures this bar, so
     a wrapped header must stay under that or #settings rides up under it. Measured after this
     change and it does. If a control is ever added here, re-measure. */
  header{display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:9px 14px;border-bottom:1px solid var(--line);background:var(--panel);position:sticky;top:0;z-index:20}
  header h1{font-size:16px;font-weight:700;letter-spacing:.3px}
  header .sub{font-size:12px;color:var(--dim);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  header button,header select{font:inherit;font-size:13px;padding:6px 12px;border-radius:8px;border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer}
  header button.primary{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
  #save{font-size:12px;color:var(--dim);min-width:52px;text-align:right}
  #pagebar{display:flex;align-items:center;gap:6px;padding:7px 14px;border-bottom:1px solid var(--line);background:var(--panel);position:sticky;top:49px;z-index:19;overflow-x:auto}
  #pagebar .plabel{font-size:11px;letter-spacing:.5px;color:var(--dim);margin-right:2px}
  /* Preview shows the PAGE, not the builder. Header and page tabs hide with the tray; the
     preview/edit button is moved out of the header (see _previewChrome) and floats on its own so
     there is still a way back into editing. */
  body:not(.editing) header,body:not(.editing) #pagebar{display:none}
  #preview.fx-float{position:fixed;top:10px;right:12px;z-index:2147482000;opacity:.82;
    box-shadow:0 4px 14px rgba(0,0,0,.35);border:1px solid var(--line);background:var(--panel);
    color:var(--ink);font:inherit;font-size:13px;padding:6px 12px;border-radius:8px;cursor:pointer}
  #preview.fx-float:hover{opacity:1}
  .tab{font:inherit;font-size:13px;padding:6px 12px;border-radius:9px 9px 0 0;border:1px solid var(--line);border-bottom:none;background:var(--bg);color:var(--ink);cursor:pointer;white-space:nowrap;display:flex;align-items:center;gap:7px}
  .tab.on{background:var(--accent);border-color:var(--accent);color:#fff;font-weight:600}
  /* the tab's own controls — rename, add-a-sub-page, delete. One rule for all three so a tab's buttons
     can't drift apart; each still sets its own glyph size/weight inline. */
  .tab .x,.tab .sub,.tab .ren{font-size:13px;opacity:1;border:none;background:none;color:inherit;cursor:pointer;padding:0 1px;font-weight:700}
  .tab .ren{font-weight:400;opacity:.8}.tab .ren:hover{opacity:1}
  #newpage{font:inherit;font-size:13px;padding:6px 11px;border-radius:8px;border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer}
  /* Scrolls by default, exactly like the shipped page. A page that sets `noScroll` (options.json)
     gets main.style.overflow='clip' from applyPage, so the builder matches whichever the page chose
     rather than deciding for it. */
  /* THE BUILDER SHELL MUST NOT SCROLL THE DOCUMENT (2026-08-21, third report of "it still scrolls").
     main is a flex item, and a flex item's default min-height:auto lets its CONTENT force it taller
     than the box -- measured 1804px inside a 720px window. So `main{overflow:clip}` held main still
     while the WHOLE PAGE scrolled underneath it: html scrollHeight 1880, scrollY 300, canvas moved
     300px. Clipping the wrong element is why two fixes and three of my measurements said it was fine.
     min-height:0 makes main respect the shell, and the shell itself clips, so main is the only thing
     that can ever scroll -- and whether it does is the page's own noScroll choice, applied by
     applyPage. Scoped off .fx-inplace, where this stylesheet floats over a member's real page and the
     document belongs to them. */
  /* margin:0 as well, and it is not cosmetic: the reset above is `:where(body:not(.fx-inplace)) *`,
     which matches body's DESCENDANTS, never body itself -- so body kept the UA's default 8px margin
     and the document stayed 8px taller than the window. That was the last of the scroll: after the
     shell fix the canvas still moved exactly 8px. */
  body:not(.fx-inplace){overflow:clip;margin:0}
  main{flex:1;min-height:0;overflow:auto;padding:0 0 200px}
  /* the narrow-window inspector fallback, and nothing else: _reservePanelWidth padded main so
     the covered canvas is still reachable, and that padding is only worth anything if it can
     be scrolled to. Set and cleared by _reservePanelWidth (editor.js). */
  body.fx-panelscroll main{overflow:auto}
  #canvas{position:relative;width:100%;min-height:1600px}
  /* The grid is drawn by #fxgrid (editor.js applyPage), a layer at z-index -1 so it sits ON TOP of the
     page background instead of underneath it. Painting it here as the canvas's own background put it
     BELOW #pagebg (a negative-z child), so her artwork hid it -- see the note at #fxgrid. Left as a
     no-grid rule rather than deleted so the selector still documents where the grid used to live. */
  body.editing #canvas{background-image:none}
  .el{position:absolute}
  body.editing .el{outline:1px dashed color-mix(in srgb,var(--accent) 30%,transparent);border-radius:4px}
  body.editing .el:hover{outline-color:color-mix(in srgb,var(--accent) 55%,transparent)}
  body.editing .el.sel{outline:1.5px solid var(--accent)}
  .el .ctl{position:absolute;top:-26px;left:0;display:none;gap:4px;z-index:3;white-space:nowrap}
  body.editing .el.sel .ctl{display:flex}
  .el .ctl button{font-size:12px;padding:3px 9px;border-radius:6px;border:1px solid var(--accent);background:var(--panel);color:var(--ink);cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.25)}
  .el .ctl button:last-child{color:#c0392b}
  .el .rz{position:absolute;right:-6px;bottom:-6px;width:14px;height:14px;border:2px solid var(--accent);background:var(--panel);border-radius:3px;cursor:nwse-resize;display:none;z-index:3}
  body.editing .el.sel .rz{display:block}
  body.editing .el .inner{pointer-events:none}
  body.editing .el.sec{outline:1px dashed color-mix(in srgb,var(--accent) 45%,transparent);outline-offset:2px;border-radius:6px}
  .seclabel{font-weight:700;font-size:15px;letter-spacing:.3px;margin-bottom:8px}
  /* h2.blk / p.blk / a.blk / img.blk,video.blk / audio.blk / hr.blk moved to /shared_pieces.css
     (2026-08-15) -- the ONE place the builder AND the live served page both load them from. Pulled
     in via the @import at the top of this file, so nothing here needs to change to keep using them. */
  /* A RAIL BESIDE THE CANVAS, NOT A DROPDOWN OVER IT (Katri, 2026-08-20 — "design elements don't have
     any business being in a dropdown menu... it's crappy design aesthetic"). No border-radius, no
     all-around card shadow, no max-height card box: it runs the full height of the work area (top to
     bottom:0) with a single border/shadow on its LEFT edge only, the side that actually meets the
     canvas — reading as a fixed part of the builder's frame, the same family as `header`/`#pagebar`
     above, not as a menu that happened to land on top of the page. */
  #settings{position:fixed;top:var(--inspectorDockTop);right:var(--inspectorDockRight);bottom:0;width:var(--inspectorDockW);min-width:280px;max-width:96vw;overflow:auto;background:var(--panel);border-left:1px solid var(--line);box-shadow:-8px 0 24px rgba(0,0,0,.12);padding:14px;z-index:40;display:none}
  /* long uniform lists flow into columns when the panel is widened -> less scrolling, and each cell is bounded so fields don't stretch/distort */
  #usettingslist,#capslist{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:2px 20px;align-items:start}
  #usettingslist>div,#capslist>div{min-width:0}
  /* the drag-to-resize strip on the LEFT edge of each side panel */
  .panelgrip{position:fixed;z-index:60;width:8px;cursor:ew-resize;display:none;background:rgba(124,92,255,.14)}
  .panelgrip:hover{background:rgba(124,92,255,.4)}
  .panelgrip.corner{width:16px;height:16px;cursor:nesw-resize;background:transparent}
  .panelgrip.corner::after{content:"";position:absolute;left:2px;bottom:2px;width:9px;height:9px;border-left:2px solid rgba(124,92,255,.6);border-bottom:2px solid rgba(124,92,255,.6)}
  #settings h3,#elpanel h3{font-size:12px;letter-spacing:.6px;color:var(--dim);margin:2px 0 10px}
  #settings label,#elpanel label{display:flex;align-items:center;gap:8px;font-size:13px;margin:0 0 9px}
  /* THE NAME SITS BESIDE ITS CONTROL. Katri, 2026-09-04: "bridge the gap. the headers or whatever
     each dropdown is titled is way too far away." The label was flex:1 against a flex:1.5 control, so
     the wider she made the panel the further apart they got -- a one-word label holding a third of the
     row open and the control pushed to the far edge, with the eye having to cross the gap to pair
     them. A bounded label column instead: they line up with each other, long ones still wrap rather
     than truncate, and the control starts right after the name it belongs to. */
  /* A SLIDER IS AS LONG AS IT NEEDS TO BE. Katri, 2026-09-05: "the toggles don't need to be this
     long." They stretched the full width of the panel while the dropdowns beside them were capped at
     280 -- so one row of the same panel was twice the length of the next, and the value at the end
     sat a screen away from the name at the start. Same cap as every other control here. */
  #settings input[type=range],#elpanel input[type=range]{flex:0 1 280px;max-width:280px;min-width:0}
  #settings label span,#elpanel label span{flex:0 1 auto;min-width:120px;max-width:210px}
  #settings input[type=color],#elpanel input[type=color],#sitepanel input[type=color],
  #acctpanel input[type=color],.fxonpiece input[type=color]{
    width:var(--swatch-w);height:var(--swatch-h);padding:0;border:1px solid var(--line);
    border-radius:6px;background:none;cursor:pointer;flex:none}
    /* A CONTROL IS AS WIDE AS IT NEEDS TO BE. Katri, 2026-09-04: "there's no reason for all of these
     dropdowns to be so damn long. i hate that that shit is defaulted across the board in here and i
     have to correct every single one of them." They were flex:1.5 against the row, so every dropdown
     and every box stretched the full width of the panel however wide she made it -- a two-word menu
     600px long. Bounded instead: they still grow on a narrow panel and stop at a readable width. */
#settings input[type=text],#settings input[type=file],#settings input[type=number],#settings select,#elpanel input[type=text],#elpanel input[type=file],#elpanel input[type=number],#elpanel select{flex:0 1 280px;max-width:280px;min-width:0;font:inherit;font-size:12px;padding:5px 7px;border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink)}
  #settings .clr,#elpanel .clr{font-size:11px;color:var(--dim);cursor:pointer;border:none;background:none;padding:2px 0;text-align:left}
  #settings hr,#elpanel hr{border:none;border-top:1px solid var(--line);margin:8px 0 12px}
  #sitepanel{position:fixed;top:var(--panelDockTop);right:var(--panelDockRight);width:var(--panelDockW);min-width:280px;max-width:96vw;max-height:88vh;min-height:220px;overflow:auto;background:var(--panel);border:1px solid var(--line);border-radius:12px;box-shadow:0 10px 30px rgba(0,0,0,.2);padding:14px;z-index:41;display:none}
  #sitepanel,#acctpanel,#settings{container-type:inline-size}
  #sitepanel h3{font-size:12px;letter-spacing:.6px;color:var(--dim);margin:2px 0 10px}
  #sitepanel input[type=text],#sitepanel input[type=number],#sitepanel select{font:inherit;font-size:12px;padding:5px 7px;border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink)}
  /* cap field width so inputs don't stretch/distort when the panel is widened */
  #sitepanel input[type=text],#sitepanel input[type=number],#sitepanel select,#sitepanel textarea,#settings input[type=text],#settings input[type=number],#settings select,#settings textarea{max-width:280px}
  /* inside a card the CARD sets the width, so the 420 cap only leaves it half empty */
  .fxfield input[type=text],.fxfield input[type=number],.fxfield input[type=email],
  .fxfield input[type=password],.fxfield select,.fxfield textarea{max-width:none!important}
  #sitepanel button{font:inherit;font-size:12px;padding:6px 11px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  #sitepanel hr{border:none;border-top:1px solid var(--line);margin:11px 0}
  /* ⟳ THE .ssfold / .ssfoldtag RULES ARE GONE (2026-09-08). They dressed a fold nothing sets any more:
     the hide-some/show-all engine was removed on Katri's instruction, along with the count line it
     printed. A rule left behind for a class no code applies is how a deleted behavior walks back in. */
  /* A PIECE HOUSES ITS OWN SETTINGS (Katri, 2026-08-12). The section's own element is MOVED here from
     the SITE panel, so it arrives without the #sitepanel rules that dressed it. These are those rules,
     re-aimed — declared after the #settings block so they win on equal specificity. Nothing is copied
     but the styling; the editor itself is the same node. */
  #e-piecesecs{margin:2px 0 10px}
  #e-piecesechead{font-size:10px;letter-spacing:1px;color:var(--dim);border-top:1px solid var(--line);padding-top:9px;margin:6px 0 8px}
  #e-piecesecs h3{font-size:12px;letter-spacing:.6px;color:var(--dim);margin:2px 0 10px}
  #e-piecesecs label{display:flex;align-items:center;gap:8px;font-size:13px;margin:0 0 9px}
  #e-piecesecs label span{flex:1}
  #e-piecesecs input[type=text],#e-piecesecs input[type=number],#e-piecesecs input[type=password],#e-piecesecs select{flex:none;font:inherit;font-size:12px;padding:5px 7px;border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink);max-width:420px}
  #e-piecesecs button{font:inherit;font-size:12px;padding:6px 11px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  #e-piecesecs hr{border:none;border-top:1px solid var(--line);margin:11px 0}
  #acctpanel{position:fixed;top:var(--panelDockTop);right:var(--panelDockRight);width:var(--panelDockW);min-width:280px;max-width:96vw;max-height:88vh;min-height:220px;overflow:auto;background:var(--panel);border:1px solid var(--line);border-radius:12px;box-shadow:0 10px 30px rgba(0,0,0,.2);padding:14px;z-index:42;display:none}
  #acctpanel h3{font-size:12px;letter-spacing:.6px;color:var(--dim);margin:2px 0 10px}
  #acctpanel label{display:flex;align-items:center;gap:8px;font-size:13px;margin:0 0 9px}
  #acctpanel label span{flex:1}
  #acctpanel input{font:inherit;font-size:12px;padding:6px 8px;border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink);flex:1.4}
  #acctpanel button{font:inherit;font-size:12px;padding:6px 11px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;flex:none}
  /* A COLOR HAS TO BE VISIBLE AS A COLOR (Katri, 2026-08-29): "make it to where i can see the colors
     the way i can in the site and setting pages". `#acctpanel input` above is a blanket rule -- it
     matches EVERY input, and it puts 6px 8px of padding inside one. On a text box that is right; on a
     color swatch the padding eats the swatch, so a 50x24 control showed the color as a 34x12 sliver
     and every one of the six Factory Appearance rows read as an empty grey box. #settings has always
     had this rule (padding:0, its own size) and #acctpanel never did, which is exactly the difference
     she was pointing at.
     WHAT THIS RULE USED TO DO, AND WHY IT IS GONE (same day, her next report): it fixed the padding
     and then made the swatch 100% wide at 34px tall -- "bigger than the 38x26 those panels use,
     because these cards are half a page wide". She had asked for them to look THE WAY THE SITE AND
     SETTINGS PAGES DO, and the answer went past what she asked for in the same breath as quoting
     her. Her next words were that the account swatches are way bigger than the other two pages.
     The padding fix was the whole bug; the size was an invention. The account panel now takes the
     shared swatch rule above like every other surface. */
  #acctpanel hr{border:none;border-top:1px solid var(--line);margin:11px 0}
  #acctpanel .note{font-size:11px;color:var(--dim)}
  #e-iconpick{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;margin-bottom:6px}
  #e-iconpick button{padding:5px;border:1px solid var(--line);border-radius:6px;background:var(--bg);color:var(--ink);cursor:pointer;display:flex;align-items:center;justify-content:center}
  #e-iconpick button.sel{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 20%,transparent)}
  #e-iconpick button svg{width:18px;height:18px}
  .emailed{border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--bg)}
  .emailed .ee-subject{width:100%;border:none;border-bottom:1px solid var(--line);padding:8px 10px;font:inherit;font-size:13px;background:var(--panel);color:var(--ink)}
  .emailed .ee-tb{display:flex;flex-wrap:wrap;gap:2px;padding:5px;border-bottom:1px solid var(--line);background:var(--panel)}
  .emailed .ee-tb button,.emailed .ee-tb select{font:inherit;font-size:12px;padding:3px 7px;border:1px solid var(--line);border-radius:6px;background:var(--bg);color:var(--ink);cursor:pointer}
  .emailed .ee-body{min-height:120px;max-height:280px;overflow-y:auto;padding:12px;font-size:13px;background:#fff;color:#111}
  .emailed .ee-body img{max-width:100%}
  .emailed .ee-att{padding:6px 10px;font-size:11px;color:var(--dim);border-top:1px solid var(--line);background:var(--panel)}
  #site-more label{display:flex;align-items:center;gap:8px;font-size:13px;margin:0 0 9px}
  #site-more label span{flex:1}
  #site-more input[type=range]{flex:1.5}
  .tierrow,.userrow{display:flex;align-items:center;gap:8px;font-size:13px;margin-bottom:6px}
  /* THE PERSONA ROW'S BUTTONS HAD NO RULE AT ALL and fell back to the browser's own chrome, which
     is what Katri meant by "the buttons need to match the rest of the factory" -- the same thing
     she said about Place it, and about reuse last. The fix is the same each time: JOIN a rule.
     Written once here, on the row, so a button added to it later cannot miss. */
  .userrow button{font:inherit;font-size:11px;padding:4px 8px;border:1px solid var(--line);
    border-radius:6px;background:var(--bg);color:var(--ink);cursor:pointer;flex:none}
  .userrow button:hover:not([disabled]){border-color:var(--accent)}
  /* AND THE NAME FIELD STOPS BEING A LONG THIN SLOT (Katri: "i don't like how the name field looks
     like a really long and skinny box"). It was flex:1 with no padding and no border of its own,
     so it stretched across the whole row and read as a rule rather than a field. */
  .userrow input[type=text]{font:inherit;font-size:13px;padding:6px 9px;border:1px solid var(--line);
    border-radius:7px;background:var(--bg);color:var(--ink);min-width:0}
  .userrow input[type=text]:focus{border-color:var(--accent);outline:none}
  /* MAKING A PERSONA: the same field and button rules as everything else in this panel. */
  #newpersonarow{display:flex;gap:6px;margin-top:8px;flex-wrap:wrap}
  #newpersonarow input{flex:1;min-width:150px;font:inherit;font-size:13px;padding:7px 10px;
    border:1px solid var(--line);border-radius:8px;background:var(--bg);color:var(--ink)}
  #newpersonarow input:focus{border-color:var(--accent);outline:none}
  #newpersonarow button{font:inherit;font-size:12px;padding:7px 12px;border:1px solid var(--line);
    border-radius:8px;background:var(--bg);color:var(--ink);cursor:pointer;flex:none}
  #newpersonarow button:hover{border-color:var(--accent)}
  .tierrow .nm,.userrow .nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  #tray{position:fixed;left:0;right:0;bottom:0;background:var(--panel);border-top:1px solid var(--accent);padding:9px 14px calc(env(safe-area-inset-bottom) + 9px);box-shadow:0 -8px 24px rgba(0,0,0,.12);max-height:40vh;overflow-y:auto;z-index:15}
  #tray .lbl{font-size:11px;letter-spacing:.5px;color:var(--dim);margin-bottom:7px}
  /* In-place editing (member): the live page IS the canvas — pieces render straight into the served
     section, so the injected <main>/#canvas stay empty and hidden. The chrome (header/pagebar/settings/tray)
     floats as translucent overlays ABOVE the live page's own chrome (its bars run up to z-index:1002), hence
     the 99998+ tier — matches the #ip-insp/.ip-grip overlays which share this same high tier. */
  body.fx-inplace main{display:none!important}
  body.fx-inplace #gtopbar{display:none!important}
  body.fx-inplace header{position:fixed;top:0;left:0;right:0;z-index:100000;background:rgba(16,16,22,.82);backdrop-filter:blur(9px);-webkit-backdrop-filter:blur(9px);border-bottom:1px solid rgba(255,255,255,.12);color:#f2efe9;padding:8px 14px}
  body.fx-inplace header h1,body.fx-inplace header .sub,body.fx-inplace header #save{color:#f2efe9}
  body.fx-inplace header button{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.2);color:#f2efe9}
  body.fx-inplace #pagebar{position:fixed;top:46px;left:0;right:0;z-index:99999;background:rgba(16,16,22,.66);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);border-bottom:1px solid rgba(255,255,255,.1);color:#f2efe9}
  body.fx-inplace #pagebar .plabel{color:rgba(242,239,233,.7)}
  body.fx-inplace #pagebar .tab{background:rgba(255,255,255,.08);color:#f2efe9;border-color:rgba(255,255,255,.14)}
  body.fx-inplace #pagebar .tab.on{background:var(--accent);color:#fff}
  /* in-place editing floats over a member's live page (its own chrome runs to z-index:1002), so this
     variant keeps its own top offset and z-index tier — but right/width are the SAME --inspectorDockRight/
     --inspectorDockW window._dockPanels writes for the builder's own #settings, so a member editing in
     place gets the SAME real-column treatment (parity — the standing law), never a second calculation.
     bottom:0 replaces the old max-height card box for the same rail-not-dropdown reason as the builder's
     own #settings, just for this surface's own top offset. */
  body.fx-inplace #settings{position:fixed;top:98px;right:var(--inspectorDockRight);bottom:0;width:var(--inspectorDockW);border-radius:0;z-index:99998;background:#1b1a22;color:#ece7dc;border-left:1px solid rgba(255,255,255,.14);box-shadow:-8px 0 24px rgba(0,0,0,.4)}
  body.fx-inplace #settings h3{color:rgba(236,231,220,.6)}
  body.fx-inplace #settings .clr{color:rgba(236,231,220,.7)}
  body.fx-inplace #tray{position:fixed;z-index:99998;max-height:34vh;background:rgba(18,17,24,.96);border-top-color:var(--accent,#3a7d6e)}
  body.fx-inplace #tray .chips{justify-content:center}
  /* the page's chat bar rides above the tray while editing (--fxTrayH is measured by site_runtime.js _fxSyncChrome) */
  body.fx-inplace #fxcompanion{bottom:calc(var(--fxTrayH,44px) + 6px)!important}
  body.fx-inplace #tray .chip{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.2);color:#ece7dc}
  body.fx-inplace section.page{padding-top:92px!important;padding-bottom:38vh!important}
  #tray .chips{display:flex;gap:8px;flex-wrap:wrap}
  .chip{font:inherit;font-size:13px;padding:8px 12px;border-radius:10px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;white-space:nowrap}
  .chip[disabled]{opacity:.45;cursor:default}
  #banner{display:none;background:#7a2d2d;color:#fff;font-size:13px;padding:8px 14px;text-align:center}
  /* THE SIGN-IN SCROLLS WHEN IT DOES NOT FIT (Katri, 2026-09-15, on her phone: "it shows i need to log in
     but it's cut off to where i can't actually do that"). The screen was fixed and centered, so when the box
     was taller than the room left -- a phone with its keyboard up, or larger text -- its top and bottom were
     cut off with nothing able to scroll to them. It scrolls now, and the box's auto margins center it only
     when there is room; without room it starts at the top, where the username is, and Log In is a scroll away. */
  #authgate{position:fixed;inset:0;background:var(--bg);z-index:200;display:none;align-items:flex-start;justify-content:center;overflow-y:auto;padding:12px 0;box-sizing:border-box}
  #authgate .gatebox{margin:auto;width:min(92vw,340px);background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:22px;display:flex;flex-direction:column;gap:10px;box-shadow:0 16px 50px rgba(0,0,0,.35)}
  #authgate h2{margin:0;font-size:20px}
  #authgate .sub{margin:0 0 4px;color:var(--dim);font-size:13px}
  #authgate input[type=text],#authgate input[type=password],#authgate #gateuser,#authgate #gatepass{font:inherit;font-size:15px;padding:10px 12px;border-radius:9px;border:1px solid var(--line);background:var(--bg);color:var(--ink)}
  #authgate .gaterem{display:flex;align-items:center;gap:7px;font-size:13px;color:var(--dim)}
  #authgate #gatebtn{font:inherit;font-size:15px;padding:11px;border-radius:9px;border:none;background:var(--accent);color:#10201c;font-weight:600;cursor:pointer}
  #authgate #gatemsg{color:#c0392b;font-size:13px;min-height:16px}
  #bdui{position:fixed;inset:4vh 4vw;background:var(--panel);border:1px solid var(--line);border-radius:14px;box-shadow:0 16px 50px rgba(0,0,0,.35);z-index:60;display:none;flex-direction:column;overflow:hidden}
  #bdhd{display:flex;align-items:center;gap:10px;padding:10px 14px;border-bottom:1px solid var(--line);font-size:15px}
  #bdhd button,#bdhd a{font:inherit;font-size:13px;padding:5px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;text-decoration:none}
  #bdframe{flex:1;border:none;width:100%;background:var(--bg)}
  #forgeui{position:fixed;inset:6vh 6vw;background:var(--panel);border:1px solid var(--line);border-radius:14px;box-shadow:0 16px 50px rgba(0,0,0,.35);z-index:60;display:none;flex-direction:row;overflow:hidden}
  /* A ROOM THAT IS ONLY A CONVERSATION: the rail, the log, the composer. The Forge adds its
     session bar, its viewer and its promote bar on top of this; nothing else needs them. */
  /* THE SITUATION ROOM AS A TEAM VIEW. One cell per character, the grid sizing itself to how many
     there are -- no ceiling, because she settled that: a view meant to stay up cannot be built to
     close panes to save work. Each cell is the same account window the single view uses. */
  .sitgrid{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));align-content:start}
  .sitcell{display:flex;flex-direction:column;min-height:280px;border:1px solid var(--line);
    border-radius:10px;overflow:hidden;background:var(--bg)}
  .sitwho{font-size:12px;padding:6px 10px;border-bottom:1px solid var(--line);background:var(--panel)}
  .sitbox{flex:1;min-height:0}
  .wkroom{flex:1;min-width:0;display:flex}
  /* ONE ANSWER FROM THE PANEL. Collapsed, because six of these open at once is a wall of code and
     the thing she is comparing is which ones ANSWERED, not their prose. */
  .unians{align-self:stretch;border:1px solid var(--line);border-radius:9px;background:var(--bg);
    padding:8px 11px;font-size:13px}
  .unians summary{cursor:pointer;color:var(--dim);font-size:12px}
  .unians > div{margin-top:8px;white-space:pre-wrap;line-height:1.45;max-height:40vh;overflow:auto}
  /* WHAT THE CHECKS SAID about one answer, under its text (U4). */
  .unians > .uniex{border-top:1px solid var(--line);padding-top:6px;font-size:12px;color:var(--dim)}
  .uniexbar{align-self:stretch;display:flex;gap:12px;align-items:center;font-size:12px;color:var(--dim)}
  .uniexbtn{font:inherit;font-size:12px;padding:5px 9px;border-radius:8px;border:1px solid var(--line);
    background:var(--bg);color:inherit;cursor:pointer}
  .wknew{margin:10px 8px;font:inherit;font-size:13px;padding:7px 9px;border-radius:8px;
    border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  .wknew:hover{border-color:var(--accent)}
  /* THE RAIL CAN GROW OR SHRINK, RATHER THAN OWN 210px OF EVERY LAYOUT. Katri, tablet, 2026-09-03:
     bubble was 43px wide and every word wrapped a letter at a time. Measured: her chat log had 78px
     because the rail was taking 209px of a 290px workroom. clamp() gives it a floor and a ceiling,
     and 34% of the workroom in between -- so on a full-width bench it is 210px, on a tablet it is
     ~100px, and the ellipsis on the thread names does its job either way. */
  /* ITS COLOR AND HOW SOLID IT IS ARE A ROOM'S LOOK (Katri, 2026-09-11: "the chat history bar can't be
     changed color or opacity"). --railbg is written by the Appearance rows in appearance.json; a room
     that has set nothing falls back to --bg, exactly what this was. */
  #forgerail,.wkrail{flex:0 0 clamp(90px, 34%, 210px);width:clamp(90px, 34%, 210px);border-right:1px solid var(--line);display:flex;flex-direction:column;background:var(--railbg,var(--bg));overflow:hidden}
  #forgenew{margin:10px;font:inherit;font-size:13px;padding:7px 10px;border-radius:8px;border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer;text-align:left}
  #forgenew:hover{border-color:var(--accent)}
  #forgethreads,.wkthreads{flex:1;overflow-y:auto;padding:0 8px;display:flex;flex-direction:column;gap:2px}
  /* A ROW HOLDS THE NAME AND THE X. The x is drawn only where the room passed a `del` handler, so
     it is never a control that exists to refuse. It stays out of the way until the row is hovered:
     a delete you can hit by accident on a list you click all day is worse than one more click. */
  #forgethreads .fthrow,.wkthreads .fthrow{display:flex;align-items:center;gap:2px;border-radius:7px}
  #forgethreads .fthrow:hover,.wkthreads .fthrow:hover{background:var(--panel)}
  #forgethreads .fthx,.wkthreads .fthx{font:inherit;font-size:12px;line-height:1;padding:6px 7px;border:none;background:transparent;color:var(--dim);cursor:pointer;border-radius:6px;opacity:0;flex:none}
  #forgethreads .fthrow:hover .fthx,.wkthreads .fthrow:hover .fthx{opacity:1}
  #forgethreads .fthx:hover,.wkthreads .fthx:hover{color:#c0392b;background:var(--bg)}
  #forgethreads .fthx:focus-visible,.wkthreads .fthx:focus-visible{opacity:1;outline:2px solid var(--accent);outline-offset:1px}
  #forgethreads .fth,.wkthreads .fth{flex:1;min-width:0;font:inherit;font-size:13px;padding:7px 9px;border-radius:7px;color:var(--ink);cursor:pointer;border:none;background:transparent;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  #forgethreads .fth:hover,.wkthreads .fth:hover{background:var(--panel)}
  #forgethreads .fth.on,.wkthreads .fth.on{background:var(--panel);font-weight:600;box-shadow:inset 2px 0 0 var(--accent)}
  #forgethreads .fthnone,.wkthreads .fthnone{font-size:12px;color:var(--dim);padding:8px 9px;line-height:1.45}
  #forgemain,.wkmain{flex:1;display:flex;flex-direction:column;min-width:0}
  #forgehd{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--line);font-size:15px}
  #forgehd .sub{font-size:12px;color:var(--dim)}
  #forgehd button{font:inherit;font-size:13px;padding:5px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  #forgehd button.primary{background:var(--accent);color:#10201c;border-color:var(--accent);font-weight:600}
  /* ---- THE STAGE: the thread, and the standing viewer beside it ----
     The log keeps every rule it had; it is now one column of a row instead of the whole body, which
     is what lets the viewer stand next to it without the conversation reflowing into a different
     shape. When nothing is docked the dock is display:none and the log is exactly as wide as before. */
  #forgestage{flex:1;min-height:0;display:flex;flex-direction:row}
  #forgedock{flex:0 0 46%;min-width:280px;border-left:1px solid var(--line);display:flex;flex-direction:column;overflow:hidden}
  /* AND THE THREAD BOX TOO ("...and neither can the chat thread box itself"). --threadbg comes from
     the same Appearance rows; unset, the box stays see-through, which is what every room has today. */
  #forgelog,.wklog{flex:1;min-width:0;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:8px;background:var(--threadbg,transparent)}


  /* ---- THE VIEWER ----
     Two homes, one element. Inline it is a card in the conversation with a fixed height, because a
     card that grew to fit its content would push the thread around every time the build redrew.
     Docked it takes the whole column, because that is what "standing" means. */
  #fvwr{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:var(--bg);min-height:0}
  #forgelog #fvwr{align-self:stretch;height:min(420px,52vh);flex:0 0 auto}
  #forgedock #fvwr{flex:1;border:none;border-radius:0}
  #fvwrhd{display:flex;align-items:center;gap:8px;padding:7px 10px;border-bottom:1px solid var(--line);font-size:13px;flex:0 0 auto;background:var(--panel)}
  #fvwrhd select,#fvwrhd button{font:inherit;font-size:12px;padding:4px 9px;border-radius:7px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  #fvwrhd select{max-width:210px}
  #fvwrhd button:hover,#fvwrhd select:hover{border-color:var(--accent)}
  #fvwrframe{flex:1;min-height:0;width:100%;border:0;background:#fff}
  /* THE WORKROOM WEARS THE PROGRAM'S OWN CHAT. Katri, 2026-09-03: "make the chat bubbles look like
     the rest of the program ... this should just be another surface using the chat UI that already
     exists." The geometry below is the site chat's `#chatlog .msg` spec verbatim -- 85%, radius 12,
     line-height 1.4, and the two wrapping rules that stop a long url pushing the log sideways.
     WHAT SHE WAS LOOKING AT: her own message was `color:#10201c`, a near-black green hardcoded for
     ONE accent. On her build's accent that is dark-on-light -- a highlighter block belonging to no
     other part of the program. The site's chat says `#fff` and has all along, so this does too:
     one answer, and if it is ever wrong it is wrong in one place for both surfaces.
     The reply keeps the builder's own quiet card (--bg on the log's panel, with a hairline) rather
     than the site's white tint, which is invisible on a light ground. Same shape, this room's ink. */
  #forgelog .fmsg,.wklog .fmsg{max-width:85%;min-width:0;padding:8px 11px;border-radius:12px;font-size:14px;line-height:1.4;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}
  #forgelog .user,.wklog .user{align-self:flex-end;background:var(--accent);color:#fff}
  #forgelog .bot,.wklog .bot{align-self:flex-start;background:var(--bg);border:1px solid var(--line)}
  #forgelog .sys,.wklog .sys{align-self:center;background:transparent;color:var(--dim);font-size:12px;max-width:92%;text-align:center}
  #forgelog .flag,.wklog .flag{align-self:flex-start;color:#d6a93a;font-size:12px}
  #forgechange{display:flex;align-items:center;gap:10px;padding:9px 14px;border-top:1px solid var(--line);background:var(--bg);font-size:13px}
  #forgechangetext{flex:1;color:var(--ink);line-height:1.4}
  #forgechangetext .none{color:var(--dim)}
  #forgechange button{font:inherit;font-size:13px;padding:6px 14px;border-radius:8px;border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer}
  #forgechange button.primary{background:var(--accent);color:#10201c;border-color:var(--accent);font-weight:600}
  #forgebar,/* THE CHAT BAR WEARS THE HISTORY BAR'S COLOR (Katri, 2026-09-11: "it looks funny to not have the chat
   bar with any kind of color. i guess have it tied in with the chat history bar"). Same --railbg, so one
   pair of controls colors both; untouched, both are the room's --bg. */
.wkbar{display:flex;gap:8px;padding:12px;border-top:1px solid var(--line);align-items:flex-end;flex-wrap:wrap;background:var(--railbg,var(--bg))}
  #forgebar textarea,.wkbar textarea{flex:1;font:inherit;font-size:14px;padding:9px 11px;border-radius:9px;border:1px solid var(--line);background:var(--bg);color:var(--ink);resize:none;max-height:160px;line-height:1.45}
  #forgebar button,.wkbar button{font:inherit;font-size:14px;padding:9px 16px;border-radius:9px;border:none;background:var(--accent);color:#10201c;font-weight:600;cursor:pointer;align-self:stretch}
  /* THE WORKROOM PAPERCLIP AND ITS GUIDE CHIPS (fxGuideBar / fxSayGuided, editor.js) -- the Forge and the University */
  #forgebar button.fxclip,.wkbar button.fxclip{background:none;color:var(--ink);font-size:18px;font-weight:400;padding:6px 8px}
  .fxguides{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
  .fxguides:empty{display:none}
  .fxguides.fxpend{margin:0;padding:6px 12px 0}
  .fxguide{display:inline-flex;align-items:center;gap:6px;font-size:12px;line-height:1.3;padding:3px 9px;border-radius:999px;border:1px solid var(--line);background:var(--bg);color:var(--ink);white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}
  .fxguide button{background:none;border:none;color:inherit;cursor:pointer;padding:0;font-size:13px;line-height:1}

  /* CONTROLS THAT SIT OUTSIDE A CARD were never capped by anything. Measured at a 1710px panel:
     the app-wrapper button 1649, the key field 1584, the page-handover select 1621 -- each one a
     single control drawn the width of a monitor. Capped to the same measure a card uses, so a
     control is the same size whether or not the grouper happened to card it up. The find bar is
     deliberately left alone: a search field spanning the top of a panel is a convention, not a
     stretched control. */
  #sitepanel #app-cap, #sitepanel #llmkey, #settings #s-nextpage,
  #sitepanel > select, #sitepanel > input[type=text], #settings > select,
  #settings > input[type=text], #acctpanel > input[type=text]{max-width:535px}
  #sitepanel #app-cap{display:block}
  /* beaten by the .fxfield !important reset above, so it needs one of its own */
  #acctpanel #ee-subject{max-width:535px!important}

  /* ---- the rows that are not fields, and so were never capped ----
     The auto-grouper only cards up a <label> carrying a control. A hand-written row of bare inputs
     is invisible to it, so the add-an-account row and the account list ran the full width of the
     panel with nothing but empty space to the right. They are capped to a readable measure rather
     than made into cards, because they are rows: three inputs and a button that belong side by side. */
  #acctpanel #ownersadmin div:has(> #newownername){max-width:860px}
  #acctpanel #ownerlist .userrow{max-width:860px}
  /* a lone action button in these panels sizes to its words, wherever it sits */
  #acctpanel > button, #acctpanel > label > button, #sitepanel > button{width:auto; align-self:flex-start}
  #ol-save, #ol-keyclear, #addowner{width:auto!important; align-self:flex-start}

  /* ---- THE WORKSHOP: the shell that holds the tools, and the rail that lists them ----
     Forgie's own rules above are UNCHANGED. It kept #forgeui, it kept display:none/flex, and every
     line of forge code in editor.js still works -- the two overrides below only stop it painting a
     second floating window inside the one it now lives in. That is the whole cost of nesting it. */
  /* ================= THE SHELF IS THE SURFACE (owner only) =================
     Katri, 2026-08-31: "if i can access the builds from inside of the forge that makes me think that
     the whole forge should probably just be the factory... there's even a blank spot on the left just
     like the way the forge is set up now."
     She is right, and her own screenshot is the argument: the builder already had the Forge's shape --
     a gutter down the left, a canvas, a tray along the bottom. The Forge was that same layout drawn as
     a window floating six percent inside the thing it looks exactly like. Two shells doing one job.
     So the overlay stops being an overlay. Everything above is UNCHANGED and still applies -- these
     rules only undo the floating, which is the whole cost of the move.
     ONLY FOR HER. editor.js also runs inside every build as the member's in-place editor, and that
     shell must not change with this one; `fx-shelf` is set on boot for the owner and never for a
     member, so every rule here is inert there. */
  body.fx-shelf #workshopui{position:static;inset:auto;flex:1;min-height:0;border:none;border-radius:0;
    box-shadow:none;z-index:auto;background:var(--bg)}
  body.fx-shelf #wsrail{background:var(--panel)}
  /* Exit means nothing when there is nowhere to exit to. */
  body.fx-shelf [data-wsexit]{display:none}
  /* nor does a button that opens what is already open */
  body.fx-shelf #workshop{display:none}
  /* THE BUILDER'S OWN PARTS ARE NOT GONE -- Rooms borrows them, the way it already borrowed the canvas
     and the tray. Hidden where they used to sit, shown where they are borrowed to, so there is still
     exactly one of each and no copy to drift. */
  body.fx-shelf main{display:none}
  body.fx-shelf #tray{display:none}
  body.fx-shelf #roomtraymount #tray{display:block}
  body.fx-shelf #pagebar{display:none}
  body.fx-shelf #roompages #pagebar{display:flex;position:static;top:auto;border-bottom:none;padding:0}
  body.fx-shelf #roompages{flex:0 0 auto;padding:8px 14px 0;overflow-x:auto}

  #workshopui{position:fixed;inset:6vh 6vw;background:var(--panel);border:1px solid var(--line);border-radius:14px;box-shadow:0 16px 50px rgba(0,0,0,.35);z-index:60;display:none;flex-direction:row;overflow:hidden}
  /* WIDER, so a build's name fits. Katri: "can you thicken the rail a little so i can see more of
     the project's name?" It was sized when it held six one-word labels and nothing else; it now
     carries the picker too. */
  #wsrail{width:236px;flex:0 0 236px;border-right:1px solid var(--line);display:flex;flex-direction:column;background:var(--bg);overflow:hidden}
  #wsrailhd{font-size:12px;letter-spacing:.09em;text-transform:uppercase;color:var(--dim);padding:14px 14px 8px}
  /* an empty heading takes no room. She circled FORGE; blank means gone, not blank-but-22px. */
  #wsrailhd:empty{display:none}
  #wstools{display:flex;flex-direction:column;gap:2px;padding:0 8px}
  #wstools .wstool{font:inherit;font-size:14px;padding:9px 11px;border-radius:8px;border:none;background:transparent;color:var(--ink);cursor:pointer;text-align:left;display:flex;align-items:center;gap:9px}
  #wstools .wstool:hover{background:var(--panel)}
  #wstools .wstool.on{background:var(--panel);font-weight:600;box-shadow:inset 2px 0 0 var(--accent)}
  /* A ROW UNDER ANOTHER ROW. Katri: "rooms should be nested under a tab called builds." Indented
     rather than boxed -- the rail is a list of places to go, and a group that draws a container
     round its children turns a list into furniture. `inside` marks the group she is currently in
     without pretending to be the panel, which `on` already means. */
  #wstools .wstool.wschild{padding-left:26px;font-size:13px}
  /* What a row brought down with it. Indented to the row it belongs to, and laid out as a row of
     controls rather than restyled -- the picker is the SAME element, so its own styling still
     applies and nothing here has to know what it looks like. */
  /* the council picker is borrowed the same way, so it wears the same clothes */
  /* A LABEL IS A LABEL. Katri: "change that label to 'Council'" -- it was the picker's own
     placeholder option, which she had to toggle past to choose anything. */
  /* THE BENCH INSIDE A ROOM. Same shape wherever a room declares one -- the Rooms panel already
     lays its mounts out with fx-roomforge; this is the header strip above a borrowed surface. */
  #charsbench{display:flex;flex-direction:column;flex:1;min-height:0;margin-top:10px;
    border-top:1px solid var(--line);padding-top:8px}
  #charsbenchhd{display:flex;align-items:center;gap:8px;font-size:13px;margin:0 0 6px}
  #charsbenchhd .sub{font-size:12px;color:var(--dim)}
  #charsbenchhd select{font:inherit;font-size:12px;padding:4px 6px;border:1px solid var(--line);
    border-radius:7px;background:var(--bg);color:var(--ink);max-width:240px}
  #charsbenchhd button{font:inherit;font-size:12px;padding:3px 9px;border:1px solid var(--line);
    border-radius:7px;background:var(--bg);color:var(--ink);cursor:pointer}
  #charscanvasmount{flex:1;min-height:0;overflow:auto}
  .wshd .sitlbl{font-size:12px;color:var(--dim);letter-spacing:.02em}
  .wshd .wsadopt #councilsel{flex:1;min-width:0;max-width:220px}
  /* THE SLOT LIVES IN A PANEL HEADER NOW, not in the rail (Katri: "i want them right next to
     the page's header"). Inline rather than a block, and no rail indent. */
  .wshd .wsadopt{display:inline-flex;align-items:center;gap:4px;margin-left:6px;flex-wrap:wrap}
  .wshd .wsadopt select{min-width:0;max-width:240px;font:inherit;font-size:12px;padding:4px 6px;
    border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink)}
  .wshd .wsadopt button{font:inherit;font-size:12px;padding:3px 8px;border:1px solid var(--line);
    border-radius:7px;background:var(--bg);color:var(--ink);cursor:pointer}
  .wshd .wsadopt button:hover{border-color:var(--accent)}
  #wstools .wstool.inside{color:var(--ink);font-weight:600}
  #wstools .wstool.wsgroup{letter-spacing:.02em}
  #wstools .wsicon{font-size:15px;width:17px;text-align:center;opacity:.85}
  #wsblurb{margin-top:auto;font-size:11px;color:var(--dim);padding:12px 12px 14px;border-top:1px solid var(--line);line-height:1.5}
  #wsbody{flex:1;min-width:0;display:flex}
  /* a panel on the shelf fills the shelf instead of floating over it */
  /* A PANEL IS A HEADING ABOVE A BODY, and that was being left to each panel to say for itself.
     The three older panels carry their own id rules and set it; Characters and the Situation Room
     never did, so their headings sat BESIDE their bodies in a row -- the tool bar down the left of
     its own content. Saying it once here is what makes a new row on the shelf need no CSS at all.
     Panels with an id rule of their own still win on specificity, so nothing already right moves. */
  #wsbody .wspanel{position:static;inset:auto;flex:1;min-width:0;border:none;border-radius:0;box-shadow:none;z-index:auto}
  /* THE DEFAULT DIRECTION, AND IT HAS TO LOSE EVERY ARGUMENT IT IS IN. I put flex-direction into
     the rule above and told her nothing already right would move, "because panels with an id rule
     of their own still win on specificity". THEY DO NOT: `#wsbody .wspanel` is an id AND a class,
     which outranks a bare `#forgeui` -- so Forgie's rail, which is deliberately a ROW, stacked on
     top of his conversation. She saw it immediately: "why does the layout look like this?"
     :where() carries zero specificity, so this states the default for a panel that says nothing and
     is beaten by any panel that does. That is what a default is, and the earlier version was not
     one -- it was an override wearing a default's description. */
  :where(#wsbody .wspanel){flex-direction:column}

  /* ---- Photopea: somebody else's editor, RUNNING IN THE PANEL ----
     The editor gets the whole body. Everything of ours is a thin strip above and below it, because a
     tool on this shelf should feel like part of the Forge rather than a trip to someone's website. */
  #photopeaui{flex-direction:column;overflow:hidden;background:var(--panel)}
  #pphd{display:flex;align-items:center;gap:6px;padding:8px 12px;border-bottom:1px solid var(--line);font-size:15px;flex:0 0 auto}
  #pphd button{font:inherit;font-size:12.5px;padding:5px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;white-space:nowrap}
  #pphd button:hover:not([disabled]){border-color:var(--accent)}
  #pphd button[disabled]{opacity:.38;cursor:default}
  #pphd button.primary{background:var(--accent);color:#10201c;border-color:var(--accent);font-weight:600}
  #pphd button.primary[disabled]{opacity:.38}
  #ppbody{flex:1;min-height:0;position:relative;background:#1a1815}
  #ppframewrap{position:absolute;inset:0}
  #ppframe{width:100%;height:100%;border:0;display:block;background:#1a1815}
  #ppveil{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;text-align:center;background:var(--panel);padding:24px}
  #ppveil.gone{display:none}
  #ppveil > div{max-width:430px}
  #ppveiltitle{font-size:17px;font-weight:600;margin:0 0 8px;color:var(--ink)}
  #ppveil .ppveilsub{font-size:13px;color:var(--dim);line-height:1.6;margin:0 0 16px}
  #ppveil button{font:inherit;font-size:14px;padding:9px 20px;border-radius:9px;border:1px solid var(--accent);background:var(--accent);color:#10201c;font-weight:600;cursor:pointer}
  #ppstate{position:absolute;left:12px;bottom:10px;font-size:11px;color:var(--dim);background:rgba(0,0,0,.55);padding:3px 8px;border-radius:6px;pointer-events:none}
  #ppstate:empty{display:none}
  #ppfoot{flex:0 0 auto;display:flex;align-items:center;gap:10px;padding:7px 12px;border-top:1px solid var(--line);font-size:12px;color:var(--dim);min-height:18px}
  #ppfoot:has(#ppstatus:empty):has(#ppout:empty){display:none}
  #ppstatus.bad{color:#d67a5a}
  #ppout a{color:var(--accent);text-decoration:none}
  #ppout img{height:34px;border-radius:5px;border:1px solid var(--line);vertical-align:middle;margin-right:7px}

  /* ---- Rerender ---- */
  #rerenderui{flex-direction:column;overflow:hidden;background:var(--panel)}
  /* ONE HEADER BAR FOR EVERY WORKSHOP PANEL, AND NOW ONE CLASS TOO (2026-09-02): #forgehd, #flhd
     and #pphd carry `wshd` in the markup as well as their own ids, because a panel's header has
     to be FINDABLE -- the adopted project picker, + and x land in it. "The element with class
     wshd" is only true if it is true of all of them. Their id rules still win where they differ.
     Was: #flhd was this rule; #pphd and #forgehd are their own
     near-copies elsewhere in this file. A new panel adding a fourth copy is how five swatch rules
     happened, so `.wshd` is the shared one and the older ids ride with it rather than beside it. */
  .wshd,#flhd{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--line);font-size:15px;flex:0 0 auto}
  .wshd .sub{font-size:12px;color:var(--dim)}
  .wshd button,.wshd select{font:inherit;font-size:13px;padding:5px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  .wshd button:hover:not([disabled]),.wshd select:hover{border-color:var(--accent)}
  .wsbodypane{flex:1;min-height:0;display:flex;flex-direction:column;padding:14px;gap:10px;overflow:auto}

  /* ---- ROOMS: the builder's own canvas and tray, sitting inside the Forge ----
     Nothing here restyles the tray -- it re-parents it. #tray is position:fixed because in the
     builder it is a strip pinned to the bottom of the window; inside a panel that is inset 6vh from
     the top it would still paint against the window's bottom edge, half of it behind the shelf. So
     the pinning is undone for as long as it is borrowed, and the tray becomes what it looks like it
     is here: the bottom row of the panel. The chips, their labels and everything they do are
     untouched -- they are the same elements. */
  #roombody{padding:0;gap:0;overflow:hidden}
  #roomcanvasmount{flex:1;min-height:0;overflow:auto;padding:14px;position:relative}
  #roomtraymount{flex:0 0 auto;display:flex}
  body.fx-roomforge #roomtraymount #tray{position:static;left:auto;right:auto;bottom:auto;z-index:auto;
    width:100%;max-height:30vh;border-top:1px solid var(--line);box-shadow:none;border-radius:0}
  .wsnote{font-size:11.5px;color:var(--dim)}
  /* THE SHARED FILE COLUMN'S LIST. The box belongs to a list that HAS rows in it; with none, the
     words stand on their own -- Katri, 2026-09-08: an empty bordered box above files that plainly
     existed read as an input she was supposed to type in. A class rather than an inline style,
     because clearing an inline border deletes the one the markup set and it never comes back. */
  .fxcollist{background:var(--boxbg,transparent);border:1px solid var(--line);border-radius:6px}
  .fxcollist.empty{background:none;border:none}
  /* A SECTION THAT FOLDS AWAY, the same gesture the Engine's stage rows already use -- the header
     opens it and the header closes it again. Markup only: a fourth section is a .wsfold in the page
     and no code, because wshopFolds wires whatever is there. */
  .wsfoldhd{display:flex;gap:8px;align-items:center;padding:9px 13px;cursor:pointer;user-select:none}
  .wsfoldcar{flex:none;width:12px;opacity:.55}
  .wsfoldbody{padding:0 13px 12px}
  /* The situation room is a WINDOW, so its mount takes the room it is given rather than the height of
     whatever happens to be inside it. */
  #sitroommount{flex:1;min-height:320px;position:relative;border:1px solid var(--line);border-radius:10px;overflow:hidden;background:#111}
  #charsmount{flex:0 0 auto}
  #flhd .sub{font-size:12px;color:var(--dim)}
  #flhd button{font:inherit;font-size:13px;padding:5px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  /* A ROOM'S OWN SETTINGS GEAR, beside its name. Only rooms that declare settings get one -- see
     wsShow -- so this rule styles a button that is never drawn on a room with nothing to set. */
  .wsgear{font:inherit;font-size:13px;line-height:1;padding:4px 7px;margin-left:6px;border-radius:7px;
    border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;opacity:.75}
  .wsgear:hover{border-color:var(--accent);opacity:1}
  #wssettings input[type=range]{max-width:110px}
  #flhd button:hover:not([disabled]){border-color:var(--accent)}
  #flbody{flex:1;display:flex;min-height:0}
  #flstage{flex:1;min-width:0;display:flex;flex-direction:column;padding:12px;gap:10px}
  /* BOTH WAYS A PICTURE GETS IN, ON ONE ROW. It carries four controls now rather than two -- the
     base picture's Upload and gallery door, and the layer's, which moved up from the hand column --
     so it wraps rather than pushing the last one off the edge on a narrow window. */
  #flpick{display:flex;gap:8px;flex:0 0 auto;flex-wrap:wrap;align-items:center}
  /* THE LAYER SLOT KEEPS ITSELF TOGETHER when the row wraps: thumbnail, Upload layer, its own door
     to her pictures and the x are one control, not four that may be split across two lines. */
  #flpick #flfilewrap{flex:0 0 auto}
  #flpath{flex:1;font:inherit;font-size:13px;padding:8px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink)}
  #flload{font:inherit;font-size:13px;padding:8px 14px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  /* THE PICTURE IS NEVER SCALED UP. A render shown larger than life makes a box drawn on it land
     imprecisely, and the box is the one thing in this tool that must be exact. */
  /* THE WRAPPER STOPS CLIPPING, because a corner is allowed off the picture now and a handle she
     cannot see is a handle she cannot grab. It clips no further than its own box either way --
     the panel around it is what bounds the view. */
  /* SCROLLS NOW, BECAUSE IT ZOOMS. The picture used to be shrunk to fit and could never exceed
     the box, so `overflow:visible` cost nothing; zoomed past fit it has to be pannable. The
     overlays are absolutely positioned children of this box and are drawn in its CONTENT
     coordinates -- see _flPlane, which adds the scroll offset back for exactly that reason. */
  /* CENTRED WHEN IT FITS, REACHABLE WHEN IT DOES NOT (Katri, 2026-09-16: "the zoom doesn't get me to
     the top of the page").
     Plain `center` puts the overflow on BOTH sides of a flex item bigger than its box -- half above
     the top edge and half left of the left edge -- and a scroll container cannot scroll to a negative
     offset, so everything centring pushed past the start is unreachable. Zooming to 240% therefore cut
     off the top of her picture with no way to scroll to it. `safe center` is the keyword for exactly
     this: it centres while the item fits and falls back to start-alignment the moment it overflows, so
     the top-left corner stays inside the scrollable area. The plain pair is kept first as the fallback
     for anything that does not understand `safe`, which then behaves as it always did. */
  #flcanvaswrap{flex:1;min-height:0;position:relative;display:flex;align-items:center;justify-content:center;background:var(--bg);border:1px solid var(--line);border-radius:10px;overflow:auto;user-select:none;overscroll-behavior:contain}
  #flcanvaswrap{align-items:safe center;justify-content:safe center}
  /* the stage says it will take a drop, and shows that it is taking one */
  /* THE DROP HIGHLIGHT BELONGS TO THE CLASS, NOT TO ONE BOX. The stage had it; the file field now
     takes drops too, and a second copy of these two declarations is how five swatch rules happened. */
  /* BOTH IDS RIDE THE ONE RULE. `.fldrop` alone lost to `#flfilewrap`'s own border declaration --
     an id outranks a class -- so the field took drops and never lit up while doing it. Measured, not
     assumed: the computed border-color was transparent in both states. */
  .fldrop,#flcanvaswrap.fldrop,#flfilewrap.fldrop{border-color:var(--accent); background:rgba(201,100,66,.07)}
  #flfilewrap{border:1px dashed transparent;border-radius:9px;padding:2px}
  /* THE PICKER, KATRI'S ONE ROW. The text box is always open, an Upload button sits
     beside it, the thumb reflects what is currently loaded (empty square when nothing
     yet, a play glyph over a video), and an x appears only when there is a value to
     clear. The pencil is gone. Dragging a file onto the text box highlights the drop
     zone. */
  .fllp{display:flex;align-items:center;gap:6px;min-height:44px}
  .fllp .fllpthumb{flex:none;width:44px;height:44px;border-radius:7px;background-size:cover;background-position:center;background-color:var(--panel);border:1px solid var(--line);position:relative;cursor:pointer}
  .fllp .fllpthumb.fllpempty{background-image:repeating-linear-gradient(45deg,var(--panel) 0 6px,var(--bg) 6px 12px);border-style:dashed}
  .fllp .fllpthumb.fllpempty::before{content:"+";position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:var(--dim);font-size:22px;font-weight:300}
  .fllp .fllpthumb:hover{border-color:var(--accent)}
  .fllp .fllpthumb.fllpvid::after{content:"B6";position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#fff;font-size:16px;text-shadow:0 1px 3px rgba(0,0,0,.7);pointer-events:none}
  .fllp input.fllptxt{flex:1;min-width:0;font:inherit;font-size:12px;padding:8px 10px;border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink)}
  .fllp input.fllptxt::placeholder{color:var(--dim)}
  .fllp input.fllptxt.fllpdrop{border-color:var(--accent);box-shadow:0 0 0 2px rgba(58,125,110,.28)}
  .fllp button.fllpupload{flex:none;font:inherit;font-size:12px;padding:7px 12px;border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink);cursor:pointer}
  .fllp button.fllpupload:hover{border-color:var(--accent);color:var(--accent)}
  /* THE CLEAR-THIS-SLOT X, AND IT IS ONE LOOK BECAUSE IT IS ONE IDEA. It was scoped to `.fllp`
     (the layer picker) when that was the only slot that could be emptied. The base picture is a
     slot too -- Katri, 2026-09-04: "can you add a way to X out of the base layer on Rerender?
     right now all i can do is drag an image over it." Unscoping it rather than writing a second
     rule beside it, so the two x's cannot drift apart. */
  button.fllpx{flex:none;font:inherit;font-size:12px;padding:5px 8px;border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--dim);cursor:pointer}
  button.fllpx:hover{color:#c0392b;border-color:#c0392b}
  .rrprice{font-size:11px;color:var(--dim);line-height:1.5;margin:2px 0 8px;overflow-wrap:anywhere}
  /* THE CORNER HANDLES USED TO BE STYLED HERE and are now drawn as circles in the same SVG as the
     outline they belong to, with their appearance on their own attributes. Katri could not see them
     at all ("i don't see them") while the grid and outline in the same screenshot were fine -- the
     difference being that those are SVG and these were divs depending on this rule. The rule is gone
     rather than kept beside the new drawing: a dead selector is how the next person concludes the
     handles are styled here and edits something nothing reads. See flDrawCorners. */
  .flprevx{margin-left:7px;padding:0 3px;border-radius:4px;color:var(--dim);cursor:pointer;font-size:13px;line-height:1}
  .flprevx:hover{color:#c0392b;background:rgba(192,57,43,.12)}
  #flcanvaswrap.fldrop #flempty{color:var(--accent)}
  #flforgiebar button{min-width:38px; font-size:15px; line-height:1; padding:7px 11px}
  #flimg{max-width:100%;max-height:100%;display:block;cursor:crosshair;-webkit-user-drag:none}
  /* Positioned onto the image's own rect by flVidSync -- never sized by CSS, because the image is
     shrunk to fit and only the browser knows where it landed. */
  #flvid{position:absolute;pointer-events:none;z-index:2}
  #flempty{color:var(--dim);font-size:13px;padding:24px;text-align:center;line-height:1.6}
  #flbox{position:absolute;border:1.5px solid var(--accent);background:rgba(255,255,255,.10);box-shadow:0 0 0 9999px rgba(0,0,0,.34);pointer-events:none}
  /* THE CROPS SHE ALREADY DREW ON THIS PICTURE. A quiet strip under the picture -- present when
     there is something to offer, absent when there is not, never an empty box saying "none". */
  #flprev{display:flex;gap:7px;flex-wrap:wrap;align-items:center;flex:none}
  #flprev .fllab{width:100%;margin:0 0 1px}
  #flprev button{font:inherit;font-size:11.5px;padding:5px 10px;border:1px solid var(--line);
    border-radius:999px;background:var(--bg);color:var(--ink);cursor:pointer;line-height:1.3}
  #flprev button:hover{border-color:var(--accent);color:var(--accent)}
  #flprev button.on{border-color:var(--accent);color:var(--accent);font-weight:600}
  #flprev button span{color:var(--dim);font-weight:400}
  #flprev button:hover span{color:inherit}
  /* EVERY PICTURE SHE HAS, WITH A WINDOW ON IT. The route listing them has existed all along; this
     is the room it opens into -- a grid over the top of the stage, bounded so it never pushes the
     canvas off the screen, closed again the moment she picks one. */
  #flgal{flex:none;max-height:38vh;overflow:auto;border:1px solid var(--line);border-radius:10px;
    background:var(--panel);padding:9px}
  .flgalhd{font-size:11.5px;color:var(--dim);margin:0 0 8px}
  .flgalgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:8px}
  .flgaltile{position:relative;height:84px;padding:0;border:1px solid var(--line);border-radius:8px;
    background-size:cover;background-position:center;background-color:var(--bg);cursor:pointer;
    overflow:hidden;display:block}
  .flgaltile:hover{border-color:var(--accent)}
  .flgaltile span{position:absolute;left:0;right:0;bottom:0;font-size:10.5px;line-height:1.35;
    color:#f4f2ee;background:rgba(0,0,0,.66);padding:3px 5px;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
  #flgalbtn.on{border-color:var(--accent);color:var(--accent)}
  /* THE LAYER SLOT'S OWN DOOR ONTO THE SAME LIST, lit the same way when it is the one that opened
     it -- so the list on screen always says which slot a tile is about to fill. */
  #flgallayerbtn.on{border-color:var(--accent);color:var(--accent)}
  /* WHICH FRAME OF THE CLIP. One row, always on the panel, receded when there is no clip -- the
     same bargain every other control in this room keeps. */
  #flscrub{display:flex;align-items:center;gap:8px;flex:none}
  #flscrub .fllab{margin:0;flex:none}
  /* IT IS NOT A FULL-WIDTH CONTROL. Katri, 2026-09-06: "that frame toggle doesn't need to be that
     long at all." `flex:1` made the slider eat every pixel the stage was wide -- a bar the width of
     the picture for a value that is usually a few dozen frames, drawn directly under the picture it
     is competing with. Fixed at a readable length, and the row no longer stretches to the stage. */
  #flscrub input[type=range]{flex:0 0 160px;width:160px;min-width:0;accent-color:var(--accent)}
  #flscrub{margin-right:auto}
  .flscrubstep{font:inherit;font-size:13px;line-height:1;padding:4px 9px;border:1px solid var(--line);
    border-radius:7px;background:var(--bg);color:var(--ink);cursor:pointer;flex:none}
  .flscrubstep:hover{border-color:var(--accent);color:var(--accent)}
  #flscrublab{font-size:11.5px;color:var(--dim);flex:none;min-width:96px;text-align:right}
  /* AND WHEN THE ROOM SLIDES UNDER A PLACEMENT, THAT IS NOT A FOOTNOTE. The reading comes back with
     every frame and nothing showed it until now. */
  #flscrublab.flscrubmoves{color:#c0392b}
  /* RENDERS SHE HAS ALREADY PAID FOR. A still as readily as a clip -- which is the entire reason
     fill_library was written -- so the thumb is the finished picture where there is one. */
  #flreuse{display:flex;gap:7px;flex-wrap:wrap;align-items:center;flex:none}
  #flreuse .fllab{margin:0}
  #flreuserefresh{font:inherit;font-size:12px;line-height:1;padding:3px 7px;border:1px solid var(--line);
    border-radius:7px;background:var(--bg);color:var(--dim);cursor:pointer}
  #flreuserefresh:hover{border-color:var(--accent);color:var(--accent)}
  .flreusechips{display:flex;gap:7px;flex-wrap:wrap;width:100%}
  .flreusechip{display:flex;align-items:center;gap:7px;font:inherit;font-size:11.5px;
    padding:4px 10px 4px 4px;border:1px solid var(--line);border-radius:999px;background:var(--bg);
    color:var(--ink);cursor:pointer;line-height:1.3;max-width:260px}
  .flreusechip:hover{border-color:var(--accent);color:var(--accent)}
  .flreusechip.on{border-color:var(--accent);color:var(--accent);font-weight:600}
  .flreusethumb{flex:none;width:28px;height:28px;border-radius:50%;background-size:cover;
    background-position:center;background-color:var(--panel);border:1px solid var(--line)}
  .flreusethumb.flreusefilm{background-color:#111;position:relative}
  .flreusethumb.flreusefilm::after{content:"\25B6";position:absolute;inset:0;display:flex;
    align-items:center;justify-content:center;color:#fff;font-size:10px}
  .flreusename{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:130px}
  .flreusewhen{color:var(--dim);font-weight:400;flex:none}
  .flreusechip:hover .flreusewhen{color:inherit}
  /* READABLE ON ITS OWN BACKGROUND. This was var(--dim) -- a muted grey meant for text on the
     PANEL -- printed on a dark translucent pill, so the file name it carries could not be read at
     all. Katri: "and i cant read the name of this file". A colour is only dim relative to what is
     behind it, and what is behind this one is black. */
  #flsize{position:absolute;left:8px;bottom:8px;font-size:11.5px;color:#f4f2ee;background:rgba(0,0,0,.72);padding:4px 9px;border-radius:6px;pointer-events:none;max-width:calc(100% - 16px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  /* BANDS, NOT COLUMNS (Katri, 2026-09-01: "change the lay out to where the column is horizontal
     rather than vertical so that the area where a prompt goes has more room").
     The rail was one narrow stack, then two narrow columns; both left the prompt box about 280px
     wide, which is not enough to write in. The two jobs still sit apart -- what she does by hand
     above, what she asks a model for below -- but each spans the whole rail, so the hand controls
     lay themselves out ACROSS and the prompt gets the full width instead of a slot. */
  /* SCROLLBARS WITHOUT THEIR UP/DOWN ARROW BUTTONS. Katri, 2026-09-03: "it should just show
     them all and grow the rail without a little triangle" -- on Edge/Windows the OS scrollbar
     draws a triangle-shaped arrow button at each end, which read as fold controls next to the
     layer rows. This makes the rail's scrollbar a thin unadorned track instead. */
  #flside::-webkit-scrollbar{width:8px}
  #flside::-webkit-scrollbar-button{display:none;width:0;height:0}
  #flside::-webkit-scrollbar-thumb{background:var(--line);border-radius:6px}
  #flside::-webkit-scrollbar-thumb:hover{background:var(--dim)}
  #flside{scrollbar-width:thin}
  #flside{width:600px;flex:0 0 600px;border-left:1px solid var(--line);padding:12px;
    overflow-y:auto;display:flex;flex-direction:column;gap:10px}
  .flcol{display:flex;flex-direction:column;gap:6px;min-width:0}
  /* THE HAND CONTROLS RUN ACROSS. Each group keeps itself together and the row wraps when it must,
     so nothing is stacked one-per-line down a column that is 600px wide and mostly empty. */
  #flmanual{flex-flow:row wrap;align-items:flex-start;gap:8px;
    border-bottom:1px solid var(--line);padding-bottom:10px}
  #flmanual > *{flex:0 1 auto;margin:0}
  #flmanual #fllayers{flex:1 1 100%;order:-1}
  /* #flopts IS GONE -- it held Duration (now in the prompt column) and the layer picker (now beside
     the base picture's Upload button), and an empty box with a 210px basis is a hole in a wrapping
     row. Its rule went with it rather than being left to size something that is not there. */
  /* A SECTION OF ITS OWN, EACH ON ITS OWN LINE. Katri, 2026-09-06: "in their own section." A
     perspective's controls and its guides both wrap to several rows, so sharing a wrapped line with
     the modes would make either one a different width on every reflow -- the same reason the layer
     list and the place row carry this. */
  #flmanual .flsecbox{flex:1 1 100%}
  .flsecbox{border-top:1px solid var(--line);margin:4px 0 0;padding:5px 0 0}
  /* .fpextras is indented 22px because it hangs under a layer's row. Down here it hangs under a
     caption of its own and the indent would be a step to nowhere. */
  .flsecbox .fpextras{margin-left:0}
  /* WHY A SECTION CANNOT BE USED, IN THE SECTION. Not a missing box: the caption stays, the reason
     sits under it, and the controls come back into the same place when there is a perspective. */
  .flsecnone{font-size:11px;color:var(--dim);opacity:.7;padding:2px 0}
  #flmanual #flplaneoff{flex:1 1 100%}
  /* AND THE DOING BUTTONS KEEP THEIR OWN LINE. Place / Move / On every frame / Undo / Delete moved
     up under the mode bar (see factory.html); without this they would share a wrapped line with the
     modes at 330px and the row would be a different width on every reflow. Same declaration the
     layer list and the off-plane readout already carry, for the same reason. */
  #flmanual .flplacerow{flex:1 1 100%}
  @media (max-width:1180px){#flside{width:330px;flex:0 0 330px}}
  .fllab{font-size:12px;color:var(--dim);margin-top:8px;display:flex;flex-direction:column;gap:1px}
  .fllab .flhelp{font-size:10.5px;opacity:.72}
  #fltreatbtns{display:flex;gap:6px;margin-top:5px}
  /* THE PANEL'S ORDINARY BUTTON. Katri: "the place it button should look like the rest of the UI
     buttons not bespoke." It arrived with no class and no rule, so it fell back to the browser's
     own chrome and looked like something from another program. Rather than write it a rule of its
     own -- which is how a surface ends up with five buttons and five appearances -- it joins the
     one that was already here. `flex:1` stays with the row that needs it. */
  #fltreatbtns button{flex:1}
  /* AND THE SAME RULE AGAIN RATHER THAN A FOURTH APPEARANCE (Katri, 2026-09-01: "the 'reuse
     last' button needs to look like the rest of the buttons. same for +plane and +light").
     Exactly the note above, one surface later: the fix is to JOIN this rule, never to write
     each button its own. #flundo and #fldellayer are here for the same reason. */
  /* RERENDER'S DROP-DOWNS WEAR THE PROGRAM'S OWN FACE. Katri, 2026-09-04: "the drop down UI needs
     to match the rest of the program." These three were raw browser selects -- the OS's grey chrome
     sitting in a panel where every button beside them had already been brought onto a shared rule.
     Same declaration the panels' selects use (#settings/#sitepanel/#e-piecesecs), so a select in
     Rerender and a select in the piece panel are the same control, not two that resemble each other.
     A NEW SELECT IN THIS PANEL SHOULD BE ADDED HERE rather than given its own inline style -- that
     is how these three came to look like strangers in the first place. */
  #upspick,#flsecs,#fltreat{font:inherit;font-size:12px;padding:5px 7px;border-radius:7px;
    border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;max-width:100%}
  #fltreatbtns button,#flplace,#flseedpast,#flundo,#fldellayer,.fllbar button{font:inherit;font-size:12px;padding:6px 8px;border-radius:7px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  #fltreatbtns button:hover:not([disabled]),#flplace:hover:not([disabled]),#flseedpast:hover:not([disabled]),#flundo:hover:not([disabled]),#fldellayer:hover:not([disabled]),.fllbar button:hover:not([disabled]){border-color:var(--accent)}
  #fltreatbtns button[disabled],#flplace[disabled],#flundo[disabled],#fldellayer[disabled]{opacity:.4;cursor:default}
  .fllbar button.on{border-color:var(--accent);color:var(--accent)}
  /* NO MARGIN AND NO WIDTH ON THE BUTTON ITSELF. Katri, 2026-09-03: "buttons are crooked again."
     Both were left over from when Place it was a full-width button standing on its own, before it
     was put in a row beside Undo and Delete. `width:100%` is dead (the row's `flex:1` governs) but
     `margin-top:8px` was not: with align-items:center a top margin on ONE child centres its margin
     box, so Place it sat 4px below its neighbours and made a 27px row 35px tall. The space above
     belongs to the ROW -- it is the gap under the size fields, not a property of one button. */
  #flfilebrowsebtn{margin-top:5px;font:inherit;font-size:12px;padding:7px 10px;border-radius:7px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;width:100%}
  #flfilebrowsebtn:hover{border-color:var(--accent)}
  #flbrowsebtn{font:inherit;font-size:13px;padding:8px 14px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;white-space:nowrap}
  #flbrowsebtn:hover{border-color:var(--accent)}
  #flside select,#flside textarea,#flside input{font:inherit;font-size:13px;padding:8px 9px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);width:100%;box-sizing:border-box}
  #flside textarea{resize:vertical;line-height:1.45}
  /* FOUR CARDS WITH A PARAGRAPH EACH FRAMED THE WHOLE PANEL, and put "Make it move" at the top of
     it, so the tool read as a video tool with three afterthoughts. Katri: "these 'make it move'
     'Redraw it' etc are still boxing in my design intent... fix the design layout to where video is
     not the only option." They are a row of chips now, below the prompt rather than above it: what
     she wants leads, and what comes back is a consequence of it. The explanation moves to the
     tooltip -- it is worth reading once, not every time. */
  #flfills{display:flex;flex-wrap:wrap;gap:5px;margin-top:2px}
  #flfills .flfill{font:inherit;font-size:12.5px;text-align:left;padding:6px 11px;border-radius:999px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;line-height:1.3;white-space:nowrap}
  #flfills .flfill:hover{border-color:var(--accent)}
  #flfills .flfill.on{border-color:var(--accent);background:var(--accent);color:#10201c;font-weight:600}
  /* Forgie, in the Rerender side panel: a strip under the prompt box he writes into. */
  #flforgie{margin-top:6px;border:1px solid var(--line);border-radius:9px;background:var(--bg);overflow:hidden}
  #flforgielog{max-height:190px;overflow-y:auto;padding:8px 9px;display:flex;flex-direction:column;gap:6px;font-size:12px;line-height:1.45}
  #flforgielog:empty{display:none}
  /* RERENDER'S OWN BUBBLES ARE GONE, and this note is the record of it rather than the rules.
     They were `.fgm`, scoped to `#flforgielog` -- an id that stopped existing when this room was
     mounted on the shared workroom, so they had already stopped painting anything. The look now
     comes from `.wklog .fmsg` above, the same rules the Forge and the University wear, because
     Katri asked for "another surface using the chat UI that already exists" and this was a third
     one. A stylesheet block for an element that no longer exists carries no record; the mount's
     comment and this line do. */
  #forgelog .bad,.wklog .bad{align-self:flex-start;color:#d67a5a;background:transparent;border:none;padding:2px}
  /* A PROMPT HE WROTE. It is not speech and it must not be dressed as speech -- Katri, reading one
     in a reply bubble: "that 'answer' reads like a prompt the user wrote." So it is quoted, set in
     the panel's mono, and carries its own label and the one button worth having on it. */
  #forgelog .prompt,.wklog .prompt{align-self:flex-start;background:var(--panel);border:1px solid var(--line);border-left:3px solid var(--accent);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;color:var(--ink)}
  /* A STATUS LINE. The panel reporting what happened -- "Answer enacted: Put a light" -- never
     Forgie speaking. Katri: "that needs to not be conversational." So it is not a bubble at all:
     centred, quiet, and visibly the room's own voice rather than anyone's words. */
  #forgelog .act,.wklog .act{align-self:center;background:transparent;border:none;color:var(--dim);font-size:11.5px;letter-spacing:.02em;padding:2px 0;max-width:92%;text-align:center}
  /* THE STARTER CARD, DRAWN ONLY IN AN EMPTY CONVERSATION. Four buttons -- one per open
     hall fault -- each opening the conversation with her own question in her own voice. */
  .flstarters{align-self:stretch;background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:12px;display:flex;flex-direction:column;gap:6px;margin:6px 0 2px}
  .flstartershead{font-size:12.5px;color:var(--dim);padding:0 2px 4px;line-height:1.4}
  .flstarter{display:flex;align-items:center;gap:8px;padding:9px 10px;border:1px solid var(--line);border-radius:8px;background:var(--bg);color:var(--ink);cursor:pointer;font:inherit;font-size:13px;text-align:left}
  .flstarter:hover{border-color:var(--accent);background:var(--panel)}
  .flstarter:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
  .flstarternum{flex:none;font-family:ui-monospace,monospace;font-size:11px;color:var(--accent);text-transform:uppercase;letter-spacing:.05em;min-width:52px}
  .flstartertitle{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;line-height:1.35}
  .flstartertag{flex:none;font-family:ui-monospace,monospace;font-size:10.5px;color:var(--dim);padding:2px 7px;border:1px solid var(--line);border-radius:5px;white-space:nowrap}
  .fpromptbar{display:flex;align-items:center;gap:8px;margin-top:7px;padding-top:6px;border-top:1px solid var(--line);font-family:var(--fx-ui,inherit)}
  .fpromptbar span{font-size:11px;color:var(--dim);letter-spacing:.02em}
  .fpromptbar button{font:inherit;font-size:11px;padding:3px 9px;border-radius:6px;border:1px solid var(--accent);background:transparent;color:var(--accent);cursor:pointer}
  .fpromptbar button:hover{background:var(--accent);color:#fff}
  /* THE ONE THAT WILL RUN. With the prompt box gone (Katri: "forgie writes the prompt inside the
     chat window"), the armed wording is shown in the log and nowhere else -- so it has to be
     findable at a glance in a scrolling conversation. The mark is on the BUBBLE, not a badge
     beside it, because a badge is the read-out panel coming back one element at a time. */
  .msg.fpromptarmed{border-color:var(--accent); box-shadow:inset 3px 0 0 var(--accent)}
  .msg.fpromptarmed .fpromptbar span::after{content:" — this is the one that will run"; color:var(--accent)}
  .msg.fpromptarmed .fpromptbar button{opacity:.5}
  #flforgiebar{display:flex;gap:6px;padding:7px;border-top:1px solid var(--line)}
  #flforgiebar input{flex:1;font:inherit;font-size:12.5px;padding:7px 9px;border-radius:7px;border:1px solid var(--line);background:var(--panel);color:var(--ink);min-width:0}
  #flforgiebar button{font:inherit;font-size:12.5px;padding:7px 13px;border-radius:7px;border:1px solid var(--line);background:var(--panel);color:var(--ink);cursor:pointer}
  #flforgiebar button:hover:not([disabled]){border-color:var(--accent)}
  #flforgiebar button[disabled]{opacity:.45;cursor:default}
/* THE PLACEMENTS SHE HAS MADE. A row per hung picture -- click the name to bring its handles
   back, the x to take it off. Styled from the same tokens the rest of the panel uses, because a
   bespoke list is how a panel stops looking like one panel. */
/* THE PERSPECTIVE PLANES -- the grid itself, and the little panel that lists them. The svg sits
   UNDER the box and the corner handles (z-index 1 against their 2 and 3) and takes no pointer
   events except on a focal-point handle, so laying a grid over the picture never costs her a drag. */
#flgrid{position:absolute;left:0;top:0;pointer-events:none;z-index:1;overflow:visible}
#flgrid .fpline{fill:none;vector-effect:non-scaling-stroke}
/* the fat transparent twin that takes the pointer, so a hairline is grabbable */
#flgrid .fphit{pointer-events:stroke;cursor:crosshair;vector-effect:non-scaling-stroke}
#flgrid .fpline.bent{stroke-dasharray:none}
#flgrid .fphoriz{fill:none;stroke-dasharray:5 4;vector-effect:non-scaling-stroke}
#flgrid .fplamp{pointer-events:auto;cursor:grab}
#flgrid .fplamp:active{cursor:grabbing}
#flgrid .fpvp{pointer-events:auto;cursor:grab}
#flgrid .fpvp:active{cursor:grabbing}
.fpoff{font-size:11px;opacity:.72;margin:3px 2px 0;min-height:14px}
.fpoff.ok{color:var(--good,#6fa383)}
.fpoff.bad{color:var(--bad,#c4705a);opacity:1;font-weight:600}
#flbox.offplane{outline:1px dashed var(--bad,#c4705a);outline-offset:2px}
/* THE LAYER PANEL -- one row per layer, both kinds, stacking order top-first. */
#fllayers{display:flex;flex-direction:column;gap:3px;margin:8px 0 0}
.fllayer{display:flex;align-items:center;gap:2px;border:1px solid var(--line);border-radius:7px;
  background:var(--panel);overflow:hidden;font-size:12px}
/* SELECTED PICTURE: green, the panel accent, matching its outline and corner dots on the canvas. */
.fllayer.on{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
/* CURRENT WALL: blue, matching the plane it draws on the picture. A wall being the one in force
   and a picture being the one selected are different facts; drawn the same they read as two
   things picked at once, which is exactly how Katri read them. */
.fllayer.on.onwall{border-color:#8fc7ff;box-shadow:0 0 0 1px #8fc7ff inset}
.fllayer.off{opacity:.5}
/* PICKED IS NOT THE SAME AS BEING EDITED. One layer has the handles on it (.on, the accent
   outline); several can be in the selection an operation runs on. Two states that mean
   different things must not share one look, or she cannot tell what Merge is about to take. */
/* SELECTED HAS TO BE OBVIOUS, NOT DETECTABLE. A 14%-grey wash on a panel that is already grey is a
   difference she has to look for, and the selection is what four operations ACT ON -- "the ones you
   have selected" is a scope, so being wrong about which rows are in it is being wrong about what
   Merge is going to take. An accent rail down the left edge, a tinted ground and the name in the
   accent color: three cues, all reading at a glance, none of them a border (the border is already
   spoken for -- it says which layer has the handles on it). */
.fllayer.picked{background:color-mix(in srgb,var(--accent) 13%,transparent);
  box-shadow:inset 3px 0 0 var(--accent)}
.fllayer.picked .fllname{color:var(--accent)}
/* DRAGGING ONE TO RESTACK IT. The row being carried fades; the row it would land against draws the
   line it would land on. A line rather than a highlight, because "between these two" is the thing
   being said and a highlighted row says "onto this one". */
.fllayer[draggable=true]{cursor:grab}
.fllayer.fldragging{opacity:.4;cursor:grabbing}
.fllayer.fldropabove{box-shadow:inset 0 3px 0 var(--accent)}
.fllayer.fldropbelow{box-shadow:inset 0 -3px 0 var(--accent)}
.fllayer.picked.fldropabove{box-shadow:inset 3px 0 0 var(--accent),inset 0 3px 0 var(--accent)}
.fllayer.picked.fldropbelow{box-shadow:inset 3px 0 0 var(--accent),inset 0 -3px 0 var(--accent)}
/* WHAT THE LIST DOES, UNDER THE LIST. Quiet, permanent, and the only place ctrl-click and
   shift-click have ever been written down where she could see them. */
.fllhint{font-size:10.5px;line-height:1.35;color:var(--dim);opacity:.75;margin:5px 2px 0}
  /* THE PENCIL IS ALWAYS THERE, QUIETLY. It was opacity:0 until the row was hovered, which is the
     same fault the gesture it replaces had: a rename you can only find by sweeping the mouse over
     every row is a rename found by luck. Faint enough to sit under the name without competing with
     it, solid enough to be seen without hunting. */
  .fllpen{font:inherit;font-size:12px;padding:2px 5px;border:none;background:transparent;color:var(--dim);cursor:pointer;border-radius:5px;opacity:.45;flex:none;margin-left:4px}
  .fllayer:hover .fllpen{opacity:.8}
  .fllpen:hover{opacity:1;color:var(--accent);background:var(--bg)}
  .fllpen:focus-visible{opacity:1;outline:2px solid var(--accent);outline-offset:1px}
.fllayer.picked .fllname{font-weight:600}
/* A group's members sit under its header, indented, the way a folder's contents do. */
.fllayer.flingroup{margin-left:14px}
.fllayer.flgroup{background:var(--panel2,rgba(128,128,128,.10));border-style:dashed}
.fllayer.flgroup .fllname{font-weight:600;opacity:.9}
.fllayer .flkind{flex:none;padding:0 2px 0 8px;font-size:13px;opacity:.75}
/* A PICTURE LAYER carries a live thumb of its file so two of them do not read as one. */
.fllayer .flkind.flkindpic{width:22px;height:22px;padding:0;margin:0 4px 0 6px;border-radius:5px;background-size:cover;background-position:center;background-color:var(--panel);border:1px solid var(--line);opacity:1;position:relative}
.fllayer .flkind.flkindpic.flkindvid::after{content:"B6";position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#fff;font-size:11px;text-shadow:0 1px 2px rgba(0,0,0,.7);pointer-events:none}
.fllname{flex:1;min-width:0;text-align:left;background:none;border:none;color:inherit;font:inherit;
  font-size:12px;padding:6px 5px;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fllname:hover{background:var(--panel2,rgba(128,128,128,.12))}
/* THE NAME BEING TYPED, IN THE ROW ITSELF. It takes the name button's own metrics so the row does
   not change height or shuffle its neighbours the moment she starts typing -- the rail staying
   still is the thing she has asked for twice. */
.fllnameedit{cursor:text;border:1px solid var(--accent);border-radius:5px;background:var(--bg);
  color:inherit;outline:none;padding:5px 4px}
/* THE CONTROL STRIP UNDER A ROW. It WRAPS and its sliders do not grow: `.fpbar input[type=range]`
   sets flex:1, which in the old wide column stretched every dial to the width of the picture --
   most of what Katri meant by "huge and spread out". Inside the rail they stay dial-sized. */
.fpextras{flex-wrap:wrap;gap:4px;margin:0 0 4px 22px;padding:4px 0}
.fpextras input[type=range]{flex:none}
.fllock{flex:none;font-size:11px;opacity:.75;padding:0 2px}
.fllbar{flex-wrap:wrap;gap:5px;margin:6px 0 2px;opacity:1}
/* `.fllop` IS GONE BECAUSE THE CONTROL IS. The long unlabelled slider beside `cutout` WAS opacity
   -- Katri, 2026-09-05: "nothing says what it is" -- and it is now drawn by the same call as the
   five room dials, so it carries a caption and its value like they do and needs no rule of its own.
   The class is not left behind: a selector for a control that no longer exists is residue. */

/* ── THE STRIP IS SECTIONED, AND THE SECTIONS ARE WEIGHTED ──────────────────────────────────────
   Katri, 2026-09-05: "this whole thing needs a sensible overhaul." It was a flat field -- a grain
   percentage at exactly the importance of opacity, and Blend If, the control that most decides
   whether a placement is believable, sitting anonymous among the dials. Nothing is hidden here and
   nothing is behind an "advanced" disclosure; what changed is that each group says its name and is
   sized by what it does. */
.flstrip{display:block}
.flsec{margin:0 0 6px;padding:0}
/* THE HEADING IS SEARCHED, NOT READ, so it carries the NOUN and her sentence drops below it. Same
   weight as before -- nothing got louder; the two jobs were separated. */
.flseccap{display:block;font-size:9px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--dim,#8a8a8a);margin:0}
.flsecsub{display:block;font-size:10px;line-height:1.3;color:var(--dim,#8a8a8a);opacity:.72;
  margin:0 0 3px;text-transform:none;letter-spacing:0}
.flsecbody{display:flex;flex-wrap:wrap;align-items:center;gap:4px}
/* BLEND IF CARRIES THE WEIGHT, because it is the most valuable control on the strip and it was the
   one wearing no name. An accent rule down its side and its own room -- not a bigger font, which
   would fight the rest of the panel. */
.flsec-bif{border-left:2px solid var(--accent);padding-left:7px;margin-bottom:8px}
.flsec-bif .flseccap{color:var(--ink,#1c1c1c)}
/* THE ROOM CUES ARE THE QUIET END. Five dials that restore something a picture should have had --
   they are nudges, not decisions, and they read as such. */
.flsec-cues{opacity:.9}
.flsec-cues .flsecbody{gap:2px 6px}
.flsec-acts .flsecbody{gap:5px}
/* A CAPTIONED FIELD: the word, then the control it names. Same reading order as a dial, so a
   dropdown and a slider are not two different skills. */
.fllfield{display:inline-flex;align-items:center;gap:5px;margin:0 8px 2px 0;font-size:9.5px;
  color:var(--dim);cursor:pointer;line-height:1.1;white-space:nowrap}
.fllfield .fllfieldcap{font-size:9.5px;letter-spacing:.02em;color:var(--dim);text-transform:uppercase}
/* ── THE WORDS ──────────────────────────────────────────────────────────────────────────────────
   A text layer is a picture layer that draws itself, so its type controls are a section of the same
   strip rather than a panel of their own -- and they sit FIRST, because what a layer says is what
   she opened it for. Every field here is the shape the rest of the strip already uses: a caption,
   then the control it names. */
.flsec-type .flsecbody{gap:4px 6px}
/* THE TEXT BOX GETS THE WHOLE WIDTH. Words wrap and a two-line field showing eight characters is a
   field she cannot read what she typed in. */
.fllwide{flex-basis:100%;align-items:flex-start;white-space:normal}
.flltext{flex:1 1 auto;min-width:0;font:inherit;font-size:11px;line-height:1.35;resize:vertical;
  background:var(--bg);border:1px solid var(--line);border-radius:5px;color:var(--ink);
  padding:4px 6px;min-height:36px}
.flltext:focus{outline:none;border-color:var(--accent)}
/* A NUMBER SHE TYPES. Narrow, because every one of these is two or three digits, and its
   placeholder is the engine's own default rather than a value written into the box -- so an
   untouched field reads as untouched. */
#flside .fllnum,
.fllnum{flex:none;width:58px;font:inherit;font-size:10.5px;background:var(--bg);
  border:1px solid var(--line);border-radius:5px;color:var(--ink);padding:3px 5px}
#flside .fllnum:focus,
.fllnum:focus{outline:none;border-color:var(--accent)}
#flside .fllcolor,
.fllcolor{flex:none;width:30px;height:22px;padding:1px;background:var(--bg);
  border:1px solid var(--line);border-radius:5px;cursor:pointer}
/* BOLD AND ITALIC READ AS WHAT THEY DO. They are the only two toggles in the strip whose state is
   the point rather than a number, so they wear the face they set. */
.flltog{font-size:10.5px;padding:3px 9px}
.fllbtn.flltog.on{background:var(--accent);border-color:var(--accent);color:var(--onaccent,#fff);
  opacity:1}
.flsec-type .flltog:nth-of-type(1){font-weight:700}
.flsec-type .flltog:nth-of-type(2){font-style:italic}
/* AND EVERY DROPDOWN LOOKS LIKE ONE. Katri, 2026-09-03, about the Blend If source: "it doesn't look
   like it can actually be selected" -- it was fixed there and nowhere else, so the blend picker, the
   traced-shape picker and the guide's edge picker were still bare labels. One class, every picker. */
/* AND IT IS WRITTEN AT ID STRENGTH, because `#flside select` (an id selector, so it wins over any
   class) sets `background:var(--bg)` as a SHORTHAND -- which resets background-image to none and
   quietly wiped the chevron off every picker in this panel, including the Blend If one that was
   given a chevron in September precisely so it would "look like it can actually be selected". Same
   rule also forced `width:100%`, which is why a picker in a row of small controls stretched. Both
   are answered here rather than by loosening the panel-wide rule, which other rooms rely on. */
#flside .flpick,
.flpick{flex:none;width:auto;font:inherit;font-size:10.5px;max-width:200px;background:var(--bg);
  border:1px solid var(--line);border-radius:5px;color:var(--ink);padding:3px 18px 3px 6px;
  cursor:pointer;appearance:none;-webkit-appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--dim) 50%),linear-gradient(-45deg,transparent 50%,var(--dim) 50%);
  background-position:calc(100% - 10px) 50%,calc(100% - 6px) 50%;
  background-size:4px 4px,4px 4px;background-repeat:no-repeat}
#flside .flpick:hover,
.flpick:hover{border-color:var(--accent)}
/* ── WHICH LAYERS, ASKED ONCE ───────────────────────────────────────────────────────────────────
   The panel used to answer this question in every button's NAME -- Merge down, Merge selected,
   Merge group, Flatten, Copy, Copy selected, Copy group -- which is one button per operation times
   every scope it accepts, and it grew every time she asked for a new verb. It is one chooser now,
   and the buttons below it read its answer. */
.flscopes{display:flex;flex-wrap:wrap;align-items:center;gap:3px;flex-basis:100%;margin:0 0 5px}
.flscopecap{font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:var(--dim);
  margin-right:3px}
.flscope{background:none;border:1px solid var(--line);border-radius:999px;color:inherit;font:inherit;
  font-size:10.5px;padding:3px 9px;cursor:pointer;opacity:.7;white-space:nowrap}
.flscope:hover{opacity:1;border-color:var(--accent)}
.flscope.on{background:var(--accent);border-color:var(--accent);color:var(--onaccent,#fff);opacity:1}
/* THE PARKED BOXES UNDER THE LIST -- the perspective she is working on, and the layer she is
   working on. Captioned, because a field of controls under a list is only readable if it says whose
   controls they are. */
.fllsel{border-top:1px solid var(--line);margin:6px 0 0;padding:6px 0 0}
.fllselcap{font-size:10px;font-weight:600;color:var(--ink,#1c1c1c);margin:0 0 4px 22px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.flguidecap{margin:4px 0 2px;flex-basis:100%}
/* ARMED: the next click on the picture is putting a point down, not drawing a box. */
#flcanvaswrap.flarmed{cursor:crosshair;border-color:var(--accent)}
/* WHAT IS DRAWN OVER THE PICTURE. The one-button "▢ hide" that used to live here hid the outline
   and its four corners and nothing else, while nine other families of annotation drew at full
   strength over the artwork she was trying to judge. It is now one row in #flovreg like every other
   family, and the rules that switch a family off or make it recede are GENERATED from those rows
   into <style id="flovcss"> by flOvStyle -- so a family added to the registry is styled without a
   line being added here. One class on the wrapper per family, exactly as the old rule worked:
   nothing has to remember to skip drawing, and nothing can come back out of step.

   THE TOGGLE ROW ITSELF NEVER MOVES. It sits under the picture, outside #fllayers (which is emptied
   and rebuilt constantly), so it cannot be wiped, reordered or made a different length by what the
   picture happens to hold. Same metrics as .fpbar, so it does not read as a different kind of thing
   from the bar below it. */
.flovbar{margin:4px 0 0;gap:4px}
.flovbar .flovcap{font-size:10px;font-weight:600;opacity:.7;align-self:center;margin-right:2px}
.flovbar button{font-size:11px;padding:2px 7px}
.flovbar button.off{opacity:.42}
/* The small shortcuts in the layer footer: the per-layer eye is the control, these are shortcuts. */
/* THE SIZE ROW. Same metrics as the band's buttons, so a field beside them does not read as a
   different kind of thing -- which is the same fault C6 fixed one row up. */
/* NAMING A STATE. The field and its button take the band's own metrics -- the same rule C6
   settled -- so a text box beside those buttons does not read as a different kind of thing. */
#flstaterow{display:flex;align-items:center;gap:6px;margin-top:2px}
#flstaterow input{flex:1;min-width:0;font:inherit;font-size:11.5px;line-height:1.15;
  min-height:27px;padding:4px 9px;border-radius:7px;border:1px solid var(--line);
  background:var(--bg);color:var(--ink)}
#flstaterow input:focus{border-color:var(--accent);outline:none}
#flstates{display:flex;flex-wrap:wrap;gap:5px;margin-top:2px}
#flstates:empty{display:none}
#flsizerow{display:none;align-items:center;gap:6px;margin-top:2px}
#flsizerow input{width:72px;flex:none;font:inherit;font-size:11.5px;line-height:1.15;
  min-height:27px;padding:4px 8px;border-radius:7px;border:1px solid var(--line);
  background:var(--bg);color:var(--ink);font-variant-numeric:tabular-nums}
#flsizerow input:focus{border-color:var(--accent);outline:none}
#flsizerow .flunit{opacity:.55}
.fldens{width:62px;flex:none;font:inherit;font-size:11px;padding:3px 5px;border:1px solid var(--line);
  border-radius:6px;background:var(--bg);color:var(--ink)}
/* The lock, on the row. Dim when open, solid when set -- readable while scanning the list. */
/* The lock on a layer row keeps its own width and nothing else -- it is an icon in a row of
   icons, not one of the band's standalone buttons. */
.fllrowop{flex:none}
/* THE FOUR CUE DIALS. Katri, 2026-09-03: "those little toggles can't get turned back off
   after i hit them and i also don't know what they're attached to." They were 40-48px range
   sliders with no visible caption -- so they read as unlabelled binary switches and 0% was
   only reachable by dragging to a place the eye could not see. The dial now stacks a caption
   above the slider and its live value under it, spaced apart. Every one of light, shadow,
   bevel, grain uses the same shape. */
/* CAPTION, SLIDER AND VALUE ON ONE LINE. Katri, 2026-09-03: "these labels are too far away for
   me to know which goes with which toggle." They were stacked -- a centred word above a 56px
   slider above a number -- and four of those side by side put every caption an equal distance
   from two sliders, so nothing said which belonged to which. Reading order does it instead:
   the word, then the control it names, then what it is set to. Each dial is one unbreakable
   run, and the strip around it already wraps. */
.flldial{display:inline-flex;flex-direction:row;align-items:center;gap:5px;margin:0 8px 2px 0;
  font-size:9.5px;color:var(--dim);cursor:pointer;line-height:1.1;white-space:nowrap}
.flldial .flldialcap{font-size:9.5px;letter-spacing:.02em;color:var(--dim);text-transform:uppercase}
/* AT ID STRENGTH, BECAUSE THE DIAL WAS NEVER ACTUALLY 56px WIDE. Measured 2026-09-05: every slider
   in this panel computed to `width:100%`, `padding:8px 9px` and a 1px border, because `#flside
   select,#flside textarea,#flside input` is an ID selector and beat this class rule -- so the room
   dials, the Blend If band and the opacity slider were all full-rail boxes. That is Katri's "the
   long slider beside cutout ... sized as though it were the most important control there", and it is
   most of why the strip read as a flat wall: nothing could be smaller than anything else. */
#flside .flldial .flldialrng,
.flldial .flldialrng{width:56px;flex:none;margin:0;padding:0;border:none;background:none;
  height:auto;accent-color:var(--accent)}
.flldial .flldialval{font-size:9.5px;color:var(--ink);font-variant-numeric:tabular-nums;
  min-width:2.6em;text-align:right}
/* WHAT A TRACED SHAPE IS FOR, beside the layer's other controls. Narrow like they are -- the labels
   are long enough to read in the open list and the hint carries the rest. */
/* HOW BIG A LAYER IS ON ITS WALL, on its row. Small and quiet -- it is there to be compared at a
   glance across a list, not to compete with the name. */
.fllsize{flex:none;font-size:10px;font-variant-numeric:tabular-nums;color:var(--dim,#8a8a8a);
  margin:0 4px;white-space:nowrap}
/* THESE TWO AND THE GUIDE'S EDGE PICKER ARE `.flpick` NOW, so what is left here is only how wide
   each may grow. Everything they used to declare about background, border and padding was a second
   answer to a question `.flpick` answers once -- and where those answers differed, one dropdown in
   the panel looked selectable and the others did not. */
.fllrole{max-width:130px}
.fllblend{max-width:110px}
/* BLEND IF -- four controls that belong together, kept together. Boxed rather than loose on the
   strip because a lone select between two sliders reads as three unrelated things, and the whole
   point of this control is that the numbers are ABOUT the source next to them. */
/* IT WRAPS ONTO THE NEXT LINE INSTEAD OF RUNNING OFF THE PANEL. Katri, 2026-09-03, on the
   third slider ("soften the cut") hanging past the edge: "should it even be there and if so
   it should wraparound to the next row." `.fpextras` around it already wraps, but this group
   was `flex:none` and could not break, so it stayed one unbreakable run four controls wide --
   a dropdown and three sliders -- and whatever did not fit was simply off the edge. */
.fllbif{display:flex;align-items:center;gap:2px 4px;flex:1 1 100%;flex-wrap:wrap;max-width:100%}
  /* THE DROPDOWN LOOKS LIKE ONE. Katri, 2026-09-03: "it doesn't look like it can actually
     be selected." It did not: background:none + border:none read as a static label. And the
     96px cap left the label as "The room" for two of three entries. Given a real background,
     border and chevron, and widened so the full label ("The room's temperature beneath")
     fits. */
.fllbifsrc{flex:none;font:inherit;font-size:10.5px;max-width:200px;background:var(--bg);
  border:1px solid var(--line);border-radius:5px;color:var(--ink);padding:3px 18px 3px 6px;
  cursor:pointer;appearance:none;-webkit-appearance:none;
  background-image:linear-gradient(45deg,transparent 50%,var(--dim) 50%),linear-gradient(-45deg,transparent 50%,var(--dim) 50%);
  background-position:calc(100% - 10px) 50%,calc(100% - 6px) 50%;
  background-size:4px 4px,4px 4px;background-repeat:no-repeat}
.fllbifsrc:hover{border-color:var(--accent)}
/* THE THREE ARE DIALS NOW -- caption, slider, value -- so their width comes from `.flldialrng` and
   all that is left here is which accent they carry. They were 40px and 30px stubs with nothing but a
   `title` to say what they were, which is Katri's "nothing says what it is" in its purest form. */
.fllbiflo,.fllbifhi{accent-color:var(--accent)}
.fllbifsoft{accent-color:var(--dim,#8a8a8a)}
.fllbif .flldial{margin:0 8px 1px 0}
/* THE MEASUREMENT SHE IS BEING ASKED FOR. Boxed and accented because it is a question with
   an open cursor in it -- the one moment in this panel where the tool is waiting on her. */
/* THE GUIDE CONTROL, on the plane's own strip: a distance, an edge, a button. Boxed so the
   three read as one sentence rather than as three unrelated widgets. */
.flguideadd{display:inline-flex;align-items:center;gap:3px;flex:none;
  border:1px solid var(--line);border-radius:6px;padding:1px 3px}
/* THE DISTANCE BOX, AT ID STRENGTH FOR THE REASON ABOVE: `#flside input` set `width:100%`, so the
   one field in the guide sentence stretched across the rail and pushed "+guide" out of sight. A
   feature Katri had never seen was also a feature drawn wrong. */
#flside .flguidenum,
.flguidenum{width:56px;flex:none;max-width:56px;font:inherit;font-size:11px;padding:2px 4px;
  box-sizing:border-box}
.flguidefrom{max-width:130px}
.flguidechip{font-variant-numeric:tabular-nums}
/* THE ALIGN ROW. Icons rather than words because there are eight of them and they only appear
   while she has a multiple selection -- the tip carries the sentence. */
.flbarop{flex:none;min-width:26px;padding:4px 6px;font-size:13px;line-height:1}
/* HER OWN SEAT, NAMED. A grid of identical panes where one of them writes and the rest do not
   is a grid she has to remember the rule for. */
.sitwho.sitmine{color:var(--accent);font-weight:600}
.sitcell:has(> .sitmine){outline:1px solid var(--accent);outline-offset:2px;border-radius:8px}
/* IT STAYS ONE LINE. The row grew a leading "Real size" (see flMeasRow), and the obvious guard --
   flex-wrap -- is the wrong one here: `#flside input,#flside select` sets width:100% at a
   specificity #flmeaslen cannot beat, so those two are only ever the right size BECAUSE they
   shrink. Allowed to wrap they stop shrinking, take a full line each and the row goes 144px tall.
   Measured in the 330px rail: 41 + 63 + 63 + 39 + 57 plus gaps = 279 of 295. It fits. */
#flmeasrow{gap:4px;margin:6px 0 0;align-items:center;
  border:1px solid var(--accent);border-radius:7px;padding:5px 7px}
.flmeaslab{flex:none;color:var(--dim);white-space:nowrap}
#flmeaslen{width:82px;font:inherit;font-size:11.5px;padding:3px 6px}
#flmeasunit{font:inherit;font-size:11.5px;padding:3px 4px;background:var(--bg);
  color:var(--ink);border:1px solid var(--line);border-radius:6px}
/* HOW MANY TO MAKE. Beside the price, because the two are one sentence: this many, that much. */
#flcountbar{margin:6px 0 0;align-items:center;gap:6px}
#flcount{width:52px;font:inherit;font-size:11.5px;padding:3px 6px}
#flcountnote{font-size:11px;color:var(--dim)}
#flseedbar{margin:6px 0 0}
#flseed{width:92px;font:inherit;font-size:11.5px;padding:3px 6px}
#flpending{display:flex;flex-direction:column;gap:3px;margin:8px 0;
  border:1px solid var(--bad,#c4705a);border-radius:7px;padding:7px 9px}
.flpendhead{font-size:11.5px;color:var(--bad,#c4705a);font-weight:600}
.flpend{display:flex;align-items:center;gap:8px;font-size:11.5px}
.flpend span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.8}
#flmodes{flex-wrap:wrap}
/* ONE APPEARANCE FOR EVERY STANDALONE BUTTON IN THE HAND BAND (Katri, 2026-09-01: "buttons
   aren't even"). There were four in the same few inches: the mode pills at 11px in a 100px
   radius, the layer-bar buttons at 12px with a border, the plane strip's at 11.5px with none,
   and Place/Undo/Delete at 12px again -- so a band that wraps put rows of different heights
   under each other and nothing lined up.
   THE FIX IS THE ONE THIS FILE KEEPS RE-LEARNING: join a rule, never write each control its own.
   `min-height` is what actually makes them even -- padding alone leaves a button with an emoji
   taller than one with text, because the glyph is taller than the line box.
   THE ICONS IN A LAYER ROW ARE NOT IN THIS -- they are inline marks in a row, not controls in a
   band, and giving them borders would be evening up two things that are not the same thing. */
#flmanual .fllbar button,
#flmanual .flmode,
#flmanual .flplacerow button,
#flmanual #flstaterow button,
#flmanual #flstates button,
#flmanual .fpextras .fllbtn{font:inherit;font-size:11.5px;line-height:1.15;min-height:27px;
  padding:5px 10px;border-radius:7px;border:1px solid var(--line);background:var(--bg);
  color:var(--ink);cursor:pointer;white-space:nowrap;opacity:1}
#flmanual .fllbar button:hover:not([disabled]),
#flmanual .flmode:hover:not([disabled]),
#flmanual .flplacerow button:hover:not([disabled]),
#flmanual #flstaterow button:hover,
#flmanual #flstates button:hover,
#flmanual .fpextras .fllbtn:hover:not(:disabled){border-color:var(--accent);background:var(--bg)}
#flmanual .fllbar button[disabled],
#flmanual .flplacerow button[disabled],
#flmanual .fpextras .fllbtn:disabled{opacity:.4;cursor:default}
/* THE ONE DIFFERENCE THAT MEANS SOMETHING: which mode is chosen, and which toggles are on. */
#flmanual .flmode.on,
#flmanual .fpextras .fllbtn.on,
#flmanual .fllbar button.on{background:var(--accent);border-color:var(--accent);
  color:var(--onaccent,#fff)}
.flmode{background:none;border:1px solid var(--line);border-radius:7px;color:inherit;
  font:inherit;font-size:11.5px;padding:5px 10px;cursor:pointer}
.flmode.on{background:var(--accent);border-color:var(--accent);color:var(--onaccent,#fff);opacity:1}
.flmodehint{font-size:10.5px;opacity:.55;flex-basis:100%}
.fllbtn{background:none;border:none;color:inherit;font:inherit;font-size:11.5px;padding:6px 7px;
  cursor:pointer;opacity:.7;white-space:nowrap}
.fllbtn:hover:not(:disabled){opacity:1;background:var(--panel2,rgba(128,128,128,.12))}
.fllbtn:disabled{opacity:.22;cursor:default}
.fllbtn.fllx:hover{color:var(--bad,#c4705a)}
/* `.fpextras` WAS DECLARED TWICE, forty lines apart, with different padding and different
   margins -- so the strip's spacing was whichever one happened to come last, and editing
   the first had no effect at all. That is a cascade collision, and it is a good part of
   what Katri saw as "buttons aren't even". One declaration, above. */
#flplanes{display:flex;flex-direction:column;gap:4px;margin:8px 0 0}
.fprow{display:flex;align-items:center;gap:6px;border:1px solid var(--line);border-radius:7px;
  background:var(--panel);overflow:hidden;font-size:12px}
.fprow.on{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
.fprow .swatch{width:12px;height:12px;border-radius:3px;flex:none;margin-left:8px}
.fprow .fpname{flex:1;min-width:0;text-align:left;background:none;border:none;color:inherit;
  font:inherit;padding:6px 4px;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fprow button.fpx,.fprow button.fpeye{background:none;border:none;border-left:1px solid var(--line);
  color:inherit;font:inherit;font-size:12px;padding:6px 9px;cursor:pointer;opacity:.7}
.fprow button:hover{opacity:1}
.fprow .fpvps{font-family:ui-monospace,monospace;font-size:10px;opacity:.55;padding:0 4px;flex:none}
.fpbar{display:flex;align-items:center;gap:8px;margin-top:2px;font-size:11px;opacity:.8}
.fpbar input[type=range]{flex:1;min-width:0}
#flmarks{display:flex;flex-direction:column;gap:4px;margin:8px 0}
.flmark{display:flex;align-items:center;gap:6px;border:1px solid var(--line);border-radius:7px;
  background:var(--panel);overflow:hidden}
.flmark.on{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent) inset}
.flmarkname{flex:1;min-width:0;text-align:left;background:none;border:none;color:inherit;
  font:inherit;font-size:12px;padding:6px 9px;cursor:pointer;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.flmarkname:hover{background:var(--panel2,rgba(128,128,128,.12))}
.flmarkcut{background:none;border:none;border-left:1px solid var(--line);color:inherit;
  font:inherit;font-size:11px;padding:6px 9px;cursor:pointer;opacity:.7;white-space:nowrap}
.flmarkcut:hover{opacity:1;background:var(--panel2,rgba(128,128,128,.12))}
.flmarkx{background:none;border:none;border-left:1px solid var(--line);color:inherit;
  font:inherit;font-size:14px;line-height:1;padding:6px 10px;cursor:pointer;opacity:.65}
.flmarkx:hover{opacity:1;color:var(--bad,#c4705a)}
.flplacerow{display:flex;gap:6px;align-items:center;margin-top:8px}
.flplacerow #flplace,.flplacerow #flmove{flex:1}
/* THE PICTURE IS BEHIND THE OUTLINE, AND IT SAYS SO IN TWO PLACES.
   Katri, 2026-09-05: "the fix should be to be able to move the quad, which being that it is just 4
   lines, should move fast and then once it's in place put the layer inside of it because the image
   takes a long time to change." So a drag writes her corners and the render waits for one press --
   which means the panel has a state where the shape on screen is not the shape in the picture, and
   that state is EXACTLY what the 2026-09-04 report looked like from her side. The difference between
   the two is entirely whether it is said out loud, so it is said twice: the outline turns amber and
   dashed (attributes, in flPlaceCorners) and these words stand on the picture naming the press that
   ends it.
   AMBER, NOT GREEN AND NOT RED. Green in this panel means "this is where your picture is", which is
   the one thing that is not true here; red would read as a fault, and nothing has gone wrong. */
.flstalebadge{position:absolute;z-index:6;pointer-events:none;
  font:inherit;font-size:11px;line-height:1;font-weight:600;letter-spacing:.02em;
  padding:4px 7px;border-radius:5px;white-space:nowrap;
  color:#2a1c05;background:#ffb020;border:1px solid #b87a10;
  box-shadow:0 1px 4px rgba(0,0,0,.35);transition:opacity .35s ease}
/* IT SAYS ITS PIECE AND LEAVES (2026-09-16). The amber outline and Move it's amber ring carry the
   state after the words fade, so nothing about it is hidden -- only stated once. */
.flstalebadge.flgone{opacity:0}
/* AND THE BUTTON THAT ENDS IT IS THE ONE SHE IS LOOKING FOR. On 2026-09-04 the render waited on a
   press she had to FIND; a press she cannot miss is the whole of the difference. */
#flmanual .flplacerow #flmove.flmovedue,
.flplacerow #flmove.flmovedue{border-color:#ffb020;box-shadow:0 0 0 2px rgba(255,176,32,.28)}
  #flquote{font-size:12px;color:var(--dim);margin-top:10px;min-height:16px;line-height:1.45}
  #flgo,#upsgo{margin-top:6px;font:inherit;font-size:14px;padding:10px 16px;border-radius:9px;border:1px solid var(--accent);background:var(--accent);color:#10201c;font-weight:600;cursor:pointer}
  #flgo[disabled],#upsgo[disabled]{opacity:.42;cursor:default}
  #flstatus,#upsstatus{font-size:12px;color:var(--dim);margin-top:8px;line-height:1.5;white-space:pre-wrap}
  #flstatus.bad,#upsstatus.bad{color:#d67a5a}
  #flout{margin-top:8px}
  #flout img,#flout video{max-width:100%;border-radius:8px;border:1px solid var(--line);display:block}
  #flout a{display:inline-block;margin-top:6px;font-size:12px;color:var(--accent)}

  /* A PIECE HOUSES ITS OWN CAPABILITY (Katri, 2026-08-12) — the notice that says out loud what a piece
     just attached or detached. Attaching is the SYSTEM acting, not her, so it is never silent: this
     strip names what arrived (or left), which piece carried it, and on a detach offers "Keep them",
     which records the same departure a tick in the SITE panel would. */
  #fx-attachnote{position:fixed;left:50%;transform:translateX(-50%);bottom:22px;z-index:100001;max-width:min(560px,92vw);
    display:flex;align-items:center;gap:12px;padding:11px 14px;border-radius:12px;font-size:13px;line-height:1.4;
    background:var(--panel);color:var(--ink);border:1px solid var(--accent);box-shadow:0 14px 40px rgba(0,0,0,.42)}
  #fx-attachnote.off{border-color:#c99a3a}
  #fx-attachnote .fxatth{font-weight:600}
  #fx-attachnote .fxatts{font-size:11px;color:var(--dim);margin-top:2px}
  #fx-attachnote>div{flex:1;min-width:0}
  #fx-attachnote .fxattk{flex:none;font:inherit;font-size:12px;padding:6px 12px;border-radius:8px;border:1px solid var(--accent);
    background:var(--accent);color:#10201c;font-weight:600;cursor:pointer}
  #fx-attachnote .fxattx{flex:none;font:inherit;font-size:13px;padding:2px 6px;border:none;background:none;color:var(--dim);cursor:pointer}


  /* THE LAYOUT TRAVELS WITH THE CONTROL (2026-08-22). Every rule above used to be written `#settings x`
     only, so the moment #elpanel was mounted ON THE PIECE (editor.js _pieceStudioMount) its rows lost
     their flex layout and collapsed into each other -- "Font / Fill / background" on one line, labels
     colliding with inputs. That was not a mounting bug: a control that only lays out correctly inside
     one host is a control that cannot be moved, and moving it is the entire feature. Naming #elpanel
     beside #settings is what makes each control carry its own layout wherever it is put. */

  /* THE CONTROLS, INSIDE THE PIECE. Host for #elpanel while a piece is selected on the canvas. It is a
     CHILD of that piece's .el, so it inherits the page transform (scaled with the piece), is clipped by
     the piece's box, and moves when the piece moves -- none of which is code, all of which follows from
     being a descendant. */
  /* NOT inset:0 any more. `top` is written by _pieceStudioFit to the piece's OWN height, so the
     controls begin exactly where the piece ends -- she sees the thing she is editing and the controls
     at the same time, and nothing overlaps, which is why no part of this needs to be see-through. */
  .fxonpiece{position:absolute;top:0;left:0;right:0;height:auto;z-index:12;
    overscroll-behavior:contain;
    padding:8px 10px 10px;border-radius:inherit;cursor:default;text-align:left;
    /* OPAQUE, FULL STOP (2026-08-22). This was color-mix(... 93%, transparent) and Katri could read the
       page and the piece straight through her own controls. --panel is the theme's own solid ground and
       is legible dark or light with nothing authored, which is the point: she must never have to pick a
       fill to make her controls readable. An authored fill paints on .fxonpiecebg ABOVE this, through
       the shared engine, so a translucent or absent one still cannot let the page through. */
    background-color:var(--panel);color:var(--ink);
    box-shadow:inset 0 0 0 2px var(--accent);
    font:13px/1.45 -apple-system,"Segoe UI",Roboto,sans-serif}
  /* the piece's OWN authored fill, painted by _applyPieceSurface (shared_client.js) -- one fill engine,
     and it reaches the editing surface too. Behind the controls, above the opaque ground. */
  .fxonpiece > .fxonpiecebg{position:absolute;inset:0;z-index:0;pointer-events:none;
    border-radius:inherit;overflow:hidden}
  .fxonpiece > *:not(.fxonpiecebg){position:relative;z-index:1}
  .fxonpiece #elpanel{display:block}
  /* A FLOATING WINDOW (2026-08-23 -- Katri: "having it physically attached to the piece isn't
     practical and neither is having the window resize attached to resizing the piece. and also can no
     longer resize it because it's off screen. make it a floating window. apply to all pieces").
     Being a child of `.el` used to be called the whole design: the controls inherited the page
     transform, were clipped by the piece's box and moved with it. Every one of those is a cost here --
     the panel was drawn at the page's scale, it grew the piece to hold itself, and a piece low on the
     page put its own resize grip past the bottom of the screen where she could not reach it. It is
     `position:fixed` on the body now: its own box, its own size, clamped into the viewport, over
     everything. The piece is never resized to hold it and never repainted to make room. */
  #piecewin{position:fixed;z-index:70;display:none;flex-direction:column;
    background:var(--panel);color:var(--ink);border:1px solid var(--line);border-radius:12px;
    box-shadow:0 18px 50px rgba(0,0,0,.45);overflow:hidden;
    font:13px/1.45 -apple-system,"Segoe UI",Roboto,sans-serif}
  #piecewin.on{display:flex}
  #piecewin > .pwhead{flex:none;display:flex;align-items:center;gap:8px;padding:8px 10px;
    border-bottom:1px solid var(--line);cursor:move;user-select:none;background:var(--panel)}
  #piecewin > .pwhead b{flex:1;font-size:12px;letter-spacing:.02em;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
  #piecewin > .pwhead button{flex:none;font:inherit;font-size:11px;line-height:1;padding:4px 8px;
    border-radius:7px;border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
  #piecewin > .pwhead button:hover{border-color:var(--accent);color:var(--accent)}
  #piecewin > .pwhead button.on{border-color:var(--accent);color:var(--accent)}
  #piecewin > .pwhead .pwlive{border-color:var(--accent);color:var(--accent);font-weight:600}
  #piecewin > .pwbody{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain;padding:10px 12px}
  #piecewin > .pwgrip{position:absolute;right:2px;bottom:2px;width:16px;height:16px;z-index:5;
    cursor:nwse-resize;border-right:2px solid var(--accent);border-bottom:2px solid var(--accent);
    border-radius:3px;opacity:.75}
  #piecewin > .pwgrip:hover{opacity:1}
  /* inside the window the host is just a plain block -- no ground, no inset ring, no rounding of its
     own; the WINDOW is the surface now. */
  #piecewin .fxonpiece{position:static;background:none;box-shadow:none;padding:0;overflow:visible}
  /* SIZED BY HAND (2026-08-23 -- Katri: "make the popup windows to where i can drag the corner to
     resize, in case that's needed relative to where the piece is sitting on the canvas"). Until she
     drags, the host is exactly what it was: full piece width, height:auto, the piece growing to hold
     it. Once she drags, it carries its own width and height and SCROLLS -- which is also what stops a
     tall panel from growing a small piece into a wall. One remembered size for every piece. */
  .fxonpiece{overflow:auto;overscroll-behavior:contain}
  .fxonpiece > .fxpiecegrip{position:absolute;right:2px;bottom:2px;width:15px;height:15px;
    z-index:20;cursor:nwse-resize;border-radius:3px;
    border-right:2px solid var(--accent);border-bottom:2px solid var(--accent);
    opacity:.75}
  .fxonpiece > .fxpiecegrip:hover{opacity:1}
  .fxonpiece.closed > .fxpiecegrip{display:none}
  /* FLIPPED ABOVE THE PIECE when there is no room below it -- see _pieceStudioFit. Nothing changes
     but which edge `top` is measured from; the host is still a child of the piece. */
  /* the panel's drawer seam -- a leading rule and a "SELECTED PIECE" heading -- belongs to the drawer.
     Inside the piece, the piece IS the heading. */
  .fxonpiece #elpanel > hr:first-child,.fxonpiece #elpanel > h3{display:none}

  /* NOTHING IS CUT OFF AT THE PIECE'S REAL WIDTH (2026-08-22). Her player screenshot showed "track nar"
     and "...or paste an aud" sheared off mid-word against the piece edge. The panel's rows are a rigid
     `flex` with `span{flex:1}` and `input{flex:1.5}` -- fine in a 420px drawer, and a guillotine in a
     300px piece. In the piece the row WRAPS instead: the caption takes its own line as soon as the two
     will not sit side by side, and every field is allowed to shrink (min-width:0) rather than overflow.
     This is the same point as the layout rules above -- a control has to lay itself out wherever it is
     mounted -- applied to width instead of to the box model. */
  /* THE ROW STYLES ITSELF, not "whatever host it is in" (2026-08-22, the SECOND time this exact mistake
     cost her a screenshot). The first pass added `#elpanel label` beside `#settings label` -- and then the
     cosmetics split LIFTED rows out of #elpanel into .fxcosbody, so the selector stopped matching and every
     lifted row fell back to `display:inline`. Measured on her text piece: the Font row was `display:inline`
     with `column-gap:normal`, which is exactly "Fill / backgroundClear" and "Font size (px)" jammed against
     the field above it. Chasing hosts is the bug; these rules name .fxonpiece, which CONTAINS both halves,
     so a row lays out the same in the body and in the drawer and would in a third place too. */
  .fxonpiece label{display:flex;flex-wrap:wrap;align-items:center;column-gap:8px;row-gap:3px;
    font-size:13px;margin:0 0 9px}
  .fxonpiece h3{font-size:12px;letter-spacing:.6px;color:var(--dim);margin:2px 0 10px}
  .fxonpiece hr{border:none;border-top:1px solid var(--line);margin:8px 0 12px}
  .fxonpiece input[type=text],.fxonpiece input[type=file],.fxonpiece input[type=number],
  .fxonpiece select,.fxonpiece textarea{font:inherit;font-size:12px;padding:5px 7px;
    border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink)}
  .fxonpiece label > span{flex:1 1 9em;min-width:0;overflow-wrap:anywhere}
  .fxonpiece label > input,.fxonpiece label > select,.fxonpiece label > textarea{
    flex:1 1 11em;min-width:0;max-width:100%}
  .fxonpiece label > input[type=color]{flex:0 0 var(--swatch-w)}
  .fxonpiece label > input[type=checkbox]{flex:0 0 auto}
  .fxonpiece div{max-width:100%;overflow-wrap:anywhere}
  .fxonpiece input,.fxonpiece select,.fxonpiece textarea{max-width:100%}

  /* THE GEAR. Cosmetics live behind it, closed by default -- the Google Docs shape: the piece looks like
     the thing and its formatting is a menu you open when you want it. It hides itself entirely when the
     selected piece has no cosmetic controls at all, because a control that opens an empty drawer is a lie. */
  /* CLOSED = THE PIECE, PLUS A WAY IN. No ground, no ring, no panel -- the host shrinks to the chip and
     sits in the corner, so a selected piece looks exactly like an unselected one with one small control
     added. Her Characters piece came up as a white band and a lone gear because the host was painting an
     opaque ground over a piece that had nothing drawn under it yet; closed, there is nothing to paint. */
  .fxonpiece.closed{position:absolute;top:auto!important;bottom:2px;right:2px;left:auto;width:auto;
    height:auto;padding:0;background:none;box-shadow:none;overflow:visible}
  .fxonpiece.closed > .fxonpiecebg,.fxonpiece.closed > .fxcosmetics,
  .fxonpiece.closed #elpanel{display:none}
  /* CLOSED, THE HOST IS NOTHING AT ALL. The bar is no longer inside it (see .el > .fxpiecebar),
     so a closed host has no content left to show and a zero-size box over the piece can only get
     in the way of clicking the piece itself. */
  .fxonpiece.closed{display:none}
  .fxonpiece .fxchip{flex:none;font-size:12px;line-height:1;padding:3px 7px;border-radius:6px;
    border:1px solid var(--line);background:var(--panel);color:var(--dim);cursor:pointer;opacity:.85}
  .fxonpiece .fxchip:hover{opacity:1;border-color:var(--accent);color:var(--accent)}
  /* THE SETTINGS SYMBOL IS ON THE PIECE, IN ITS TOP-RIGHT CORNER (2026-08-23 -- Katri: "i want to be
     able to click on a top right hand corner on the piece itself and configure there", and then, when
     it was not: "now i can't find the controllers for the piece to rotate"). The bar used to live
     INSIDE .fxonpiece, which hangs BELOW the piece -- so the button was at the top-right of the
     control panel, not of the piece, and while the editor was closed the appearance gear was hidden
     entirely. Reaching Rotate meant: select, find a small pencil, click it, notice an unlabeled gear,
     click that too. It is a child of .el now, the same as .ctl and .rz, so it sits on the piece and
     is drawn wherever the piece is drawn. */
  .el > .fxpiecebar{position:absolute;top:3px;right:3px;z-index:14;display:none;gap:4px;
    align-items:center;pointer-events:auto}
  body.editing .el.sel > .fxpiecebar{display:flex}
  .el > .fxpiecebar .fxchip,.el > .fxpiecebar .fxgear{flex:none;line-height:1;cursor:pointer;
    border:1px solid var(--line);background:var(--panel);color:var(--ink);
    box-shadow:0 1px 4px rgba(0,0,0,.25)}
  .el > .fxpiecebar .fxchip{font-size:14px;padding:4px 8px;border-radius:7px}
  .el > .fxpiecebar .fxgear{font-size:11px;padding:4px 8px;border-radius:7px;white-space:nowrap}
  .el > .fxpiecebar .fxchip:hover,.el > .fxpiecebar .fxgear:hover{border-color:var(--accent);color:var(--accent)}
  .el > .fxpiecebar .fxgear.on{border-color:var(--accent);color:var(--accent)}
  .fxonpiece .fxgear{flex:none;font-size:14px;line-height:1;padding:3px 8px;border-radius:7px;
    border:1px solid var(--line);background:var(--bg);color:var(--dim);cursor:pointer}
  .fxonpiece .fxgear.on{border-color:var(--accent);color:var(--accent)}
  .fxonpiece .fxcosmetics{margin-top:8px;padding-top:8px;border-top:1px solid var(--line)}
  .fxonpiece .fxcoshead{font-size:10px;letter-spacing:.05em;color:var(--dim);margin:0 0 6px}

  .fxonpiece button{font:inherit;font-size:12px;padding:5px 10px;border-radius:7px;
    border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer;max-width:100%}
  .fxonpiece .clr{border:none;background:none;padding:2px 0}
  .fxonpiece input[type=range]{flex:1 1 8em;min-width:0}
  .fxonpiece textarea{width:100%;font:inherit;font-size:12px;padding:5px 7px;
    border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink)}

  /* COMES WITH THIS PIECE, folded. It stays ON the piece -- her rule names capabilities as belonging
     there -- but these are whole SITE sections, and a persona's unfolded one made the piece 1131px of
     form rows. The header is the toggle. Scoped to .fxonpiece, so the SITE panel is untouched. */
  .fxonpiece #e-piecesecs > *:not(#e-piecesechead){display:none}
  .fxonpiece #e-piecesecs.open > *:not(#e-piecesechead){display:block}
  .fxonpiece #e-piecesechead{cursor:pointer;font-size:10px;letter-spacing:.05em;color:var(--dim);
    margin:2px 0 6px;padding:3px 0}
  .fxonpiece #e-piecesechead::before{content:"▸ ";font-size:9px}
  .fxonpiece #e-piecesecs.open > #e-piecesechead::before{content:"▾ "}

  /* A DOOR THAT OPENS NOTHING (editor.js _markDeadDoors). The live site refuses to draw a door whose
     destination is gone, and it is right to; the builder keeps drawing it so the author can fix it,
     and that is right too. This is the sentence between them -- without it she sees five buttons while
     building and three on the live site and nothing says why. Marked only while EDITING: in preview it
     would be a note about a problem on a surface meant to show the finished thing. The title attribute
     carries the words; this is the thing that makes her look. */
  body.editing .el.fxdeaddoor{outline:2px dashed #c2603f!important;outline-offset:2px;border-radius:4px}
  body.editing .el.fxdeaddoor::after{content:"opens nothing";position:absolute;top:-9px;left:6px;
    font:600 9px/1.4 -apple-system,"Segoe UI",Roboto,sans-serif;letter-spacing:.04em;text-transform:uppercase;
    background:#c2603f;color:#fff;padding:1px 5px;border-radius:6px;pointer-events:none;z-index:3}

/* THE PANELS ARE WHOLE SURFACES, NOT A SIDE PANEL (2026-08-29 -- Katri: "there needs to be a surface
   inside of the factory that isn't a side panel. i hate the side panel and that's why i'm trying to
   empty it as much as possible. each of those buttons up at the top need to open a whole page, the way
   forge, manual and vison already do").
   Account, Site and Page Settings were docked to the right edge at --panelDockW, which is why emptying
   them mattered so much: a narrow column is the wrong shape for the amount they carry, and everything
   in them competed for one strip beside the canvas. They take the geometry manual and forge already
   use -- fixed, inset from the window, their own scroll -- so the same content gets a page's width. The
   dock variables are left alone: the resize rig still reads them for anything that is still docked. */
#acctpanel, #sitepanel, #settings{
  position:fixed; inset:5vh 5vw; top:5vh; right:5vw; left:5vw; bottom:5vh;
  width:auto; max-width:none; max-height:none; min-height:0;
  border:1px solid var(--line); border-left:1px solid var(--line); border-radius:14px;
  box-shadow:0 16px 50px rgba(0,0,0,.35); z-index:60;
  overflow:auto; padding:0 22px 26px;
}
/* the header rides with the surface, so Close is reachable from anywhere in a long panel */
.fxpanelhd{position:sticky; top:0; z-index:2; display:flex; align-items:center; gap:10px;
  margin:0 -22px 12px; padding:12px 22px; background:var(--panel);
  border-bottom:1px solid var(--line); font-size:15px; font-weight:600}
.fxpanelhd button{margin-left:auto; font:inherit; font-size:13px; padding:5px 14px;
  border:1px solid var(--line); border-radius:8px; background:var(--bg); color:var(--ink); cursor:pointer}

/* AND THE LAYOUT HAS TO CHANGE WITH THE SHAPE (Katri, 2026-08-29): "instead of leaving the settings
   formatted the same way as it was in a skinny panel drop down, give some thought to design and make
   it actually look professional."
   The row format -- label on the left, control shoved to the right by `flex:1` -- was designed for a
   280px column, where the two are two inches apart. At 1260px they are a foot apart with nothing in
   between, and the eye has to travel the whole width to find out which control belongs to which words.
   Widening the surface without changing the format made that worse, not better.
   So: each control and its explanation become one CARD, the cards flow into as many columns as the
   window affords, and the label sits ABOVE its control where a short line of text and the thing it
   names stay together. Headings span the full width and break the flow into readable sections.
   `auto-fill` means this is the SAME rule at every width -- three columns on a wide monitor, one on a
   narrow one, and it degrades back to the old single stack with no separate mobile case. */
/* THE PAGE IS TWO COLUMNS. PERIOD. (Katri, 2026-08-29): "the pages are designed as an over arching
   single column that is then broken down into sections and those sections then have two columns. the
   way the layout should be is overarchingly 2 columns. period."
   The two columns used to live INSIDE each section, so the page was still one stack -- a full-width
   heading bar every few hundred pixels with a two-up patch under it. The columns belong to the PAGE
   now: a section is one block, editor.js deals the blocks into the two columns by height, and the
   cards inside a section run down it in single file. */
.fxlead{margin:0 0 4px}
.fxcols{display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:0 30px; align-items:start}
.fxcol{min-width:0}
.fxsection{margin:0 0 26px; break-inside:avoid}
.fxsection > hr{display:none}      /* the column gap and the heading rule already divide these */
/* ONE BLOCK CANNOT FILL TWO COLUMNS -- Page Settings with nothing selected is a single section. The
   block spans and runs its OWN cards in two columns, so the page is two columns either way. Newspaper
   flow is right here and wrong for the page: this is two screens tall, not five, so reading down one
   column and up the next costs nothing -- and it needs no second measure of what a card is. */
.fxcols.fxsolo{grid-template-columns:1fr}
/* display:block is not decoration -- A GRID CONTAINER IGNORES `columns` ENTIRELY, so with the section
   still a grid the rule below did nothing and the cards drew 1649px wide. Measured, not assumed. */
.fxcols.fxsolo .fxsection{display:block; columns:2; column-gap:30px; break-inside:auto}
.fxcols.fxsolo .fxsection .fxgrid{display:block}
.fxcols.fxsolo .fxsection .fxfield{margin:0 0 14px}
.fxcols.fxsolo .fxsection > h3{column-span:all}
.fxfield{break-inside:avoid}
/* INSIDE A SECTION, ONE FILE. The section is already half the page; splitting it again gives a card
   the width of a phone with a foot of empty page beside it. */
/* minmax(0,1fr), NEVER a bare 1fr -- a bare fr track floors at MIN-CONTENT, so one long <select> in
   Personas pushed its whole card to 768px inside a 531px column and hung over the page. */
.fxgrid{display:grid; grid-template-columns:minmax(0,1fr); gap:14px 22px; align-items:stretch}
@container (max-width: 900px){ .fxcols{grid-template-columns:1fr}
  .fxcols.fxsolo .fxsection{columns:1} }
@supports not (container-type: inline-size){
  @media (max-width: 1100px){ .fxcols{grid-template-columns:1fr}
    .fxcols.fxsolo .fxsection{columns:1} }
}
.fxgrid > *:not(.fxfield){grid-column:1/-1}
/* A SECTION WRAPPER THAT SPANS THE GRID MUST STILL LAY ITS OWN CARDS OUT. `#s-pagenext` is a plain
   div holding one card: the rule above sends the WRAPPER full width, and the card inside then fills
   it, so a single dropdown was drawn 1621px wide inside a panel of 535px cards. Measured, not
   guessed -- the card was 1649 and every card beside it was 535. Any wrapper holding fields becomes
   a grid of the same measure, so a card is the same size however deeply it is nested. */
.fxgrid > *:not(.fxfield):has(> .fxfield){display:grid;
  grid-template-columns:minmax(0,1fr); gap:14px 22px; align-items:start}

/* TWO COLUMNS, NOT SIX AND NOT ONE (Katri, 2026-08-29): "they read as stretched. it would be better
   for them to have two column and not scroll than to have buttons and boxes stretched fully across
   the window simply because there's a single column design."
   The old 290px floor gave SIX skinny columns on her monitor and one stretched column on a laptop --
   two failure modes from the same number. A 520px floor inside a 1180px cap settles at two on
   anything wide and falls back to one only when the panel genuinely cannot hold two. */

/* A BARE CONTROL IS STILL A CONTROL, NOT A HEADING. The full-bleed rule above is there so headings
   can break the flow into sections -- and it caught every loose button and input with them, which is
   the "save login changes" and "remove this account's key" bars running the whole width. They take
   one cell at their own size now. */
.fxgrid > button{grid-column:auto; justify-self:start; align-self:center; width:auto}
.fxgrid > input, .fxgrid > select, .fxgrid > textarea{grid-column:auto; min-width:0; max-width:100%}
/* A LONE CARD IS STILL A CARD. The grouper only adds .fxgrid when it made TWO or more, so a
   section holding exactly one field never became a grid and its card filled the whole panel --
   measured at 1649px beside cards of 535. Capping the card itself covers every such section without
   naming any of them; inside a grid the track is already 535, so this changes nothing there. */
.fxfield{display:block; margin:0; padding:11px 13px 12px; border:1px solid var(--line);
  border-radius:10px; background:rgba(127,127,127,.045); min-width:0}
.fxfield > label{display:flex!important; flex-direction:column; align-items:stretch!important;
  gap:6px; margin:0!important; font-size:13px}
.fxfield > label > span:first-child{flex:none!important; font-weight:600; letter-spacing:.005em;
  line-height:1.35}
/* A LONE CARD FILLS ITS COLUMN, AND THAT IS ALREADY HALF THE PAGE. This used to cap every ungrouped
   card at half the PANEL, which was the only thing keeping the old full-width sections from going
   ragged. With the columns on the page that cap would make a card a quarter of the window. */


/* a checkbox is the one control that reads better beside its words than under them */
.fxfield > label:has(> input[type=checkbox]){flex-direction:row; align-items:center!important}
.fxfield > label:has(> input[type=checkbox]) > span:first-child{flex:1!important; font-weight:500}
/* the clear-this-setting action sits under the field it clears, quiet but visibly a control */
#settings .fxfield .clr, #sitepanel .fxfield .clr, #acctpanel .fxfield .clr{
  align-self:flex-start; margin-top:2px; text-decoration:underline; text-underline-offset:2px}
.fxfield > label > select, .fxfield > label > input[type=text], .fxfield > label > input[type=number],
.fxfield > label > input[type=file], .fxfield > label > textarea{width:100%; box-sizing:border-box}
.fxfield .fxhelp, .fxfield > div{margin:7px 0 0!important; font-size:11px; line-height:1.5;
  color:var(--dim)}
/* a button in a card has to look like one -- the panel's own rule leaves it as plain text, which
   read as an empty card once the row became a bordered block */
.fxfield label > button, .fxfield > button{font:inherit; font-size:12.5px; padding:6px 12px;
  border:1px solid var(--line); border-radius:8px; background:var(--bg); color:var(--ink);
  cursor:pointer; width:100%}
/* the headings that split the flow -- they were inline-sized for a narrow strip */
#acctpanel h3, #sitepanel h3, #settings h3{margin:16px 0 10px; font-size:12px; letter-spacing:.09em;
  text-transform:uppercase; color:var(--dim); border-bottom:1px solid var(--line); padding-bottom:7px}
#acctpanel h4, #sitepanel h4, #settings h4{margin:14px 0 2px; font-size:13px}

/* A PICKER IN A ROW WITH A BUTTON. Sized to its content up to a sensible ceiling, never to the
   panel: Katri, 2026-09-03, on the council's "add someone already here" -- "supposed to not be the
   entire width of the page". Inline `flex:1` did exactly that. The rest matches what a select gets
   in #settings, which is the point of it being a class rather than four declarations written out
   at the one call site that needed them. */
.fxpick{flex:0 1 auto;max-width:min(100%,260px);font:inherit;font-size:12px;padding:5px 7px;
  border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink)}

/* A LAYER OP THAT CANNOT APPLY RIGHT NOW. It LOOKS refused -- so the state still reads at a glance
   -- but it is pressable, because the reason it is refused is a sentence worth hearing and a
   `title` is unreachable on a tablet. Pressing says it. */
.fllbtn.fllrefused{opacity:.45}
.fllbtn.fllrefused:hover{border-color:var(--line)}

/* ===== UNAVAILABLE, AND IT RECEDES ==============================================================
   Katri, after dozens of hours in Rerender: "its not intuitive, it's not user friendly." The
   diagnosis: THE ROOM REARRANGES ITSELF -- controls were removed when they did not apply, so the
   panel was a different shape every time she opened it and the hours never became fluency. Nothing
   disappears now; a control that cannot apply stays exactly where it was and looks unavailable.

   AND IT MUST NOT BECOME THE THING SHE ALREADY STRUCK. On 2026-09-03 she had dead buttons taken
   out: "a row of eight buttons sitting dead above the picture the rest of the time is exactly the
   clutter." What she objected to was a control at FULL STRENGTH that does nothing. So this is tuned
   to RECEDE, not to shout: no border emphasis, no hover lift, no color, and light enough that the
   eye passes over it while scanning -- a map of where the tools live, read only when looked for.

   THE CLASS IS NAMED BY A ROW. layerops.json's `presence` rows carry the class (`flp-dim`), so a
   third way of being unavailable -- struck through, outlined, whatever she asks for -- is a row
   there plus a rule here, and no engine code anywhere. `.flunavail` is the shared half every
   presence wears: the cursor, and the promise that a press explains itself. */
/* ===== THE ZOOM CONTROL, UNDER THE PICTURE ======================================================
   The wheel has zoomed at the pointer for days and there was no control anywhere -- no percentage,
   no plus, no minus, no fit -- so nothing on screen said the tool could do it or how far in she was.
   Small, permanent, directly under the canvas where a magnification readout lives in every editor
   she has used. */
#flzoombar{display:flex;align-items:center;gap:4px;margin:4px 0 0;font-size:11px}
#flzoombar button{font:inherit;font-size:12px;line-height:1;min-width:26px;padding:4px 7px;
  border:1px solid var(--line);border-radius:7px;background:var(--bg);color:var(--ink);cursor:pointer}
#flzoombar button:hover{border-color:var(--accent)}
#flzoombar button.flp-dim:hover{border-color:var(--line)}
#flzoompc{min-width:44px;text-align:center;color:var(--dim);font-variant-numeric:tabular-nums}
#flzoombar #flzoomfit{min-width:0;font-size:11px}

.flunavail{cursor:default}
.flp-dim{opacity:.34;filter:saturate(.4)}
.flp-dim:hover{opacity:.5}
/* AND AT THE SAME WEIGHT AS THE RULES THAT SET THESE CONTROLS TO opacity:1. The band rule above
   (`#flmanual .fllbar button …`) is two levels more specific than a bare class, so without this the
   bar's own buttons -- Paste, the six aligns, the two spaces -- stayed at full strength while
   carrying the class, which is precisely the dead-at-full-strength button she struck. */
#flmanual .fllbar button.flp-dim,
#flmanual .flmode.flp-dim,
#flmanual .flplacerow button.flp-dim,
#flmanual #flstaterow button.flp-dim,
#flmanual #flstates button.flp-dim,
#flmanual .fpextras .fllbtn.flp-dim{opacity:.34}
#flmanual .fllbar button.flp-dim:hover,
#flmanual .flmode.flp-dim:hover,
#flmanual .flplacerow button.flp-dim:hover,
#flmanual #flstaterow button.flp-dim:hover,
#flmanual #flstates button.flp-dim:hover,
#flmanual .fpextras .fllbtn.flp-dim:hover{opacity:.5;border-color:var(--line);background:var(--bg)}
/* NO HOVER EMPHASIS OF ANY KIND. Every one of these rules exists somewhere above for the live
   control; repeating them here at their resting value is what stops an unavailable button
   brightening, bordering or lifting under the pointer and reading as pressable. */
.fllbtn.flp-dim:hover,
button.flp-dim:hover,
.flbarop.flp-dim:hover{background:none;border-color:var(--line);color:inherit}
button.fllpx.flp-dim:hover{color:var(--dim);border-color:var(--line)}
/* THE NUMBER BOXES KEEP THEIR PLACE AND GO QUIET -- the row still holds its height in the column,
   so nothing below it moves when a rectangle is drawn or cleared. */
#flsizerow.flp-dim input{background:none}
/* AND A LABEL THAT IS ONLY A LABEL. #flseclab is a <label>: it cannot be pressed away, so it
   carries the sentence for the <select> beside it, which a browser will not let be pressed at all. */
label.flp-dim{cursor:default}

/* WHERE SHE LEFT OFF. Its own bar under the panel header, so nothing that writes to the status line
   can wipe it. Quiet, but it is the first thing in the panel and it stays until she answers it. */
#flresume{display:none;align-items:center;gap:9px;margin:0 0 6px;padding:7px 10px;font-size:12.5px;
  border:1px solid var(--accent);border-radius:9px;background:color-mix(in srgb,var(--accent) 8%,transparent);
  color:var(--ink)}
#flresume span{flex:1;min-width:0}
#flresume button{font:inherit;font-size:12px;padding:3px 10px;border-radius:7px;cursor:pointer;
  border:1px solid var(--accent);background:transparent;color:var(--accent)}
#flresume button.flresumeno{border:none;color:var(--dim);padding:3px 6px}

/* THE PANEL'S VOICE, AT THE TOP. It used to be the last thing in a scrolling column, so a refusal
   spoke where she was not looking -- which is most of why a refused control read as a broken one.
   Empty it takes no room at all, so a quiet panel looks exactly as it did. */
#flstatus:empty,#upsstatus:empty{display:none}
#flstatus,#upsstatus{margin:0 0 6px;padding:6px 10px;font-size:12.5px;line-height:1.45;border-radius:8px;
  background:color-mix(in srgb,var(--ink) 6%,transparent);color:var(--ink)}
#flstatus.bad,#upsstatus.bad{background:color-mix(in srgb,#d67a5a 16%,transparent);color:var(--ink)}

/* THE UPSCALE SUBROOM. Same shape as Rerender's own stage-plus-side (#flbody/#flstage/#flside),
   because it is doing a smaller version of the same job -- show a picture, offer the ways to act
   on it. It builds no controls of its own beyond the picker/status pair already joined to
   Rerender's shared rules above. */
#upsbody{flex:1;display:flex;min-height:0}
#upsstage{flex:1;min-width:0;display:flex;flex-direction:column;padding:12px;gap:8px}
#upscanvaswrap{flex:1;min-height:0;position:relative;display:flex;align-items:center;
  justify-content:center;background:var(--bg);border:1px solid var(--line);border-radius:10px;
  overflow:auto}
#upsimg{max-width:100%;max-height:100%;display:block}
#upsempty{color:var(--dim);font-size:13px;padding:24px;text-align:center;line-height:1.6}
#upsdims{font-size:12px;color:var(--dim);text-align:center}
#upsside{width:300px;flex:0 0 300px;border-left:1px solid var(--line);padding:12px;
  display:flex;flex-direction:column;gap:6px;overflow-y:auto}
#upsinfo{font-size:12px;color:var(--dim);line-height:1.5;min-height:16px}
/* THE ROUTE MOUNTS. upscaleroutes.json's buttons are appended into these spans, and a span in a
   panel header already wears `.wshd button` -- so a route in a header needs no rule of its own and
   gets none. This is the mount's own layout and nothing else. An empty mount takes no space, which
   is what keeps a header from growing a gap for a row that is not drawn. */
.upsroutes{display:inline-flex;align-items:center;gap:6px}
.upsroutes:empty{display:none}
/* WHAT CAME OUT OF THE RUN, HELD. Not a message: the panel's status line is wiped by the next
   thing that speaks, and this has to survive until she uses one of the ways out. It is quiet
   while the room is only holding a picture, and wears the accent once a run has finished, so the
   thing that says "it worked" is the block itself rather than a sentence that can be missed. */
#upsdone{margin-top:10px;padding:10px;border:1px solid var(--line);border-radius:10px;
  display:flex;flex-direction:column;gap:8px}
#upsdone.done{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 8%,transparent)}
#upsdonepic{max-width:100%;max-height:150px;align-self:center;border-radius:6px;display:block}
#upsdonesay{font-size:12.5px;line-height:1.45;color:var(--ink)}
#upsdonesay:empty{display:none}
#upsroutesout{flex-wrap:wrap}
#upsroutesout button{font:inherit;font-size:12.5px;padding:5px 10px;border-radius:8px;
  border:1px solid var(--line);background:var(--bg);color:var(--ink);cursor:pointer}
#upsroutesout button:hover:not([disabled]){border-color:var(--accent)}
#upsroutesout button[disabled]{opacity:.42;cursor:default}
@media (max-width:900px){#upsbody{flex-direction:column}
  #upsside{width:auto;flex:0 0 auto;border-left:none;border-top:1px solid var(--line)}}

/* ===== FIELDS: ALL SEE-THROUGH, OR NONE ==========================================================
   Katri, 2026-09-07, looking at the Engine with a picture behind it: "either all of those fields
   need to be see through or none of them do. really, i'd like the option for both."
   They disagreed because they were styled in different places -- a bare <input> had no background
   rule at all and showed the picture, while every <select> and .fxpick carried background:var(--bg)
   and did not. One rule now decides for every field in a room, and WHAT it decides is the appearance
   row `fieldFill`: its chosen option writes its own CSS into --fieldbg, so See-through and Solid are
   two rows' worth of data and a third look is a third option. The fallback keeps a panel correct
   before the setting has loaded. */
.wspanel input:not([type=color]):not([type=checkbox]):not([type=radio]),
.wspanel select,
.wspanel textarea,
.wspanel .fxpick,
#wssettings input:not([type=color]):not([type=checkbox]):not([type=radio]),
#wssettings select,
#wssettings textarea{background:var(--fieldbg,var(--bg))}

/* AND THEY LOOK LIKE THE PROGRAM (Katri, 2026-09-09: "change the text boxes to match the rest of the
   program", then, of one more, "here's another one that don't match the program's UI"). The rule
   above decided their BACKGROUND and nothing else, so a field that had been given .fxpick had the
   house border and radius while a bare <input> or <select> beside it arrived in the operating
   system's own chrome -- square corners, a grey well, a native arrow. Two controls doing the same
   job looked like two different programs.

   BY ELEMENT, NOT BY CLASS, AND THAT IS THE POINT. Tagging each field with .fxpick fixes the ones
   somebody remembers; the next control added to any room arrives wrong again and nobody notices
   until she does. Every field in a room panel now inherits the same border, radius, padding and type
   -- which is exactly what .fxpick declares, so .fxpick is now only its width cap.

   The arrow is drawn rather than left to the platform, because that is the part that gives a native
   select away at a glance. */
/* AND THE GEAR PANEL IS A ROOM'S FIELDS TOO (Katri, 2026-09-10, of the font picker in Engine
   settings: "that drop down box needs to match the rest of the program's UI"). #wssettings is
   appended to the BODY rather than inside the room's own panel -- it has to be, because it floats
   over everything -- so every `.wspanel` rule missed it and its fields arrived in the operating
   system's chrome while the identical fields six inches away wore the house style. Same declaration,
   two homes, rather than a second copy that drifts. */
.wspanel input:not([type=color]):not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]),
.wspanel select,
.wspanel textarea,
#wssettings input:not([type=color]):not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]),
#wssettings select,
#wssettings textarea{font:inherit;font-size:12px;padding:5px 7px;border:1px solid var(--line);
  border-radius:7px;color:var(--ink)}
/* AND THE ACCOUNT PANEL (Katri, 2026-09-11: "i found another drop down that doesn't match the program's
   UI" -- the Font and Picture size pickers in Account). Its inputs already wore the house fields, so only
   its selects arrived in the operating system's chrome. Same declaration, a third home. */
#acctpanel select{font:inherit;font-size:12px;padding:5px 7px;border:1px solid var(--line);
  border-radius:7px;color:var(--ink);background-color:var(--fieldbg,var(--bg))}
/* #projsel, the build picker, sits in the header rather than the room, so it drew as a plain box with no
   arrow while the page picker beside it had one -- and on a build whose page shares its name (The Commons
   / Commons) she read the two the wrong way round. It wears the same arrow now. */
.wspanel select,
#wssettings select,
#projsel,
#acctpanel select{appearance:none;-webkit-appearance:none;padding-right:24px;
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                   linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 14px) calc(50% + 1px),calc(100% - 9px) calc(50% + 1px);
  background-size:5px 5px,5px 5px;background-repeat:no-repeat}
/* the dial's colour still wins -- it is the background LAYER under the arrow */
.wspanel select,
#wssettings select{background-color:var(--fieldbg,var(--bg))}
.wspanel textarea,
#wssettings textarea{border-radius:7px}


/* A CONTROL THAT OPENS A LIST LOOKS LIKE ONE (Katri, 2026-09-09: "make the persona selection a
   regular drop down like everything else is"). The room seats several personas, so it cannot BE a
   <select> -- a select picks one. It is a button that opens a list of ticks, and it now wears the
   same border, radius and drawn arrow every select in a room wears, so the difference she sees is
   none. */
.wspanel .fxlikeselect{appearance:none;-webkit-appearance:none;padding-right:24px;cursor:pointer;
  font:inherit;font-size:12px;padding-top:5px;padding-bottom:5px;padding-left:7px;
  border:1px solid var(--line);border-radius:7px;color:var(--ink);
  background-color:var(--fieldbg,var(--bg));
  background-image:linear-gradient(45deg,transparent 50%,currentColor 50%),
                   linear-gradient(135deg,currentColor 50%,transparent 50%);
  background-position:calc(100% - 14px) calc(50% + 1px),calc(100% - 9px) calc(50% + 1px);
  background-size:5px 5px,5px 5px;background-repeat:no-repeat}


/* THE BOOK PANE -- what a book looks like when a room shows one, in the Workshop's reader and on
   the Library's page both. The program resets margins everywhere so panels stack tight, which is
   right for panels and wrong for a novel: 2,449 paragraphs with no space between them is one solid
   block of ink. A book gets book spacing, and only inside this pane. */
.fxbook{color:var(--ink)}
.fxbook p{margin:0 0 .75em}
.fxbook h1,.fxbook h2,.fxbook h3,.fxbook h4{margin:1.4em 0 .5em;line-height:1.25;font-weight:700}
.fxbook h1{font-size:1.35em}
.fxbook h2{font-size:1.2em}
.fxbook h3{font-size:1.08em}
.fxbook hr{margin:1.8em 0;border:0;border-top:1px solid var(--line)}
.fxbook ol,.fxbook ul{margin:0 0 1em;padding-left:1.5em}
.fxbook li{margin:0 0 .3em}
.fxbook blockquote{margin:0 0 1em 1.2em;padding-left:.8em;border-left:2px solid var(--line);opacity:.9}
.fxbook img{max-width:100%;height:auto}
.fxbook a{color:var(--accent)}
.fxbook a:not([href]){color:inherit;text-decoration:none}


/* THE E-READER, EMBEDDED IN A ROOM RATHER THAN OPENED OVER IT. Katri, 2026-09-10: "i don't want the
   e-reader to be a window that opens. i want it embedded in the page."

   THE READER ITSELF IS UNCHANGED. Its markup and its stylesheet are the ones every build gets, and
   on a live site it is still the overlay it has always been -- what changes here is where it is
   put. A room hands it a box; these rules take the fixed position off and let the box decide, so
   there is one reader with one look and no second copy of it that happens to sit in a page. */
#readerui.fxreaderin{position:static;inset:auto;width:100%;height:100%;min-height:0;
  border-radius:10px;box-shadow:none;z-index:auto}
#readerui.fxreaderin .hd{border-radius:10px 10px 0 0}
/* The contents drawer is positioned against the reader, so the reader has to be what it measures. */
.fxreaderbox{position:relative;display:flex;min-height:0;flex:1 1 0}

/* ---- THE FACTORY ON A PHONE ---------------------------------------------------------------------
   Katri, 2026-09-15: "If we can get the phone app version's UI right, then it'll be better to just use
   it that way." body.fx-narrow is set by _fxNarrowSync (editor.js) below layouts.json's phone `below`,
   the same width a build's pages stack at. At 375px the room rail took 236px and left a room 139px.
   So on a phone: the rail is a drawer opened by the Rooms button in each room's header, every room
   gets the whole screen, and a room's side-by-side columns stack. */
.wsrailbtn{display:none}
body.fx-narrow .wsrailbtn{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;font-size:17px!important;padding:4px 10px!important}
body.fx-narrow #workshopui{inset:0!important;border-radius:0;border:none;box-shadow:none}
body.fx-narrow #wsrail{position:absolute;left:0;top:0;bottom:0;z-index:80;width:min(82vw,280px);flex:none;
  transform:translateX(-102%);transition:transform .2s ease}
body.fx-narrow.fx-wsrail-open #wsrail{transform:none;box-shadow:0 0 0 100vmax rgba(0,0,0,.35)}
body.fx-narrow #wsbody{width:100%}
body.fx-narrow .wshd{flex-wrap:wrap;row-gap:6px;padding:8px 10px}
body.fx-narrow .wshd select,body.fx-narrow .wshd .fxpick{min-width:0!important;max-width:100%}
body.fx-narrow .wsbodypane{padding:10px}
/* the Engine: the file column drops under the line */
body.fx-narrow #enginefiles{flex:1 1 100%!important;max-width:none!important;min-width:0!important}
/* the Workshop: the conversation on top at most of a screen, the book under it; the pane scrolls */
body.fx-narrow #wshopui>.wsbodypane{flex-direction:column!important;overflow-y:auto!important}
body.fx-narrow #wshopui>.wsbodypane>div{flex:0 0 auto!important;overflow:visible!important}
body.fx-narrow #wshopui>.wsbodypane>div:first-child{height:78vh;min-height:420px}
/* the Library: shelf, then the open book, then the conversation */
body.fx-narrow #libraryui>div:last-child{flex-direction:column!important;overflow-y:auto!important}
body.fx-narrow #libraryui>div:last-child>div{flex:0 0 auto!important;min-width:0!important;width:100%}
body.fx-narrow #libraryui>div:last-child>div:first-child{max-height:34vh}
body.fx-narrow #libraryshelf{flex:0 1 auto!important;min-height:60px}
body.fx-narrow #libraryui>div:last-child>div:last-child{height:72vh}
/* a conversation's own thread list becomes a short strip above the thread */
body.fx-narrow .wkroom{flex-direction:column}
body.fx-narrow .wkroom>.wkrail{width:auto!important;min-width:0!important;max-width:none!important;flex:0 0 auto!important;
  max-height:118px;overflow-y:auto;border-right:none!important;border-bottom:1px solid var(--line)}
body.fx-narrow .wkroom>.wkmain{min-height:0;flex:1 1 auto}
/* the Forge keeps its own conversation rail (#forgerail); it becomes the same strip */
body.fx-narrow #wsbody #forgeui{flex-direction:column!important}
body.fx-narrow #forgerail{width:auto!important;min-width:0!important;max-width:none!important;flex:0 0 auto!important;
  max-height:118px;overflow-y:auto;border-right:none!important;border-bottom:1px solid var(--line)}
body.fx-narrow #forgemain{min-width:0!important;min-height:0;flex:1 1 auto!important}
/* the build stamp moves to the bottom corner instead of covering the header's buttons */
body.fx-narrow #fx-buildstamp{top:auto!important;bottom:2px;left:2px!important;transform:none!important;font-size:9px!important;padding:2px 6px!important;opacity:.45}
