/* ───────────────────────────────────────────────
   FieldCut. White or black everywhere, ultramarine as the accent.
   Chrome (bars, panels, sheets, gates): --ch-* tokens, which are now the page
   itself rather than a blue surface of their own.
   Working area (grid, loupe, filmstrip, clip editor): --bg/--sf/--tx.
   Blue marks kept and chosen, and blue is the logo too. Vermilion (#cf2500)
   means dropped, and nothing else.
   SF Pro for everything, read off the machine. No web fonts, no second face.
   See docs/2026-09-07-look.md.
   ─────────────────────────────────────────────── */

/* ── TOKENS ── (names are fixed: inline styles + JS reference them.
   VALUES are the hdwOS design system: layered translucent greys over a
   near-black canvas, liquid glass chrome, 12px controls / 20px boxes,
   4/8 spacing, spring motion, soft diffused elevation.) */
/* ── TOKENS ──
   Names are fixed: inline styles and JS reference them. Every VALUE now comes
   from ui/css/tokens.css, which is the only file allowed to invent one.
   Light and dark follow the device; app.js writes data-theme on <html> and the
   kit answers to it.

   What changed on 8 September 2026: the chrome was ultramarine in both themes.
   It is now simply the page, white or black, and ultramarine became the accent.
   Orange kept exactly one job, reject, under --rj. */
:root{
  color-scheme:light dark;

  /* canvas and surfaces */
  --bg:var(--fc-canvas);
  --sf:var(--fc-bg);
  --s2:var(--fc-bg2);
  --s3:var(--fc-bg3);
  --br:var(--fc-border);
  --bh:var(--fc-border2);

  /* ink */
  --tx:var(--fc-fg);
  --di:var(--fc-fg2);
  --mu:var(--fc-fg2);

  /* ACCENT — ultramarine. Primary action, selection, kept. */
  --ac:var(--fc-ac);
  --ac2:var(--fc-ac-ink);
  --ad:var(--fc-ac-wash);
  --ab:var(--fc-ac-line);

  /* REJECT — orange, and orange means nothing else anywhere in FieldCut. */
  --rj:var(--fc-rj);
  --rj-ink:var(--fc-rj-ink);
  --rj-on:var(--fc-rj-on);
  --rj-soft:var(--fc-rj-wash);
  --rj-line:var(--fc-rj-line);

  /* status — meaning, never decoration */
  --ok:var(--fc-ok);--er:var(--fc-err);--wn:var(--fc-warn);
  --pr:var(--fc-ac);
  --ok-soft:var(--fc-ok-wash);--er-soft:var(--fc-err-wash);--wn-soft:var(--fc-warn-wash);

  /* layout constants that are FieldCut's own, not the kit's */
  --sb-w:208px;--rb-w:212px;--pan-x:14px;
  /* The bar height lives here because four other rules measure from it. */
  --top-h:48px;
  /* The Mac app's traffic-light strip above the bar. Every desktop rule that
     clears the bar adds this to --top-h rather than naming a total, because
     the total was typed in three places and two of them were still counting
     a 56px bar after it became 48: the three columns started 18px under
     where the bar ended, and the progress hairline floated 8px below it. */
  --strip-h:30px;
  /* Whatever sits above the bar right now: the Mac strip, or the web page's
     "get the app" bar, or nothing. Every rule that clears the bar adds this
     to --top-h, so a bar above it moves the whole app down by one number. */
  --above:0px;

  /* --rad-xs is the app's own dense 6px corner. The alias below it now
     means 8px here, exactly as it does on the marketing pages, so the
     same name can never resolve to two values again. */
  --rad:var(--fc-r-box);--rad-xs:var(--fc-r-sm);--rad-sm:var(--fc-radius);--rad-lg:var(--fc-r-panel);--rad-pill:var(--fc-r-pill);
  --glass:var(--fc-glass);--glass-thick:var(--fc-glass);--glass-fx:var(--fc-glass-fx);
  --hairline:var(--fc-border);
  --sh1:var(--fc-sh-1);--sh2:var(--fc-sh-soft);--sh3:var(--fc-sh-pop);
  --tile:var(--fc-sh-2);--tile-h:var(--fc-sh-soft);
  --glow:0 6px 18px var(--fc-ac-wash);
  --hair:inset 0 1px 0 rgba(255,255,255,.04);
  --ease:var(--fc-ease);--ease-out:var(--fc-ease-out);--spring:cubic-bezier(.34,1.56,.64,1);
  --dur:var(--fc-dur);--dur-slow:var(--fc-dur-slow);

  /* ── CHROME ── bars, panels, sheets, gates. It used to be ultramarine in
     both themes, which is what made the whole app read as a blue product.
     It is now the page itself, and the blue is spent on the accent. */
  --ch:var(--fc-canvas);
  --ch-sf:var(--fc-bg);
  --ch-s2:var(--fc-bg2);
  --ch-tx:var(--fc-fg);
  --ch-di:var(--fc-fg2);
  --ch-mu:var(--fc-fg2);
  --ch-br:var(--fc-border);
  --ch-bh:var(--fc-border2);
  --ch-ok:var(--fc-ok);--ch-er:var(--fc-err);
  --display:var(--fc-font-display);
}
/* The one value that genuinely differs by theme and has no kit equivalent:
   the top hairline that sells a raised surface. */
[data-theme=light]{--hair:inset 0 1px 0 rgba(255,255,255,.6)}

*{box-sizing:border-box;margin:0;padding:0}
/* ── CHROME EXTRAS ── (the chrome tokens themselves live in the block above)
   --wn measures 4.0:1 on the blue in dark and 2.1:1 in light, so the chrome
   gets its own amber. Motion: --ch-in enters, --ch-out leaves shorter and
   softer, --ch-hov is the quick one for hover and press. */
:root{--ch-wn:var(--fc-warn);
  --ch-in:var(--fc-dur) var(--fc-ease-out);
  --ch-out:var(--fc-dur-out) var(--fc-ease-in);
  --ch-hov:var(--fc-dur-tap) var(--fc-ease)}
/* Every blue surface. Bone ink, dark native controls and scrollbars, and a
   bone focus ring: the orange one measures 2.4:1 on the blue. */
#mob-gate-screen,#loading,#lic-gate,.top,.sb,.rb,.tb,#simbar,.sb-expand,.es,.card,.warn-card,.mbox,.about-box,.bug-box,.dup-panel,.nle-dest-inner,.sort-dd-drop,.av-menu,.fmenu,.ctx,.ts,#htu-panel,#ts-panel,.pb-stop{color:var(--ch-tx);scrollbar-color:var(--ch-br) transparent}
:is(#mob-gate-screen,#loading,#lic-gate,.top,.sb,.rb,.tb,#simbar,.es,.card,.warn-card,.mbox,.about-box,.bug-box,.dup-panel,.nle-dest-inner,.sort-dd-drop,.av-menu,.fmenu,.ctx,.ts,#htu-panel,#ts-panel) :focus-visible,.sb-expand:focus-visible,.pb-stop:focus-visible,#expp-hide:focus-visible{outline-color:var(--ch-tx)}
:is(#mob-gate-screen,#loading,#lic-gate,.sb,.rb,.card,.mbox,.bug-box,.dup-panel,.nle-dest-inner,#htu-panel,#ts-panel) ::-webkit-scrollbar-thumb{background-color:var(--ch-br)}
:is(#mob-gate-screen,#loading,#lic-gate,.sb,.rb,.card,.mbox,.bug-box,.dup-panel,.nle-dest-inner,#htu-panel,#ts-panel) ::-webkit-scrollbar-thumb:hover{background-color:var(--ch-bh)}
:is(.card,.mbox,.bug-box,.warn-card,#lic-gate,.nr,.nle-dest-inner) ::placeholder{color:var(--ch-mu);opacity:.8}
*:focus{outline:none}
*:focus-visible{outline:2px solid var(--ac);outline-offset:2px}
.ti:focus-visible,.sfi:focus-visible,.sort-dd-opt:focus-visible,.ci:focus-visible,.ts-item:focus-visible{outline-offset:-2px}
/* A class rule with `display` outranks the bare `hidden` attribute, so
   anything shipped hidden in the markup would render anyway. */
[hidden]{display:none!important}
/* Counts that change sit in tabular figures so a row does not jump sideways
   when 9 becomes 10. base.css carries this for the public pages and the app
   deliberately does not load base.css, so it is repeated rather than inherited. */
.num{font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}
/* The page itself never moves. overscroll-behavior stops the Mac app's WebView
   from rubber-banding the whole window when there is nothing to scroll, which
   reads as the empty app scrolling; the grid keeps its own scrolling. */
html,body{height:100%;overflow:hidden;overscroll-behavior:none}
html{min-width:760px}
/* Between the phone gate (ends at 600px) and the app's 760px minimum there was
   a band — a half-screen window on a 1280 or 1366 laptop — where the layout was
   cut off at the right edge with no way to scroll to Export. Let that band
   scroll sideways instead of hiding the controls. */
@media (min-width:601px) and (max-width:759px){
  html,body{overflow-x:auto}
}
body{font-family:var(--fc-font-body);background:var(--bg);color:var(--tx);font-size:var(--fc-fs-3);transition:background var(--fc-dur),color var(--fc-dur);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-optical-sizing:auto}
input,textarea,button{font-family:var(--fc-font-body)}
/* A browser's own bold is 700, which is a fourth weight nobody asked for.
   Everything that arrives bold by default is pulled down to the one step up,
   so the whole product really is 300 / 400 / 600 and nothing else. */
b,strong,th,h5,h6,optgroup{font-weight:var(--fc-fw-up)}
/* The app does not load base.css, so the clamp is repeated here. It used to
   sweep h1-h4 in with it and hold every app heading at 600, on the argument
   that a sheet or alert title is semibold on macOS. That stopped being true
   of this product on 16 September: the retune put every heading on 500, and
   half the app's own titles had already moved there by class, so the blanket
   600 was making bare <h2> heavier than the .onb-h beside it. Headings now
   match base.css exactly, and 600 is left to the six selectors that arrive
   bold on their own. */
h1,h2,h3,h4{font-weight:var(--fc-fw-mid);line-height:var(--fc-lh-snug);letter-spacing:var(--fc-ls-tight)}
input,textarea{text-transform:none;letter-spacing:normal}
::selection{background:var(--fc-ac-wash);color:var(--tx)}

/* ── BUTTONS ── chrome. 12px corners, press = scale .98.
   Primary is bone on blue (7.7:1). The orange fill only ever sits on the
   photo ground, where its own controls (.nle-play-btn, .nle-extract-btn) keep it. */
.ba,.bp,.ob{background:var(--ac);border:1px solid var(--ac);color:var(--fc-ac-on);font-size:var(--fc-fs-3);height:32px;padding:0 var(--fc-s-4);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;white-space:nowrap;border-radius:var(--rad-pill);transition:box-shadow var(--ch-hov),transform var(--ch-hov),opacity var(--ch-hov)}
.ob{padding:var(--fc-s-3) var(--fc-s-5);margin-top:var(--fc-s-2)}
/* Quiet twin of .ob: same shape, no fill, for a second action beside the primary one. */
.ob-alt{height:32px;padding:var(--fc-s-3) var(--fc-s-5);margin-top:var(--fc-s-2);border-radius:var(--rad-pill);font-size:var(--fc-fs-3);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;white-space:nowrap;background:var(--fc-bg2);color:var(--fc-fg);border:1px solid var(--fc-border2);transition:background var(--ch-hov),color var(--ch-hov),border-color var(--ch-hov)}
.ob-alt:hover{background:var(--fc-bg3)}
.ba:hover,.bp:hover,.ob:hover{box-shadow:0 0 0 4px var(--ch-s2)}
.ba:active,.bp:active,.ob:active{transform:scale(.96)}
.ba:disabled,.bp:disabled,.ob:disabled{background:transparent;border-color:var(--ch-br);color:var(--ch-mu);opacity:1;cursor:not-allowed;box-shadow:none;transform:none}
/* secondary = bone hairline */
.bg{background:transparent;border:1px solid var(--ch-br);color:var(--ch-di);font-size:var(--fc-fs-3);height:32px;padding:0 var(--fc-s-4);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;white-space:nowrap;border-radius:var(--rad-pill);transition:background var(--ch-hov),color var(--ch-hov),border-color var(--ch-hov),transform var(--ch-hov)}
.bg:hover{background:var(--ch-sf);border-color:var(--ch-bh);color:var(--ch-tx)}
.bg:active{transform:scale(.96)}
.bg:disabled{opacity:.34;cursor:not-allowed}
/* toggle chips. Active = bone chip, blue text */
.bs{background:transparent;border:1px solid transparent;color:var(--ch-di);font-size:var(--fc-fs-3);height:30px;padding:0 var(--fc-s-3);display:inline-flex;align-items:center;cursor:pointer;white-space:nowrap;flex-shrink:0;border-radius:var(--rad-pill);transition:background var(--ch-hov),color var(--ch-hov),transform var(--ch-hov)}
.bs:hover{color:var(--ch-tx);background:var(--ch-s2)}
.bs:active{transform:scale(.96)}
.bs:disabled{opacity:.34;cursor:not-allowed}
/* A tool does something to the shoot. A view state only changes what you are
   looking at. They sat at identical weight, so the bar read as ten equal
   things; the outline says "this acts" before the label is even read. */
#btnsim,#btnqc{border-color:var(--ch-br);border-radius:var(--rad-pill);color:var(--ch-di)}
#btnsim:hover,#btnqc:hover{border-color:var(--ch-bh);background:transparent;color:var(--ch-tx)}
#btnsim.on,#btnqc.on{background:var(--ad);border-color:var(--ab);color:var(--ac2)}
.bs.on{background:var(--ad);color:var(--ac2);border-color:var(--ab)}
.bs.danger{color:var(--ch-er)}
.bs.danger:hover{background:var(--ch-s2);color:var(--ch-er)}
/* The same chip on the photo ground keeps the working tokens. */
.fd-tab,.fdr-acts .bs,.sim-hd-btns .bs,.sim-merge-banner .bs{color:var(--di)}
.fd-tab:hover,.sim-hd-btns .bs:hover,.sim-merge-banner .bs:hover{color:var(--tx);background:var(--s2)}
.fdr-acts .bs:hover{color:var(--tx)}
.icon-btn{background:none;border:none;color:var(--di);cursor:pointer;font-size:var(--fc-fs-5);padding:var(--fc-s-1) var(--fc-s-2);transition:color var(--fc-dur-tap);line-height:1;border-radius:var(--rad-pill)}
.icon-btn:hover{color:var(--tx)}
.bi{width:28px;height:28px;background:transparent;border:none;color:var(--ch-mu);font-size:var(--fc-fs-7);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:color var(--ch-hov),background var(--ch-hov);line-height:1;flex-shrink:0;border-radius:var(--rad-pill)}
.bi:hover{color:var(--ch-tx);background:var(--ch-s2)}
.bi:active{transform:scale(.96)}

/* The app draws its own controls denser than the kit does (28-32px against
   the kit's 40px) on purpose, so the reach has to be bolted on separately
   here, the same way ui/css/components.css does it for .hit-pad and its
   siblings: only the reach grows, never the drawing. */
/* :where() gives this zero specificity on purpose. The overlay only needs a
   positioned ancestor, and an absolute or fixed control already is one — so
   any other position rule must win, wherever it sits in the file. Without
   it, .wc-x (an .icon-btn pinned to a window corner) kept its
   position:absolute only because it happened to be declared further down;
   moving either rule would have dropped the close button into the flow. */
:where(.ba,.bp,.ob,.ob-alt,.bg,.bs,.bi,.icon-btn,.cut-btn){position:relative}
.ba::before,.bp::before,.ob::before,.ob-alt::before,.bg::before,.bs::before,
.bi::before,.icon-btn::before,.cut-btn::before{
  content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:max(100%,var(--fc-hit));height:max(100%,var(--fc-hit));
}
/* Exception: #src-add and #src-copy are two 28px .bi side by side in
   .sb-ph, whose gap is var(--fc-s-1), 4px, and both can be on screen at once
   (a job open, one of its folders still on a slow drive). Two 40px overlays
   would overlap by 12px and steal each other's clicks.

   Switching the overlay off would leave them at 28px, so each takes exactly
   the reach that fits instead. Across, it grows by the gap and no further:
   2px a side, so the two overlays meet in the middle of the gap and never
   cross. Down, nothing is in the way — .ph is a 40px row — so it takes the
   full --fc-hit and stays inside its own row. 32 by 40, not 28 by 28. */
.sb-add::before{width:calc(100% + var(--fc-s-1));height:max(100%,var(--fc-hit))}

/* ── OVERLAYS / SHEETS ──
   One scrim, no blur: a full-window backdrop-filter re-samples the whole grid
   on every frame of the sheet's own animation, and the sheets are solid now.
   The scrim fades out over 180 ms and only then leaves the layout, which is
   what `display … allow-discrete` buys; browsers without it snap, as before. */
.ov,#drive-ov,#dexp-ov,#warn-open-ov,#about-ov,#bug-ov,.dup-ov{position:fixed;inset:0;background:rgba(10,10,10,.6);display:none;align-items:center;justify-content:center;z-index:600;opacity:0;pointer-events:none;transition:opacity var(--ch-out),display var(--ch-out) allow-discrete}
.ov.op,#drive-ov.op,#dexp-ov.op,#warn-open-ov.op,#about-ov.op,#bug-ov.op,.dup-ov.op{display:flex;opacity:1;pointer-events:auto;transition:opacity var(--ch-in),display var(--ch-in) allow-discrete}
/* The folder picker also opens from inside the loupe (More folders…), which sits at 800, and Compare at 820. */
#moveov{z-index:850}
@starting-style{.ov.op,#drive-ov.op,#dexp-ov.op,#warn-open-ov.op,#about-ov.op,#bug-ov.op,.dup-ov.op{opacity:0}}
@keyframes ovIn{from{opacity:0}to{opacity:1}}
/* Every sheet, menu and bar enters the same way: fade plus a 6px rise. */
@keyframes chIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.card{background:var(--ch);border-radius:var(--rad);padding:var(--fc-s-5);min-width:300px;max-width:460px;width:92%;box-shadow:var(--sh3);animation:chIn var(--ch-in) both}
.card h3{font-size:var(--fc-fs-5);margin-bottom:var(--fc-s-2);color:var(--ch-tx)}
.card p{font-size:var(--fc-fs-2);color:var(--ch-di);margin-bottom:var(--fc-s-4);line-height:1.7;text-transform:none;letter-spacing:normal}
/* Text fields in a dialog fill its width. Tick boxes must be left alone: this
   rule used to catch them too, which stretched every tick box in the quality
   review to the full width of the row and left its filename no room to appear
   in at all. */
.card input:not([type=checkbox]):not([type=radio]){width:100%;background:var(--ch-sf);border:1px solid var(--ch-br);border-radius:var(--rad-xs);color:var(--ch-tx);font-size:var(--fc-fs-2);padding:var(--fc-s-3) var(--fc-s-4);outline:none;margin-bottom:var(--fc-s-3);text-transform:none;letter-spacing:normal;transition:border-color var(--ch-hov),box-shadow var(--ch-hov)}
.card input:not([type=checkbox]):not([type=radio]):focus{border-color:var(--ch-tx);box-shadow:0 0 0 3px var(--ch-s2)}
.card input[type=checkbox],.card input[type=radio]{flex:none;margin:0;accent-color:var(--ch-tx);cursor:pointer}
/* The update sheet's own bar. The header bar it borrows the class from is
   absolute and hidden, so without this it never showed at all. */
.card .pbar{position:static;display:block;height:2px;border-radius:var(--rad-pill);overflow:hidden}
.upd-prog-txt{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--ch-di);font-variant-numeric:tabular-nums}
.rw{display:flex;gap:var(--fc-s-2);justify-content:flex-end}

/* ── LOADING ── the display face and a big mono counter, on the blue */
#loading{position:fixed;inset:0;background:var(--ch);display:none;flex-direction:column;align-items:center;justify-content:center;z-index:900;gap:var(--fc-s-3);padding:var(--fc-s-5);text-align:center}
#loading.op{display:flex}
.ld-logo svg{width:40px;height:40px;fill:var(--ch-tx);margin-bottom:var(--fc-s-3);display:block}
.ld-title{font-family:var(--display);font-weight:var(--fc-fw-mid);font-size:var(--fc-fs-8);line-height:var(--fc-lh-snug);letter-spacing:var(--fc-ls-tight);color:var(--ch-tx);text-wrap:balance;max-width:560px}
.ld-timer{font-size:var(--fc-fs-8);line-height:1;color:var(--ch-tx);letter-spacing:-.01em;font-variant-numeric:tabular-nums;margin-top:var(--fc-s-2)}
/* The bar steps with each paint (every 200 ms) rather than tweening its
   width: width is layout, and layout never animates here. Bone, not --pr:
   the progress blue measures 3.1:1 on this ground. */
.ld-bar-wrap{width:240px;height:2px;background:var(--ch-br);border-radius:var(--rad-pill);overflow:hidden;margin-top:var(--fc-s-2)}
.ld-bar{height:100%;background:var(--ac);width:0%}
.ld-info{font-family:var(--fc-font-mono);font-size:var(--fc-fs-2);color:var(--ch-mu);letter-spacing:.04em;text-align:center;min-height:16px;font-variant-numeric:tabular-nums}

/* ── APP SHELL ── (liquid glass topbar — content scrolls beneath it) */
#app{display:flex;flex-direction:column;height:100vh;position:relative}
/* In the Mac app the window's own close/minimise/zoom buttons get a strip of
   their own above the app bar, closed off with a hairline. The wordmark then
   keeps its normal left margin instead of being shoved right to clear them,
   and the whole strip is draggable since there is no title bar to grab. */
body.is-desktop::before{
  content:'';position:fixed;top:0;left:0;right:0;height:var(--strip-h);z-index:60;
  background:var(--ch);border-bottom:1px solid var(--ch-br);-webkit-app-region:drag;
}
/* The strip says the app's name, centred, the way a window title would. */
body.is-desktop:not(.is-windows)::after{content:'FieldCut';position:fixed;top:0;left:0;right:0;height:var(--strip-h);line-height:var(--strip-h);z-index:61;text-align:center;font-size:var(--fc-fs-2);color:var(--ch-mu);pointer-events:none}
body.is-desktop{--above:var(--strip-h)}
/* The test build's strip: the word Test, in the dropped red, so the window
   that is running unreleased work is the one that looks it. */
body.is-desktop.is-test:not(.is-windows)::after{content:'FieldCut Test';color:var(--fc-rj-ink);font-weight:var(--fc-fw-up)}
body.is-desktop .top{-webkit-app-region:drag}
body.is-desktop .top button,body.is-desktop .top .bi,body.is-desktop .top input,
body.is-desktop .top select,body.is-desktop .top .seg,body.is-desktop .top [onclick]{-webkit-app-region:no-drag}
/* Windows draws its own title bar, so the strip that stands in for the Mac's
   hidden one is not wanted, and nothing needs pushing down to clear it. */
body.is-desktop.is-windows::before{display:none}
body.is-desktop.is-windows{--above:0px}
body.is-desktop.is-windows .top{-webkit-app-region:no-drag}
/* The loupe, Compare and the clip editor cover the whole window, including
   over where the traffic lights sit, so they start clear of the strip
   instead of padding their top row to dodge it sideways. The strip itself
   drags the window now, so their own top rows no longer have to. */
body.is-desktop:not(.is-windows) #lb,body.is-desktop:not(.is-windows) #cmp,body.is-desktop:not(.is-windows) #nle-ov{top:var(--above)}
/* Solid: nothing scrolls under it (.mn pads 56px for it), so glass bought
   nothing but a blur pass. */
.top{display:flex;align-items:center;padding:0 var(--fc-s-3);height:var(--top-h);background:var(--ch);flex-shrink:0;position:absolute;top:var(--above);left:0;right:0;z-index:50;gap:0;border-bottom:1px solid var(--ch-br)}
/* ── GET THE APP ── the one line the website shows and the Mac app never does.
   Ultramarine, because it is the one thing on this page asking to be pressed
   and the accent is what asks. It sits above the bar and moves the app down
   by its own height through --above, so nothing under it has to know. The
   close remembers itself; the next visit is quiet. */
.mac-bar{display:flex;align-items:center;justify-content:center;gap:var(--fc-s-2);position:absolute;top:0;left:0;right:0;height:var(--mac-bar-h);z-index:52;
  background:var(--fc-ac);color:var(--fc-ac-on);font-size:var(--fc-fs-3);text-decoration:none;padding:0 var(--fc-s-7) 0 var(--fc-s-4);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mac-bar:hover .mac-bar-go{text-decoration-thickness:2px}
.mac-bar-go{font-weight:var(--fc-fw-up);text-decoration:underline;text-underline-offset:3px}
.mac-bar-x{position:absolute;right:var(--fc-s-2);top:50%;transform:translateY(-50%);width:28px;height:28px;border:0;background:transparent;color:var(--fc-ac-on);font-size:var(--fc-fs-4);line-height:1;cursor:pointer;border-radius:var(--rad-pill);display:inline-flex;align-items:center;justify-content:center;opacity:.8;transition:opacity var(--ch-hov),background var(--ch-hov)}
.mac-bar-x:hover{opacity:1;background:rgba(255,255,255,.14)}
.mac-bar-x:focus-visible{outline-color:var(--fc-ac-on)}
body.mac-bar-on{--mac-bar-h:32px;--above:var(--mac-bar-h)}
body.is-desktop .mac-bar{display:none}
.logo svg{width:22px;height:22px;fill:var(--ch-tx);display:block;flex-shrink:0}
/* The name of the open card, in the room the wordmark used to take. The tab
   says FieldCut and so does the window, and the card name was 11px of mono in
   the left panel under a caps label, which is the wrong way round. This is the
   same #tp span the app already writes the folder name into, so there is no
   second copy to keep in step. Empty means nothing is open, and then the
   wordmark is exactly the right thing to say.

   It sits inside .cut-btn now, so the flex-shrink weight that used to live
   here moved out to .cut-wrap — the span itself only owns its own text
   truncation, not its place in the bar. */
.top-card{font-size:var(--fc-fs-6);line-height:var(--fc-lh-snug);letter-spacing:var(--fc-ls-tight);color:var(--ch-tx);
  max-width:22ch;min-width:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.top-card:empty::after{content:'FieldCut'}
/* The name is now the door into this shoot's other cuts. .cut-wrap is the
   flex item .top-card used to be — same huge shrink weight, so the name is
   still the first thing to give when the window closes — and .cut-btn is
   the pressable shape around the span plus a caret, sized and hovered like
   the bar's other dropdown triggers (.sort-dd-btn) so pressing it feels like
   the same family of control, just holding a bigger word. The span's own
   left margin is gone; the button owns that gap now, so it is not paid
   twice. */
.cut-wrap{flex:0 999 auto;min-width:0;display:flex}
.cut-btn{background:transparent;border:none;display:inline-flex;align-items:center;min-width:0;
  gap:var(--fc-s-1);margin-left:var(--fc-s-2);padding:var(--fc-s-1) var(--fc-s-2);
  border-radius:var(--rad-xs);cursor:pointer;transition:background var(--ch-hov),transform var(--ch-hov)}
.cut-btn:hover{background:var(--ch-s2)}
.cut-btn:active{transform:scale(.96)}
.cut-btn .cut-caret{flex-shrink:0}
/* Nothing open, nothing to switch between: the button still occupies the
   bar (so the layout does not jump the moment a folder opens) but takes no
   clicks, shows no caret and never hovers. Two routes to the same empty
   state — the welcome screen's body class, and the ordinary case where a
   folder was open and got closed, which only ever empties #tp rather than
   toggling a class — so both are covered here in CSS, live, without
   depending on cutsChanged() having run. 26-cuts-ui.js also sets the real
   `disabled` attribute on #cut-btn to the same effect, so a screen reader
   and a stray Enter key agree with what the pointer already cannot do. */
body.welcome .cut-wrap,
.cut-wrap:has(.top-card:empty){pointer-events:none}
body.welcome .cut-caret,
.cut-wrap:has(.top-card:empty) .cut-caret,
.cut-btn:disabled .cut-caret{display:none}
.cut-btn:disabled{cursor:default;pointer-events:none}
/* Cut names read left to right, not centred like the three or four short
   words the other dropdowns hold, and a long one truncates rather than
   wrapping the menu wide. */
.cut-drop{max-width:280px}
.cut-drop .sort-dd-opt{text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The word each caps label carried, said once, inside the control it belongs
   to. "Order by Capture time" is one thing to read; ORDER BY above Capture
   time was two, and five of those down the middle of a bar is scaffolding. */
.top-pre{color:var(--ch-mu);padding-right:var(--fc-s-1)}
/* 24px between groups and 4px inside them. That ratio is the whole grouping:
   four thin vertical rules used to be drawn to say what six times the space
   says on its own. It was 16, and 16 is right between two plain words and too
   little between two filled boxes: Mosaic and Similar both draw a background
   when they are on, and two boxes 16px apart read as one run of buttons. The
   gap steps back down to 16 at the width where the card name leaves, because
   by then the row needs the room more than it needs the grouping. The controls themselves come in from 12px of side padding
   to 8px, because the air between the groups is doing that work now and two
   kinds of gap at once only make the row wider.

   overflow-x:auto below that is the permanent safety net: on a window narrower
   than the row's natural width it scrolls inside itself rather than bleeding
   over the logo and the Open and Export buttons. */
/* The row of controls is never the thing that gives way. It is sized by its
   own content and pushed to the middle by auto margins, and the card name
   beside it carries a shrink weight twenty times its own, so when the window
   closes the name is what yields, by ellipsis, down to nothing. Every control
   keeps its word and its full hit area at every width the app is used at.
   Only once the name is gone does the row itself start to give. */
.top-c{flex:0 1 auto;margin:0 auto;display:flex;align-items:center;justify-content:flex-start;gap:var(--fc-s-5);min-width:0;overflow-x:auto;overflow-y:hidden;scrollbar-width:none}
.top-c .bs,.top-c .sort-dd-btn{padding:0 var(--fc-s-2)}
.top-c::-webkit-scrollbar{display:none}
/* The bar is one row and stays one row, and it gives its room up in a fixed
   order as the window closes. First the card name, by ellipsis and never by
   disappearing, because it is the one thing on the bar that says what you are
   looking at. Then Size, because a slider under a row of thumbnails is the
   one control that needs no word at all. Then the rest of the words inside
   the controls: they are read once and then never again, while Unsorted and
   Capture time are read every time. Nothing ever moves position, so nothing
   has to be relearned on a smaller screen. */
/* Then the order in which the rest gives way, once the name has nothing left
   to give and would be one letter and an ellipsis. The name goes first, and
   the left panel still says which folder is open, so it is moved rather than
   lost. Then Size, because a slider sitting under a grid of thumbnails is the
   one control on the bar that needs no word at all. Then Group by, then Order
   by, then Show, which is the order nobody misses them in. Every one of them
   keeps its title, so the word that left is one hover away. Nothing ever
   changes position, so nothing has to be relearned on a smaller screen. */
/* The name used to disappear below this width rather than sit there as a
   one-letter sliver. It stays now — a cut is a saved piece of work and the
   door to the others like it, not a label to spare — so instead of hiding
   it the steps below just cap it shorter, the same way Group by and Order
   by already give their own words up further down. Six characters and an
   ellipsis is the least that still names anything, which is the floor the
   960px step is built to hold even with every control in the centre group
   still carrying its full word. */
@media (max-width:1260px){.top-card{max-width:16ch}.top-c{gap:var(--fc-s-4)}}
@media (max-width:1180px){.top-slider-wrap .top-pre{display:none}}
@media (max-width:1140px){#group-btn .top-pre{display:none}}
@media (max-width:1080px){#sort-btn .top-pre{display:none}}
@media (max-width:1024px){
  #filter-btn .top-pre{display:none}
  .top-c{gap:var(--fc-s-3)}
  .top-card{max-width:12ch}
}
@media (max-width:960px){.top-card{max-width:8ch}}
.top-grp{display:flex;align-items:center;gap:var(--fc-s-1);line-height:1}
.top-slider-wrap{display:flex;align-items:center;gap:var(--fc-s-2);flex-shrink:0}
.tr{display:flex;align-items:center;gap:var(--fc-s-2);flex-shrink:0}

.pbar{height:3px;background:var(--ch-br);flex-shrink:0;display:none;position:absolute;top:calc(var(--above) + var(--top-h));left:0;right:0;z-index:49;overflow:visible}
.pb-stop{position:absolute;right:14px;top:8px;background:var(--ch);border:1px solid var(--ch-br);font:inherit;font-size:var(--fc-fs-3);padding:var(--fc-s-2) var(--fc-s-4);border-radius:var(--rad-pill);cursor:pointer;box-shadow:var(--sh2);transition:border-color var(--ch-hov),color var(--ch-hov),transform var(--ch-hov)}
.pb-stop:hover{border-color:var(--ch-er);color:var(--ch-er)}
.pb-stop:active{transform:scale(.96)}
.pb-stop:disabled{opacity:.6;cursor:default;transform:none}
.pbf{height:100%;background:var(--ac);width:0%}

/* ── EXPORT PROGRESS ── docked, never modal. The export is the slowest thing
   the app does and the grid behind it is what you were looking at, so it sits
   in the corner rather than over the top. Same face as the loading screen: a
   number you can read from across the room, a hairline under it, and the
   count in mono so the digits do not shuffle as they change. */
.expp{position:fixed;right:var(--pan-x);bottom:var(--pan-x);z-index:70;width:272px;
  background:var(--ch-sf);border-radius:var(--rad);
  padding:var(--fc-s-4);box-shadow:var(--sh3);color:var(--ch-tx);
  animation:chIn var(--ch-in) both}
.expp-head{display:flex;align-items:center;justify-content:space-between;gap:var(--fc-s-2)}
.expp-title{font-size:var(--fc-fs-2);color:var(--ch-di);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Hide and Stop now sit as a pair. Hide is .bs, the app's quietest button —
   transparent until touched, never bordered, never red — so Stop stays the
   more definite of the two exactly by doing nothing extra. Getting them
   confused costs an export, so they read as different KINDS of control, not
   two buttons the same weight with different labels. */
.expp-acts{display:flex;align-items:center;gap:var(--fc-s-1)}
/* The panel's Stop is in the flow, not pinned to the window like the one the
   hairline used to carry. The inset pseudo-element gives it a 40px reach
   without making the button itself look heavy. */
.expp .pb-stop{position:relative;top:auto;right:auto;flex-shrink:0;min-height:30px;padding:0 var(--fc-s-3);
  display:inline-flex;align-items:center;box-shadow:none}
.expp .pb-stop::after{content:'';position:absolute;inset:-5px}
.expp-eta{font-size:var(--fc-fs-8);line-height:1.1;letter-spacing:-.02em;color:var(--ch-tx);
  font-variant-numeric:tabular-nums;margin-top:var(--fc-s-2)}
.expp-bar{height:2px;background:var(--ch-br);border-radius:var(--rad-pill);overflow:hidden;margin-top:var(--fc-s-3)}
.expp-barf{height:100%;background:var(--ac);transform-origin:left;transform:scaleX(0);
  transition:transform var(--dur) var(--ease-out)}
.expp-meta{display:flex;justify-content:space-between;gap:var(--fc-s-2);margin-top:var(--fc-s-2);
  font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);letter-spacing:.04em;color:var(--ch-mu);
  font-variant-numeric:tabular-nums}
.expp-file{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--ch-mu);margin-top:var(--fc-s-1);
  min-height:14px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The app itself is gated above phone widths, so there is no narrow case to
   answer here: 272px plus its margins fits every window the grid appears in. */
@media (prefers-reduced-motion:reduce){.expp{animation:none}.expp-barf{transition:none}}
.ws{display:flex;flex:1;overflow:hidden;position:relative;min-height:0;gap:0;padding:0}

/* ── SIDEBARS ── blue, one frame with the top bar around the black grid */
.sb{width:var(--sb-w);flex-shrink:0;background:var(--ch);display:flex;flex-direction:column;overflow:hidden;position:relative;min-width:150px;max-width:340px;padding:calc(var(--above) + var(--top-h)) var(--pan-x) 0;border-right:1px solid var(--ch-br)}
.rb{width:var(--rb-w);flex-shrink:0;background:var(--ch);display:flex;flex-direction:column;overflow:hidden;position:relative;min-width:160px;max-width:360px;padding:calc(var(--above) + var(--top-h)) var(--pan-x) 0;border-left:1px solid var(--ch-br)}
/* The two panels that sit directly against the grid take the WORKING ground,
   not the blue. This is the fallback the brief (docs/2026-09-07-look.md) put
   in writing: a saturated cool surround pushes skin and warm tones warmer, and
   at 1280 the panels are about 400px of it either side of the photos. Judging
   colour is the one job the ground must not interfere with.

   It is one rule because the panels' children all read --ch-* and custom
   properties inherit: redefining them here re-grounds everything inside, in
   both themes, without touching a single child rule. Delete this block to put
   the panels back to blue. Bars, sheets, menus, gates and every empty state
   stay blue, so the identity is still on screen the whole time. */
.sb,.rb{
  --ch:var(--bg);--ch-sf:var(--sf);--ch-s2:var(--s3);
  --ch-tx:var(--tx);--ch-di:var(--di);--ch-mu:var(--mu);
  --ch-br:var(--br);--ch-bh:var(--bh);
  --ch-ok:var(--ok);--ch-er:var(--er);--ch-wn:var(--wn);
}
.rh{position:absolute;top:0;width:7px;height:100%;cursor:col-resize;z-index:10;background:transparent;transition:background var(--ch-hov)}
.rh:hover{background:var(--ch-br)}
.sb .rh{right:0}.rb .rh{left:0}
.ph{padding:0;height:40px;flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:var(--fc-s-2)}
.pt{font-family:var(--fc-font-mono);font-size:var(--fc-fs-2);letter-spacing:.02em;color:var(--ch-mu);flex-shrink:0}
.src-name{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);letter-spacing:.04em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}
/* Stands in for .src-name once a job has more than one folder, so it must
   read as the same plain label and not as a form field until someone
   actually clicks in: no border, no fill, until :focus says otherwise.
   Shares the .src-name class for font, size and the ellipsis, so only the
   input-specific resets live here. */
.src-job-name{background:none;border:1px solid transparent;border-radius:var(--rad-xs);
  transition:border-color var(--ch-hov),box-shadow var(--ch-hov)}
.src-job-name:focus{border-color:var(--ch-tx);box-shadow:0 0 0 3px var(--ch-s2)}
.src-job-name::placeholder{color:var(--ch-mu)}
/* Same shape as the sort panel's empty state, so both sides of the grid
   speak the same way when there is nothing in them yet. */
.pe{padding:var(--fc-s-5) 0;display:flex;flex-direction:column;gap:var(--fc-s-2)}
.pe b{font-weight:var(--fc-fw);font-size:var(--fc-fs-3);color:var(--ch-tx)}
.pe span{font-size:var(--fc-fs-2);color:var(--ch-mu);line-height:1.55}
.ps{flex:1;overflow-y:auto;padding:var(--fc-s-1) 0;margin:0 calc(var(--pan-x) * -1);padding-left:var(--pan-x);padding-right:var(--pan-x)}

/* ── SOURCE TREE ── */
.ti{display:flex;align-items:center;font-size:var(--fc-fs-2);color:var(--ch-di);cursor:pointer;min-height:27px;
  transition:background var(--ch-hov),color var(--ch-hov);border-radius:var(--rad-xs);
  padding:0 var(--fc-s-2);margin:0 calc(var(--fc-s-2) * -1)}
.ti:hover{background:var(--ch-sf);color:var(--ch-tx)}
.ti.act{background:var(--ch-s2);color:var(--ch-tx)}
/* The twisty was U+25B6, a solid black triangle, set at 14px against 13px
   text, so it was both larger and heavier than the folder name beside it and
   read as the loudest thing in the column. U+25B8 is the small variant, and
   10px puts it below the name it belongs to. The 22px box is unchanged, so
   the target you actually click is exactly as big as it was. */
.tg{width:22px;flex-shrink:0;align-self:stretch;display:flex;align-items:center;justify-content:center;font-size:var(--fc-fs-4);color:var(--ch-mu);transition:transform var(--ch-hov)}
.tg.op{transform:rotate(90deg)}
.ti-ic{font-size:var(--fc-fs-3);flex-shrink:0;width:22px;display:flex;align-items:center;justify-content:center;overflow:hidden}
.tn{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tc{font-size:var(--fc-fs-1);color:var(--ch-mu);padding-right:0;flex-shrink:0;font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums}
.tch{display:none}.tch.op{display:block}
/* Removes one source folder from a multi-source job. Same box as .ti-ic
   (22px) so the row does not reflow when it appears. Waits for the pointer,
   like .ic-sel waits on a tile, and a keyboard tab lands on it directly, so
   its own :focus is listed too even though :focus-within already implies it.
   Touch has no hover, so it stays on screen there. */
.ti-x{width:22px;height:22px;flex-shrink:0;display:flex;align-items:center;justify-content:center;
  color:var(--ch-mu);opacity:0;cursor:pointer;border-radius:var(--rad-xs);
  transition:opacity var(--ch-hov),color var(--ch-hov)}
.ti-x:hover{color:var(--ch-tx)}
.ti:hover .ti-x,.ti:focus-within .ti-x,.ti-x:focus{opacity:1}
@media (hover:none){.ti-x{opacity:1}}

/* ── STATUS STRIP (always on screen) ── */
.mn{flex:1;display:flex;flex-direction:column;overflow:hidden;background:transparent;min-width:0;padding-top:calc(var(--above) + var(--top-h))}
/* The status line. It used to appear only while something was selected, which
   meant that at rest — the state you are in for most of a cull — the app told
   you nothing about how far through the card you were. It is always on screen
   now: progress at rest, the selection and its actions when there is one. */
/* It sits inside the working column and every word of it is about the
   photographs, so it takes the working ground rather than the chrome blue.
   That gives a clean line down the app: blue above the workspace, neutral
   inside it. Stacking two blue bands over the first row of a grid was a lot
   of colour to put next to what you are judging. */
/* Fixed height, always painted, never animated in or out. Without that the
   grid below it would jump by however tall this is, every time a selection
   starts or ends, which is most of a cull. The side panels' .ph header is
   40px too, and all three columns pad the top the same, so this label sits
   on the same line as "Source" and "Sort into". */
.tb{display:flex;align-items:center;background:var(--bg);flex-shrink:0;overflow:visible;position:relative;height:40px;padding:0 var(--fc-s-4);gap:var(--fc-s-2);border-bottom:1px solid var(--br);border-radius:0}
.tb,.tb *{--ch:var(--bg);--ch-sf:var(--sf);--ch-s2:var(--s3);--ch-tx:var(--tx);--ch-di:var(--di);--ch-mu:var(--mu);--ch-br:var(--br);--ch-bh:var(--bh);--ch-ok:var(--ok);--ch-er:var(--er);--ch-wn:var(--wn)}
/* Only the buttons are about a selection. At rest the first segment (the
   status readout) is the only one left; All, Clear and the flag buttons
   have nothing to act on yet, so they wait for one instead of the whole
   strip disappearing under them. */
.tb:not(.has-sel) .tb-seg:not(:first-child){display:none}
/* Hangs from the top bar, so it settles down by 4px rather than rising. */
@keyframes tbIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
/* The count is what you read; the buttons are what you press. Pushing the
   rest to the right stops them running together into one grey line. */
.tb-seg{display:flex;align-items:center;gap:var(--fc-s-1);flex-shrink:0}
.tb .tb-seg:first-child{margin-right:auto}
.tb-seg.grow{flex:1;min-width:200px;justify-content:flex-end}
/* A number you can read from across a desk, the way the deck screens do it.
   Mono and tabular so the digits do not jitter as the count climbs. */
.si{display:flex;align-items:baseline;gap:var(--fc-s-2);flex-shrink:0;white-space:nowrap;font-size:var(--fc-fs-1);color:var(--ch-mu)}
.si-n{font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-6);line-height:1;letter-spacing:-.02em;color:var(--ch-tx)}
.si-of{font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);letter-spacing:.02em;color:var(--ch-mu)}
.si-tally{font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);letter-spacing:.02em;color:var(--ch-mu);padding-left:var(--fc-s-2);border-left:1px solid var(--ch-br)}
@media (max-width:1080px){.si-tally{display:none}}
/* Rest state drops si-n's big number: one plain line, panel-label size, so it
   reads like "Source" and "Sort into" rather than a hero stat beside them. */
.tb:not(.has-sel) .si{font-family:var(--fc-font-mono);font-size:var(--fc-fs-2);letter-spacing:.02em;color:var(--mu)}

.kh{font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em;flex-shrink:0;display:flex;align-items:center;gap:var(--fc-s-1)}
.kh kbd{background:var(--ch-sf);padding:var(--fc-s-0) var(--fc-s-2);font-size:var(--fc-fs-1);color:var(--ch-di);border-radius:var(--rad-xs)}
.sort-arrow{font-size:var(--fc-fs-5);opacity:.5;line-height:1;color:var(--ch-mu)}
.sort-dd-btn{font-size:var(--fc-fs-3);color:var(--ch-tx);cursor:pointer;white-space:nowrap;display:flex;align-items:center;gap:var(--fc-s-1);user-select:none;height:30px;padding:0 var(--fc-s-3);border-radius:var(--rad-pill);transition:background var(--ch-hov),color var(--ch-hov)}
.sort-dd-btn:hover{color:var(--ch-tx);background:var(--ch-s2)}
.sort-dd-btn:active{transform:scale(.96)}
/* position:fixed + top/left set from JS at open-time (positionDropdown()) —
   .top-c scrolls horizontally now, and a plain absolute-positioned dropdown
   would get clipped by that ancestor's overflow. Fixed positioning escapes it. */
.sort-dd-drop{position:fixed;background:var(--ch-sf);border-radius:var(--rad);min-width:140px;z-index:400;display:none;flex-direction:column;box-shadow:var(--sh3);padding:var(--fc-s-1);overflow:hidden;animation:chIn var(--ch-in) both}
.lb-open .sort-dd-drop{display:none!important}
.sort-dd-drop.op{display:flex}
.sort-dd-opt{text-align:center;padding:var(--fc-s-2) var(--fc-s-3);font-size:var(--fc-fs-2);color:var(--ch-di);cursor:pointer;transition:background var(--ch-hov),color var(--ch-hov);white-space:nowrap;border-radius:var(--rad-xs)}
.sort-dd-opt:hover{background:var(--ch-s2);color:var(--ch-tx)}
.sort-dd-opt.on{color:var(--ch-tx);background:var(--ch-s2)}
.tslider{-webkit-appearance:none;appearance:none;width:76px;height:20px;background:transparent;outline:none;cursor:pointer;flex-shrink:0}
.tslider::-webkit-slider-runnable-track{height:3px;background:var(--ch-br);border-radius:var(--rad-pill)}
.tslider::-moz-range-track{height:3px;background:var(--ch-br);border-radius:var(--rad-pill)}
.tslider::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;margin-top:-4.5px;/* token-lint: geometry, centres on the track */border-radius:var(--rad-pill);background:var(--ch-tx);cursor:pointer;border:2px solid var(--ch);box-shadow:var(--sh1)}
.tslider::-moz-range-thumb{width:12px;height:12px;border-radius:var(--rad-pill);background:var(--ch-tx);border:2px solid var(--ch);box-shadow:var(--sh1)}

/* ── GRID ── */
.gw{flex:1;overflow-y:auto;padding:var(--fc-s-2) var(--fc-s-4) var(--fc-s-4);-webkit-overflow-scrolling:touch}
/* With nothing to judge the whole working area is chrome. With a folder open
   and nothing in it, the message sits as a blue sheet between the panels. */
.es{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:var(--fc-s-3);background:var(--ch);border-radius:var(--rad);padding:var(--fc-s-6) var(--fc-s-5);text-align:center}
body.welcome .mn{background:var(--ch)}
body.welcome .gw{padding:0}
/* Nothing is open, so there is no progress to report: the strip waits for a
   folder rather than saying "No files open" over the welcome. */
body.welcome .tb{display:none}
body.welcome .es{border-radius:0}
.ei{width:56px;height:56px;margin-bottom:var(--fc-s-1);color:var(--ch-tx)}
.et{font-family:var(--display);font-weight:var(--fc-fw-mid);font-size:clamp(32px,4vw,44px);line-height:var(--fc-lh-snug);letter-spacing:var(--fc-ls-tight);color:var(--ch-tx);text-wrap:balance;max-width:640px}
.ev{font-size:var(--fc-fs-3);line-height:1.8;color:var(--ch-di);max-width:480px;text-wrap:pretty}
/* Two equal ways in: a grid of equal columns, each as wide as the longer label. */
.es-actions{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:var(--fc-s-3);justify-content:center;margin-top:var(--fc-s-4)}
/* The cut opened last, offered back under the two buttons. Quieter than
   both on purpose: it is a shortcut past the picker, not a third way in.
   It was one button holding one folder. It is a short list now, because one
   slot overwritten by every open meant yesterday's card was gone the moment
   today's was open — and because a folder name on its own never answered the
   question people actually have, which is which disk it was on. Each row
   leads with the name, carries the folder's colours as dots, and says the
   disk and the path underneath in meta type. The verb sits at the end where
   the eye lands, and turns into "Not connected" when the disk is not there. */
.es-ses{display:flex;flex-direction:column;gap:var(--fc-s-0);width:min(520px,100%);margin-top:var(--fc-s-4);text-align:left}
/* One small-caps label style, reused wherever a block of cuts needs a quiet
   heading: the start screen, the cut menu, and (in the archived view) the
   export list and each folder group. es-ses-h is a separate, unrelated
   label elsewhere on this screen and keeps its own name. */
.es-ses-h,.cut-label-caps{font-size:var(--fc-fs-1);color:var(--ch-mu);text-transform:uppercase;letter-spacing:.08em;padding:0 var(--fc-s-3) var(--fc-s-2)}
.cut-row{display:flex;align-items:center;justify-content:space-between;gap:var(--fc-s-3);width:100%;
  background:transparent;border:0;padding:var(--fc-s-2) var(--fc-s-3);border-radius:var(--rad-xs);
  cursor:pointer;text-align:left;
  transition:background var(--ch-hov),color var(--ch-hov)}
.cut-row:hover{background:var(--ch-s2)}
.cut-row-main{display:flex;flex-direction:column;gap:var(--fc-s-0);min-width:0}
/* The name and its folder colours on one line, the dots after the name rather
   than dropped onto a line of their own under it. */
.cut-row-top{display:flex;align-items:center;gap:var(--fc-s-1);min-width:0}
.cut-row-top .cut-dots{margin-left:0;flex-shrink:0}
.cut-row-name{font-size:var(--fc-fs-2);color:var(--ch-tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cut-row-where{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cut-row-side{display:flex;align-items:center;gap:var(--fc-s-3);flex-shrink:0}
.cut-row-when{font-size:var(--fc-fs-1);color:var(--ch-mu)}
.cut-row-go{font-size:var(--fc-fs-2);color:var(--ac2)}
/* A drive that is not plugged in. The row stays readable and stops promising:
   an offer that cannot be met is worse than no offer, because it is a click
   that ends in a loading screen which never fills. */
.cut-row-off{opacity:.55}
.cut-row-off:hover{background:transparent;cursor:default}
.cut-row-go.cut-off{color:var(--ch-mu)}
/* The folders the shoot was cut into, as their own colours, so a row is
   recognised before it is read. cutDots() in 25-cuts.js is what actually
   writes this markup. */
.cut-dots{display:inline-flex;gap:var(--fc-s-1);margin-left:var(--fc-s-2);vertical-align:middle}
.cut-dots i{width:6px;height:6px;border-radius:50%;display:block}
.cut-more{background:transparent;border:0;padding:var(--fc-s-2) var(--fc-s-3);margin-top:var(--fc-s-0);
  font-size:var(--fc-fs-1);color:var(--ch-mu);cursor:pointer;text-align:left;border-radius:var(--rad-xs)}
.cut-more:hover{color:var(--ch-tx);background:var(--ch-s2)}
/* Chrome and Edge can open folders dropped on the window. The hint says so
   under the buttons; the ring is the whole window saying "here", drawn over
   everything and touching nothing: it takes no clicks and goes the moment
   the drag leaves. */
.es-drop-hint{font-size:var(--fc-fs-2);color:var(--ch-mu);margin-top:var(--fc-s-3)}
body.drop-over::after{content:'';position:fixed;inset:var(--fc-s-3);border:2px dashed var(--ac2);border-radius:var(--fc-r-box);pointer-events:none;z-index:9999;animation:drop-in var(--fc-dur) var(--fc-ease-out) both}
@keyframes drop-in{from{opacity:0;transform:scale(.99)}to{opacity:1;transform:none}}
.dgrp{font-size:var(--fc-fs-1);color:var(--mu);letter-spacing:.02em;padding:var(--fc-s-4) var(--fc-s-1) var(--fc-s-2);margin-bottom:var(--fc-s-1)}
.dgrp:not(:first-child){margin-top:var(--fc-s-4)}
/* The strip above already separates chrome from grid, so the very first
   heading does not need the full gap the ones lower down need. */
#grid-root>.jg-wrap:first-child>.dgrp{padding-top:var(--fc-s-1)}
/* The grid is rows that fill the width: every tile keeps its own shape and
   the JS in src/app/05-grid.js works out where each one goes. Tiles are
   positioned absolutely inside their block, and only the rows near the screen
   exist at all, so the block's height is set by hand to what the rows add up
   to and the scrollbar stays honest. */
#grid-root{position:relative;min-height:100%}
body.welcome #grid-root{display:none}
.jg-wrap{position:relative}
.jg-block{position:relative;width:100%;margin-bottom:var(--fc-s-2)}
/* Dragging across the grid's own ground draws this and selects what it covers. */
.marquee{position:absolute;z-index:20;pointer-events:none;border:1px solid var(--ac);background:var(--ad);border-radius:var(--rad-xs)}
body.marquee-on{user-select:none;-webkit-user-select:none;cursor:crosshair}
body.marquee-on .ic{pointer-events:none}
/* Nothing in the working column is prose. The grid, the strip over it and
   the two panel heads are things you press and drag across, and a drag that
   also paints filenames and group headings blue is two gestures where one
   was asked for. The class above only arrived once the box had moved 4px,
   which in the Mac app's WebView was late enough for the text selection to
   have already started. Inputs keep their own selection. */
.gw,.tb,.ph,.sb-src{-webkit-user-select:none;user-select:none}
.gw input,.gw textarea,.sb-src input{-webkit-user-select:text;user-select:text}

/* ── CELL (floating tile) ── */
/* ── ANIMATION KEYFRAMES ── */
@keyframes thumbIn{from{opacity:0}to{opacity:1}}
@keyframes sfBounce{0%,100%{transform:scale(1)}28%{transform:scale(1.2)}58%{transform:scale(.94)}78%{transform:scale(1.07)}}
@keyframes selRing{0%{box-shadow:inset 0 0 0 2px var(--ac),0 0 0 0 var(--ab)}60%{box-shadow:inset 0 0 0 2px var(--ac),0 0 0 7px transparent}100%{box-shadow:inset 0 0 0 2px var(--ac),var(--tile-h)}}
.ic{position:absolute;left:0;top:0;cursor:pointer;overflow:hidden;background:var(--s2);border-radius:var(--rad-xs);box-shadow:var(--tile);transition:transform var(--dur) var(--ease)}
/* The placeholder's own background is inline in the markup (05-grid.js), which
   beats any plain class rule on specificity alone, so !important is the only
   way this ever reaches the element: without it a loading tile is a hole
   through to the page rather than the tile's own ground. */
.ic .ph2{background:var(--s2)!important}
.ic:hover{transform:translateY(-2px);z-index:1}
.ic:active{transform:scale(.97)}
.ic img,.ic video{width:100%;height:100%;display:block;pointer-events:none;transition:transform var(--fc-dur) cubic-bezier(.2,.7,.3,1)}
.ic img.th-in{animation:thumbIn var(--fc-dur) var(--fc-ease-out) both}
.ic:hover img,.ic:hover video{transform:scale(1.04)}
.ic img.th,.ic video.th-vid{object-fit:cover}
/* selection = orange ring + lift + brief ring-pulse */
.ic.sel{box-shadow:var(--tile-h);transform:translateY(-4px);animation:selRing .32s ease both}
/* Full 4-side ring painted on the top layer, above the thumbnail, at the frame edge. */
/* Two different jobs need two different colours. Orange says a decision has
   been made about this frame; bone says this is what you are acting on right
   now. They used to both be orange, so a kept tile and a selected tile read
   the same at a glance, which is the one thing a culling grid cannot do. */
/* Fixed bone, not the theme's ink. A ring sits on a photograph, not on the
   app's background, so it has to read on a dark frame and a bright one in
   both themes; theme ink goes near-black in light mode and disappears into
   the first underexposed shot. The dark outer hairline is what keeps it
   visible on a pale sky. */
/* Inset, not outset. The tile clips its own overflow so its thumbnail keeps
   the rounded corner, and an outer ring on a layer that fills the tile is
   drawn entirely outside that clip: a selected tile showed the blue circle
   and nothing else, which is why a drag box looked as if it selected nothing.
   Inside the edge the ring survives, the dark hairline sitting just inside
   the bone so it still reads on a pale sky. */
.ic.sel::after{content:'';position:absolute;inset:0;border-radius:var(--rad-xs);z-index:6;pointer-events:none;
  box-shadow:inset 0 0 0 2px var(--fc-photo-fg),inset 0 0 0 3px rgba(0,0,0,.55)}
/* folder-colour outline on sorted thumbnails
   An inset box-shadow is painted underneath the element's own children, and a
   tile's thumbnail is a child filling all of it. So this ring was drawn on
   every sorted tile and covered by the photograph on every sorted tile: the
   one decision the grid never showed. It is a layer of its own now, .ic-ring
   below, painted over the picture exactly as the pick ring and the selection
   ring already were. */
/* flag indicators — left-edge stripe via inset shadow */
/* Kept wears the mark colour as a full ring and keeps its weight. Dropped
   does the opposite: it recedes to 40% and loses its shadow, so a screen of
   decisions reads as "these stayed, those went" without reading a single
   label. The old green and red stripes borrowed the status colours, which in
   this system mean paid and overdue, and said nothing about the photograph. */
.ic.flag-pick{box-shadow:var(--tile)}
.ic.flag-pick::before{content:'';position:absolute;inset:0;border:2px solid var(--ac);border-radius:var(--rad-xs);z-index:5;pointer-events:none;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.45)}
.ic.flag-reject{box-shadow:none;opacity:.4;filter:saturate(.55)}
/* Dropped recedes, and says why with the one colour that means dropped. */
.ic.flag-reject::before{content:'';position:absolute;inset:0;border:2px solid var(--rj);border-radius:var(--rad-xs);z-index:5;pointer-events:none;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.45)}
.ic.flag-reject:hover{opacity:.72}
.ic.sel.flag-pick{box-shadow:var(--tile-h)}
.ic.sel.flag-reject{box-shadow:var(--tile-h);opacity:1;filter:none}
/* THE THREE RINGS, AND WHY THEY NEST RATHER THAN FIGHT
   A tile can be saying three things at once: it is going to Selects, it is a
   keeper, and it is the one you are dragging right now. Painted at the same
   inset they would simply cover each other, newest on top, and two of the
   three would be invisible — which is what happened before.
   So they sit in rings, outside in:
     .ic-ring     the folder's colour, at the frame edge. Where a photograph
                  is going is a property of the photograph, so it takes the
                  outside and never moves.
     ::before     kept or dropped, one ring in.
     ::after      what you are holding, innermost, and painted last so it wins
                  a collision with the one it shares a slot with.
   Without a folder colour the inner two slide out to the edge and nothing
   changes from how the grid looked before. */
.ic-ring{position:absolute;inset:0;z-index:4;border-radius:var(--rad-xs);pointer-events:none;
  box-shadow:inset 0 0 0 3px var(--sf-col,var(--br)),inset 0 0 0 4px rgba(0,0,0,.45)}
.ic.sf-colored.flag-pick::before,.ic.sf-colored.flag-reject::before{inset:3px;border-radius:calc(var(--rad-xs) - 1px)}
.ic.sf-colored.sel::after{inset:3px;border-radius:calc(var(--rad-xs) - 1px)}
/* Dropped recedes as a whole, ring included: the point of 40% is that the eye
   skips the tile, and a full-strength ring around a faded photograph is the
   one thing that would stop it doing that. */
.ic.sel.sf-colored.flag-reject{opacity:1;filter:none}
/* One row along the bottom edge: the name on the left, the sorted-into chip
   on the right, the name giving up width so the chip never gets clipped. */
.ic-foot{position:absolute;left:var(--fc-s-2);right:var(--fc-s-2);bottom:var(--fc-s-2);display:flex;align-items:flex-end;justify-content:space-between;gap:var(--fc-s-2);z-index:3;pointer-events:none}
/* Shown on hover only: a grid full of names reads as noise while judging a
   frame on its picture. body.names-always is the settings-panel escape hatch. */
.ic-name{position:static;flex:1 1 auto;min-width:0;font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--fc-paper-fixed);letter-spacing:.02em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:none;opacity:0;transition:opacity var(--fc-dur-tap) var(--fc-ease);text-shadow:0 1px 4px rgba(0,0,0,.7)}
.ic:hover .ic-name,.ic:focus-within .ic-name,body.names-always .ic-name{opacity:1}
@media (hover:none){.ic-name{opacity:1}}
/* Which source folder a tile came from, prefixed onto .ic-name and shown
   only when more than one is open. Inherits the mono font from .ic-name.
   Coloured one step more transparent than the filename, the photo stage's
   own secondary-text step, with a hairline after it. It ellipsises on its
   own and stops at about a third of the tile so a long source name cannot
   eat the room .ic-name already keeps for the filename. */
.ic-src{display:inline-block;max-width:33%;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap;color:var(--fc-photo-fg2);padding-right:var(--fc-s-1);
  margin-right:var(--fc-s-1);border-right:1px solid var(--fc-photo-line)}
/* selection marker — circle, fills solid orange, NO tick */
/* Four thousand of these were on screen at once, each one asking to be
   noticed, none of them telling you anything until you use it. It now waits
   for the pointer, or for the tile to actually be selected. Touch has no
   hover, so there it stays visible. */
.ic-sel{position:absolute;top:8px;right:8px;width:20px;height:20px;border:1.5px solid rgba(255,255,255,.85);background:rgba(0,0,0,.35);border-radius:var(--rad-pill);z-index:3;cursor:pointer;flex-shrink:0;
  opacity:0;transform:scale(.85);transition:opacity var(--ch-hov),transform var(--ch-hov),background var(--ch-hov),border-color var(--ch-hov)}
.ic:hover .ic-sel,.ic:focus-within .ic-sel,.ic.sel .ic-sel{opacity:1;transform:scale(1)}
@media (hover:none){.ic-sel{opacity:1;transform:none}}
.ic.sel .ic-sel{background:var(--fc-photo-ac);border-color:var(--fc-photo-ac)}
.ic-exp{display:none}
/* sort tag = folder colour background. Lives in .ic-foot now, right side. */
.st{position:static;flex:0 0 auto;max-width:50%;margin-left:auto;display:flex;align-items:center;gap:var(--fc-s-1);background:var(--stc,var(--ac));color:var(--fc-ac-on);font-size:var(--fc-fs-1);padding:var(--fc-s-1) var(--fc-s-2);border-radius:var(--rad-pill);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.02em;box-shadow:0 1px 4px rgba(0,0,0,.3)}
.st .st-dot{display:none}
/* Dark text on a light folder colour (yellow, mint, lime). Set from JS by brightness. */
.st.st-light{color:var(--fc-ink-fixed);text-shadow:none}
.dupbadge{position:static;background:var(--wn);color:var(--fc-warn-on);font-size:var(--fc-fs-1);padding:var(--fc-s-0) var(--fc-s-2);z-index:2;letter-spacing:.04em;pointer-events:none;border-radius:var(--rad-xs)}
.vp{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;background:var(--s2);gap:var(--fc-s-1)}
.vp-icon{font-size:var(--fc-fs-7);opacity:.45}.vp-ext{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--di);letter-spacing:.04em}

/* ── RIGHT PANEL: new folder + sort folders ── */
.nr{padding:var(--fc-s-2);display:flex;gap:var(--fc-s-2);flex-shrink:0}
.nr input{flex:1;background:var(--ch-sf);border:none;border-radius:var(--rad-xs);color:var(--ch-tx);font-size:var(--fc-fs-2);padding:var(--fc-s-2) var(--fc-s-3);outline:none;min-width:0;text-transform:none;letter-spacing:normal;transition:box-shadow var(--ch-hov)}
.nr input:focus{box-shadow:0 0 0 2px var(--ch-bh)}
.nr button{background:var(--ch-s2);border:none;color:var(--ch-tx);font-size:var(--fc-fs-5);padding:var(--fc-s-0) var(--fc-s-3);cursor:pointer;flex-shrink:0;border-radius:var(--rad-xs);transition:background var(--ch-hov),transform var(--ch-hov)}
.nr button:hover{background:var(--ch-bh)}
.nr button:active{transform:scale(.96)}
.sfi{display:flex;align-items:center;gap:var(--fc-s-2);padding:var(--fc-s-2);cursor:pointer;transition:background var(--ch-hov),box-shadow var(--ch-hov);position:relative;border-radius:var(--rad-xs)}
.sfi:hover{background:var(--ch-sf)}
.sfi.sf-active{background:var(--ch-s2)}
/* While dragging photos, every folder reads as a droppable target… */
body.dragging-files .sfi{box-shadow:inset 0 0 0 1px var(--ch-br)}
/* …and the one under the cursor takes a bone ring and a "drop N" label. */
.sfi.drag-over{background:var(--ch-s2);box-shadow:inset 0 0 0 2px var(--ch-tx)}
.sfi.drag-over .sfc{opacity:0}
.sfi.drag-over::after{content:'drop 'attr(data-dropn);position:absolute;right:9px;top:50%;transform:translateY(-50%);font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);letter-spacing:.04em;color:var(--ch-tx);pointer-events:none}
/* Real thumbnails now, stacked front tile first: dg-t1 painted last (see the
   .reverse() in onCellDragStart) so the top of the stack is the top of the DOM. */
.drag-ghost{position:absolute;top:-1000px;left:-1000px;width:72px;height:72px;pointer-events:none}
.drag-ghost .dg-t{position:absolute;width:56px;height:56px;object-fit:cover;background:var(--s2);border-radius:var(--rad-xs);box-shadow:0 0 0 1px var(--fc-img-edge),var(--fc-sh-soft)}
.drag-ghost .dg-t1{left:0;top:0}
.drag-ghost .dg-t2{left:var(--fc-s-2);top:var(--fc-s-2)}
.drag-ghost .dg-t3{left:var(--fc-s-4);top:var(--fc-s-4)}
.drag-ghost .dg-badge{position:absolute;right:0;top:0;min-width:20px;height:20px;padding:0 var(--fc-s-1);background:var(--ac);color:var(--fc-ac-on);border-radius:var(--rad-pill);font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);display:flex;align-items:center;justify-content:center}
.sfi.sf-bounce{animation:sfBounce .52s cubic-bezier(.36,.07,.19,.97) both;transform-origin:center}
.sfn{flex:1;font-size:var(--fc-fs-3);color:var(--ch-tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-transform:none}
.sfn-inp{flex:1;background:transparent;border:none;border-bottom:1px solid var(--ch-tx);color:var(--ch-tx);font-size:var(--fc-fs-3);outline:none;padding:0}
.sfc{font-size:var(--fc-fs-1);color:var(--fc-fg3);flex-shrink:0;min-width:20px;text-align:center;padding:var(--fc-s-0) var(--fc-s-2);background:var(--ch-sf);border-radius:var(--rad-pill);cursor:pointer;transition:background var(--ch-hov),color var(--ch-hov);font-variant-numeric:tabular-nums;margin-left:auto}
.sfc:hover{color:var(--ch-tx);background:var(--ch-s2)}
/* One chip carries the folder colour and the hotkey number both: colour on
   the left edge, transparent where a folder never had one. Replaces the old
   separate dot (.sf-dot) and hotkey badge (.sfhk). */
.sf-num{display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;padding:0 var(--fc-s-2);border-radius:var(--fc-r-box);background:var(--fc-bg3);color:var(--fc-fg);font-size:var(--fc-fs-2);font-weight:var(--fc-fw-mid);font-variant-numeric:tabular-nums;border-left:3px solid var(--sfc,transparent)}
.sfia{display:none;gap:var(--fc-s-0);flex-shrink:0}
.sfi:hover .sfia,.sfi:focus-within .sfia{display:flex}
.sfib{background:none;border:none;color:var(--ch-mu);font-size:var(--fc-fs-5);cursor:pointer;padding:var(--fc-s-0) var(--fc-s-2);transition:color var(--ch-hov),background var(--ch-hov);border-radius:var(--rad-xs);line-height:1}
.sfib:hover{color:var(--ch-tx);background:var(--ch-s2)}
.sfe{padding:var(--fc-s-5) 0;display:flex;flex-direction:column;gap:var(--fc-s-2)}
.sfe b{font-weight:var(--fc-fw);font-size:var(--fc-fs-3);color:var(--ch-tx)}
.sfe span{font-size:var(--fc-fs-2);color:var(--ch-mu);line-height:1.55}
.sfi.sub{padding-left:var(--fc-s-5)}.sfi.sub2{padding-left:var(--fc-s-6)}
.sfi .sf-tg{width:16px;cursor:pointer}.sfi .sf-tg:hover{color:var(--ch-tx)}

/* ── FOLDER ⋯ MENU ── */
.fmenu{position:fixed;z-index:999;background:var(--ch-sf);border-radius:var(--rad);min-width:184px;padding:var(--fc-s-2);box-shadow:var(--sh3);display:flex;flex-direction:column;animation:chIn var(--ch-in) both}
.fmenu button{background:none;border:none;color:var(--ch-di);font-family:var(--fc-font-body);font-size:var(--fc-fs-3);text-align:left;padding:var(--fc-s-2) var(--fc-s-3);cursor:pointer;border-radius:var(--rad-xs);transition:background var(--ch-hov),color var(--ch-hov)}
.fmenu button:hover{background:var(--ch-s2);color:var(--ch-tx)}
.fmenu button.danger{color:var(--ch-er)}
.fmenu button.danger:hover{background:var(--ch-s2)}

/* ── SORT-INTO DESTINATION ──
   Sits directly under the panel heading, because it says what every folder
   below it is. Quiet while empty — the panel still works the way it always
   has — and takes the accent line once a real folder is open, since at that
   point the rows underneath are folders that exist on somebody's disk. */
/* .rb already pads itself by --pan-x, and this padded itself by it again, so
   the two dotted boxes stood 14px inside the SORT INTO caption and the + above
   them. Every edge in this panel is one line now.

   It sits at the foot of the panel, under the list it is an alternative to,
   with a hairline above it. A rule is worth drawing here where it was not
   worth drawing on the top bar: this is a different answer to the same
   question, not the next item in a row. */
.dst{padding:var(--fc-s-3) 0 var(--fc-s-4);display:flex;flex-direction:column;gap:var(--fc-s-2);
  flex-shrink:0;border-top:1px solid var(--ch-br)}
.dst-cap{font-size:var(--fc-fs-1);line-height:1.5;color:var(--ch-mu)}
.dst-open,.dst-self{width:100%;text-align:left;background:transparent;border:1px dashed var(--ch-br);color:var(--ch-mu);font-size:var(--fc-fs-2);padding:var(--fc-s-2) var(--fc-s-3);border-radius:var(--rad-xs);cursor:pointer;transition:border-color var(--ch-hov),color var(--ch-hov),background var(--ch-hov)}
.dst-open:hover,.dst-self:hover{border-color:var(--ch-bh);color:var(--ch-tx);background:var(--ch-sf)}
.dst-open:active,.dst-self:active{transform:scale(.96)}
.dst-set{display:flex;align-items:center;gap:var(--fc-s-2);min-width:0;border:1px solid var(--ch-bh);border-radius:var(--rad-xs);padding:var(--fc-s-2) var(--fc-s-2) var(--fc-s-2) var(--fc-s-2)}
.dst-arrow{color:var(--ch-tx);flex:none;font-size:var(--fc-fs-1)}
/* The name is the control: clicking it picks a different folder. A separate
   "Change" button next to it left the name itself with about forty pixels. */
.dst-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--fc-fs-2);color:var(--ch-tx);background:transparent;border:none;padding:0;text-align:left;cursor:pointer;font-family:inherit}
.dst-name:hover{text-decoration:underline;text-underline-offset:3px}
.dst-btn{flex:none;background:transparent;border:1px solid var(--ch-br);color:var(--ch-di);font-size:var(--fc-fs-1);padding:var(--fc-s-1) var(--fc-s-2);border-radius:var(--rad-pill);cursor:pointer;transition:border-color var(--ch-hov),color var(--ch-hov)}
.dst-btn:hover{border-color:var(--ch-bh);color:var(--ch-tx)}
#dst-reconnect{border-color:var(--ch-bh);color:var(--ch-tx)}
.dst-x{flex:none;width:22px;height:22px;padding:0;border:none;background:transparent;color:var(--ch-mu);font-size:var(--fc-fs-4);line-height:1;cursor:pointer;border-radius:var(--rad-pill);display:inline-flex;align-items:center;justify-content:center;transition:background var(--ch-hov),color var(--ch-hov)}
.dst-x:hover{color:var(--ch-tx);background:var(--ch-s2)}
.dst-note{font-size:var(--fc-fs-1);line-height:1.45;color:var(--ch-mu)}
.dst-note.wn{color:var(--ch-wn)}
/* The second way in to the same panel. .dst is a stretch flex column, so
   #dst-drive-open fills the row's width the same way .dst-set already does
   with none of its own — matched here explicitly rather than left to rely on
   it, the way .dst-open/.dst-self already choose to. .dst-drive stacks
   .drive-inp (already full width and already carrying the gap under it) over
   a right-hung Go button, the same shape as the Drive modal's own .rw row. */
/* The Drive answer is the same dashed row as the two either side of it. It
   was a solid .bg button, centred and 32px tall between two 30px dashed
   rows with their text on the left, so the middle of the three was the one
   that did not line up with anything. */
#dst-drive-open{width:100%}
.dst-drive{display:flex;flex-direction:column;align-items:flex-end}

/* A row that already exists on disk reads as a fact; one you invented reads as
   a plan. The count on the right is what is already inside it, so a folder you
   are adding to never looks empty. */
.sfi.sf-real .sfn{color:var(--ch-tx)}
.sf-gly{font-size:var(--fc-fs-1);flex-shrink:0}
.sf-gly-new{color:var(--ch-tx);font-size:var(--fc-fs-1)}
/* Already on disk. Muted and unboxed, so it never competes with the pill
   beside it — that one is this session's work and is the number that matters. */
.sf-have{flex:none;font-size:var(--fc-fs-1);color:var(--ch-mu);font-variant-numeric:tabular-nums;margin-right:calc(var(--fc-s-1) * -1)}
.sfc.sfc-on{color:var(--fc-ac-on);background:var(--ac)}

/* ── EXPORT DROPDOWN ── */
.exp-wrap{position:relative;flex-shrink:0}

/* ── BACKGROUND JOBS ── a quiet status pill, not a button you press for its
   own sake, so it sits between Export and the licence disc: past the actions
   people take, short of the identity corner. Panel is the same recipe as
   .av-menu below it (bordered popover, anchored under its
   trigger) but toggles on the panel's own `hidden` attribute rather than a
   `.op` class, because fcJobsToggle() in src/data/jobs.js only flips
   el.hidden — the kit's own rule for visibility, not style.display. */
.jobs-wrap{position:fixed;left:var(--fc-s-4);bottom:var(--fc-s-4);z-index:40}
.jobs-pill{gap:var(--fc-s-1);background:var(--fc-bg);border:1px solid var(--fc-border);color:var(--fc-fg2);font-size:var(--fc-fs-2);padding:var(--fc-s-1) var(--fc-s-3);border-radius:var(--fc-r-box);box-shadow:0 1px 2px rgba(0,0,0,.08)}
.jobs-pill:hover{border-color:var(--ch-bh);background:var(--fc-bg2)}
#jobs-pill-t{font-variant-numeric:tabular-nums}
/* Blue already means "in progress" in this kit, so the dot needs no colour of
   its own to invent. Only .pulse animates, and only opacity: the motion rule
   is transform/opacity, never a repeating colour shift. fcJobsRender() adds
   .pulse while fcJobs.busy() and drops it once everything is done, so a
   lingering "Done" pill sits still rather than keep announcing activity that
   has finished. prefers-reduced-motion is handled globally, further down. */
.jobs-dot{width:7px;height:7px;border-radius:50%;background:var(--ac);flex-shrink:0}
.jobs-dot.pulse{animation:jobsPulse 1.6s ease-in-out infinite}
@keyframes jobsPulse{0%,100%{opacity:1}50%{opacity:.35}}
.jobs-panel{position:absolute;bottom:calc(100% + var(--fc-s-1));left:0;width:300px;
  max-height:min(480px,70vh);overflow-y:auto;background:var(--ch-sf);
  border-radius:var(--rad);box-shadow:var(--sh3);
  padding:var(--fc-s-1);z-index:210;display:flex;flex-direction:column;
  animation:chIn var(--ch-in) both}
/* Named .jobs-row*, not .job-row* — matched to what src/data/jobs.js (B1)
   actually renders rather than the .job-row* naming this brief was given, so
   the panel is styled, not just the pill. Flagged for the lead to reconcile;
   see the report for this task. */
.jobs-row{display:flex;flex-direction:column;gap:var(--fc-s-1);
  padding:var(--fc-s-2) var(--fc-s-3);border-bottom:1px solid var(--ch-br)}
.jobs-row:last-child{border-bottom:none}
.jobs-row-top{display:flex;align-items:baseline;justify-content:space-between;gap:var(--fc-s-2)}
.jobs-row-label{font-size:var(--fc-fs-2);color:var(--ch-tx);min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.jobs-row-note{flex-shrink:0;font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);
  letter-spacing:.04em;color:var(--ch-mu);font-variant-numeric:tabular-nums}
/* Same bar recipe as .expp-bar/.expp-barf just below: track and fill from the
   same tokens as the header hairline .pbar/.pbf, but scaling transform rather
   than width, which is what the motion hard rule actually asks for. jobs.js
   sets the fraction as an inline transform on every render, so there is no
   default scaleX here to fight it. */
.jobs-row-bar{height:2px;background:var(--ch-br);border-radius:var(--rad-pill);overflow:hidden}
.jobs-row-barf{height:100%;background:var(--ac);transform-origin:left;
  transition:transform var(--dur) var(--ease-out)}
/* Already carries .bs from jobs.js (Cancel and Retry both do) — this is only
   what .bs does not give it: content width in a stretch column. */
.jobs-row-act{align-self:flex-start}
/* Failed reads as failed two ways at once, never colour alone: the ✕ this kit
   already uses for rejected/failed everywhere else, plus the row's own text,
   since a failed job's note becomes its error message. */
.jobs-row.is-failed .jobs-row-label{color:var(--ch-er)}
.jobs-row.is-failed .jobs-row-label::before{content:'✕ '}
.jobs-row.is-failed .jobs-row-note{color:var(--ch-er)}

/* ── LIGHTBOX ── */
#lb{position:fixed;inset:0;background:var(--bg);z-index:800;display:none;flex-direction:column}
#lb.op{display:flex}
.lbt{display:flex;align-items:center;padding:var(--fc-s-3) var(--fc-s-4);flex-shrink:0;gap:var(--fc-s-3);background:transparent}
/* A filename and a position in a stack are both machine values, so they are
   mono and the digits are tabular — the counter must not jitter as you arrow
   through a card. Same treatment as the filename on a tile. */
.lbn{font-family:var(--fc-font-mono);font-size:var(--fc-fs-2);color:var(--tx);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.02em}
.lbct{font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);color:var(--di);letter-spacing:.04em;flex-shrink:0}
.lbb{flex:1;display:flex;overflow:hidden;position:relative;background:transparent;min-height:0;max-height:calc(100vh - 110px)}
.lbiw{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;overflow:hidden;cursor:default;padding:0 var(--fc-s-8)}
.lbiw.zoomed{cursor:grab;overflow:auto}
/* A click on the photo does nothing, and that includes a double click: without
   this the browser selected the picture and washed it in its selection blue. */
.lbiw,#lbi{-webkit-user-select:none;user-select:none;-webkit-user-drag:none}
/* FILL THE FRAME, DO NOT MERELY FIT INSIDE IT.
   max-width and max-height only ever shrink a picture, they never grow one, so
   anything smaller than the frame sat at its own size in the middle of a large
   black screen and read as zoomed out. That is every case where the full file
   has not arrived yet: the grid thumbnail, the preview a camera embeds, a RAW
   with nothing bigger inside it. lbFit() in 13-lightbox.js does the growing,
   in JS, by measuring the stage and setting an explicit pixel width and
   height once the picture's own shape is known — width and height here are
   auto until then, with max-width/max-height as the only guard against
   overflowing the stage on that first paint. The point of doing it this way
   rather than 100%/100% is the box this element ends up with is the
   photograph's own box, not the whole stage, so a ring drawn on it sits on
   the picture. object-fit:contain stays as a harmless backstop for the
   instant before lbFit runs. */
#lbi{width:auto;height:auto;max-width:100%;max-height:100%;object-fit:contain;display:block;transform-origin:center;transition:opacity var(--fc-dur-tap) ease;pointer-events:none;border-radius:var(--rad-xs)}
/* Bone, not orange: orange says a decision has been made, bone says this is the
   frame you are acting on — the same split the grid makes. Fixed bone rather
   than theme ink, because the ring sits on a photograph; the dark hairline is
   what keeps it visible on a pale sky. */
#lbi.lb-selected{outline:3px solid var(--fc-photo-ac);outline-offset:-3px;box-shadow:0 0 0 1px rgba(0,0,0,.55)}
/* The loupe carries the grid's decision language. app.js sets no flag class on
   the image — only on the two pills — so the ring and the recede hang off the
   pill state. Kept = the same 3px orange ring the tile wears. Dropped = the
   frame recedes to 40%, exactly as the tile does. */
#lb:has(#lb-flag-pick.pick) #lbi{outline:3px solid var(--ac);outline-offset:-3px;box-shadow:0 0 0 1px rgba(0,0,0,.45)}
#lb:has(#lb-flag-pick.pick) #lbi.lb-selected{outline-color:var(--fc-photo-ac);box-shadow:0 0 0 3px var(--ac),0 0 0 4px rgba(0,0,0,.45)}
/* No transition on the dropped state. The grid shows it instantly, so the
   loupe should too, and fading it in on every arrow keypress is motion on the
   one action a person repeats four thousand times. */
#lb:has(#lb-flag-reject.reject) .lbiw,#lb:has(#lb-flag-reject.reject) .vid-wrap{opacity:.4;filter:saturate(.55)}
#lb:has(#lb-flag-reject.reject) .lbiw:hover{opacity:.72}
#lb:has(#lb-flag-reject.reject):has(#lbi.lb-selected) .lbiw{opacity:1;filter:none}
#lbi.ld{opacity:.15}
.vid-wrap{display:none;flex-direction:column;position:absolute;inset:0}
/* The one screen a person meets when a file cannot be shown. It says what
   happened, what is still safe, and it takes the dead transport away with it. */
.vid-err{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--fc-s-3);text-align:center;padding:var(--fc-s-5);color:var(--fc-photo-fg);pointer-events:none;z-index:3}
.vid-err-ic{width:44px;height:44px;color:var(--fc-photo-fg2);opacity:.7}
.vid-err-t{font-size:var(--fc-fs-5);line-height:1.3;color:var(--fc-photo-fg)}
.vid-err-b{font-size:var(--fc-fs-3);line-height:var(--fc-lh-prose);color:var(--fc-photo-fg2);max-width:40ch;text-wrap:pretty}
/* With a frame of the clip behind it, the words move to the foot of the
   picture so the frame stays the thing on screen. */
.vid-err.on-frame{justify-content:flex-end;padding-bottom:var(--fc-s-8);background:linear-gradient(to top,rgba(10,10,10,.85),rgba(10,10,10,0) 45%)}
.vid-err.on-frame .vid-err-ic{display:none}
.vid-wrap.no-play .vc,.nle-vid-wrap.no-play .nle-tl{display:none}
.vid-wrap.op{display:flex}
#lbv{flex:1;width:100%;min-height:0;object-fit:contain;background:#000;outline:none;display:block;border-radius:var(--rad-xs)}
.vc{display:flex;align-items:center;gap:var(--fc-s-3);padding:var(--fc-s-2) var(--fc-s-4);background:var(--glass-thick);backdrop-filter:var(--glass-fx);-webkit-backdrop-filter:var(--glass-fx);border:1px solid var(--hairline);border-radius:var(--rad-pill);flex-shrink:0;margin:var(--fc-s-2) var(--fc-s-4) 0}
.vc-play{background:var(--ac);border:none;color:var(--fc-ac-on);width:32px;height:32px;border-radius:var(--rad-pill);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:var(--fc-fs-4);flex-shrink:0}
.vc-play{transition:transform var(--fc-dur-out) var(--ease)}
.vc-play:hover{transform:scale(1.06)}
.vc-play:active{transform:scale(.96)}
/* A running timecode. Tabular, or the whole control shuffles every tenth. */
.vc-time{font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);color:var(--di);letter-spacing:.02em;flex-shrink:0;white-space:nowrap}
.vc-prog{flex:1;position:relative;height:4px;background:var(--s3);border-radius:var(--rad-pill);cursor:pointer;min-width:60px;touch-action:none;outline:none}
/* The bar is 4px tall and the hand needs 40: the reach is a pseudo element. */
.vc-prog::before{content:'';position:absolute;left:0;right:0;top:-18px;bottom:-18px}
.vc-prog:focus-visible{box-shadow:0 0 0 2px var(--fc-ring)}
/* Moved with transform, never width: the bar is redrawn every frame while a
   clip plays and width is layout. */
.vc-prog-fill{position:absolute;left:0;top:0;width:100%;height:100%;background:var(--pr);pointer-events:none;border-radius:var(--rad-pill);transform:scaleX(0);transform-origin:0 50%;will-change:transform}
.vc-prog-thumb{position:absolute;top:50%;left:0;width:11px;height:11px;border-radius:var(--rad-pill);background:var(--ac);pointer-events:none;transform:translate(-50%,-50%);will-change:transform}
.vc-prog:hover .vc-prog-thumb,.vc-prog.drag .vc-prog-thumb{width:14px;height:14px}
.vc-spd-wrap{position:relative;flex-shrink:0}
.vc-spd-btn{background:var(--s2);border:none;color:var(--di);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);padding:var(--fc-s-1) var(--fc-s-3);cursor:pointer;letter-spacing:.02em;white-space:nowrap;display:flex;align-items:center;gap:var(--fc-s-1);border-radius:var(--rad-xs);transition:background var(--fc-dur-tap) var(--ease),color var(--fc-dur-tap) var(--ease),transform var(--fc-dur-tap) var(--ease)}
.vc-spd-btn:hover{color:var(--tx);background:var(--s3)}
.vc-spd-btn:active{transform:scale(.96)}
.vc-spd-drop{position:absolute;bottom:calc(100% + 6px);right:0;background:var(--glass-thick);backdrop-filter:var(--glass-fx);-webkit-backdrop-filter:var(--glass-fx);border:1px solid var(--hairline);border-radius:var(--rad-xs);min-width:74px;z-index:900;display:none;flex-direction:column;padding:var(--fc-s-1);box-shadow:var(--sh2)}
.vc-spd-drop.op{display:flex}
.vc-spd-opt{padding:var(--fc-s-2) var(--fc-s-3);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);color:var(--di);cursor:pointer;letter-spacing:.02em;transition:background var(--fc-dur-tap) var(--ease),color var(--fc-dur-tap) var(--ease);border-radius:var(--rad-xs)}
.vc-spd-opt:hover{background:var(--s2);color:var(--tx)}
.vc-spd-opt.on{color:var(--tx);background:var(--s2)}
.vc-btn{background:none;border:none;color:var(--di);font-size:var(--fc-fs-1);cursor:pointer;padding:var(--fc-s-1) var(--fc-s-2);letter-spacing:.02em;transition:color var(--fc-dur-tap) var(--ease),transform var(--fc-dur-tap) var(--ease);flex-shrink:0;border-radius:var(--rad-xs)}
.vc-btn:hover{color:var(--tx)}
.vc-btn:active{transform:scale(.96)}
.vc-btn.on{color:var(--tx)}
/* Mute and fullscreen are icons, not words: a 32px square each. */
.vc-mute,.vc-fs{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0}
.vc-ic{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.vc-mute .vc-ic-off{display:none}
.vc-mute.on .vc-ic-on{display:none}
.vc-mute.on .vc-ic-off{display:block}
.vc-fs.on{color:var(--tx)}
.la{position:absolute;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--di);width:52px;height:100px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:var(--fc-glyph);transition:color var(--fc-dur-tap),background var(--fc-dur-tap);z-index:2;padding:0;border-radius:var(--rad-xs)}
.la:hover{color:var(--tx)}
.la:active:not(:disabled){transform:translateY(-50%) scale(.96)}
.la:disabled{opacity:.15;cursor:default}
#lbp{left:8px}#lbn2{right:8px}
.lbbar{padding:var(--fc-s-3) var(--fc-s-4);flex-shrink:0;display:flex;align-items:center;gap:var(--fc-s-2);flex-wrap:wrap;background:transparent}
.lbbl{display:flex;align-items:center;gap:var(--fc-s-2);flex:1;flex-wrap:nowrap;min-width:0}
.lbrl{font-size:var(--fc-fs-1);color:var(--mu);letter-spacing:.06em;flex-shrink:0}
.lbsb{background:transparent;border:none;color:var(--di);font-size:var(--fc-fs-1);padding:var(--fc-s-2) var(--fc-s-3);cursor:pointer;white-space:nowrap;flex-shrink:0;border-radius:var(--rad-xs);transition:background var(--fc-dur-tap) var(--ease),color var(--fc-dur-tap) var(--ease),transform var(--fc-dur-tap) var(--ease)}
.lbsb:hover{background:var(--s2);color:var(--tx)}
.lbsb:active{transform:scale(.96)}
.lbsb.on{border:none;color:var(--tx);background:var(--s3)}
/* `all` swept in the border-radius and the padding on every hover. */
.lbfb{display:flex;align-items:center;gap:var(--fc-s-1);background:var(--s2);border:none;color:var(--di);font-size:var(--fc-fs-1);padding:var(--fc-s-2) var(--fc-s-3);cursor:pointer;white-space:nowrap;transition:background var(--fc-dur-tap) var(--ease),color var(--fc-dur-tap) var(--ease),transform var(--fc-dur-tap) var(--ease);border-radius:var(--rad-xs)}
.lbfb:hover{color:var(--tx);background:var(--s3)}
.lbfb:active{transform:scale(.96)}
.lbfb.on{background:var(--s3);color:var(--tx)}
.lbfb .st-dot{width:8px;height:8px;border-radius:50%;background:var(--stc,var(--ac));flex-shrink:0}
/* lightbox flag buttons — legacy outline style (used for Compare close button) */
.lb-flag-btn{background:none;border:1px solid var(--br);color:var(--di);font-size:var(--fc-fs-1);padding:var(--fc-s-1) var(--fc-s-3);cursor:pointer;border-radius:var(--rad-xs);transition:background var(--fc-dur-tap) var(--ease),color var(--fc-dur-tap) var(--ease),border-color var(--fc-dur-tap) var(--ease),transform var(--fc-dur-tap) var(--ease);font-family:var(--fc-font-body)}
.lb-flag-btn:hover{color:var(--tx);background:var(--s2);border-color:var(--bh)}
.lb-flag-btn:active{transform:scale(.96)}
/* filled flag pill — pick / reject buttons in tray and lightbox */
/* Kept wears the mark as a ring, the way the tile does. Dropped recedes, the
   way the tile does. The ink stays the working ink in both: orange measures
   3.4:1 on the light canvas, so it rings a control, it never letters one.
   These were green and red, which in this system mean paid and overdue. */
.flag-pill{display:inline-flex;align-items:center;gap:var(--fc-s-2);background:var(--s2);border:1px solid var(--br);color:var(--di);font-size:var(--fc-fs-2);padding:var(--fc-s-2) var(--fc-s-4);cursor:pointer;border-radius:var(--rad-pill);transition:background var(--fc-dur-tap) var(--ease),color var(--fc-dur-tap) var(--ease),border-color var(--fc-dur-tap) var(--ease),opacity var(--fc-dur-tap) var(--ease),transform var(--fc-dur-tap) var(--ease);font-family:var(--fc-font-body);white-space:nowrap;flex-shrink:0}
.flag-pill:hover{background:var(--s3);color:var(--tx);border-color:var(--bh)}
.flag-pill:active{transform:scale(.96)}
.flag-pill.pick{background:var(--ad);border:2px solid var(--ac);color:var(--tx);padding:var(--fc-s-1) var(--fc-s-4)}
.flag-pill.pick:hover{background:var(--ab);color:var(--tx);border-color:var(--ac)}
.flag-pill.reject{background:var(--rj-soft);border:2px solid var(--rj);color:var(--tx);padding:var(--fc-s-1) var(--fc-s-4);opacity:1}
.flag-pill.reject:hover{background:var(--rj-soft);color:var(--tx);border-color:var(--rj);opacity:1}
/* The status line sits on the working ground and remaps the chrome tokens to
   it, so these need nothing of their own beyond a quieter resting state. */
/* The same 30px box as All, Clear and Compare beside them. The pill's own
   padding made it 33px, and the marked state's 2px border took it to 34, so
   the two right-hand buttons stood a pixel or two proud of the row and the
   row read as misaligned rather than as one line of controls. */
.tb .flag-pill{background:transparent;border-color:var(--br);color:var(--di);height:30px;padding:0 var(--fc-s-4);font-size:var(--fc-fs-3)}
.tb .flag-pill:hover{background:var(--s2);border-color:var(--bh);color:var(--tx)}
.tb .flag-pill.pick{background:var(--ad);border:2px solid var(--ac);color:var(--tx);padding:0 var(--fc-s-4)}
.tb .flag-pill.reject{background:var(--rj-soft);border:2px solid var(--rj);color:var(--tx);opacity:1;padding:0 var(--fc-s-4)}
.tb .flag-pill.reject:hover{background:var(--rj-soft);color:var(--tx);border-color:var(--rj);opacity:1}
/* Orange lettering measures 3.4:1 on the light canvas. The ring keeps the mark,
   the ink stays legible, and the count is tabular so it does not jitter. */
.lbsel-badge{display:none;align-items:center;gap:var(--fc-s-1);background:var(--ad);box-shadow:inset 0 0 0 1px var(--ab);padding:var(--fc-s-1) var(--fc-s-2) var(--fc-s-1) var(--fc-s-3);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);color:var(--tx);letter-spacing:.04em;flex-shrink:0;border-radius:var(--rad-pill)}
.lbsel-badge.op{display:flex}
.lbsel-badge button{background:none;border:none;color:var(--di);cursor:pointer;font-size:var(--fc-fs-1);padding:0 0 0 var(--fc-s-0);line-height:1;transition:color var(--fc-dur-tap) var(--ease),transform var(--fc-dur-tap) var(--ease)}
.lbsel-badge button:hover{color:var(--tx)}
.lbsel-badge button:active{transform:scale(.96)}
.lbbr{flex-shrink:0;display:flex;flex-direction:column;align-items:flex-end;gap:var(--fc-s-1)}
.lbbr span{font-size:var(--fc-fs-1);color:var(--mu);letter-spacing:.02em;display:flex;align-items:center;gap:var(--fc-s-1)}
.lbbr kbd{background:var(--s2);padding:var(--fc-s-0) var(--fc-s-1);font-size:var(--fc-fs-1);color:var(--di);border-radius:var(--rad-xs)}

/* ── COMPARE OVERLAY ── */
@keyframes cmpPaneIn{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:scale(1)}}
#cmp{position:fixed;inset:0;background:var(--bg);z-index:820;display:none;flex-direction:column}
#cmp.op{display:flex}
.cmp-top{height:50px;display:flex;align-items:center;padding:0 var(--fc-s-4);gap:var(--fc-s-4);border-bottom:1px solid var(--br);flex-shrink:0}
.cmp-title{font-size:var(--fc-fs-2);color:var(--tx)}
.cmp-hint{font-size:var(--fc-fs-1);color:var(--mu);flex:1;letter-spacing:.02em}
.cmp-row{display:flex;flex:1;gap:var(--fc-s-0);overflow:hidden;background:var(--br)}
.cmp-pane{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:pointer;position:relative;overflow:hidden;padding:var(--fc-s-4);background:var(--bg);transition:background var(--fc-dur-out);gap:0;animation:cmpPaneIn .22s ease both}
.cmp-pane:hover{background:var(--s2)}
.cmp-pane:active{transform:scale(.99)}
/* Compare speaks the grid's language: kept rings orange, dropped recedes.
   .picked and .flag-pick are always set together by app.js, so the wash and the
   ring are one state, not two. */
.cmp-pane.picked{background:var(--ad)}
.cmp-pane.flag-pick{box-shadow:inset 0 0 0 3px var(--ac)}
.cmp-pane.flag-reject{box-shadow:none;opacity:.45;filter:saturate(.55)}
.cmp-pane.flag-reject:hover{opacity:.85}
.cmp-img{max-width:100%;max-height:calc(100% - 48px);object-fit:contain;border-radius:var(--rad-xs);display:block;pointer-events:none}
.cmp-meta{position:absolute;bottom:10px;left:10px;right:10px;display:flex;align-items:center;gap:var(--fc-s-2);pointer-events:none}
.cmp-name{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-shadow:0 1px 4px rgba(0,0,0,.7)}
.cmp-flag-pill{font-size:var(--fc-fs-1);padding:var(--fc-s-0) var(--fc-s-2);border-radius:var(--rad-pill);letter-spacing:.02em;flex-shrink:0;font-family:var(--fc-font-mono)}
/* Orange fill with near-black lettering measures 5.3:1 and only ever appears on
   the photo ground, which is where the brief allows an orange fill. */
.cmp-flag-pill.pick{background:var(--ac);color:var(--fc-ac-on)}
.cmp-flag-pill.reject{background:var(--s3);color:var(--tx);box-shadow:inset 0 0 0 1px var(--br)}
.cmp-kbd{position:absolute;top:10px;left:10px;font-size:var(--fc-fs-1);color:var(--di);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;letter-spacing:.06em;pointer-events:none}

/* ── FOLDER DRAWER ── */
#fd{position:fixed;inset:0;background:rgba(0,0,0,.6);backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);z-index:700;display:none;align-items:flex-end;justify-content:center}
#fd.op{display:flex}
.fdd{width:100%;max-width:920px;max-height:66vh;background:var(--glass-thick);backdrop-filter:var(--glass-fx);-webkit-backdrop-filter:var(--glass-fx);border:1px solid var(--hairline);border-bottom:none;border-radius:var(--rad-lg) var(--rad-lg) 0 0;display:flex;flex-direction:column;box-shadow:var(--sh3)}
.fdh{display:flex;align-items:center;padding:var(--fc-s-4);border-bottom:1px solid var(--br);flex-shrink:0;gap:var(--fc-s-3)}
.fdt{font-size:var(--fc-fs-3);flex:1;color:var(--tx)}.fdcnt{font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);color:var(--di)}
.fdgw{flex:1;overflow-y:auto;padding:var(--fc-s-3)}
.fdg{display:grid;grid-template-columns:repeat(auto-fill,minmax(104px,1fr));gap:var(--fc-s-3)}
.fdc2{aspect-ratio:1;position:relative;cursor:grab;overflow:hidden;background:var(--s2);border-radius:var(--rad-xs);transition:transform var(--fc-dur-tap) var(--ease),opacity var(--fc-dur-tap) var(--ease)}
.fdc2 .fd-num{position:absolute;left:5px;top:5px;z-index:3;min-width:18px;height:18px;padding:0 var(--fc-s-1);background:var(--ac);color:var(--fc-ac-on);border-radius:var(--rad-pill);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);display:flex;align-items:center;justify-content:center;pointer-events:none}
.fdc2.fd-dragging{opacity:.4;cursor:grabbing}
.fdc2.fd-over{box-shadow:inset 0 0 0 2px var(--ac);transform:translateY(-3px)}
.fdc2 img{width:100%;height:100%;object-fit:cover;display:block;transition:transform var(--fc-dur-tap);pointer-events:none}
.fdc2:hover img{transform:scale(1.05)}
.fdl{position:absolute;bottom:0;left:0;right:0;background:linear-gradient(transparent,rgba(0,0,0,.72));font-size:var(--fc-fs-1);color:rgba(255,255,255,.8);padding:var(--fc-s-3) var(--fc-s-1) var(--fc-s-1);opacity:0;transition:opacity var(--fc-dur-tap);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;letter-spacing:.02em}
.fdc2:hover .fdl{opacity:1}
.fdhint{font-size:var(--fc-fs-1);color:var(--mu);padding:var(--fc-s-2) var(--fc-s-4);border-top:1px solid var(--br);flex-shrink:0;letter-spacing:.02em}

/* ── CONTEXT MENU ── */
.ctx{position:fixed;background:var(--ch-sf);border-radius:var(--rad);padding:var(--fc-s-1);min-width:200px;z-index:999;box-shadow:var(--sh3);display:none;animation:chIn var(--ch-in) both}
.ci{padding:var(--fc-s-2) var(--fc-s-3);font-size:var(--fc-fs-2);color:var(--ch-di);cursor:pointer;display:flex;align-items:center;gap:var(--fc-s-2);transition:background var(--ch-hov),color var(--ch-hov);border-radius:var(--rad-xs)}
.ci:hover{background:var(--ch-s2);color:var(--ch-tx)}
.ci.plain{color:var(--ch-tx)}
.chr{height:1px;background:var(--ch-br);margin:var(--fc-s-1) var(--fc-s-2)}
/* Add to… opens a submenu beside the menu. */
.ci-sub{justify-content:space-between}
.ci-arrow{color:var(--ch-mu);font-size:var(--fc-fs-4);line-height:1}
.ctx-sub{min-width:220px;max-width:280px;max-height:60vh;overflow-y:auto;z-index:1000}
.ctx-sub .sf-num{min-width:20px;height:20px;padding:0 var(--fc-s-1)}
.ctx-sub .ci-nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ctx-sub-new{padding:var(--fc-s-1)}
.ctx-sub-new input{width:100%;height:30px;padding:0 var(--fc-s-2);border:1px solid var(--ch-bh);border-radius:var(--rad-xs);background:var(--ch);color:var(--ch-tx);font:inherit;font-size:var(--fc-fs-2)}

/* ── TOAST ── enters 240 ms out with a 6px rise, leaves 180 ms in */
.ts{position:fixed;bottom:40px;left:50%;transform:translateX(-50%) translateY(6px);background:var(--ch-sf);border:1px solid var(--ch-br);border-radius:var(--rad-pill);font-size:var(--fc-fs-2);padding:var(--fc-s-3) var(--fc-s-5);z-index:10020;opacity:0;transition:opacity var(--ch-out),transform var(--ch-out);pointer-events:none;max-width:min(440px,90vw);white-space:normal;text-align:center;text-wrap:balance;line-height:1.45;box-shadow:var(--sh2)}
.ts.sh{opacity:1;transform:translateX(-50%) translateY(0);transition:opacity var(--ch-in),transform var(--ch-in)}
.ts.ok{border-color:var(--ch-ok);color:var(--ch-ok)}.ts.er{border-color:var(--ch-er);color:var(--ch-er)}.ts.wn{border-color:var(--ch-wn);color:var(--ch-wn)}
.ts.er,.ts.has-act{pointer-events:auto;cursor:pointer}
.ts-act{margin-left:var(--fc-s-3);background:none;border:1px solid currentColor;color:inherit;border-radius:var(--rad-pill);padding:var(--fc-s-1) var(--fc-s-3);font:inherit;font-size:var(--fc-fs-2);cursor:pointer;transition:background var(--ch-hov)}
.ts-act:hover{background:var(--ch-s2)}

/* ── HINTS ── the spotlight, src/app/23-hints.js. Shape from the kit (.spot
   in ui/css/components.css); this is only where it sits: above the panels
   and the bar, under every sheet, dialog and the loupe, so a hint never
   argues with a modal. */
.spot-app{z-index:550}
body.welcome .spot-app{display:none}
.spot-app .bs{height:var(--fc-ctrl-h-sm);padding:0 var(--fc-s-2)}

.dv{width:1px;height:14px;background:var(--br);flex-shrink:0}
::-webkit-scrollbar{width:8px;height:8px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--bh);border-radius:var(--rad-pill);border:2px solid transparent;background-clip:padding-box}
::-webkit-scrollbar-thumb:hover{background:var(--bh)}

/* ── TROUBLESHOOT ── (the panel that used to carry its styles inline) */
/* ── TROUBLESHOOT ──────────────────────────────────────────
   A modal, like About and the Guide, because all three arrive from one group
   of one menu and used to be three different objects. It was a 310px column
   pinned under the top bar carrying a hairline under every one of its fifteen
   rows, which draws a ruled page rather than a list of things to press. Worse,
   its hover painted --ch-sf, which is the panel's own background, so putting
   the pointer on a row did nothing visible at all. Type started at 8px. */
.ts-box{background:var(--ch);border-radius:var(--rad);
  box-shadow:var(--sh3);width:min(440px,92vw);max-height:min(78vh,640px);
  display:flex;flex-direction:column;overflow:hidden;animation:chIn var(--ch-in) both}
.ts-head{display:flex;align-items:center;justify-content:space-between;
  padding:var(--fc-s-4) var(--fc-s-4) var(--fc-s-3);flex-shrink:0}
.ts-title{font-size:var(--fc-fs-5);color:var(--ch-tx)}
.ts-scroll{flex:1;overflow-y:auto;overscroll-behavior:contain;
  padding:0 var(--fc-s-2) var(--fc-s-2)}
.ts-sec{border-top:1px solid var(--fc-border);padding:var(--fc-s-3) var(--fc-s-2) var(--fc-s-1);margin-top:var(--fc-s-3);font-size:var(--fc-fs-1);color:var(--ch-mu);
  letter-spacing:.02em}
.ts-sec:first-child{border-top:0;margin-top:0;padding-top:0}
.ts-scroll>.ts-sec:first-child{padding-top:var(--fc-s-1)}
/* 12px of padding on a 13px line clears the 40px of reach that anything
   anybody presses is required to have. */
.ts-item{padding:var(--fc-s-3) var(--fc-s-2);border-radius:var(--rad-xs);cursor:pointer;
  transition:background var(--ch-hov)}
.ts-item:hover{background:var(--ch-s2)}
.ts-item:active{background:var(--ch-br)}
.ts-item:focus-visible{background:var(--ch-s2)}
.ts-q{display:flex;align-items:center;gap:var(--fc-s-2);font-size:var(--fc-fs-3);
  color:var(--ch-tx);text-transform:none}
.ts-sec .ts-ic{display:inline-block;width:14px;height:14px;margin-right:var(--fc-s-2);vertical-align:-2px;stroke:currentColor;fill:none;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
.ts-q .ts-ic{width:14px;height:14px;margin-right:var(--fc-s-2);vertical-align:-2px;stroke:currentColor;fill:none;stroke-width:1.6}
/* Says pressable without claiming what happens, which is as much as any of
   these rows can promise: each runs a check and reports what it finds. */
.ts-go{margin-left:auto;color:var(--ch-mu);opacity:0;
  transition:opacity var(--ch-hov),transform var(--ch-hov)}
.ts-item:hover .ts-go,.ts-item:focus-visible .ts-go{opacity:1;transform:translateX(2px)}
.ts-a{font-size:var(--fc-fs-2);color:var(--ch-ok);margin-top:var(--fc-s-1);display:none;
  text-transform:none;line-height:var(--fc-lh-prose)}
.ts-a.wn{color:var(--ch-wn)}.ts-a.er{color:var(--ch-er)}
.ts-foot{padding:var(--fc-s-3) var(--fc-s-4);border-top:1px solid var(--ch-br);
  font-size:var(--fc-fs-1);color:var(--ch-mu);flex-shrink:0}
/* Close button shared by the floating panels */
.pane-x{background:none;border:none;color:var(--ch-mu);cursor:pointer;font-size:var(--fc-fs-4);line-height:1;width:28px;height:28px;margin:calc(var(--fc-s-2) * -1) calc(var(--fc-s-2) * -1) calc(var(--fc-s-2) * -1) 0;border-radius:var(--rad-xs);display:inline-flex;align-items:center;justify-content:center;transition:color var(--ch-hov),background var(--ch-hov)}
.pane-x:hover{color:var(--ch-tx);background:var(--ch-s2)}

/* ── HOW TO USE ── */
/* ── GUIDE AND SHORTCUTS ───────────────────────────────────
   The same modal as About and Troubleshoot. It was a 372px panel pinned to the
   bottom right corner while About sat in the middle of the screen, which is
   two shapes for two rows of one menu. The fourth tab in here used to be
   About, repeating the About box word for word, and it is gone rather than
   kept in two places. Everything under 11px went up: 8px caps labels and 9px
   keycaps are not small, they are unread. */
.htu-box{background:var(--ch);border-radius:var(--rad);
  box-shadow:var(--sh3);width:min(560px,92vw);max-height:min(82vh,720px);
  display:flex;flex-direction:column;overflow:hidden;animation:chIn var(--ch-in) both}
.htu-head{display:flex;align-items:center;justify-content:space-between;
  padding:var(--fc-s-4) var(--fc-s-4) var(--fc-s-3);flex-shrink:0}
.htu-head-t{display:flex;align-items:baseline;gap:var(--fc-s-2);min-width:0}
.htu-title{font-size:var(--fc-fs-5);color:var(--ch-tx)}
.htu-version{font-size:var(--fc-fs-2);color:var(--ch-mu)}
/* Real buttons now rather than divs wearing role="button", so Enter and Space
   work and the focus ring lands where the tab is. A button inherits nothing
   from the page, hence font-family. */
/* Content width, not a third of the modal each. Three equal slabs with one
   filled blue reads as three buttons to choose between; a tab strip reads as
   one thing you are already inside. */
.htu-tabs{display:flex;flex-shrink:0;margin:0 var(--fc-s-2) var(--fc-s-2);gap:var(--fc-s-1);background:var(--ch-sf);border-radius:var(--rad-pill);padding:var(--fc-s-1)}
.htu-tab{flex:0 0 auto;padding:var(--fc-s-2) var(--fc-s-4);font-family:inherit;font-size:var(--fc-fs-3);
  color:var(--ch-di);background:transparent;border:1px solid transparent;cursor:pointer;
  text-align:center;border-radius:var(--rad-pill);
  transition:background var(--ch-hov),color var(--ch-hov)}
.htu-tab:hover{color:var(--ch-tx);background:var(--ch-s2)}
.htu-tab:active{transform:scale(.96)}
.htu-tab.on{color:var(--fc-ac-on);background:var(--ac)}
.htu-body{flex:1;overflow-y:auto;overscroll-behavior:contain;padding:0}
.htu-section{padding:var(--fc-s-4);border-bottom:1px solid var(--ch-br)}
.htu-section:last-child{border-bottom:none}
.htu-sec{font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em;
  margin-bottom:var(--fc-s-2)}
.htu-step{display:flex;gap:var(--fc-s-3);margin-bottom:var(--fc-s-3);align-items:flex-start}
.htu-step:last-child{margin-bottom:0}
.htu-num{width:22px;height:22px;background:var(--ac);color:var(--fc-ac-on);font-size:var(--fc-fs-1);
  display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:var(--fc-s-0);border-radius:var(--rad-pill)}
.htu-step-text{font-size:var(--fc-fs-3);color:var(--ch-di);line-height:1.7;text-transform:none;text-wrap:pretty}
.htu-step-text b{color:var(--ch-tx);font-weight:var(--fc-fw)}
.htu-find{width:100%;margin:0 0 var(--fc-s-3);padding:var(--fc-s-2) var(--fc-s-3);border:0;border-radius:var(--fc-r-box);background:var(--fc-bg3);color:var(--fc-fg);font:inherit}
.htu-kb-grid{display:grid;grid-template-columns:1fr;gap:var(--fc-s-2) var(--fc-s-5)}
@media(min-width:720px){.htu-kb-grid{grid-template-columns:1fr 1fr}}
.htu-kb-row{display:flex;gap:var(--fc-s-3);align-items:baseline}
.htu-kb-key{flex:none;display:inline-block;min-width:22px;padding:var(--fc-s-0) var(--fc-s-2);border:1px solid var(--fc-border2);border-radius:var(--rad-xs);font-size:var(--fc-fs-2);font-variant-numeric:tabular-nums;text-align:center;line-height:1.5;white-space:nowrap}
.htu-kb-desc{font-size:var(--fc-fs-2);color:var(--fc-fg2);text-transform:none}
.htu-feat-row{display:flex;align-items:flex-start;gap:var(--fc-s-3);padding:var(--fc-s-2) 0;
  border-bottom:1px solid var(--ch-br)}
.htu-feat-row:last-child{border-bottom:none}
.htu-feat-icon{font-size:var(--fc-fs-5);flex-shrink:0;margin-top:var(--fc-s-0)}
.htu-feat-info{flex:1;min-width:0}
.htu-feat-name{font-size:var(--fc-fs-3);color:var(--ch-tx)}
.htu-feat-desc{font-size:var(--fc-fs-2);color:var(--ch-mu);line-height:var(--fc-lh-prose);text-transform:none;
  margin-top:var(--fc-s-0);text-wrap:pretty}
/* The demo canvas draws a little grid, so it keeps the photo ground and the
   working tokens for its label. */
.htu-anim{width:100%;height:140px;background:var(--bg);border-bottom:1px solid var(--ch-br);
  flex-shrink:0;position:relative;overflow:hidden}
.htu-anim-label{position:absolute;bottom:var(--fc-s-2);left:50%;transform:translateX(-50%);
  font-size:var(--fc-fs-2);color:var(--mu);white-space:nowrap}

/* responsive */
.ws{min-height:0}
.sb{min-width:150px}.rb{min-width:160px}.mn{min-width:220px}

/* device gate — blue, the display face for its one title */
#mob-gate-screen{display:none;position:fixed;inset:0;background:var(--ch);z-index:9999;flex-direction:column;align-items:center;justify-content:center;gap:var(--fc-s-4);padding:var(--fc-s-6) var(--fc-s-6) calc(env(safe-area-inset-bottom,0px) + var(--fc-s-6));text-align:center;font-family:var(--fc-font-body)}
.mgate-head{display:flex;align-items:center;gap:var(--fc-s-3);margin-bottom:var(--fc-s-4)}
.mgate-logo{width:26px;height:26px;fill:var(--ch-tx)}
.mgate-wordmark{font-size:var(--fc-fs-6);color:var(--ch-tx)}
.mgate-title{font-family:var(--display);font-weight:var(--fc-fw-mid);font-size:var(--fc-fs-8);color:var(--ch-tx);letter-spacing:var(--fc-ls-tight);line-height:var(--fc-lh-snug);max-width:320px;text-wrap:balance}
.mgate-sub{font-size:var(--fc-fs-4);color:var(--ch-di);line-height:1.7;max-width:300px;margin-bottom:var(--fc-s-4);text-wrap:pretty}
.mgate-foot{display:flex;flex-direction:column;gap:var(--fc-s-1);margin-top:var(--fc-s-5)}
.mgate-foot-url{font-size:var(--fc-fs-1);color:var(--ch-mu);font-family:var(--fc-font-mono);letter-spacing:.02em;margin-top:var(--fc-s-2);text-decoration:none;border-bottom:1px solid var(--ch-br);padding-bottom:var(--fc-s-1);align-self:center}
.mgate-foot-url:hover{color:var(--ch-tx)}
@media (max-width:600px),(pointer:coarse) and (max-width:768px),(pointer:coarse) and (max-height:500px){
  #mob-gate-screen{display:flex!important}
  #app{display:none!important}
}
body.browse-only [data-desktop-only]{opacity:.4;pointer-events:none;cursor:not-allowed}
body.browse-only #browse-only-hint{display:block}
.browse-only-hint{display:none;max-width:280px;margin:var(--fc-s-4) auto 0;font-size:var(--fc-fs-1);color:var(--ch-mu);line-height:var(--fc-lh-prose);text-transform:none}

/* ═══════════ v2 FEATURES ═══════════ */
/* like heart — INK, never accent */
.ic-like{position:absolute;top:8px;left:8px;width:20px;height:20px;display:flex;align-items:center;justify-content:center;font-size:var(--fc-fs-2);color:rgba(255,255,255,.9);background:rgba(0,0,0,.3);border-radius:var(--rad-pill);z-index:3;cursor:pointer;opacity:0;transform:scale(.85);transition:opacity var(--fc-dur-tap),transform var(--fc-dur-tap),color var(--fc-dur-tap);line-height:1;text-shadow:0 1px 2px rgba(0,0,0,.6);backdrop-filter:blur(2px)}
.ic:hover .ic-like{opacity:1;transform:scale(1)}
.ic-like:hover{color:var(--fc-paper-fixed)}
.ic-like.on{opacity:1;transform:scale(1);color:var(--fc-paper-fixed);background:rgba(0,0,0,.5)}
.st{display:flex;align-items:center;gap:var(--fc-s-1)}
/* lightbox like — ink */
.lblike{background:var(--s2);border:none;color:var(--di);width:32px;height:30px;display:flex;align-items:center;justify-content:center;font-size:var(--fc-fs-3);cursor:pointer;flex-shrink:0;border-radius:var(--rad-xs);transition:background var(--fc-dur-tap) var(--ease),color var(--fc-dur-tap) var(--ease),transform var(--fc-dur-tap) var(--ease)}
.lblike:hover{color:var(--tx);background:var(--s3)}
.lblike:active{transform:scale(.96)}
.lblike.on{background:var(--s3);color:var(--tx)}
.lbfb-unsort{margin-left:var(--fc-s-1)}
/* colour picker */
.mbox{background:var(--ch);border-radius:var(--rad);padding:var(--fc-s-5);min-width:300px;max-width:380px;width:92%;box-shadow:var(--sh3);animation:chIn var(--ch-in) both}
.mh{font-size:var(--fc-fs-5);color:var(--ch-tx);margin-bottom:var(--fc-s-4);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Drop-on-panel → folder picker modal */
.rb.rb-drop{background:var(--ch-s2);box-shadow:inset 0 0 0 2px var(--ch-tx)}
.mp-box h3{font-size:var(--fc-fs-5);color:var(--ch-tx);margin-bottom:var(--fc-s-4)}
.mp-list{max-height:min(46vh,340px);overflow-y:auto;display:flex;flex-direction:column;gap:var(--fc-s-1);margin-bottom:var(--fc-s-4)}
.mp-folder{display:flex;align-items:center;gap:var(--fc-s-2);width:100%;padding:var(--fc-s-2) var(--fc-s-3);background:none;border:1px solid var(--ch-br);border-radius:var(--rad-xs);color:var(--ch-tx);font-size:var(--fc-fs-3);cursor:pointer;text-align:left;transition:background var(--ch-hov),border-color var(--ch-hov)}
.mp-folder:hover{background:var(--ch-sf);border-color:var(--ch-bh)}
.mp-folder:active{transform:scale(.96)}
.mp-folder .st-dot{width:9px;height:9px;border-radius:50%;flex-shrink:0}
.mp-nm{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mp-cnt{font-size:var(--fc-fs-1);color:var(--ch-mu);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums}
.mp-empty{font-size:var(--fc-fs-2);color:var(--ch-mu);padding:var(--fc-s-4) var(--fc-s-1);text-align:center}
.mp-new{display:flex;gap:var(--fc-s-2);margin-bottom:var(--fc-s-3)}
.mp-new input{flex:1;background:var(--ch-sf);border:1px solid var(--ch-br);border-radius:var(--rad-xs);color:var(--ch-tx);font-size:var(--fc-fs-2);padding:var(--fc-s-2) var(--fc-s-3);outline:none;text-transform:none;letter-spacing:normal;transition:border-color var(--ch-hov)}
.mp-new input:focus{border-color:var(--ch-tx)}
.cp-box .cp-swatches{display:grid;grid-template-columns:repeat(8,1fr);gap:var(--fc-s-2);margin-bottom:var(--fc-s-4)}
.cp-sw{width:100%;aspect-ratio:1;border:1px solid var(--ch-br);border-radius:var(--rad-pill);cursor:pointer;padding:0;transition:transform var(--ch-hov),box-shadow var(--ch-hov);position:relative}
.cp-sw:hover{transform:scale(1.12)}
.cp-sw.on{box-shadow:0 0 0 2px var(--ch),0 0 0 4px var(--ch-tx)}
.cp-row{display:flex;gap:var(--fc-s-2);align-items:center;margin-bottom:var(--fc-s-4)}
.cp-row input[type=color]{width:36px;height:36px;padding:0;border:1px solid var(--ch-br);border-radius:var(--rad-xs);background:var(--ch-sf);cursor:pointer;flex-shrink:0}
.cp-row input[type=color]::-webkit-color-swatch-wrapper{padding:var(--fc-s-0)}
.cp-row input[type=color]::-webkit-color-swatch{border:none;border-radius:var(--rad-xs)}
.cp-row input[type=text]{flex:1;background:var(--ch-sf);border:1px solid var(--ch-br);border-radius:var(--rad-xs);color:var(--ch-tx);font-size:var(--fc-fs-2);padding:var(--fc-s-2) var(--fc-s-3);outline:none;text-transform:none;transition:border-color var(--ch-hov),box-shadow var(--ch-hov)}
.cp-row input[type=text]:focus{border-color:var(--ch-tx);box-shadow:0 0 0 3px var(--ch-s2)}
.cp-actions{display:flex;gap:var(--fc-s-2);justify-content:flex-end}
/* export settings */
#exps-summary{font-size:var(--fc-fs-2);color:var(--ch-mu);margin-bottom:var(--fc-s-4);text-transform:none;line-height:var(--fc-lh-prose)}
.exps-field{margin-bottom:var(--fc-s-4)}
.exps-field>label,.exps-lbl{display:block;font-size:var(--fc-fs-1);color:var(--ch-mu);margin-bottom:var(--fc-s-2)}
.exps-seg{display:flex;gap:var(--fc-s-2);flex-wrap:wrap;background:var(--ch-sf);padding:var(--fc-s-1);border-radius:var(--rad-pill)}
.exps-seg .bs{flex:1;min-width:0;text-align:center;justify-content:center}
.exps-q{margin:var(--fc-s-4) 0}
.exps-q-l{font-size:var(--fc-fs-3);color:var(--fc-fg2);margin-bottom:var(--fc-s-2)}
/* A bigger, two-up version of .exps-seg for a question that needs a line of
   its own explanation under each choice, not just a word. */
.exps-seg-big{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--fc-s-2)}
.exps-seg-big .bs{display:flex;flex-direction:column;align-items:flex-start;gap:var(--fc-s-0);padding:var(--fc-s-3) var(--fc-s-3);height:auto;text-align:left;white-space:normal;min-width:0}
.exps-seg-big .bs b{font-size:var(--fc-fs-4);font-weight:var(--fc-fw-up)}
.exps-seg-big .bs span{font-size:var(--fc-fs-2);color:var(--fc-fg2);text-wrap:balance}
.exps-seg-big .bs.on span{color:inherit;opacity:.85}
/* Stands in for the name field when the export needs no wrapper folder. */
.exps-flat-note{font-size:var(--fc-fs-2);color:var(--ch-tx);text-transform:none;line-height:var(--fc-lh-prose)}
/* What the chosen destination means for how long the export takes. Sits under
   the four buttons because the difference between the two Google Drive answers
   is a minute a gigabyte against a second and a quarter, and no button label
   can carry that. */
.exps-dest-note{font-size:var(--fc-fs-2);color:var(--ch-tx);text-transform:none;line-height:var(--fc-lh-prose);margin-top:var(--fc-s-2)}
.exps-dest-note:empty{display:none}
.exps-flat-link{display:block;background:none;border:none;padding:0;margin-top:var(--fc-s-2);font:inherit;font-size:var(--fc-fs-1);color:var(--ch-mu);text-transform:none;cursor:pointer;text-decoration:underline;text-underline-offset:3px;transition:color var(--ch-hov)}
.exps-flat-link:hover{color:var(--ch-tx)}
/* What the settings above add up to. Sits on its own above the buttons, behind
   a hairline, so it reads as the result rather than as one more setting. */
.exp-live{font-size:var(--fc-fs-5);font-weight:var(--fc-fw-mid);line-height:1.45;color:var(--fc-fg);background:var(--fc-bg2);padding:var(--fc-s-3) var(--fc-s-4);border-radius:var(--fc-r-box);margin:var(--fc-s-4) 0}
.exp-inp,#exps-name{width:100%;background:var(--ch-sf);border:1px solid var(--ch-br);border-radius:var(--rad-xs);color:var(--ch-tx);font:inherit;font-size:var(--fc-fs-2);padding:var(--fc-s-3) var(--fc-s-4);outline:none;text-transform:none;transition:border-color var(--ch-hov),box-shadow var(--ch-hov)}
.exp-inp:focus,#exps-name:focus{border-color:var(--ch-tx);box-shadow:0 0 0 3px var(--ch-s2)}
#dexp-dest-link{margin-top:var(--fc-s-2)}
/* The rest of the settings, closed by default behind the live summary above. */
.exps-more{margin:var(--fc-s-2) 0 var(--fc-s-3)}
.exps-more summary{cursor:pointer;color:var(--fc-fg2);font-size:var(--fc-fs-3);list-style:none;display:flex;align-items:center;gap:var(--fc-s-2)}
.exps-more summary::before{content:'▸';font-size:var(--fc-fs-1)}
.exps-more[open] summary::before{content:'▾'}
.exps-more .exps-seg{margin-top:var(--fc-s-2)}
/* export summary — the counts are the report, so they read big and mono */
#expsum-body{margin-bottom:var(--fc-s-4)}
.expsum-rows{display:grid;gap:var(--fc-s-1);margin:var(--fc-s-3) 0}
.expsum-rows div{display:flex;justify-content:space-between;gap:var(--fc-s-4)}
.expsum-rows dt{color:var(--fc-fg2)}
.expsum-rows dd{margin:0;font-variant-numeric:tabular-nums;font-weight:var(--fc-fw-mid)}
.exsum-row{display:flex;justify-content:space-between;align-items:baseline;padding:var(--fc-s-2) 0;border-bottom:1px solid var(--ch-br);font-size:var(--fc-fs-2);color:var(--ch-di)}
.exsum-row:last-child{border-bottom:none}
.exsum-row b{color:var(--ch-tx);font-weight:var(--fc-fw);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-5)}
.exsum-row.err{color:var(--ch-er)}
.exsum-row.err b{color:var(--ch-er)}
/* failed files, named — the filename in mono, the reason in plain words */
.exsum-fails{margin-top:var(--fc-s-3);border-top:1px solid var(--ch-br);padding-top:var(--fc-s-2);max-height:180px;overflow-y:auto}
.exsum-fail{display:flex;flex-direction:column;gap:var(--fc-s-0);padding:var(--fc-s-2) 0}
.exsum-fail b{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);font-weight:var(--fc-fw);color:var(--ch-tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.exsum-fail span{font-size:var(--fc-fs-1);color:var(--ch-er)}
.exsum-fail-more{font-size:var(--fc-fs-1);color:var(--ch-mu);padding:var(--fc-s-2) 0 0}
.exsum-row.wn b{color:var(--ch-wn)}
.exsum-note{font-size:var(--fc-fs-2);color:var(--ch-di);line-height:var(--fc-lh-prose);padding:var(--fc-s-2) 0;text-transform:none}
#expsum-retry{margin-right:auto}
/* activity trail */
.act-list{max-height:min(52vh,420px);overflow-y:auto;margin-bottom:var(--fc-s-4)}
.act-row{display:flex;gap:var(--fc-s-3);align-items:baseline;padding:var(--fc-s-2) 0;border-bottom:1px solid var(--ch-br)}
.act-row:last-child{border-bottom:none}
.act-t{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em;flex-shrink:0;min-width:52px;text-align:right;font-variant-numeric:tabular-nums}
.act-m{font-size:var(--fc-fs-2);color:var(--ch-di);line-height:1.45;min-width:0;overflow-wrap:anywhere}
.act-row.act-er .act-m{color:var(--ch-er)}
.act-row.act-ok .act-m{color:var(--ch-tx)}
.act-empty{font-size:var(--fc-fs-2);color:var(--ch-mu);padding:var(--fc-s-5) 0;text-align:center}
/* ── CUTS ── one block per cut, newest first. The Activity trail's rows are
   reused inside each one, because "what happened" is the same list it always
   was, only cut into the cuts it happened in rather than running as one
   unbroken 200-row scroll nobody could place in time. */
/* The kit's segmented control counts on base.css to strip a button bare, and
   the app does not load base.css, so without this its tabs drew the browser's
   own grey fill and outset border. Only the bare-button parts: the kit's own
   rules still decide size, colour and which tab reads as chosen. */
button.seg__btn{-webkit-appearance:none;appearance:none;border:0;background-color:transparent;font-family:inherit;cursor:pointer}
#cutsov .seg{margin-bottom:var(--fc-s-3)}
/* In the archived view the group labels sit over a grid that starts at the
   card's own edge, so they drop the row indent the start screen gives them. */
#cutview .cut-label-caps{padding-left:0;padding-right:0}
.cut-list{max-height:min(58vh,480px);overflow-y:auto;margin-bottom:var(--fc-s-4)}
.cut-item{padding:var(--fc-s-3) 0;border-bottom:1px solid var(--ch-br)}
.cut-item:last-child{border-bottom:none}
.cut-item-t{display:flex;align-items:baseline;gap:var(--fc-s-2);flex-wrap:wrap}
.cut-item-t b{font-size:var(--fc-fs-3);color:var(--ch-tx);font-weight:var(--fc-fw-mid)}
.cut-item-when{font-size:var(--fc-fs-1);color:var(--ch-mu);margin-left:auto}
.cut-item-w{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em;
  margin-top:var(--fc-s-0);overflow-wrap:anywhere}
.cut-item-c{font-size:var(--fc-fs-2);color:var(--ch-di);margin-top:var(--fc-s-0)}
/* Doubles as the archived row's export summary and the active row's "Cut
   into …" folder line — same slot, same weight, whichever one a row has. */
.cut-item-f{font-size:var(--fc-fs-2);color:var(--ch-mu);margin-top:var(--fc-s-0);overflow-wrap:anywhere}
.cut-item-a{display:flex;gap:var(--fc-s-2);margin-top:var(--fc-s-3);flex-wrap:wrap}
.cut-log{margin-top:var(--fc-s-3);padding-left:var(--fc-s-3);border-left:2px solid var(--ch-br)}
/* The one row that is also the folders it is opened on right now. Ultramarine
   because that is what the kit's accent means: chosen, in progress. */
.cut-item-open{font-size:var(--fc-fs-2);color:var(--ac2);font-weight:var(--fc-fw-mid);margin-top:var(--fc-s-0)}
/* Delete for good is the only place in Cuts that removes anything, so it is
   the only place that gets the kit's dropped ink rather than the ordinary
   muted action colour every other row button uses. */
.cut-danger{color:var(--rj-ink)}
.cut-danger:hover{background:var(--rj-soft);color:var(--rj-ink)}
/* ── ARCHIVED CUT VIEW ── a read-only look back: what was exported, and small
   previews of what was kept and sorted, grouped by folder. */
.cut-view-meta{font-family:var(--fc-font-mono);font-size:var(--fc-fs-2);color:var(--ch-mu);margin-bottom:var(--fc-s-4)}
.cut-exp-list{max-height:min(30vh,220px);overflow-y:auto;margin-bottom:var(--fc-s-4)}
.cut-exp-row{display:flex;gap:var(--fc-s-3);align-items:baseline;padding:var(--fc-s-2) 0;border-bottom:1px solid var(--ch-br)}
.cut-exp-row:last-child{border-bottom:none}
.cut-exp-when{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em;flex-shrink:0}
.cut-exp-what{font-size:var(--fc-fs-2);color:var(--ch-di);min-width:0}
.cut-view-grp{margin-bottom:var(--fc-s-4)}
.cut-view-thumbs{display:grid;grid-template-columns:repeat(auto-fill,minmax(88px,1fr));gap:var(--fc-s-2);margin-top:var(--fc-s-2)}
/* Never past --fc-r-sm: a grid tile is a corner, not a rounded photograph. */
.cut-view-thumb{aspect-ratio:1/1;width:100%;object-fit:cover;border-radius:var(--fc-r-sm);
  box-shadow:0 0 0 1px var(--fc-img-edge);background:var(--ch-s2);display:block}
/* duplicate review */
.fd-tab{font-size:var(--fc-fs-1)}
.fd-tab.on{background:var(--s3);color:var(--tx)}
#fddupn{font-variant-numeric:tabular-nums;opacity:.8}
.fdreview{flex:1;overflow-y:auto;padding:var(--fc-s-3) var(--fc-s-4)}
.fdr-empty{font-size:var(--fc-fs-1);color:var(--mu);text-align:center;padding:var(--fc-s-7) 0}
.fdr-top{display:flex;align-items:center;justify-content:space-between;gap:var(--fc-s-3);padding-bottom:var(--fc-s-3);margin-bottom:var(--fc-s-3);border-bottom:1px solid var(--br)}
.fdr-top span{font-size:var(--fc-fs-1);color:var(--di)}
.fdr-grp{margin-bottom:var(--fc-s-4);background:var(--s2);border-radius:var(--rad);padding:var(--fc-s-3)}
.fdr-grp-h{display:flex;align-items:baseline;gap:var(--fc-s-2);margin-bottom:var(--fc-s-3)}
.fdr-name{font-size:var(--fc-fs-1);color:var(--tx);text-transform:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}
.fdr-cnt{font-size:var(--fc-fs-1);color:var(--wn);letter-spacing:.04em;flex-shrink:0}
.fdr-items{display:flex;gap:var(--fc-s-3);overflow-x:auto;padding-bottom:var(--fc-s-1)}
.fdr-item{width:132px;flex-shrink:0;background:var(--bg);border:1px solid var(--br);border-radius:var(--rad-xs);overflow:hidden;display:flex;flex-direction:column}
.fdr-thumb{width:100%;aspect-ratio:1;background:var(--s3);overflow:hidden;position:relative}
.fdr-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.fdr-meta{font-size:var(--fc-fs-1);color:var(--mu);padding:var(--fc-s-2);letter-spacing:.02em;display:flex;align-items:center;gap:var(--fc-s-1);border-bottom:1px solid var(--br)}
.fdr-like{color:var(--tx)}
.fdr-acts{display:flex;flex-direction:column;gap:var(--fc-s-1);padding:var(--fc-s-2)}
.fdr-acts .bs{width:100%;text-align:center;justify-content:center;font-size:var(--fc-fs-1);padding:var(--fc-s-2);background:var(--s3)}

/* ═══════════ MONO RESERVE — numbers / codes / metadata ═══════════ */
.tc,.sfc,.sfhk,.si b,.lbct,.vc-time,.vc-spd-btn,.ld-timer,
.fdr-cnt,.fdcnt,.exsum-row b,.dupbadge,
.ic-name,.lbn,.fdl,.fdr-meta,
.kh kbd,.lbbr kbd,.htu-kb-key,.htu-num,.sfhk,
#simstat{
  font-family:var(--fc-font-mono);
}


/* ── LEFT SIDEBAR COLLAPSE ──
   Snaps. Width is layout, and the old 220 ms width tween reflowed the whole
   grid on every frame of it. */
.sb.sb-collapsed{width:0!important;min-width:0!important;padding:0!important;overflow:hidden;opacity:0;pointer-events:none}
/* collapse toggle — sits on the mn (main) left edge */
.sb-tog-inner{background:none;border:none;color:var(--ch-mu);font-size:var(--fc-fs-2);cursor:pointer;width:28px;height:28px;border-radius:var(--rad-xs);transition:color var(--ch-hov),background var(--ch-hov);flex-shrink:0;line-height:1;display:inline-flex;align-items:center;justify-content:center}
.sb-tog-inner:hover{color:var(--ch-tx);background:var(--ch-s2)}
/* Open lives in the Source panel now, beside the folder it would replace.
   The top bar is for what you do to the files, not for where they came from. */
/* The folded panel's way back: big enough to find at a glance, and a little
   above centre, where the eye lands first. */
.sb-expand{position:absolute;width:26px;height:44px;left:0;top:40%;transform:none;background:var(--fc-bg2);border:1px solid var(--fc-border);border-left:0;color:var(--ch-mu);font-size:var(--fc-fs-5);cursor:pointer;display:none;align-items:center;justify-content:center;z-index:10;padding:0;border-radius:0 var(--fc-r-box) var(--fc-r-box) 0;transition:color var(--ch-hov)}
.sb-expand:hover{color:var(--ch-tx)}

/* ── OPEN-FOLDER WARNING OVERLAY ── (scrim and fade shared with .ov above) */
#warn-open-ov,#drive-ov,#dexp-ov{z-index:700}
.warn-card{background:var(--ch);border-radius:var(--rad);padding:var(--fc-s-6);width:92%;max-width:380px;box-shadow:var(--sh3);animation:chIn var(--ch-in) both;position:relative}
.wc-x{position:absolute;top:var(--fc-s-2);right:var(--fc-s-2)}
.warn-card h3{font-size:var(--fc-fs-5);margin-bottom:var(--fc-s-3);color:var(--ch-tx);text-align:center}
.warn-card p{font-size:var(--fc-fs-3);color:var(--ch-di);margin-bottom:var(--fc-s-5);line-height:1.8;text-align:center;letter-spacing:normal;text-transform:none}
/* Replace/Add/Cancel is one more button than this row was built for, so it
   wraps to two lines on a narrow window instead of squeezing. Same gap token
   either way. */
.warn-card .rw{justify-content:center;gap:var(--fc-s-3);flex-wrap:wrap}
.rw-pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--fc-s-2)}
.rw-pair .bg,.rw-pair .ba{width:100%}

/* ── DUPLICATE REVIEW ── a housekeeping sheet: same file twice, pick one.
   The thumbnails keep the photo ground; the sheet around them is chrome. */
.dup-ov{z-index:8500;padding:var(--fc-s-5)}
.dup-panel{background:var(--ch);border-radius:var(--rad-lg);width:100%;max-width:700px;max-height:88vh;display:flex;flex-direction:column;box-shadow:var(--sh3);overflow:hidden;animation:chIn var(--ch-in) both}
.dup-hdr{display:flex;align-items:flex-start;justify-content:space-between;padding:var(--fc-s-5) var(--fc-s-5) var(--fc-s-4);border-bottom:1px solid var(--ch-br);flex-shrink:0}
.dup-hdr-left{display:flex;flex-direction:column;gap:var(--fc-s-1)}
.dup-title{font-size:var(--fc-fs-5);color:var(--ch-tx);letter-spacing:.005em}
.dup-subtitle{font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em}
.dup-close{background:var(--ch-sf);border:none;color:var(--ch-di);font-size:var(--fc-fs-4);cursor:pointer;width:28px;height:28px;border-radius:var(--rad-xs);display:flex;align-items:center;justify-content:center;transition:color var(--ch-hov),background var(--ch-hov);flex-shrink:0;margin-top:var(--fc-s-0)}
.dup-close:hover{color:var(--ch-tx);background:var(--ch-s2)}
.dup-body{flex:1;overflow-y:auto;padding:var(--fc-s-4) var(--fc-s-5) var(--fc-s-2);display:flex;flex-direction:column;gap:var(--fc-s-5)}
.dup-grp{display:flex;flex-direction:column;gap:var(--fc-s-3)}
.dup-grp.dup-off{opacity:.45}
.dup-take{display:inline-flex;align-items:center;gap:var(--fc-s-2);font-size:var(--fc-fs-3);color:var(--fc-fg2);margin-right:var(--fc-s-3)}
.dup-grp-lbl{font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em;font-family:var(--fc-font-mono)}
.dup-items{display:flex;gap:var(--fc-s-3);flex-wrap:wrap}
.dup-item{flex:1;min-width:110px;max-width:180px;cursor:pointer;border-radius:var(--rad-xs);border:2px solid var(--ch-br);transition:border-color var(--ch-hov),box-shadow var(--ch-hov);overflow:hidden;background:var(--ch-sf)}
.dup-item:hover{border-color:var(--ch-bh)}
.dup-item.keep{border-color:var(--ch-ok);box-shadow:0 0 0 1px var(--ch-ok)}
.dup-img-wrap{position:relative;width:100%;aspect-ratio:1;overflow:hidden;background:var(--bg)}
.dup-img{width:100%;height:100%;object-fit:cover;display:block;transition:opacity var(--fc-dur)}
.dup-img[src=""]{opacity:0}
.dup-img-wrap .vp{position:absolute;inset:0}
.dup-badge{position:absolute;top:6px;right:6px;width:22px;height:22px;border-radius:var(--rad-pill);background:var(--ch-ok);color:var(--ch);font-size:var(--fc-fs-2);display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity var(--ch-hov);box-shadow:0 2px 6px rgba(0,0,0,.3)}
.dup-item.keep .dup-badge{opacity:1}
.dup-meta{padding:var(--fc-s-2) var(--fc-s-2) var(--fc-s-2);display:flex;flex-direction:column;gap:var(--fc-s-0)}
.dup-meta-size{font-size:var(--fc-fs-1);color:var(--ch-di);letter-spacing:.02em;font-variant-numeric:tabular-nums}
.dup-meta-path{font-size:var(--fc-fs-1);color:var(--ch-mu);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dup-keep-tag{font-size:var(--fc-fs-1);color:var(--ch-ok);letter-spacing:.02em;font-family:var(--fc-font-mono);display:none;margin-top:var(--fc-s-0)}
.dup-item.keep .dup-keep-tag{display:block}
.dup-footer{display:flex;align-items:center;gap:var(--fc-s-3);padding:var(--fc-s-4) var(--fc-s-5);border-top:1px solid var(--ch-br);flex-shrink:0}
.dup-footer-info{flex:1;font-size:var(--fc-fs-1);color:var(--ch-mu);line-height:1.5}
.dup-footer-info b{color:var(--ch-di);font-weight:var(--fc-fw)}

/* ── VIDEO THUMBNAIL INLINE PLAY BUTTON ── */
/* Lightbox video: click anywhere on video toggles play */
#lbv{cursor:pointer}

/* Clip badge on grid thumbnails — scissors icon, no background square */
/* A clip has to read as a clip from across the grid. Photography's whole
   vocabulary — a square, a filename — says nothing about a thing that has a
   length. The badge is fixed bone on a dark plate rather than a theme colour,
   because it sits on a photograph. It shows a play mark the instant the tile
   draws and gains the running time when the file has been read, so a clip is
   never mistaken for a still while it waits. */
.vidbadge{position:static;z-index:3;display:flex;align-items:center;gap:var(--fc-s-1);
  padding:var(--fc-s-1) var(--fc-s-2) var(--fc-s-1) var(--fc-s-2);border-radius:var(--rad-pill);background:rgba(0,0,0,.62);
  color:var(--fc-photo-fg);font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);font-variant-numeric:tabular-nums;
  letter-spacing:.02em;line-height:1;pointer-events:none;backdrop-filter:blur(3px)}
.vidbadge-i{width:0;height:0;flex:none;border-left:6px solid currentColor;
  border-top:4px solid transparent;border-bottom:4px solid transparent;margin-left:var(--fc-s-0)}
.clip-badge{position:static;color:rgba(255,255,255,.92);font-size:var(--fc-fs-5);pointer-events:none;z-index:3;text-shadow:0 1px 5px rgba(0,0,0,.95);line-height:1}
/* The tile's top-left row: duplicate, clip and running-time badges side by
   side, so none of them ever sits under the selection circle top-right. */
.ic-tl{position:absolute;top:6px;left:6px;z-index:3;display:flex;align-items:center;gap:var(--fc-s-1);pointer-events:none;max-width:calc(100% - 40px)}
.ic-tl:empty{display:none}

/* ── NLE FULL-SCREEN CLIP EDITOR ── */
#nle-ov{position:fixed;inset:0;background:var(--bg);z-index:8700;display:none;flex-direction:column;overflow:hidden}
#nle-ov.op{display:flex;animation:ovIn .18s ease}

/* Guide bar */
.nle-guide{display:flex;align-items:center;gap:var(--fc-s-3);padding:var(--fc-s-2) var(--fc-s-5);border-bottom:1px solid var(--br);background:var(--bg);flex-shrink:0;min-height:38px}
.nle-guide-dots{display:flex;gap:var(--fc-s-1);align-items:center;flex-shrink:0}
.nle-gdot{width:7px;height:7px;border-radius:50%;background:var(--bh);transition:background var(--fc-dur) var(--ease)}
.nle-gdot.active{background:var(--ac)}
.nle-gdot.done{background:var(--ok)}
/* "Step 2 of 3". Orange lettering measures 3.4:1 on the light canvas, so the
   step number takes the working ink and the orange stays on the dots. */
.nle-guide-step{font-size:var(--fc-fs-1);color:var(--di);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;flex-shrink:0}
/* --br is a hairline, not an ink: as text it measures 1.8:1 and is invisible. */
.nle-guide-sep{color:var(--mu);flex-shrink:0}
.nle-guide-msg{font-size:var(--fc-fs-2);color:var(--tx);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:color var(--fc-dur)}

/* Header */
.nle-hdr{display:flex;align-items:center;gap:var(--fc-s-3);padding:0 var(--fc-s-5);height:52px;border-bottom:1px solid var(--br);background:var(--bg);flex-shrink:0}
.nle-hdr-icon{font-size:var(--fc-fs-5);color:var(--ac);flex-shrink:0}
.nle-hdr-label{font-size:var(--fc-fs-2);color:var(--mu);flex-shrink:0}
.nle-hdr-fname{font-family:var(--fc-font-mono);font-size:var(--fc-fs-2);color:var(--tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0}
.nle-time-disp{font-size:var(--fc-fs-1);color:var(--di);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;flex-shrink:0}
.nle-close-btn{width:30px;height:30px;background:var(--s2);border:1px solid var(--br);color:var(--mu);cursor:pointer;font-size:var(--fc-fs-3);display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background var(--fc-dur-tap),color var(--fc-dur-tap),border-color var(--fc-dur-tap);border-radius:var(--rad-xs);line-height:1}
.nle-close-btn:hover{background:var(--s3);color:var(--tx);border-color:var(--bh)}
.nle-close-btn:active{transform:scale(.96)}

/* Body layout */
.nle-body{flex:1;min-height:0;display:flex;overflow:hidden}

/* Left column */
.nle-left{flex:1;min-width:0;display:flex;flex-direction:column;border-right:1px solid var(--br)}
.nle-vid-wrap{flex:1;min-height:0;background:#000;display:flex;align-items:center;justify-content:center;position:relative;cursor:pointer;overflow:hidden}
/* FILL THE FRAME, DO NOT MERELY FIT INSIDE IT — the same fault #lbi had.
   max-width and max-height only ever shrink, never grow. A <video> is 300x150
   until its metadata arrives, so a clip opened here sat as a small black
   rectangle in the middle of a large black panel and read as zoomed out; a
   poster or a thumbnail smaller than the panel did the same. width and height
   at 100% with object-fit contain scales to the panel and keeps the shape. */
#nle-vid{width:100%;height:100%;object-fit:contain;display:block;pointer-events:none}
.nle-play-hint{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:var(--fc-glyph-lg);color:rgba(255,255,255,.25);pointer-events:none;transition:opacity var(--fc-dur)}
.nle-vid-wrap:hover .nle-play-hint{opacity:.65}
.nle-in-badge{position:absolute;top:14px;left:14px;background:var(--ac);color:var(--fc-ac-on);font-size:var(--fc-fs-1);padding:var(--fc-s-1) var(--fc-s-3);font-family:var(--fc-font-mono);display:none;border-radius:var(--rad-pill);box-shadow:var(--glow)}
.nle-in-badge.vis{display:block}
.nle-preview-banner{position:absolute;bottom:16px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,.68);color:var(--fc-paper-fixed);font-size:var(--fc-fs-1);padding:var(--fc-s-2) var(--fc-s-4);border-radius:var(--rad-pill);pointer-events:none;display:none;white-space:nowrap;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid rgba(255,255,255,.12)}
.nle-preview-banner.vis{display:block}

/* Transport row */
.nle-transport{display:flex;align-items:center;gap:var(--fc-s-2);padding:var(--fc-s-3) var(--fc-s-5);border-top:1px solid var(--br);background:var(--bg);flex-shrink:0}
.nle-skip-btn{background:var(--s2);border:1px solid var(--br);color:var(--mu);cursor:pointer;font-size:var(--fc-fs-1);height:30px;padding:0 var(--fc-s-3);display:flex;align-items:center;gap:var(--fc-s-1);transition:background var(--fc-dur-tap),color var(--fc-dur-tap),border-color var(--fc-dur-tap);font-family:var(--fc-font-mono);flex-shrink:0;letter-spacing:.02em;border-radius:var(--rad-xs)}
.nle-skip-btn span{font-size:var(--fc-fs-1);color:var(--di)}
.nle-skip-btn:hover{background:var(--s3);color:var(--tx);border-color:var(--bh)}
.nle-skip-btn:active{transform:scale(.96)}
.nle-play-btn{width:36px;height:36px;background:var(--ac);border:none;color:var(--fc-ac-on);cursor:pointer;font-size:var(--fc-fs-4);display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:transform var(--fc-dur-out) var(--ease);letter-spacing:0;border-radius:var(--rad-pill);box-shadow:var(--glow)}
.nle-play-btn:hover{transform:scale(1.06)}
.nle-play-btn:active{transform:scale(.92)}
.nle-tran-hint{font-size:var(--fc-fs-1);color:var(--di);margin-left:auto;flex-shrink:0}

/* Mark buttons */
.nle-mark-zone{display:flex;border-top:1px solid var(--br);flex-shrink:0;background:var(--bg);padding:var(--fc-s-3) var(--fc-s-5);gap:var(--fc-s-3)}
.nle-mark-div{display:none}
.nle-mark-btn{flex:1;height:52px;background:var(--s2);border:1px solid var(--br);color:var(--tx);cursor:pointer;display:flex;align-items:center;justify-content:center;gap:var(--fc-s-3);position:relative;overflow:hidden;transition:background var(--fc-dur-out),color var(--fc-dur-out),border-color var(--fc-dur-out),transform var(--fc-dur-tap);font-family:var(--fc-font-body);border-radius:var(--rad-xs)}
.nle-mark-btn:disabled{opacity:.28;cursor:not-allowed}
.nle-mark-btn:not(:disabled):hover{background:var(--s3);border-color:var(--bh)}
.nle-mark-btn:not(:disabled):active{transform:scale(.97)}
.nle-mark-label{font-size:var(--fc-fs-3)}
.nle-mark-key{background:var(--bg);border:1px solid var(--br);color:var(--di);font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);padding:var(--fc-s-0) var(--fc-s-2);font-style:normal;position:absolute;top:8px;right:10px;border-radius:var(--rad-xs);transition:background var(--fc-dur) var(--ease),color var(--fc-dur) var(--ease),border-color var(--fc-dur) var(--ease)}
.nle-mark-btn.pulse .nle-mark-key{background:var(--ad);border-color:var(--ab);color:var(--ac)}
@keyframes nlePulse{0%,100%{background:var(--s2);border-color:var(--br)}50%{background:var(--ad);border-color:var(--ab)}}
.nle-mark-btn.pulse{color:var(--ac);animation:nlePulse 1.6s ease-in-out infinite}

/* Right panel */
.nle-right{width:288px;flex-shrink:0;display:flex;flex-direction:column;background:var(--bg);overflow:hidden}
.nle-right-hdr{display:flex;align-items:center;justify-content:space-between;padding:var(--fc-s-3) var(--fc-s-4) var(--fc-s-3);font-size:var(--fc-fs-2);color:var(--tx);border-bottom:1px solid var(--br);flex-shrink:0}
.nle-clip-count{background:var(--ac);color:var(--fc-ac-on);font-size:var(--fc-fs-1);padding:var(--fc-s-0) var(--fc-s-2);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;min-width:20px;text-align:center;border-radius:var(--rad-pill);box-shadow:var(--glow)}
.nle-clip-list{flex:1;min-height:0;overflow-y:auto;padding:var(--fc-s-3)}
.nle-clip-empty{display:flex;flex-direction:column;align-items:center;padding:var(--fc-s-6) var(--fc-s-4);gap:var(--fc-s-2);text-align:center}
/* These three read as text and were painted with the hairline token, which
   measures 1.8:1 on the canvas — invisible in both themes. */
.nle-empty-icon{font-size:var(--fc-glyph-sm);color:var(--bh)}
.nle-clip-empty span:not(.nle-empty-icon){font-size:var(--fc-fs-1);color:var(--di);line-height:var(--fc-lh-prose)}
.nle-empty-sub{font-size:var(--fc-fs-1)!important;color:var(--mu)!important}

/* Clip cards */
.nle-card{display:flex;gap:var(--fc-s-3);padding:var(--fc-s-3);border:1px solid var(--br);margin-bottom:var(--fc-s-2);background:var(--sf);transition:border-color var(--fc-dur-out) var(--ease),background var(--fc-dur-out) var(--ease),transform var(--fc-dur-tap) var(--ease);cursor:pointer;border-radius:var(--rad-xs)}
.nle-card:hover{border-color:var(--ab);background:var(--s2)}
.nle-card:active{transform:scale(.99)}
.nle-card.previewing{border-color:var(--ac);background:var(--ad)}
.nle-card-thumb{width:80px;height:48px;background:var(--s2);flex-shrink:0;overflow:hidden;display:flex;align-items:center;justify-content:center;border-radius:var(--rad-xs)}
.nle-card-thumb img{width:100%;height:100%;object-fit:cover;display:block}
.nle-card-thumb-ph{font-size:var(--fc-fs-7);color:var(--bh)}
.nle-card-info{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center}
/* Clip number, in/out range and length are all machine values, and they stack
   in a column, so they are mono and tabular or the columns will not line up. */
.nle-card-num{font-size:var(--fc-fs-1);color:var(--di);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;margin-bottom:var(--fc-s-1)}
.nle-card-range{font-size:var(--fc-fs-1);color:var(--tx);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nle-card-dur{font-size:var(--fc-fs-1);color:var(--di);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;margin-top:var(--fc-s-1)}
.nle-card-actions{display:flex;flex-direction:column;gap:var(--fc-s-1);flex-shrink:0;align-self:flex-start;margin-top:var(--fc-s-0)}
.nle-card-del{background:none;border:none;color:var(--mu);cursor:pointer;font-size:var(--fc-fs-2);padding:var(--fc-s-1) var(--fc-s-2);line-height:1;transition:color var(--fc-dur-tap),background var(--fc-dur-tap);border-radius:var(--rad-xs)}
.nle-card-del:hover{color:var(--er);background:var(--s2)}
.nle-card-del:active{transform:scale(.96)}

/* Extract button */
.nle-right-foot{padding:var(--fc-s-3) var(--fc-s-4);border-top:1px solid var(--br);flex-shrink:0}
.nle-extract-btn{width:100%;height:42px;background:var(--ac);border:none;color:var(--fc-ac-on);cursor:pointer;font-size:var(--fc-fs-4);transition:transform var(--fc-dur-out) var(--ease),opacity var(--fc-dur-tap) var(--ease);border-radius:var(--rad-xs);box-shadow:var(--glow)}
.nle-extract-btn:not(:disabled):hover{transform:translateY(-1px)}
.nle-extract-btn:not(:disabled):active{transform:scale(.97)}
.nle-extract-btn:disabled{opacity:.28;cursor:not-allowed;box-shadow:none}
.nle-foot-note{font-size:var(--fc-fs-1);color:var(--di);margin-top:var(--fc-s-2);text-align:center;line-height:1.5}
.nle-foot-dest{font-size:var(--fc-fs-1);color:var(--di);margin-top:var(--fc-s-1);text-align:center;line-height:1.5}
/* Orange lettering measures 3.4:1 on the light canvas, so the link is the ink
   with a rule under it, which is what says "link" anyway. */
.nle-foot-link{background:none;border:none;padding:0;font:inherit;color:var(--tx);cursor:pointer;text-decoration:underline;text-decoration-color:var(--ac);text-decoration-thickness:1.5px;text-underline-offset:2px;transition:color var(--fc-dur-tap) var(--ease)}
.nle-foot-link:hover{color:var(--ac)}

/* Timeline scrubber */
.nle-tl-section{flex-shrink:0;background:var(--bg);border-top:1px solid var(--br);padding:var(--fc-s-3) var(--fc-s-5) var(--fc-s-3)}
.nle-tl{position:relative;width:100%;height:72px;background:var(--s2);border:1px solid var(--br);cursor:ew-resize;overflow:hidden;user-select:none;border-radius:var(--rad-xs)}
.nle-tl-thumb-strip{position:absolute;inset:0;pointer-events:none;overflow:hidden;opacity:.55}
.nle-tl-thumb-strip img{position:absolute;top:0;height:100%;object-fit:cover;pointer-events:none}
.nle-tl-bg{position:absolute;inset:0;background:repeating-linear-gradient(90deg,transparent,transparent calc(10% - 1px),var(--br) calc(10% - 1px),var(--br) 10%);opacity:.15;pointer-events:none}
#nle-tl-segs{position:absolute;inset:0;pointer-events:none}
.nle-tl-seg{position:absolute;top:0;bottom:0;background:var(--ab);border-left:2px solid var(--ac);border-right:2px solid var(--ac);pointer-events:none}
.nle-tl-inzone{position:absolute;top:0;bottom:0;background:var(--wn-soft);border-left:2px solid var(--wn);display:none;pointer-events:none}
.nle-tl-ph{position:absolute;top:0;bottom:0;width:2px;background:rgba(255,255,255,.75);pointer-events:none;left:0%}
/* Timecodes under the scrubber. They sit over a strip of thumbnails, so they
   take a fixed bone with a dark shadow — theme ink disappears into a frame. */
.nle-tl-labels{position:absolute;bottom:0;left:0;right:0;display:flex;justify-content:space-between;padding:0 var(--fc-s-1) var(--fc-s-1);font-size:var(--fc-fs-1);color:var(--fc-photo-fg);font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;text-shadow:0 1px 4px rgba(0,0,0,.85);pointer-events:none}
.nle-tl-hint{font-size:var(--fc-fs-1);color:var(--di);margin-top:var(--fc-s-1);text-align:center}

/* Extract progress overlay */
.nle-prog-ov{position:absolute;inset:0;background:rgba(0,0,0,.88);z-index:10;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
.nle-prog-box{display:flex;flex-direction:column;align-items:center;gap:var(--fc-s-4);min-width:320px}
.nle-prog-msg{font-size:var(--fc-fs-3);color:var(--tx);font-variant-numeric:tabular-nums}
.nle-prog-track{width:320px;height:3px;background:var(--br);border-radius:var(--rad-pill);overflow:hidden}
.nle-prog-fill{height:100%;background:var(--pr);width:0%;border-radius:var(--rad-pill)}
.nle-prog-done{display:flex;flex-direction:column;align-items:center;gap:var(--fc-s-3);animation:ovIn .25s ease}
.nle-prog-check{font-size:var(--fc-glyph);color:var(--ok)}
.nle-prog-done>span{font-size:var(--fc-fs-1);color:var(--di);text-align:center;max-width:280px;line-height:1.7}

/* Destination picker sheet */
.nle-dest-sheet{position:absolute;inset:0;background:rgba(0,0,0,.78);z-index:11;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
.nle-dest-inner{background:var(--ch);border-radius:var(--rad);padding:var(--fc-s-5);width:420px;max-width:90vw;max-height:78vh;overflow-y:auto;display:flex;flex-direction:column;gap:var(--fc-s-3);box-shadow:var(--sh3);animation:chIn var(--ch-in) both}
.nle-dest-title{font-size:var(--fc-fs-5);color:var(--ch-tx);margin-bottom:var(--fc-s-0)}
.nle-dest-sub{font-size:var(--fc-fs-1);color:var(--ch-mu);margin-bottom:var(--fc-s-1)}
.nle-dest-opt{display:flex;align-items:center;gap:var(--fc-s-4);padding:var(--fc-s-3) var(--fc-s-4);border:1px solid var(--ch-br);background:var(--ch-sf);color:var(--ch-tx);cursor:pointer;font-size:var(--fc-fs-2);transition:background var(--ch-hov),border-color var(--ch-hov),transform var(--ch-hov);text-align:left;width:100%;border-radius:var(--rad-xs);font-family:var(--fc-font-body)}
.nle-dest-opt:hover{background:var(--ch-s2);border-color:var(--ch-bh)}
.nle-dest-opt:active{transform:scale(.96)}
.nle-dest-opt-icon{font-size:var(--fc-fs-6);flex-shrink:0;color:var(--ch-mu);line-height:1}
.nle-dest-opt-name{font-size:var(--fc-fs-2);color:var(--ch-tx)}
.nle-dest-opt-desc{font-size:var(--fc-fs-1);color:var(--ch-mu);margin-top:var(--fc-s-1);line-height:1.5}
.nle-dest-sep{font-size:var(--fc-fs-1);color:var(--ch-mu);font-family:var(--fc-font-mono);padding:var(--fc-s-2) 0 var(--fc-s-0)}
.nle-dest-new-btn{background:transparent;border:1px dashed var(--ch-br);color:var(--ch-mu);cursor:pointer;font-size:var(--fc-fs-1);padding:var(--fc-s-3) var(--fc-s-4);width:100%;text-align:left;transition:background var(--ch-hov),color var(--ch-hov),border-color var(--ch-hov);font-family:var(--fc-font-body);border-radius:var(--rad-xs)}
.nle-dest-new-btn:hover{background:var(--ch-sf);color:var(--ch-tx);border-color:var(--ch-bh)}
.nle-dest-new-form{display:none;flex-direction:column;gap:var(--fc-s-2)}
.nle-dest-new-form.vis{display:flex}
.nle-dest-new-input{background:var(--ch-sf);border:1px solid var(--ch-br);border-radius:var(--rad-xs);color:var(--ch-tx);font-size:var(--fc-fs-2);padding:var(--fc-s-3) var(--fc-s-4);outline:none;font-family:var(--fc-font-body);transition:border-color var(--ch-hov),box-shadow var(--ch-hov);width:100%;box-sizing:border-box}
.nle-dest-new-input:focus{border-color:var(--ch-tx);box-shadow:0 0 0 3px var(--ch-s2)}
.nle-dest-new-row{display:flex;gap:var(--fc-s-2)}

/* ── SIMILAR GROUPING ─────────────────────────────── */
#simbar{display:none;align-items:center;gap:var(--fc-s-3);padding:var(--fc-s-3) var(--fc-s-4);background:var(--ch);border-bottom:1px solid var(--ch-br);flex-shrink:0;border-radius:0 0 var(--rad-xs) var(--rad-xs);animation:tbIn var(--ch-in) both}
#simstat{font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.04em}
.simbar-sp{flex:1}
.simbar-lbl{font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em}
/* The same control as the thumbnail size slider, because it is the same
   gesture. It was a bare browser range input with an accent-color, standing
   one bar away from a fully styled one, and a mismatched pair like that is
   most of what "unfinished" means on a screen. Only the width differs: this
   one has a wider band to cover, so it gets more track to cover it on. */
#simth{width:130px}
#simbar .bs{border:1px solid var(--ch-br)}
.sim-hd{display:flex;align-items:center;gap:var(--fc-s-3);padding:var(--fc-s-4) var(--fc-s-1) var(--fc-s-2);margin-bottom:var(--fc-s-1);cursor:default}
.sim-hd:not(:first-child){margin-top:var(--fc-s-4)}
.sim-hd-t{font-size:var(--fc-fs-1);color:var(--tx);letter-spacing:.02em}
.sim-grip{color:var(--ch-mu);cursor:grab;padding-right:var(--fc-s-2)}
.sim-hd:active .sim-grip{cursor:grabbing}
/* The one header that is not a group is a stack: the count, then the sentence
   that says why these are not in one. A count on its own does not say whether
   they were looked at and passed over or never reached. */
.sim-hd-un{flex-direction:column;align-items:flex-start;gap:var(--fc-s-1);padding-bottom:var(--fc-s-3)}
.sim-hd-un .sim-hd-t{color:var(--mu)}
.sim-hd-note{font-size:var(--fc-fs-2);color:var(--mu);line-height:1.5;max-width:var(--fc-measure);text-wrap:pretty}
.sim-hd-btns{display:flex;gap:var(--fc-s-2);opacity:.55;transition:opacity var(--fc-dur-tap)}
.sim-hd:hover .sim-hd-btns{opacity:1}
.sim-hd-btns .bs{border:1px solid var(--br);font-size:var(--fc-fs-1);padding:var(--fc-s-1) var(--fc-s-3)}
.sim-merge-src{outline:1px solid var(--ac);outline-offset:2px}
.sim-merge-banner{position:sticky;top:0;z-index:6;display:flex;align-items:center;gap:var(--fc-s-4);padding:var(--fc-s-3) var(--fc-s-4);margin:var(--fc-s-2) 0 var(--fc-s-3);background:var(--bg);border:1px solid var(--ac);font-size:var(--fc-fs-2);color:var(--tx)}
.sim-merge-banner b{color:var(--ac)}
.sim-merge-banner .bs{margin-left:auto;border:1px solid var(--br)}
.sim-gwrap{position:relative}
.sim-gwrap:not(:first-child) .sim-hd{margin-top:var(--fc-s-4)}
.sim-gwrap-src{opacity:.45}
.sim-merge-hit{position:absolute;inset:0;z-index:5;display:flex;align-items:center;justify-content:center;cursor:pointer;background:transparent;transition:background var(--fc-dur-tap),outline-color var(--fc-dur-tap);outline:1px solid transparent;outline-offset:-1px}
.sim-merge-hit span{opacity:0;padding:var(--fc-s-2) var(--fc-s-4);background:var(--ac);color:var(--fc-ac-on);font-size:var(--fc-fs-2);letter-spacing:.02em;transition:opacity var(--fc-dur-tap);pointer-events:none}
.sim-merge-hit:hover{background:var(--rj-soft);outline-color:var(--rj)}
.sim-merge-hit:hover span{opacity:1}
.qc-card{max-width:560px;width:92vw}
#qc-list{max-height:52vh;overflow-y:auto;margin:var(--fc-s-4) 0;border:1px solid var(--ch-br);border-radius:var(--rad-xs)}
.qc-item{display:flex;align-items:center;gap:var(--fc-s-3);padding:var(--fc-s-2) var(--fc-s-3);cursor:pointer;border-bottom:1px solid var(--ch-br);transition:background var(--ch-hov)}
.qc-item:last-child{border-bottom:none}
.qc-item:hover{background:var(--ch-sf)}
.qc-item img{width:72px;height:48px;object-fit:cover;flex:none;background:var(--bg);border-radius:var(--rad-xs)}
.qc-noimg{width:72px;height:48px;flex:none;background:var(--bg);border:1px solid var(--ch-br);border-radius:var(--rad-xs)}
.qc-nm{font-size:var(--fc-fs-2);color:var(--ch-tx);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-family:var(--fc-font-mono);font-size:var(--fc-fs-1)}
.qc-why{font-size:var(--fc-fs-1);color:var(--ch-wn);flex:none;letter-spacing:.02em}
.sim-kick{position:absolute;top:6px;left:6px;z-index:5;width:22px;height:22px;border:1px solid var(--hairline);background:rgba(0,0,0,.55);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);color:var(--tx);font-size:var(--fc-fs-1);cursor:pointer;border-radius:var(--rad-xs);opacity:0;transition:opacity var(--fc-dur-tap);line-height:1}
.ic:hover .sim-kick{opacity:1}
.sim-kick:hover{border-color:var(--ac);color:var(--ac)}

/* ── DRIVE LINK LOADING ── (scrims shared with .ov above) */
.drive-card{max-width:420px}
.drive-inp{width:100%;background:var(--ch-sf);border:1px solid var(--ch-br);border-radius:var(--rad-xs);color:var(--ch-tx);font:inherit;font-size:var(--fc-fs-3);padding:var(--fc-s-3);margin-bottom:var(--fc-s-3);outline:none;transition:border-color var(--ch-hov),box-shadow var(--ch-hov)}
.drive-inp:focus{border-color:var(--ch-tx);box-shadow:0 0 0 3px var(--ch-s2)}
.drive-err{font-size:var(--fc-fs-2);color:var(--ch-er);line-height:var(--fc-lh-prose);min-height:0;margin-bottom:var(--fc-s-2);text-align:center}
.drive-err:not(:empty){margin-bottom:var(--fc-s-4)}
.drive-key-change{display:block;text-align:center;font-size:var(--fc-fs-1);color:var(--ch-di);margin-top:var(--fc-s-4);cursor:pointer;text-decoration:underline;text-underline-offset:3px;transition:color var(--ch-hov)}
.drive-key-change:hover{color:var(--ch-tx)}
/* The Drive export card is now the local Export card — same rows, same rhythm —
   so all it needs of its own is an error line that agrees with the layout. */
#dexp-ov .drive-err{text-align:left;margin-bottom:0}
#dexp-ov .drive-err:not(:empty){margin-bottom:var(--fc-s-4)}
/* The confirm sits above every modal, the Drive ones at 700 and the clip
   editor at 8700 included, and below the licence gate at 9000. A warning
   hidden behind the card it belongs to is worse than no warning. */
#_cfm-ov{z-index:8900}

/* Lightbox skeleton — shimmer while the full-res image is still arriving.
   Was its own animation on background-position, which repaints the whole
   overlay every frame; it is the kit's fc-sweep now, which moves a transform
   and nothing else. The wash stays white rather than --fc-bg3 because this
   one sits on the photo stage, where a grey step would read as fog over the
   picture. Reduced motion is handled by the kit's own guard. */
.lbiw.lb-sk::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.07),transparent);
  transform:translateX(-100%);
  animation:fc-sweep 1.4s var(--fc-ease) infinite;
}
@media (prefers-reduced-motion:reduce){ .lbiw.lb-sk::after{animation:none} }

/* ── LICENCE GATE ─────────────────────────────────────────
   Covers everything until the device is activated. */
#lic-gate{position:fixed;inset:0;z-index:9000;background:var(--ch);display:none;align-items:center;justify-content:center;padding:var(--fc-s-5)}
#lic-gate.op{display:flex}
.lic-box{width:100%;max-width:420px;text-align:left}
.lic-mark{font-size:var(--fc-fs-1);letter-spacing:.02em;color:var(--ch-mu);font-family:var(--fc-font-mono);margin-bottom:var(--fc-s-6)}
/* The gate is the first full screen a buyer ever sees, so it carries the
   display face like the other full-screen moments do: the loading screen,
   the empty state and the phone gate. */
.lic-h{font-family:var(--display);font-weight:var(--fc-fw-mid);font-size:var(--fc-fs-8);color:var(--ch-tx);letter-spacing:var(--fc-ls-tight);margin:0 0 var(--fc-s-3);line-height:var(--fc-lh-snug);text-wrap:balance}
.lic-sub{font-size:var(--fc-fs-3);color:var(--ch-di);line-height:var(--fc-lh-prose);margin:0 0 var(--fc-s-5);text-wrap:pretty}
#lic-inp{width:100%;background:var(--ch-sf);border:1px solid var(--ch-br);border-radius:var(--rad-xs);color:var(--ch-tx);padding:var(--fc-s-3) var(--fc-s-4);font-size:var(--fc-fs-4);font-family:var(--fc-font-mono);letter-spacing:.01em;outline:none;transition:border-color var(--ch-hov),box-shadow var(--ch-hov)}
#lic-inp:focus{border-color:var(--ch-tx);box-shadow:0 0 0 3px var(--ch-s2)}
#lic-inp::placeholder{letter-spacing:.08em}
.lic-err{font-size:var(--fc-fs-2);color:var(--ch-er);min-height:18px;margin:var(--fc-s-2) 0 0;line-height:1.5}
/* The key button is the outlined one: the free week above it is the solid
   button, so a first visit meets one primary action, not two. */
/* The key is the main way in now, so Activate is the filled button and the
   free week's button under it is the quiet one. */
.lic-btn{width:100%;margin-top:var(--fc-s-3);padding:var(--fc-s-3);background:var(--ch-tx);border:1px solid var(--ch-tx);color:var(--ch);box-shadow:none;font-size:var(--fc-fs-4)}
.lic-btn:hover{box-shadow:0 0 0 4px var(--ch-s2);background:var(--ch-tx);color:var(--ch);border-color:var(--ch-tx)}
.lic-key{margin-top:var(--fc-s-3)}
.lic-link{background:none;border:none;padding:0;font:inherit;font-size:var(--fc-fs-3);color:var(--ch-tx);cursor:pointer;text-decoration:underline;text-underline-offset:3px;transition:color var(--ch-hov)}
.lic-link:hover{color:var(--ch-di)}
.lic-note{font-size:var(--fc-fs-2);color:var(--ch-mu);margin:var(--fc-s-3) 0 0;line-height:1.5}
.lic-foot{font-size:var(--fc-fs-1);color:var(--ch-mu);margin:var(--fc-s-5) 0 0;letter-spacing:.02em;line-height:1.7}
.lic-foot a{color:var(--ch-tx);text-decoration:none;border-bottom:1px solid var(--ch-br);padding-bottom:var(--fc-s-0);transition:border-color var(--ch-hov)}
.lic-foot a:hover{border-color:var(--ch-tx)}
.lic-foot-links{margin-top:var(--fc-s-5)}
.lic-trial{display:flex;flex-direction:column;gap:var(--fc-s-2);margin-top:var(--fc-s-4)}
.lic-trial[hidden]{display:none}
.lic-trial input{width:100%;background:var(--ch-sf);border:1px solid var(--ch-br);border-radius:var(--rad-xs);color:var(--ch-tx);padding:var(--fc-s-3) var(--fc-s-4);font-size:var(--fc-fs-4);font-family:inherit;outline:none;transition:border-color var(--ch-hov),box-shadow var(--ch-hov)}
.lic-trial input:focus{border-color:var(--ch-tx);box-shadow:0 0 0 3px var(--ch-s2)}
.lic-t-btn{width:100%;margin-top:var(--fc-s-1);padding:var(--fc-s-3);background:transparent;border:1px solid var(--ch-bh);border-radius:var(--rad-xs);color:var(--ch-tx);font-family:inherit;font-size:var(--fc-fs-4);cursor:pointer;transition:box-shadow var(--ch-hov),transform var(--ch-hov),opacity var(--ch-hov)}
.lic-t-btn:hover{box-shadow:0 0 0 4px var(--ch-s2)}
.lic-t-btn:active{transform:scale(.96)}
.lic-t-btn:disabled{opacity:.5;cursor:default;box-shadow:none;transform:none}
body.lic-locked #app,body.lic-locked #mob-gate-screen{display:none!important}

/* ── FIRST RUN ─────────────────────────────────────────────
   The same surface as the licence gate, because it follows it: the page
   itself, one box, left-aligned, the display face on the heading. Three
   cards swap in place with a small rise. Sits under the gate, over the app. */
#onb{position:fixed;inset:0;z-index:8900;background:var(--ch);display:none;align-items:center;justify-content:center;padding:var(--fc-s-5)}
#onb.op{display:flex}
.onb-box{width:100%;max-width:420px;text-align:left}
.onb-mark{display:flex;align-items:center;justify-content:space-between;font-size:var(--fc-fs-2);letter-spacing:.02em;color:var(--ch-mu);font-family:var(--fc-font-mono);margin-bottom:var(--fc-s-6)}
.onb-stepn{letter-spacing:.08em;font-variant-numeric:tabular-nums}
.onb-card{min-height:280px}
.onb-card.onb-in>*{animation:onbRise var(--fc-dur) var(--fc-ease-out) both}
.onb-card.onb-in>:nth-child(2){animation-delay:var(--fc-stagger)}
.onb-card.onb-in>:nth-child(3){animation-delay:calc(var(--fc-stagger)*2)}
.onb-card.onb-in>:nth-child(4){animation-delay:calc(var(--fc-stagger)*3)}
.onb-card.onb-in>:nth-child(5){animation-delay:calc(var(--fc-stagger)*4)}
@keyframes onbRise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.onb-h{font-family:var(--display);font-weight:var(--fc-fw-mid);font-size:var(--fc-fs-8);color:var(--ch-tx);letter-spacing:var(--fc-ls-tight);margin:0 0 var(--fc-s-3);line-height:var(--fc-lh-snug);text-wrap:balance}
.onb-sub{font-size:var(--fc-fs-3);color:var(--ch-di);line-height:var(--fc-lh-prose);margin:0 0 var(--fc-s-5);text-wrap:pretty;max-width:var(--fc-measure)}
.onb-actions{display:flex;flex-direction:column;gap:var(--fc-s-2)}
.onb-btn{width:100%;min-height:var(--fc-ctrl-h);padding:var(--fc-s-3);background:var(--ch-tx);border:1px solid var(--ch-tx);border-radius:var(--rad-pill);color:var(--ch);font-family:inherit;font-size:var(--fc-fs-4);cursor:pointer;transition:box-shadow var(--fc-dur-tap) var(--fc-ease),transform var(--fc-dur-tap) var(--fc-ease),opacity var(--fc-dur-tap) var(--fc-ease),background var(--fc-dur-tap) var(--fc-ease),color var(--fc-dur-tap) var(--fc-ease)}
.onb-btn:hover{box-shadow:0 0 0 4px var(--ch-s2)}
.onb-btn:active{transform:scale(.96)}
.onb-btn:focus-visible{outline:2px solid var(--fc-ring);outline-offset:2px}
.onb-btn:disabled{opacity:.5;cursor:default;box-shadow:none;transform:none}
.onb-btn[disabled]{opacity:.45;cursor:default}
/* A quiet way out of a step that is not the one Continue button. */
.onb-later{display:block;margin:var(--fc-s-3) auto 0;background:none;border:0;color:var(--fc-fg2);font:inherit;font-size:var(--fc-fs-3);cursor:pointer;text-decoration:underline;text-underline-offset:3px}
.onb-later:hover{color:var(--fc-fg)}
.onb-done{display:flex;align-items:center;gap:var(--fc-s-2);font-size:var(--fc-fs-3);color:var(--ch-tx);margin:0 0 var(--fc-s-4);padding:var(--fc-s-3) var(--fc-s-4);border:1px solid var(--ch-br);border-radius:var(--rad-xs);background:var(--ch-sf)}
.onb-done b{font-weight:var(--fc-fw-mid);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.onb-tick{width:8px;height:8px;border-radius:var(--rad-pill);background:var(--ok);flex:none}
.onb-err{font-size:var(--fc-fs-2);color:var(--ch-er);min-height:18px;margin:0 0 var(--fc-s-2);line-height:1.5}
.onb-dots{display:flex;gap:var(--fc-s-2);margin-top:var(--fc-s-6)}
.onb-dots i{display:block;width:6px;height:6px;border-radius:var(--rad-pill);background:var(--ch-bh);transition:transform var(--fc-dur) var(--fc-ease-out),background var(--fc-dur-tap) var(--fc-ease)}
.onb-dots i.on{background:var(--ac);transform:scaleX(3);transform-origin:left;margin-right:var(--fc-s-3)}

/* ── REDUCED MOTION ────────────────────────────────────────
   The public pages already honour this; the app did not, so the shimmer,
   the mark pulse and the spring bounces kept running for anyone who has
   asked their system to stop animations. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    scroll-behavior:auto!important;
  }
}

/* ── WELCOME MODE ──────────────────────────────────────────
   Before any folder is open, the side panels and the sort/filter controls
   have nothing to act on — an empty tree on the left and an empty folder
   list on the right just read as dead space. So until something is open,
   the app is one centred welcome with one action. finishLoad() lifts the
   class the moment real content exists. */
body.welcome .sb,
body.welcome .rb,
body.welcome .sb-expand,
body.welcome .top-c,
body.welcome .top-slider-wrap,
body.welcome .exp-wrap{display:none!important}
/* .top-c normally flex-grows as the spacer between the two ends of the bar —
   with it hidden the right-hand controls would drift left. */
body.welcome .tr{margin-left:auto}
/* Nothing open yet to name or act on: the mark, the card name and the two
   view toggles go quiet along with the panels. .av-wrap stays — the licence
   chip is not tied to a folder — and .tr's margin-left:auto above already
   keeps it right-aligned. */
body.welcome .top .logo,
body.welcome .top .cut-wrap,
body.welcome #btnsim,
body.welcome #btnqc{display:none!important}
body.welcome .top{background:transparent;border-bottom:0;box-shadow:none}
body.welcome .jobs-wrap{display:none}

/* ── AVATAR + ACCOUNT MENU ─────────────────────────────────
   Initials of the licence holder, top right. Everything account-shaped
   lives behind it: licence, about, report a bug, sign out. */
.av-wrap{position:relative;flex-shrink:0}
/* Was a 34px filled circle with the holder's initials in it, which is the one
   shape on a screen that means account, and FieldCut has no accounts. It is a
   chip that says what the licence IS, in one line of ordinary type with no
   edge until you go near it. Three weights in this corner now and they climb:
   the question and the licence, then Open, then Export. A licence is a fact
   you glance at, not a thing you press, so it is the last to ask for
   attention and the first to give it back. */
/* A 34px disc with the holder's initials. It was a word chip for a while, on
   the argument that a disc of initials is the one shape that means account,
   and FieldCut has none. A word in this corner reads as a fourth button beside
   Open and Export, and the corner has room for two. So: a shape, but the
   quietest one available — surface fill, hairline edge, ink text, never the
   accent — so it is still the last thing in the corner to ask for attention.
   Two capitals at 10.5px is a caps label like any other, so it takes the one
   weight step the type scale allows rather than trying to be bigger. */
.av{width:34px;height:34px;flex:0 0 34px;padding:0;border-radius:var(--rad-pill);
  background:var(--ch-s2);border:1px solid var(--ch-br);
  font-family:var(--fc-font-mono);font-weight:var(--fc-fw-label);
  font-size:var(--fc-fs-1);letter-spacing:.01em;
  color:var(--ch-tx);cursor:pointer;white-space:nowrap;
  overflow:hidden;
  display:flex;align-items:center;justify-content:center;line-height:1;
  transition:border-color var(--ch-hov),background var(--ch-hov),color var(--ch-hov),transform var(--ch-hov)}
.av:hover{border-color:var(--ch-bh);background:var(--ch-sf)}
.av:active{transform:scale(.96)}
/* No key on this machine. A dash inside a dashed edge says "nothing here"
   faster than any word did, and it does not rely on colour to say it. */
.av-none{background:transparent;border-style:dashed;color:var(--ch-mu);
  letter-spacing:0;font-size:var(--fc-fs-3)}
.av-none:hover{color:var(--ch-tx)}
.av-menu{position:absolute;top:calc(100% + var(--fc-s-1));right:0;min-width:210px;background:var(--ch-sf);
  border-radius:var(--rad);box-shadow:var(--sh3);
  padding:var(--fc-s-2);display:none;flex-direction:column;z-index:210;animation:chIn var(--ch-in) both}
.av-menu.op{display:flex}
/* Two lines of grey type is the same weight as the menu rows under it, so the
   one fact somebody opens this menu to check did not read as a fact at all.
   It is a card now: a surface of its own, the plan as a badge on the same
   line as the holder, and the key itself, which until now could not be read
   back off this machine at all once it had been typed in once. */
.av-lic{display:flex;flex-direction:column;gap:var(--fc-s-1);padding:var(--fc-s-3);
  background:var(--ch-s2);border-radius:var(--rad-xs);margin-bottom:var(--fc-s-2)}
.av-lic-top{display:flex;align-items:baseline;gap:var(--fc-s-2)}
.av-lic-name{flex:1;min-width:0;font-size:var(--fc-fs-3);color:var(--ch-tx);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.av-lic-plan{font-family:var(--fc-font-mono);font-weight:var(--fc-fw-label);
  font-size:var(--fc-fs-1);letter-spacing:.02em;
  color:var(--ch-mu);border:1px solid var(--ch-br);
  border-radius:var(--rad-pill);padding:var(--fc-s-0) var(--fc-s-2);line-height:1;white-space:nowrap;flex-shrink:0}
.av-lic-sub{font-size:var(--fc-fs-1);color:var(--ch-mu);letter-spacing:.02em;line-height:1.5}
/* The key is a row you press, not a row you read: the whole thing is the copy
   button, so there is no separate little icon to aim at. */
.av-lic-key{display:flex;align-items:center;gap:var(--fc-s-2);width:100%;
  background:var(--ch);border:1px solid var(--ch-br);border-radius:var(--rad-xs);
  padding:var(--fc-s-2);margin-top:var(--fc-s-0);cursor:pointer;text-align:left;
  transition:border-color var(--ch-hov),background var(--ch-hov)}
.av-lic-key:hover{border-color:var(--ch-bh)}
.av-lic-key-v{flex:1;min-width:0;font-family:var(--fc-font-mono);font-variant-numeric:tabular-nums;font-size:var(--fc-fs-1);
  letter-spacing:.06em;color:var(--ch-di);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.av-lic-key-a{font-family:var(--fc-font-mono);font-weight:var(--fc-fw-label);font-size:var(--fc-fs-1);letter-spacing:.02em;
  color:var(--ch-mu);flex-shrink:0}
.av-lic-key:hover .av-lic-key-a{color:var(--ch-tx)}
/* The menu is grouped, not listed. A hairline is the whole device: four
   groups of two or three read instantly, thirteen flat rows do not. */
.av-sep{height:1px;margin:var(--fc-s-2) 0;background:var(--ch-br)}
.av-item{display:flex;align-items:center;gap:var(--fc-s-2);width:100%;text-align:left;
  background:none;border:none;white-space:nowrap;
  color:var(--ch-di);font-family:var(--fc-font-body);font-size:var(--fc-fs-3);
  padding:var(--fc-s-2) var(--fc-s-3);cursor:pointer;border-radius:var(--rad-xs);
  text-decoration:none;transition:background var(--ch-hov),color var(--ch-hov)}
a.av-item{box-sizing:border-box}
.av-item:hover{background:var(--ch-s2);color:var(--ch-tx)}
/* An icon per row so the menu is scanned rather than read. They are drawn at
   the same optical weight as the type beside them, a hairline, never filled:
   a filled glyph next to 12px text reads as a second, louder column. */
.av-ic{width:15px;height:15px;flex:0 0 15px;fill:none;stroke:currentColor;
  stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round;opacity:.78}
.av-item:hover .av-ic{opacity:1}
.av-item-out .av-ic{opacity:.7}

/* ── ABOUT ───────────────────────────────────────────────── */
#about-ov,#bug-ov{z-index:300}
/* The box grew when the manual, support, setting up again and the update check
   moved in here out of the licence menu, then grew again when the release notes
   arrived, and it ended up a page rather than a box. The notes fold away now,
   so what is left standing open is the version, which is what anybody opens
   this for. It is still capped at the window and scrolls inside itself, so
   Close cannot fall below the bottom edge on a short laptop screen. */
.about-box{background:var(--ch);
  border-radius:var(--rad);box-shadow:var(--sh3);padding:var(--fc-s-6) var(--fc-s-6) var(--fc-s-5);
  max-width:420px;width:90%;text-align:center;
  max-height:88vh;overflow-y:auto;overscroll-behavior:contain;
  animation:chIn var(--ch-in) both}
.about-mark{width:40px;height:40px;fill:var(--ch-tx);margin:0 auto var(--fc-s-4);display:block}
.about-name{font-size:var(--fc-fs-6);color:var(--ch-tx);margin-bottom:var(--fc-s-1)}
/* The one fact this box exists to carry, and it used to be set at 9px in caps
   with letters pushed apart, which is a decoration of a version number rather
   than a version number. */
.about-ver{font-size:var(--fc-fs-3);color:var(--ch-di);margin-bottom:var(--fc-s-4)}
.about-line{font-size:var(--fc-fs-2);color:var(--ch-di);line-height:1.7;margin:0 0 var(--fc-s-4)}
.about-links{display:flex;flex-direction:column;gap:var(--fc-s-1);margin-bottom:var(--fc-s-5)}
.about-links a{font-size:var(--fc-fs-2);color:var(--ch-di);text-decoration:none;
  border-bottom:1px solid var(--ch-br);align-self:center;padding-bottom:var(--fc-s-0);transition:color var(--ch-hov),border-color var(--ch-hov)}
.about-links a:hover{color:var(--ch-tx);border-color:var(--ch-bh)}
.about-by{font-size:var(--fc-fs-1);color:var(--ch-mu)}
.about-close{width:100%}

/* What changed, behind a line you press. Three releases is four hundred words,
   and it used to stand open under the version, which is why the box became
   something to scroll rather than something to read. The list reads left while
   the mark, the name and the version stay centred above it. */
.about-new{text-align:left;margin:0 0 var(--fc-s-4)}
.about-new-t{display:flex;align-items:center;justify-content:center;gap:var(--fc-s-1);
  width:100%;background:transparent;border:none;font-family:inherit;font-size:var(--fc-fs-2);
  color:var(--ch-mu);cursor:pointer;padding:var(--fc-s-2) 0;margin-bottom:var(--fc-s-2);
  border-radius:var(--rad-xs);transition:color var(--ch-hov),background var(--ch-hov)}
.about-new-t:hover{color:var(--ch-tx)}
.about-new-t:active{transform:scale(.96)}
.about-new-t.on{color:var(--ch-tx)}
.about-new-ar{display:inline-block;font-size:var(--fc-fs-3);line-height:1;transition:transform var(--ch-hov)}
.about-new-t.on .about-new-ar{transform:rotate(180deg)}
.about-new .ab-rel+.ab-rel{margin-top:var(--fc-s-4);padding-top:var(--fc-s-4);border-top:1px solid var(--ch-br)}
.about-new .ab-rel-head{display:flex;align-items:baseline;gap:var(--fc-s-2);margin-bottom:var(--fc-s-2)}
.about-new .ab-rel-head b{font-family:var(--fc-font-mono);font-weight:var(--fc-fw);font-size:var(--fc-fs-2);color:var(--ch-tx)}
.about-new .ab-rel-head span{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);color:var(--ch-mu)}
.about-new .ab-sec+.ab-sec{margin-top:var(--fc-s-2)}
.about-new .ab-sec-t{font-size:var(--fc-fs-1);color:var(--ch-mu)}
.about-new .ab-sec ul{list-style:none;margin:var(--fc-s-1) 0 0;padding:0;display:flex;flex-direction:column;gap:var(--fc-s-1)}
.about-new .ab-sec li{font-size:var(--fc-fs-2);color:var(--ch-di);line-height:1.55;padding-left:var(--fc-s-3);position:relative}
.about-new .ab-sec li::before{content:'';position:absolute;left:0;top:.7em;width:5px;height:1px;background:var(--ch-br)}
/* Hierarchy in this kit is size, never weight, so the lead sentence of an entry
   steps forward on ink rather than on bold. */
.about-new .ab-sec li b{font-weight:var(--fc-fw);color:var(--ch-tx)}
/* A changelog entry naming a literal value writes it between backticks. They
   used to print as backticks, because the only markup the About box understood
   was the bold run. */
.about-new code{font-family:inherit;font-size:var(--fc-fs-1);background:var(--ch-s2);
  padding:var(--fc-s-0) var(--fc-s-1);border-radius:var(--rad-xs)}
/* Four of these now, in a two-by-two grid rather than one full-width button
   with three more stacked under it. Four stacked full-width buttons is a
   column of identical bars, which reads as a form to fill in rather than four
   places to go. */
.about-do{display:grid;grid-template-columns:1fr 1fr;gap:var(--fc-s-2);margin-bottom:var(--fc-s-4)}
.about-keys{box-sizing:border-box;width:100%;display:flex;align-items:center;justify-content:center;
  background:transparent;border:1px solid var(--ch-br);color:var(--ch-di);
  font-family:var(--fc-font-body);
  font-size:var(--fc-fs-2);padding:var(--fc-s-2) var(--fc-s-3);border-radius:var(--rad-xs);cursor:pointer;
  text-decoration:none;text-align:center;
  transition:color var(--ch-hov),border-color var(--ch-hov)}
.about-keys:hover{color:var(--ch-tx);border-color:var(--ch-bh)}
/* Only in the Mac app, and the one thing in this box that is an action rather
   than a place to go, so it is the one thing here wearing the accent. */
.about-upd{width:100%;margin-bottom:var(--fc-s-4)}
@media(max-width:420px){.about-do{grid-template-columns:1fr}}

/* ── APPEARANCE, IN THE ACCOUNT MENU ───────────────────────
   Was a permanent slider in the top bar. It is a once-a-year decision, so it
   does not earn a seat next to the controls used on every single file. */
/* 8px under the segment, so the rule beneath sits 16px from the control above
   it and 16px from the row below. It used to be 12px plus an 8px margin, and
   32px above a rule against 16px under it read as a hole in the menu. */
.av-seg-row{display:flex;flex-direction:column;gap:var(--fc-s-2);padding:var(--fc-s-2) var(--fc-s-3);
  border-bottom:0;margin-bottom:0}
.av-seg-lbl{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);letter-spacing:.02em;
  color:var(--ch-mu)}
.av-seg{display:flex;gap:var(--fc-s-1);background:var(--ch-sf);border-radius:var(--rad-pill);padding:var(--fc-s-1)}
.av-seg-btn{flex:1;background:none;border:none;color:var(--ch-mu);cursor:pointer;
  font-family:var(--fc-font-body);font-size:var(--fc-fs-2);
  padding:var(--fc-s-1) 0;border-radius:var(--rad-pill);transition:background var(--ch-hov),color var(--ch-hov)}
.av-seg-btn:hover{color:var(--ch-tx)}
.av-seg-btn.on{background:var(--ch-tx);color:var(--ch)}

/* ── REPORT A BUG ──────────────────────────────────────────
   A box to write in, so the report arrives with the detail we would otherwise
   have to ask two follow-up questions to get. */
#bug-ov{padding:var(--fc-s-5)}
.bug-box{background:var(--ch);
  border-radius:var(--rad);box-shadow:var(--sh3);padding:var(--fc-s-5) var(--fc-s-5) var(--fc-s-5);
  width:100%;max-width:420px;animation:chIn var(--ch-in) both}
.bug-h{font-size:var(--fc-fs-5);color:var(--ch-tx);font-weight:var(--fc-fw);margin:0 0 var(--fc-s-2)}
.bug-sub{font-size:var(--fc-fs-2);color:var(--ch-di);line-height:var(--fc-lh-prose);margin:0 0 var(--fc-s-4)}
.bug-txt{width:100%;background:var(--ch-sf);border:1px solid var(--ch-br);
  border-radius:var(--rad-xs);color:var(--ch-tx);font-family:var(--fc-font-body);
  font-size:var(--fc-fs-3);line-height:var(--fc-lh-prose);padding:var(--fc-s-3);outline:none;resize:vertical;
  transition:border-color var(--ch-hov),box-shadow var(--ch-hov)}
.bug-txt:focus{border-color:var(--ch-tx);box-shadow:0 0 0 3px var(--ch-s2)}
.bug-txt::placeholder{color:var(--ch-mu)}
.bug-done{margin-top:var(--fc-s-3);font-size:var(--fc-fs-4);color:var(--fc-fg)}
.bug-err{margin-top:var(--fc-s-2);font-size:var(--fc-fs-3);color:var(--fc-rj-ink);min-height:1.2em}
.bug-meta{font-family:var(--fc-font-mono);font-size:var(--fc-fs-1);letter-spacing:.06em;
  color:var(--ch-mu);margin-top:var(--fc-s-2);line-height:1.7;word-break:break-word}
.bug-acts{display:flex;gap:var(--fc-s-2);justify-content:flex-end;margin-top:var(--fc-s-5)}

/* With nothing loaded, the top bar's Open button and the big Open folder
   button in the middle are the same command twice. */
/* The welcome screen has its own big Open, so the one in the bar is a
   duplicate and goes. This used to carry an exception for the question mark
   button, which was the only .bg that had to survive here. The question mark
   is gone — About FieldCut is in the licence menu, which is where a person
   looks anyway — so the exception went with it. */
body.welcome .top .bg{display:none!important}

/* One height for everything in the top-right corner. Padding alone left the
   bordered Open button, the solid Export button and the round avatar each a
   pixel or two apart, which is exactly the sort of gap you cannot unsee. */
.tr .ba,.tr .bg{height:34px;padding-top:0;padding-bottom:0;
  display:inline-flex;align-items:center;justify-content:center}

/* ═════════════════════════════════════════════════════════════════════
   OFF THE MARKUP, 12 September 2026
   Twenty nine inline style attributes lifted out of app/index.html and
   given names here. An inline style is the one declaration a stylesheet
   cannot argue with, which is how twenty five !important rules came to
   exist in this file: each one was losing an argument it should never
   have been having.

   Every value below is the kit's. Where the markup carried a number off
   the grid it was moved to the nearest step on the way in, the same as
   every other value in this file: 6 -> 8, 3 -> 4, 5 -> 4, 10 -> 12.
   The four card widths stay raw because a width is a measurement, not a
   spacing decision, and the kit has no scale for one.
   ═════════════════════════════════════════════════════════════════════ */

/* The overlay card narrows below its own 460px cap, four ways. */
.card-sm{max-width:320px}
.card-md{max-width:380px}
.card-lg{max-width:420px}
.card-xl{max-width:440px}

/* A button and the menu it opens, so the menu positions against the pair. */
.dd-wrap{position:relative;overflow:visible}

/* The source header: the one panel head that lays its label and the folder
   name in a column rather than a row. */
/* Add, Open and the collapse arrow together once squeezed a job called
   "Wedding day one" down to "Weddi…", so the row used to wrap. Open is gone and
   the + and the arrow are two small squares, so they stay on the name's line:
   wrapping only ever moved the arrow onto a line by itself under the name. */
/* The head is now the same 40px row as "Sort into" opposite and the count
   strip between them: the label, then the + and the arrow on the label's own
   line. The folder name used to share a column with the label, which made
   this head 45px and centred the two buttons on the gap between the two
   lines, so "Source" sat 5px above "Sort into" and the + sat between words.
   The name is its own row underneath, in .sb-src. */
.sb-ph{flex-direction:row;align-items:center;flex-wrap:nowrap;gap:var(--fc-s-1)}
.sb-ph .pt{flex:1 1 auto;min-width:0}
.sb-src{display:flex;flex-shrink:0;min-width:0;padding:0 0 var(--fc-s-2)}

/* .ei is already 56px square, so the drawing fills its parent instead of
   naming the same number a second time. */
.ei svg{width:100%;height:100%;opacity:.35}

/* The + button sitting at the right end of the Sort into header. */
.ph-acts{display:flex;gap:var(--fc-s-1)}

/* The matching + in the Source header: same button as above, .bi already
   gives it the size, colour and hover, so nothing here redefines those.
   Sits left of #sb-tog in the markup, hidden until a job has more than one
   folder open. */
.sb-add{flex-shrink:0}

/* The bar in the updater card, which needs air the other .pbar does not. */
.upd-pbar{margin:var(--fc-s-4) 0 var(--fc-s-2)}

/* The one control in the video bar drawn larger than the rest: the glyph, not the box. */
.vc-fs{font-size:var(--fc-fs-6)}

/* The flag buttons under the lightbox, which wrap on a narrow window. */
/* One line that scrolls sideways, never a second row: the bar keeps its
   height whatever the folder count, and the right edge fades so a cut-off
   chip reads as more to come rather than as a mistake. */
#lbfbs{display:flex;gap:var(--fc-s-1);flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;min-width:0;flex:1 1 auto;mask-image:linear-gradient(to right,#000 calc(100% - var(--fc-s-5)),transparent);-webkit-mask-image:linear-gradient(to right,#000 calc(100% - var(--fc-s-5)),transparent)}
#lbfbs::-webkit-scrollbar{display:none}

/* The canvas fills the box that sizes it. */
#htu-canvas{width:100%;height:100%}

/* A keycap in the compare bar. Same shape as .kh kbd and .lbbr kbd, on the
   chrome ground those two sit on. */
.cmp-hint kbd{background:var(--s2);padding:var(--fc-s-0) var(--fc-s-2);
  border-radius:var(--rad-xs);font-size:var(--fc-fs-1)}

/* Create takes the room, Cancel takes what it needs. */
.nle-dest-new-row .ba{flex:1}

/* The sheet's own Cancel, which hangs at the left under the list rather
   than stretching across it. */
.nle-dest-cancel{align-self:flex-start;margin-top:var(--fc-s-0)}

/* The two buttons under a finished export. */
.nle-prog-acts{display:flex;gap:var(--fc-s-3);flex-wrap:wrap;justify-content:center}


/* ═════════════════════════════════════════════════════════════════════
   WHAT LOADING LOOKS LIKE, 12 September 2026

   The kit has shipped five skeleton shapes since it was written and the app
   used none of them. Four panels went blank while they waited and one, the
   clip editor's filmstrip, went blank for between one and nine seconds with
   nothing to say for itself.

   A skeleton is not decoration. It is the answer to "has it frozen": the
   shape of the thing that is coming, in the place it will land, so the screen
   is already the right screen before the data arrives.

   NOT THE PHOTO GRID, deliberately. A skeleton is a promise about one thing
   on its way. Four thousand of them shimmering at once is not a promise, it
   is a screen nobody can look at, and it asks the compositor for four
   thousand animated layers to say what the grid already says by being the
   right shape, in the right place, with the right number of cells. The tiles
   filling in one by one IS the progress. The same goes for the folder
   drawer's grid, which is the same tile at a smaller count.

   Reduced motion is handled once, on .skeleton itself, in components.css.
   ═════════════════════════════════════════════════════════════════════ */

/* COMPARE · two to four panes, each empty until a full-size frame decodes.
   Inset by the pane's own padding and stopping above the caption, so it
   occupies exactly the box the picture will. */
.cmp-sk{position:absolute;inset:var(--fc-s-4) var(--fc-s-4) 48px;border-radius:var(--rad-xs)}

/* DUPLICATE REVIEW · the img sits at opacity 0 until it has a src, so what
   shows through is the wrap. Square, and square to the corner: the rounding
   belongs to .dup-item outside it, and a second radius inside reads as a
   tile sitting crooked in its own frame. */
.dup-img-wrap.skeleton{border-radius:0;background-color:var(--s2)}

/* THE FILMSTRIP · one bar rather than twenty tiles, because the strip is one
   bar. The frames replace it whole when they arrive. It carries .on-photo
   alongside .skeleton: the stage is dark in both themes, so without it the
   light theme laid a near-white bar across a black strip. */
.nle-tl-sk{position:absolute;inset:0;border-radius:0}

/* And the sentence for when they never arrive. A tainted or undecodable
   source gives up after the first frame, and until now said nothing at all,
   which leaves the strip looking broken rather than finished. */
.nle-tl-note{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;font-size:var(--fc-fs-1);color:var(--fc-photo-fg2);
  letter-spacing:.02em;pointer-events:none;text-align:center;padding:0 var(--fc-s-3)}

/* SORT INTO · the destination's folders are walked one await at a time, up to
   five hundred of them. Until now the panel kept showing the last list, or the
   "Create folders" empty state, which reads as a wrong answer rather than as
   an answer on its way. Same height and rhythm as the .sfi rows replacing it. */
.sf-sk{height:30px;margin-bottom:var(--fc-s-1);border-radius:var(--rad-xs)}
.sf-sk:nth-child(2){width:82%}
.sf-sk:nth-child(3){width:64%}
.sf-sk:nth-child(4){width:74%}
