diff --git a/docs/architecture.md b/docs/architecture.md
index 8c71cfc..5aa8775 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -307,8 +307,37 @@ still scales and 7.css's `pt` shorthand stops resetting weight/line-height.
**Markup hooks**: `role="progressbar"` (+aria) on the transfer bars,
`has-scrollbar` on scroll containers (7.css only styles `::-webkit-scrollbar`
under that class), `w7-toolbar` on the three header rows (their buttons go
-flat — Explorer's command bar was flat, unlike dialog push buttons), `w7-btn`
-on the 10 dialog action buttons.
+flat — Explorer's command bar was flat, unlike dialog push buttons), `w7-flat`
+as the per-button escape hatch for the same treatment outside a toolbar (the
+status-bar transfers summary is a text toggle, not a push button), `w7-btn`
+on the 10 dialog action buttons. The flat treatment is split across two
+layers on purpose: `background: revert-layer` must sit in `skin` to reach
+`utilities`, while `border-color`/`box-shadow` sit in `skinfix` because 7.css
+restates `border-color` at (0,3,1) and no sane `.w7-flat` selector outranks
+that.
+
+**Tabs**: the workspace strip is 7.css's real tab component —
+`menu[role=tablist]` + one `button[role=tab][aria-selected]` per workspace, so
+the framework draws the joined inactive tabs and the raised active one. The
+selector is a DESCENDANT (`menu[role=tablist] button`), so the `+` button, the
+update pill and the right-hand tools deliberately sit outside the `
`.
+Tabs are exempted from the `w7-toolbar` flattening via `:not([role='tab'])` —
+they want the full Aero slab their neighbours are stripped of. There is no
+per-tab ✕ (Win7 tab controls had none, and a nested button is invalid HTML):
+closing is `Ctrl/Cmd+W` or middle-click. `role="tabpanel"` on the panel area
+was considered and rejected — its `padding: 14px`, `margin-bottom: 9px` and
+border fight the split-pane layout for a join that cannot read anyway with the
+panel toolbar row in between.
+
+**Frame transparency**: the rounded frame only reads as rounded because
+nothing paints an opaque full-bleed ground behind it. That is not free —
+`html, body` paint `var(--background)` **unlayered**, which beats every
+`@layer`, so `.win7` resets them to transparent in an equally unlayered rule
+next to the `font-family` one. A maximized window drops the radius (7.css
+declares it three times: `.window`, `::before`, `::after` — all three must be
+zeroed); the frame reads the state off its own child with
+`:has(> .title-bar.is-maximized)`, which `title-bar.ts` publishes from its
+existing `maximized` signal rather than subscribing to `windowState` twice.
**Context menu**: pure 7.css via its real menu contract —
`ul[role=menu] > li[role=menuitem] > button` for actions,
diff --git a/docs/gotchas.md b/docs/gotchas.md
index 3214c51..1ebac3e 100644
--- a/docs/gotchas.md
+++ b/docs/gotchas.md
@@ -43,6 +43,51 @@ touching the adjacent code.
`styles.css` live under `ui/src/` (esbuild emits them hashed into `media/`
and rewrites the url), NOT `ui/public/` — a public path fails the build with
"Could not resolve".
+- **Two `class` attributes on one element: the second is silently dropped.**
+ No warning, no build error — the layout classes simply never apply. This
+ collapsed the tab strip and two window headers at once (`fe028a9`), because
+ a scripted edit appended `class="…"` to elements that already had one.
+ When removing a conditional binding, check what the branch *contained*: the
+ bindings there were genuinely dead, but the layout classes inside them were
+ not, and nothing replaced them.
+- **A component `template:` is a TS template literal, so no backticks inside
+ it** — not even in an HTML comment. A backtick in a comment terminates the
+ literal and the errors point at the prose, not at the quoting.
+
+## The win7 skin (7.css)
+
+- **Unlayered declarations beat every `@layer`, regardless of specificity.**
+ `html, body { background: … }` in styles.css is unlayered, so no `skinfix`
+ rule can override it — the win7 transparent-corner reset has to be unlayered
+ too. The frame's rounded corners only read as rounded because nothing paints
+ an opaque full-bleed ground behind them; anything that does fills the corners
+ and the window becomes a rectangle with a curve drawn on it.
+- **7.css restates `border-color` at (0,3,1)** via
+ `.win7 button:not(:disabled):not(:hover)` and its `:hover` twin. A two-class
+ selector like `.win7 .w7-flat` (0,2,0) loses to it, so paint suppression for
+ flat buttons lives in `skinfix` where layer order settles it. The
+ `.w7-toolbar` arm only ever won by tying on specificity and landing later in
+ the same layer — an accident `w7-flat` did not share, which is why it
+ rendered a bordered slab while its neighbours went flat.
+- **`overflow-x: auto` also clips vertically.** CSS refuses to pair `visible`
+ with a non-visible axis, so the computed `overflow-y` becomes `auto` too.
+ 7.css raises the active tab with `margin-top: -2px`; inside the horizontally
+ scrolling tab strip that put its top border outside the scroll box and
+ deleted it silently. The strip reserves `padding-top: 2px` to compensate —
+ mirroring the `padding-left: 3px` 7.css already reserves for the same tab's
+ `-3px` left margin.
+- **7.css hardcodes light-mode literals outside its own var system** —
+ `color: #222` on tablist buttons, black/white bevels in the ListView and
+ menu. Each one has to be routed through a `--w7-*` token declared in
+ `:root.win7` and overridden in **both** dark blocks (the `[data-theme]`
+ attribute one *and* the `prefers-color-scheme` one; system dark sets no
+ attribute).
+- **Its selectors are exact, and a near-miss looks exactly like "7.css doesn't
+ support this".** `menu[role=tablist] button` is a DESCENDANT selector, so
+ every button inside the strip becomes a tab — the `+` and the right-hand
+ tools must live outside it. `.window.glass > .title-bar` is direct-child, so
+ an inner wrapper breaks the glass coupling. A wrong guess here already
+ produced one hand-written lookalike that was deleted.
## Electrobun
@@ -100,6 +145,22 @@ touching the adjacent code.
an explicit `WEBKIT_DISABLE_DMABUF_RENDERER` from the user always wins.
- A "does it boot?" log check does NOT catch this — grep for the GBM line, or
look at the window.
+- **`bun run start` leaves the old instance running, and
+ `import -window miniSCP` grabs the OLDEST match.** Instances pile up across
+ rebuilds, so every screenshot silently comes from a stale build and the fix
+ you just made looks like it did nothing. Cost three wrong readings in one
+ session, including a "the CSS is in the bundle but does not apply" dead end.
+ Kill first (`pkill -f dev-linux-x64`), confirm with
+ `pgrep -cf 'dev-linux-x64/miniSCP-dev/bin/launcher'`, then launch and capture.
+ Note `pkill -f 'electrobun dev'` also matches the shell running it and kills
+ your own command (exit 144) — match on the build path instead.
+- **X11 capture cannot answer "is this pixel transparent?"** for the frameless
+ window. `import -window` grabs the window's own pixels with no compositing,
+ so a transparent corner and an opaque black one both come out black;
+ `png32:` just fills alpha with 1 and proves nothing. XWayland's root window
+ is not the composited desktop, so `import -window root` fails too. Frame
+ transparency and the maximized corners need a human looking at the screen —
+ say so rather than reporting a screenshot as confirmation.
- Benign startup noise on Linux — non-fatal, the app runs normally:
`libayatana-appindicator is deprecated. Please use libayatana-appindicator-glib`
(upstream electrobun's choice, not ours).
diff --git a/ui/src/app/status-bar/status-bar.ts b/ui/src/app/status-bar/status-bar.ts
index b7ca324..868bf19 100644
--- a/ui/src/app/status-bar/status-bar.ts
+++ b/ui/src/app/status-bar/status-bar.ts
@@ -15,9 +15,12 @@ import { TransfersService } from '../transfers.service';
class="flex h-6 items-center justify-between gap-4 border-t border-border bg-chrome px-3 text-[11px] text-muted-foreground select-none"
>
+
@if (transfers.currentJob(); as job) {
diff --git a/ui/src/styles.css b/ui/src/styles.css
index 6ccb3ff..f1fe593 100644
--- a/ui/src/styles.css
+++ b/ui/src/styles.css
@@ -678,12 +678,17 @@ html.win7 body {
so without the exemption this rule would strip the border, gradient and
both pseudo-elements off every tab and leave 7.css's tab component drawing
nothing. Tabs want the full Aero slab; the +/Connections/options buttons
- beside them want the flat treatment. */
+ beside them want the flat treatment.
+ Only `background` stays here — it is the one property that needs to land
+ on `utilities` via revert-layer. The literals that suppress the Aero slab
+ moved to `skinfix`, because 7.css restates border-color at
+ `.win7 button:not(:disabled):not(:hover)` = (0,3,1), which outranks
+ `.win7 .w7-flat` = (0,2,0). The toolbar arm only ever won that fight by
+ tying on specificity and landing later in the same layer — an accident,
+ and one w7-flat did not share: it rendered a bordered slab regardless. */
.win7 .w7-toolbar button:not([role='tab']),
.win7 .w7-flat {
background: revert-layer;
- border-color: transparent;
- box-shadow: none;
}
.win7 .w7-toolbar button:not([role='tab'])::before,
.win7 .w7-toolbar button:not([role='tab'])::after,
@@ -853,6 +858,18 @@ html.win7 body {
color: var(--w7-mn-check-c);
}
+ /* ---- Flat buttons (toolbars + the w7-flat escape hatch) ----
+ The paint-suppression half of the rule in `skin` (see there for the split).
+ It lives in `skinfix` because 7.css restates border-color at (0,3,1) via
+ `.win7 button:not(:disabled):not(:hover)` and again on :hover, which no
+ reasonable `.w7-flat` selector outranks — layer order settles it instead
+ of a specificity arms race. */
+ .win7 .w7-toolbar button:not([role='tab']),
+ .win7 .w7-flat {
+ border-color: transparent;
+ box-shadow: none;
+ }
+
/* ---- Tabs (workspace strip) ----
7.css owns the tab CHROME; these restore the tab CONTENT layout it breaks.
`menu[role=tablist] button` sets `display: block` at (0,2,1), which