/* frb-editor.css — the editor variant of the app shell.
 *
 * Five apps are a form on the left and a rendered page on the right: Menu Genie
 * Lite and Pro, Resume Architect, CV Architect, Cover Letter Architect. They
 * were built one at a time and each carries its own copy of the same layout —
 * `.app-container`, `.editor-panel`, `.preview-panel`, `.paper-wrapper`,
 * `.toolbar` — with the `.preview-panel` rule byte-identical in all five:
 *
 *     .preview-panel { background: var(--mat-bg); position: relative;
 *                      overflow: hidden; display: flex;
 *                      justify-content: center; align-items: center; }
 *
 * Identical copies drift, and this set already had: every one of them cut its
 * own export bar off the bottom of the window by exactly 14px, which is the
 * kind of fault that only gets fixed five times or once.
 *
 * This file is the once. It owns the SHAPE of an editor — how tall the columns
 * are, what scrolls, where the dock sits — and leaves what goes in them to the
 * app, the same division frb-shell.css already makes for a rail and a bar.
 *
 * OPT-IN, via `data-frb-editor` on <body>. Nothing here reaches a page that has
 * not asked for it, so adding the file costs the other 126 pages nothing.
 *
 * ── THE 14px ──────────────────────────────────────────────────────────────
 * Worth stating, because it is the whole reason this file exists and it is not
 * where anyone looks. `.app-container` is `height: 100vh`, and it starts below
 * a fixed 44px header — so its bottom edge is at 100vh + 44, forty-four pixels
 * past the fold. `.toolbar` is `position: absolute; bottom: 30px` against that
 * container, which puts the export buttons 14px below the window on every one
 * of the five. Nothing about the toolbar was wrong; it was measuring from a box
 * that hangs off the screen.
 * ────────────────────────────────────────────────────────────────────────── */

/* ── 1. ONE FORM COLUMN, THE SAME ON EVERY EDITOR ────────────────────────── */
/* Five apps, three widths: Lite/Resume/CV declared 800, Pro 900, Cover Letter
   1000, each in its own `:root`. Nothing chose those numbers together, and at
   1440 they read as three products.
 *
 * WHERE 640 COMES FROM. It is not a form-side number — I picked it from the
 * stage, because the stage is the side that can actually run out. At 1440 the
 * grid spends 70px on the rail and 12px on the resizer, leaving 1358 for the
 * form and the page together. The page is A4: 210mm = 793.7px of layout,
 * painted at the variant's design zoom of 0.85 (`.paper-wrapper` in all five)
 * = 674.6px, and the stage holds 16px of padding on each side. So the stage
 * needs 706.6px to paint the sheet whole, and the form can have at most
 * 1358 − 706.6 = 651. 640 measured: stage 718 wide, 686 inside its padding,
 * sheet 674.6 — 11.4px of clear air, and a round number rather than a
 * to-the-pixel one that the first extra chip in the dock would break.
 *
 * WHAT I GOT WRONG FIRST. I sized it from the form — keep 800, the value three
 * of the five already had — and only then measured the other column: the stage
 * came out 558px against a 674.6px sheet, so the page paints past both edges.
 * All five are like this TODAY, at 1440 and at 1280; the widths were never
 * checked against the thing beside them. Second wrong turn, worth writing down
 * because the number looks authoritative: I read `previewPanel.scrollWidth −
 * clientWidth` as "how much of the sheet is cut off". It is not. `transform:
 * scale()` paints smaller but leaves the layout box at 793.7px, so that
 * subtraction returns 826 − clientWidth on every page at every width and never
 * mentions the zoom. The only honest test is the painted rect against the
 * stage's padding box, which is what the 674.6 above is.
 *
 * THE FLOOR IS THE HEADER, NOT THE FIELDS. The brief expected two-column field
 * rows to stack if this went too narrow. They do not — they are Bootstrap
 * breakpoint-less `col-6`, permanently 50% — and swept from 420 to 900 across
 * all five, zero rows ever stacked; the fields just get thinner (299px each at
 * 640, against 379 at 800). What genuinely runs out is `.editor-header`, which
 * is `white-space: nowrap`, and the apps already say where they expect that to
 * bite: CV sheds its sync hint at ≤620, Cover Letter at ≤640, and all five drop
 * the account label at ≤600. 640 sits at the top of that band — the widest form
 * that still paints the page whole, and no narrower than the headers were
 * designed to survive.
 *
 * OVERRIDABLE, because a later editor may genuinely hold a different form: set
 * `--frb-editor-form-w` on that page and this default steps aside.
 *
 * Declared on `body[data-frb-editor]` rather than `:root` on purpose, and the
 * reason is the resizer rather than the cascade. The drag writes `--editor-width`
 * inline on `.app-container`, which is a descendant of <body>, so it inherits
 * past this and the bar still wins every frame — measured, a 140px drag still
 * moves the column 146px on all five. A `:root` declaration would work the same
 * way; body is simply where the opt-in attribute lives, so the default sits on
 * the same element that turns the variant on. */
body[data-frb-editor] {
    --editor-width: var(--frb-editor-form-w, 640px);
    /* The floor the panel refuses to go under while the bar is dragged left.
       420 was already four of the five; Pro alone said 500, for a form that
       measures no differently from the others once the chrome sheds. */
    --editor-min-width: var(--frb-editor-form-min-w, 420px);
}

/* ── 2. THE COLUMNS ARE THE WINDOW MINUS THE CHROME ABOVE THEM ───────────── */
body[data-frb-editor] .app-container {
    /* `100vh` is the window. The container does not start at the top of the
       window — the shared header is above it, and on the shell the bar is too —
       so the height it wants is what is LEFT, not the whole thing. Both are
       given: dvh is correct on a phone where the browser chrome comes and goes,
       and vh is the fallback for anything that does not know dvh. */
    height: calc(100vh - var(--frb-editor-top, 44px));
    height: calc(100dvh - var(--frb-editor-top, 44px));
    min-height: 0;
    overflow: hidden;
}

/* The shell puts a 62px bar under the 44px header, so a shelled editor has more
   above it than an unshelled one. Declared here rather than in each page so the
   number lives beside the rule that consumes it. */
body[data-frb-editor][data-frb-shell] {
    --frb-editor-top: calc(var(--frb-header-h, 44px) + var(--frb-editor-bar-h, 0px));
}

/* ── 3. THE STAGE SCROLLS INSTEAD OF CLIPPING ────────────────────────────── */
/* `overflow: hidden` with the page centred is why "the render panel cuts out a
   lot": a page taller than the panel loses its top AND its bottom, symmetrically,
   with no way to reach either. Scrolling gives it back. The column also becomes
   a flex COLUMN so the dock can sit under the page rather than floating over it. */
body[data-frb-editor] .preview-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    overflow: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    padding: var(--frb-space-5, 20px) var(--frb-space-4, 16px) 0;
    box-sizing: border-box;
}

/* The page itself keeps whatever scale the app gave it and stops being a flex
   item that can be squashed. `margin: auto` on the block axis centres it while
   it fits and lets it scroll once it does not — which is the behaviour a
   zoomable canvas wants and `align-items: center` cannot give. */
body[data-frb-editor] .paper-wrapper {
    flex: 0 0 auto;
    margin: auto auto var(--frb-space-4, 16px);
}

/* ── 4. THE DOCK CANNOT BE CUT ───────────────────────────────────────────── */
/* Sticky against the stage's own scrollport rather than absolute against a box
   that hangs off the screen. `margin-top: auto` seats it at the bottom when the
   page is short; `bottom` holds it there while the page scrolls under it. It is
   the same pill it always was — only its frame of reference changed. */
body[data-frb-editor] .toolbar {
    position: sticky;
    bottom: var(--frb-space-4, 16px);
    left: auto;
    right: auto;
    transform: none;                    /* it centred itself with translateX */
    margin-top: auto;
    margin-bottom: var(--frb-space-4, 16px);
    flex: 0 0 auto;
    /* THE FLOATING PLAN CHIP LIVES IN THE BOTTOM RIGHT OF EVERY PAGE, and the
       dock now reaches it. 116px is that chip plus a gap — reserved rather than
       overlapped, because the thing the chip lands on is the Download button. */
    max-width: calc(100% - 24px - var(--frb-dock-reserve, 116px));
    /* ONE ROW, SCROLLING — not a block that grows. Wrapping was the obvious
       choice and it is wrong here for the same reason it was wrong in the
       shell's bar: the dock is anchored to the BOTTOM of the stage, so every
       row it gains it takes off the page above it. Cover Letter's preview
       column is 500px wide and its dock went to three rows and 149px, eating a
       sixth of the page it exists to serve. Sideways, and the height is 48px
       whatever anybody puts in it. */
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    justify-content: flex-start;
    z-index: 20;
}
body[data-frb-editor] .toolbar::-webkit-scrollbar { display: none; }
body[data-frb-editor] .toolbar > * { flex: 0 0 auto; }

/* WHAT THE DOCUMENT IS, beside the buttons that produce it. The page size and
   the page count were only discoverable by exporting the file and opening its
   properties; they are facts about what is on screen and belong on screen.
   Quiet, because they are reference rather than action. */
body[data-frb-editor] .frb-dock-info {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-right: 4px;
    margin-right: 2px;
    border-right: 1px solid var(--glass-border-color, rgba(127, 127, 127, .22));
    font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    letter-spacing: .01em;
    color: var(--text-secondary, rgba(127, 127, 127, .95));
    white-space: nowrap;
}
body[data-frb-editor] .frb-dock-info b {
    font-weight: 700;
    color: var(--text-primary);
}

/* ── 5. THE FORM SIDE FOLDS AWAY ─────────────────────────────────────────── */
/* The apps already have `.panel-collapsed` and a restore button; what they did
   not have is a way IN. This is the control that folds it, seated in the dock
   beside the exports so the two halves of "look at the page" are together. */
body[data-frb-editor] .frb-dock-fold {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}
body[data-frb-editor].panel-collapsed .frb-dock-fold-open,
body[data-frb-editor] .frb-dock-fold-close { display: inline; }
body[data-frb-editor] .frb-dock-fold-open,
body[data-frb-editor].panel-collapsed .frb-dock-fold-close { display: none; }

/* With the form gone the page has the whole window, and a page that had to be
   scaled down to fit beside a 800px form does not any more. The apps animate
   the grid; the stage follows it. */
body[data-frb-editor] .app-container.panel-collapsed .preview-panel {
    padding-left: var(--frb-space-6, 24px);
    padding-right: var(--frb-space-6, 24px);
}

/* ── AND FOLDED MEANS GONE, NOT NARROW ───────────────────────────────────── */
/* `.editor-panel` carries `min-width: var(--editor-min-width)` — 420px — so a
 * drag cannot crush the form to nothing. Collapsing sets the form's grid track
 * to 0px, and a 420px min-width simply refuses it: measured on all six at 1440
 * with the form folded, the panel is STILL 420px wide at x=70, in a 0px track,
 * with the sheet drawn over the top of it.
 *
 * On Midnight the panel is rgba(10,10,10,.8) over a black stage, so the fault
 * paints as nothing and nobody sees it. On Sunset it is rgba(30,30,30,.85) and
 * reads as a lighter band down the left third of the window — beside a sheet
 * that is centred correctly on the whole stage and therefore looks off-centre
 * against the band. Same defect, one theme showing it.
 *
 * The floor belongs to the DRAG, not to the fold: it is lifted only while
 * collapsed, so dragging the split still stops at 420. */
body[data-frb-editor] .app-container.panel-collapsed > .editor-panel {
    min-width: 0;
    overflow: hidden;
}
/* The strip the form was dragged by goes with it. Its two 1px borders are still
   2px of bar with the grip's dotted line inside them — a stray mark down the
   left of a page whose whole point, folded, is to be the only thing on screen. */
body[data-frb-editor] .app-container.panel-collapsed > .resizer-bar {
    border-left-width: 0;
    border-right-width: 0;
    overflow: hidden;
}

/* ── 6. A PHONE HAS ONE COLUMN, NOT TWO ──────────────────────────────────── */
/* The phone layout is written ONCE, at the bottom of this file. There was a
 * first version here — the one that stacked the form above the page — and
 * leaving it in place while the real one was written below is what put the
 * export dock half off the left edge of the screen.
 *
 * Both blocks were `@media (max-width: 620px)`, so neither outranked the
 * other and each declaration was settled by source order alone. The second
 * block restates `position`, `left`, `right` and `bottom`, so those came out
 * right — and it never mentions `transform`, so the only `transform` in the
 * cascade at 390px was this block's `translateX(-50%)`, left over from a dock
 * that centred itself. Measured: the dock's box at x = -179, its left half and
 * the fold control inside it off the screen entirely, its right half under the
 * floating plan chip. Every rule involved was doing exactly what it said; the
 * fault was that two of them were still in the file.
 *
 * A superseded block is not harmless just because the block that replaced it
 * is more specific about the properties it cares about. It is deleted.
 * ────────────────────────────────────────────────────────────────────────── */

/* ═══════════════════════════════════════════════════════════════════════════
   THE SECOND PASS — what the first one got wrong, and what it left out.

   Six things, reported together after looking at the five side by side:
   a grey box in the icon rail, a bar too slim to read as a bar, a fold control
   nobody could find, export buttons that belong beside the page rather than
   under it, a phone layout with no way to get the form out of the way, and a
   profile popup that the shared header already carries.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── THE BAR IS A BAND, NOT A LINE ───────────────────────────────────────── */
/* These five are not on `data-frb-shell`, so they get frb-app-shell.js's
   fallback `.frb-topbar` — an unstyled wrapper around the trail, measured at
   25px tall and sitting at y=-48 behind the app's own header. Beside the 62px
   band every shelled app carries it reads as a stray line of links. Given the
   same ground, height and hairline as the real thing, so the two variants of
   the product agree on what a trail looks like. */
body[data-frb-editor] .frb-topbar {
    position: relative;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: var(--frb-space-4, 16px);
    min-height: 46px;
    padding: 0 var(--frb-space-5, 20px);
    /* The same ground as the shared header it sits directly under, for the
       same reason: this band is permanent chrome, and .8 alpha over whatever
       the page happens to paint is a tint rather than a surface. */
    background: var(--frb-header-bg, rgba(8, 8, 10, .985));
    /* AND FLUSH AGAINST WHAT IS UNDER IT. The band carries a 12px bottom
       margin from the shared rules, where it separates a trail from a page
       head with the page's own ground showing between them. Here what is
       under it is the editor, edge to edge — so the margin is 12px of body
       showing through a seam in the chrome, and 12px the columns do not get.
       The hairline already does the separating. */
    margin-bottom: 0;
    border-bottom: 1px solid var(--glass-border-color, rgba(127, 127, 127, .22));
    font-size: 13px;
}

/* ── THE FOLD CONTROL LIVES ON THE SPLIT ─────────────────────────────────── */
/* It was in the dock, which is where the exports are and a long way from the
   thing it moves. The split itself is the honest place: the reader is already
   dragging that edge to give the page more room, so the control that gives it
   ALL the room belongs on the same 12px strip. Half of it sits proud of the
   strip on the preview side, because a control flush inside a 12px gutter is a
   control nobody sees. */
body[data-frb-editor] .resizer-bar {
    position: relative;
    overflow: visible;
}
/* POSITIONED BY ARITHMETIC ON THE SPLIT, not by being inside it. The grid is
   `70px var(--editor-width) 12px 1fr`, so the strip's centre is the rail plus
   the form plus half the gutter — and because the drag handle writes
   `--editor-width` at runtime, the control follows the edge as it is dragged
   without a single line of script. Folded, the form track is 0 and the control
   parks against the rail. */
body[data-frb-editor] .frb-split-fold {
    position: fixed;
    top: 50%;
    left: calc(70px + var(--editor-width, 640px) + 6px);
    transform: translate(-50%, -50%);
    z-index: 40;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 54px;
    padding: 0;
    cursor: pointer;
    /* A button, so it takes the button token rather than a number of its own. */
    border-radius: var(--frb-btn-radius, 8px);
    border: 1px solid var(--glass-border-color, rgba(127, 127, 127, .3));
    background: var(--panel-bg, rgba(10, 10, 10, .92));
    color: var(--text-secondary, rgba(127, 127, 127, .95));
    box-shadow: 0 6px 20px -8px rgba(0, 0, 0, .7);
    /* AND IT TRAVELS WITH THE EDGE IT MOVES. The grid animates its columns over
       half a second; `left` had no transition, so the control teleported to the
       rail the instant the class landed and then waited there while the form
       slid out from under it. That is what "the form goes in but the button
       stays pinned" looks like: not a button that failed to move, a button that
       moved without the thing it is attached to. Same easing, same duration as
       `.app-container.panel-collapsed`, so the two are one movement. */
    transition: color .15s, border-color .15s, background .15s, left .5s ease;
}
body[data-frb-editor] .frb-split-fold:hover {
    color: var(--accent-text, var(--accent-primary));
    border-color: var(--accent-glow);
}
/* FOLDED, IT IS A TAB ON THE RAIL — not a pill floating beside one. With the
   split gone there is nothing under the control, and a free-standing rounded
   rect an inch off the icon strip reads as something that came loose. Seated
   flush against the rail's right edge with its left corners squared and its
   left border dropped, it reads as what it now is: the handle you pull to get
   the form back. `--sidebar-w` rather than 70, because the arithmetic on the
   open side uses the same token and the two must not drift apart. */
body[data-frb-editor] .app-container.panel-collapsed ~ .frb-split-fold,
body[data-frb-editor].panel-collapsed .frb-split-fold {
    left: calc(var(--sidebar-w, 70px) + 13px);
    border-left-color: transparent;
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}
body[data-frb-editor] .frb-split-fold i { font-size: 12px; pointer-events: none; }
/* The strip is a drag handle; the button on it is not. Without this, grabbing
   the control starts a resize and the click never lands. */
body[data-frb-editor] .frb-split-fold { touch-action: manipulation; }

/* ── THE CONTROLS STAND BESIDE THE PAGE ──────────────────────────────────── */
/* Asked for as a vertical strip on the right rather than a pill underneath,
   and it is the better shape for the same reason the dock was: anything under
   the page is height the page does not get. On the right it costs width, and
   width is what a 794px sheet in a 700px column has spare — nothing, until the
   form folds away, which is exactly when a reader is looking at the page. */
body[data-frb-editor] .preview-panel {
    position: relative;
    padding-right: 74px;
}
/* ── AND IT IS NOT IN THE THING THAT SCROLLS ─────────────────────────────── */
/* The strip was `position: absolute` inside `.preview-panel`, which is the
 * stage — and the stage is the scrollport, so the strip scrolled away with the
 * document it controls. Measured on Menu Genie Lite at 1440: the strip's top
 * at y=111, scroll the stage 112px and it is at y=-1. One flick of the wheel
 * on a two-page document and every export button is off the top of the window.
 *
 * Its containing block is the CONTAINER instead — the box the two columns are
 * laid out in, which is `overflow: hidden` and never scrolls. Same right edge,
 * same reserved 74px in the stage's padding, but now it stays put while the
 * page moves under it, which is the entire point of chrome. frb-editor.js
 * reparents the node; this makes the container able to hold it.
 *
 * CENTRED, as asked. A strip pinned to the top of a stage reads as the top of
 * a list; these controls act on the whole sheet, and the sheet's middle is the
 * one point on it that is always on screen. */
body[data-frb-editor] .app-container { position: relative; }
body[data-frb-editor] .toolbar {
    /* STATED HERE, not assumed of the page. Every rule below about direction,
       wrapping and alignment is a flex rule, and all five apps happened to
       declare `display: flex` on their own `.toolbar` — so none of it was ever
       tested against a page that did not. Codefolio does not: with the strip
       left as a block its buttons are inline-flex boxes that simply flow, and
       on a phone the one scrolling row came out as three wrapped rows in a
       999px-radius blob across the bottom of the sheet. A block of rules that
       only works when the caller sets `display` is a block of rules with a
       missing line. */
    display: flex;
    position: absolute;
    top: 50%;
    right: var(--frb-space-4, 16px);
    bottom: auto;
    left: auto;
    margin: 0;
    transform: translateY(-50%);
    width: 54px;
    max-width: 54px;
    max-height: calc(100% - 32px);
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    justify-content: flex-start;
    gap: 6px;
    padding: 8px 7px;
    border-radius: 14px;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior-y: contain;
}
body[data-frb-editor] .toolbar > * { flex: 0 0 auto; }
/* In a 54px column a button is a glyph. The words stay in the document as the
   accessible name and as the tooltip, and simply take no room — the same
   mechanism the icon rail uses for its labels. */
body[data-frb-editor] .toolbar .frb-tb-btn,
body[data-frb-editor] .toolbar button,
body[data-frb-editor] .toolbar .btn-b {
    width: 100%;
    min-width: 0;
    justify-content: center;
    padding: 7px 4px;
    font-size: 11px;
    line-height: 1.15;
    text-align: center;
    white-space: normal;
    word-break: break-word;
}
body[data-frb-editor] .toolbar .frb-tb-sep {
    width: 100%;
    height: 1px;
    margin: 2px 0;
    background: var(--glass-border-color, rgba(127, 127, 127, .22));
}
/* The zoom readout is the one thing here that is not a control. */
body[data-frb-editor] .toolbar .frb-tb-z {
    text-align: center;
    font-size: 10px;
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary, rgba(127, 127, 127, .95));
}
body[data-frb-editor] .frb-dock-info {
    flex-direction: column;
    gap: 2px;
    padding: 2px 0 6px;
    margin: 0;
    border-right: 0;
    border-bottom: 1px solid var(--glass-border-color, rgba(127, 127, 127, .22));
    font-size: 10px;
    text-align: center;
    white-space: normal;
}

/* ── THE THREE THE STRIP WAS MISSING ─────────────────────────────────────── */
/* Fit, Info and Print — see mountExtras() in frb-editor.js for why each. An
   icon over its word, because in a 54px column that reads at a glance where a
   word alone reads as one of five similar words. */
/* EVERY BUTTON IN THE STRIP, not only the three this file adds. The apps write
   their controls as an icon beside a word, which is right in a horizontal pill
   and impossible in a 54px column: the icon takes 16px and its gap 6, leaving
   about 24 for the word, so `word-break: break-word` sets "Pages" and "Save"
   one letter per line — a vertical stack of single characters down the side of
   the sheet. Stood one above the other they both have the full 44px. */
body[data-frb-editor] .toolbar .frb-tb-btn > i,
body[data-frb-editor] .toolbar button > i,
body[data-frb-editor] .toolbar a > i {
    display: block;
    font-size: 13px;
    line-height: 1;
    margin: 0 0 3px;
}
/* And the word itself stops being breakable. A five-letter label that does not
   fit is a label to shorten, not to hyphenate down the page. */
body[data-frb-editor] .toolbar .frb-tb-btn,
body[data-frb-editor] .toolbar button,
body[data-frb-editor] .toolbar .btn-b {
    word-break: normal;
    overflow-wrap: normal;
    hyphens: none;
    /* A COLUMN, or `display: block` on the icon does nothing. These buttons are
       Bootstrap's `.btn`, which is `display: inline-flex` — and a flex child is
       blockified without being taken out of the row, so the icon stayed beside
       its word and the pair overflowed a 38px button: "Pages" and "Save" ran
       past the strip's right edge at 1440. The stacking has to be asked of the
       CONTAINER, not of the child. */
    flex-direction: column;
    align-items: center;
    justify-content: center;
    row-gap: 3px;
}
body[data-frb-editor] .toolbar .frb-tb-word {
    display: block;
    font-size: 10px;
    letter-spacing: .01em;
}
body[data-frb-editor] .toolbar .frb-tb-fit,
body[data-frb-editor] .toolbar .frb-tb-info,
body[data-frb-editor] .toolbar .frb-tb-print {
    background: transparent;
    border: 1px solid var(--glass-border-color, rgba(127, 127, 127, .22));
    border-radius: var(--frb-btn-radius, 9px);
    color: var(--text-secondary, rgba(127, 127, 127, .95));
    cursor: pointer;
    transition: color .15s, border-color .15s;
}
body[data-frb-editor] .toolbar .frb-tb-fit:hover,
body[data-frb-editor] .toolbar .frb-tb-info:hover,
body[data-frb-editor] .toolbar .frb-tb-print:hover {
    color: var(--accent-text, var(--accent-primary));
    border-color: var(--accent-glow);
}

/* ── WHAT ENDS UP INSIDE THE FILE ────────────────────────────────────────── */
/* The four fields WeasyPrint reads. They were built into a `#frbDocMeta` host
   the pages were meant to provide and none did, so the properties of the
   exported PDF were editable in principle and nowhere in practice. This is the
   same form with no markup required — a panel off the Info button, over the
   stage rather than in the form, because it describes the FILE and not the
   content. A page that provides its own host keeps it and this is not built. */
body[data-frb-editor] .frb-meta-pop {
    position: fixed;
    z-index: 70;
    right: 84px;
    top: 50%;
    transform: translateY(-50%);
    width: min(320px, calc(100vw - 110px));
    max-height: min(520px, calc(100vh - 120px));
    overflow: auto;
    padding: 16px 16px 18px;
    border-radius: 14px;
    border: 1px solid var(--glass-border-color, rgba(127, 127, 127, .28));
    background: var(--frb-header-bg, rgba(8, 8, 10, .985));
    box-shadow: 0 22px 60px -18px rgba(0, 0, 0, .85);
}
body[data-frb-editor] .frb-meta-pop[hidden] { display: none; }
body[data-frb-editor] .frb-meta-pop-head {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--text-secondary, rgba(127, 127, 127, .95));
    margin-bottom: 6px;
}
body[data-frb-editor] .frb-meta-pop .frb-meta-hint {
    margin: 0 0 14px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-secondary, rgba(127, 127, 127, .95));
}
body[data-frb-editor] .frb-meta-pop label {
    display: block;
    margin: 0 0 5px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--text-secondary, rgba(127, 127, 127, .95));
}
body[data-frb-editor] .frb-meta-pop input {
    display: block;
    width: 100%;
    margin: 0 0 13px;
    padding: 9px 11px;
    box-sizing: border-box;
    border-radius: 9px;
    border: 1px solid var(--input-border, var(--glass-border-color, rgba(127, 127, 127, .3)));
    background: var(--input-bg, rgba(127, 127, 127, .08));
    color: var(--text-primary);
    font: inherit;
    font-size: 13px;
}
body[data-frb-editor] .frb-meta-pop input:focus {
    outline: none;
    border-color: var(--accent-primary);
}
body[data-frb-editor] .frb-meta-pop input:last-child { margin-bottom: 0; }

/* ── THE PROFILE POPUP IS THE HEADER'S JOB NOW ───────────────────────────── */
/* Every one of these draws its own account chip in the editor header, from
   before the shared header carried a profile tube. Two controls for one
   account, one of which opens a panel the other one already owns. Hidden
   rather than removed: the page's own script writes into it on load, and a
   missing node is a thrown error on a page that has nothing to do with
   accounts. */
body[data-frb-editor] .editor-panel .account-badge,
body[data-frb-editor] .editor-header #frbAccountChip { display: none !important; }

/* ── THE RAIL AND THE BAND ARE THE UNIVERSAL ONES ────────────────────────── */
/* Measured against Industry Hub's country page at 390: its rail is 65px tall
 * and its trail band 62px; an editor's were 56 and 46. Reported as "still
 * using old icon sidebar and slim hub tree", and that is exactly what they
 * were — the shell's own rules never reached either.
 *
 * The rail rule is `body[data-frb-shell] > :is(.sidebar, …)`: a DIRECT CHILD
 * of body. On an editor the rail is inside `.app-container`, so the selector
 * cannot match however the body is marked — which is why adding
 * `data-frb-shell` changed nothing, and, on a desktop, pushed the rail to
 * y=215 by making the rest of the shell's column layout apply to a page that
 * has no `.main`.
 *
 * Extending the shared selectors instead was the obvious next move and it is
 * how I broke the reference page: a blanket rewrite of all seven occurrences
 * reached one inside a construct where it did not belong, and country_analysis
 * came back with its rail 356px shorter and its trail 500px down the page. The
 * numbers below are the shell's own, read from those rules and repeated for
 * the one shape they cannot reach, which is the smaller of the two prices. */
/* ── ON A DESKTOP THE RAIL TOUCHES THE HEADER ────────────────────────────── */
/* The reference runs its rail `position: fixed` from the header's bottom edge
 * to the floor. An editor kept its rail as the first column of the split grid,
 * so it began where the grid began — measured at 1440, y=231, under the trail
 * and the app head instead of at y=48 against the header.
 *
 * THE FIRST ATTEMPT AT THIS BROKE THREE THINGS AND IS WORTH RECORDING, because
 * two of them looked like the same bug and were not:
 *   - `padding-left` on the band and the head was overridden by the `padding`
 *     shorthand on their own rules further down this file, so both rendered
 *     UNDERNEATH the fixed rail — "Portals" behind the strip, the app's name
 *     clipped at x=70. MARGIN instead: nothing in this file sets a margin
 *     shorthand on either, so there is nothing to fight.
 *   - collapsing the grid's first track to zero moved the preview panel out
 *     from under the sheet. It did not need collapsing at all: taking the rail
 *     out of flow leaves that 70px track EMPTY but still 70px, which is exactly
 *     where the fixed rail ends. The grid is left alone and the two line up by
 *     construction rather than by arithmetic. */
@media (min-width: 621px) {
    body[data-frb-editor] .app-container > .sidebar {
        position: fixed;
        top: var(--frb-header-h, 47px);
        left: 0;
        bottom: 0;
        width: var(--sidebar-w, 70px);
        z-index: 40;
        overflow-y: auto;
        overscroll-behavior: contain;
        scrollbar-width: none;
    }
    body[data-frb-editor] .app-container > .sidebar::-webkit-scrollbar { display: none; }
    body[data-frb-editor] .frb-topbar,
    body[data-frb-editor] .frb-apphead {
        margin-left: var(--sidebar-w, 70px);
    }
    /* AND THE OTHER THREE STAY IN THEIR OWN TRACKS. Taking the rail out of
       flow does not leave a hole in a grid — it removes an item, and auto
       placement slides everything after it up one column: the form landed in
       the rail's 70px track and rendered underneath the fixed rail, which is
       the same symptom as the padding fault and a completely different cause.
       Named columns pin each pane where the app's own template put it, and the
       first track stays empty at exactly the width the fixed rail occupies. */
    body[data-frb-editor] .app-container > .editor-panel { grid-column: 2; }
    body[data-frb-editor] .app-container > .resizer-bar { grid-column: 3; }
    body[data-frb-editor] .app-container > .preview-panel { grid-column: 4; }
}

/* THE STRIP ENDS WHERE ITS PADDING ENDS. The light switch is the last thing in
   the rail and it carries a margin from the layout it was written for, so the
   column finished 31px above the floor against the reference's 16 — fifteen
   pixels of nothing under the switch, which is the whole of the gap reported.
   The rail's own inset is the only spacing it needs at either end. */
body[data-frb-editor] .app-container > .sidebar > :last-child { margin-bottom: 0; }
body[data-frb-editor] .app-container > .sidebar > :first-child { margin-top: 0; }
body[data-frb-editor] .app-container > .sidebar {
    padding: var(--frb-space-4, 16px) var(--frb-space-2, 8px);
    gap: var(--frb-space-1, 4px);
    background: var(--sidebar-bg, #0a0a0a);
    border-right: 1px solid var(--glass-border-color, rgba(127, 127, 127, .22));
    box-sizing: border-box;
}
@media (max-width: 620px) {
    body[data-frb-editor] .app-container > .sidebar {
        flex-direction: row;
        flex-wrap: nowrap;
        align-items: center;
        width: 100%;
        height: auto;
        /* IT TAKES ITS OWN HEIGHT. As a child of the container's phone column
           the strip was shrinkable, and it shrank: 56px for content needing 58
           — the padding plus the tallest item, which is 42 and not 40 because
           one of them carries a border. With `align-items: center` the two
           missing pixels came off BOTH ends, so every icon in the row was cut
           top and bottom and the strip sat flush against the header with no
           inset at all. Measured against the reference: its items start 12px
           into a 65px strip; these started at 0 in a 56px one. */
        flex: 0 0 auto;
        overflow-x: auto;
        overflow-y: hidden;
        scrollbar-width: none;
        /* THE REFERENCE'S OWN 65px, STATED. Two passes at making the icons
           centre by describing the intent — `align-items: center`, then
           `align-self: center` with the app's margins cleared — both left them
           at 8px from the top with the slack at the bottom: 8 and 24, then 8
           and 15, against the reference's 12 and 13. The strip is a scroll
           container with a 56px min-height and a tallest child of 42 rather
           than 40, and the line kept being placed at the start of a box taller
           than itself.
           A stated 65px did not fix it either, and the measurement said why:
           at 65 the line was TALLER than the content box and centred on it, so
           an icon rendered five pixels ABOVE the padding edge. Slack, wherever
           it comes from, is what puts the icons somewhere other than where the
           padding says. So the height is content-driven — `auto` with the
           56px min-height off — and the inset is stated as 12px, which is the
           gap the reference has. With no slack there is nothing to distribute
           and the icons sit exactly 12px below the header. */
        height: auto;
        min-height: 0;
        padding: 12px var(--frb-space-3, 12px);
        gap: var(--frb-space-2, 8px);
        border-right: 0;
        border-bottom: 1px solid var(--glass-border-color, rgba(127, 127, 127, .22));
    }
    /* CENTRED IN THE STRIP, top and bottom alike. `align-items: center` on the
       rail was not enough: these are items written for a VERTICAL rail, where
       a `margin-bottom: auto` is what pushes a group to the top of the column
       — and in a row that same margin pins them to the top of the strip.
       Measured against the reference: its icons sit 12px below the header and
       13 above the strip's floor; these sat at 8 and 24, which is what "yours
       in the five app touches the top" is. The margins are cleared and each
       item centres itself on the line. */
    body[data-frb-editor] .app-container > .sidebar > * {
        flex: 0 0 auto;
        align-self: center;
        margin-top: 0;
        margin-bottom: 0;
    }
    /* THE SWATCHES GO TO THE END OF THE ROW, not off it. The rail's theme
       footer is a vertical stack pinned to the bottom on a desktop; left that
       way in a horizontal strip it kept its column direction and its
       `margin-top: auto`, so the colour dots stacked and the last of them was
       clipped by the right edge — a gold circle and half a blue one, which is
       what "bad icon sidebar in mobile view" was. The shell already answers
       this for its own rails; the editors' rail is not one it can reach. */
    /* THE PALETTE DOTS COME OFF THE PHONE STRIP. Four colour swatches plus the
       light switch is five controls at the end of a row that already scrolls,
       and the row is 390px wide: the last of them rendered as half a blue
       circle against the screen edge. The reference's phone rail does not
       carry them either — it ends with the light switch and the account chip —
       so this is the same strip, not a shortened one. The swatches are two
       taps away in the desktop rail and on the account page. */
    /* NAMED BY THE DOT, NOT BY ITS WRAPPER — which is the whole reason this
       rule did nothing for a round. It read `> .theme-footer > .theme-dot`,
       and the four career editors do not have a `.theme-footer`: their swatch
       group is `<div id="tour-themes" style="margin-top:auto; display:flex;
       flex-direction:column">`, styled inline and classed for the tour. So the
       selector matched on the two Menu Genies and on nothing else, and Cover
       Letter, CV, Resume and Codefolio all still rendered a stacked column of
       colour dots off the right edge of a 390px strip — a gold circle and half
       a blue one, which is the exact symptom this block was written to end.
       The dot is the thing being removed; the dot is what the rule should say. */
    body[data-frb-editor] .app-container > .sidebar .theme-dot {
        display: none;
    }
    body[data-frb-editor] .app-container > .sidebar > .theme-footer {
        position: static;
        flex-direction: row;
        align-items: center;
        gap: var(--frb-space-2, 8px);
        margin-top: 0;
        margin-left: auto;
        padding-top: 0;
    }
}
/* The band matches the written bars rather than the 44px floor the injected
   fallback gets everywhere else: on these five it is the page's only bar. */
body[data-frb-editor] .frb-topbar {
    min-height: 62px;
    padding-top: var(--frb-space-3, 12px);
    padding-bottom: var(--frb-space-3, 12px);
}

/* ── THE APP HEAD, THE SAME SHAPE AS EVERY OTHER APP'S ───────────────────── */
/* Industry Hub's country page was given as the reference, and the thing it has
 * that an editor did not is this: the app's name at 28px with one sentence
 * under it, between the trail and the work. The five went straight from the
 * trail into a controls row with the name tucked in beside a template select.
 *
 * The sizes are the shell's own tokens rather than numbers of mine, so the two
 * heads are the same head — `--frb-app-title-size`, `--frb-app-sub-size` and
 * the space scale are exactly what `body[data-frb-shell] .page-head` reads.
 * The reason this is not simply that rule is the gate on it: these five are not
 * on `data-frb-shell`, because the shell's column layout is not a two-column
 * editor, and turning it on to borrow a head would bring the rest of it. */
body[data-frb-editor] .frb-apphead {
    position: relative;
    z-index: 4;
    padding: var(--frb-space-5, 20px) var(--frb-space-5, 20px) var(--frb-space-4, 16px);
    border-bottom: 1px solid var(--glass-border-color, rgba(127, 127, 127, .22));
    background: var(--frb-header-bg, rgba(8, 8, 10, .985));
}
body[data-frb-editor] .frb-apphead-title {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--frb-space-3, 12px);
    margin: 0;
    /* THE TOKENS, not the families — the same change the shell's `.page-title`
       and frb-compat's `.page-head h1` took, and for the same reason. The six
       editor heads are the app's name and its one line exactly as the other 68
       are, so they read the same two properties and land in the same face.
       Defaults are what was here, so nothing moves unless the token says to. */
    font-family: var(--frb-app-title-font, var(--frb-font, 'Outfit', system-ui, sans-serif));
    font-size: var(--frb-app-title-size, 28px);
    font-weight: var(--frb-app-title-weight, 800);
    line-height: var(--frb-app-title-leading, 1.2);
    letter-spacing: var(--frb-app-title-tracking, -0.01em);
    color: var(--text-primary, CanvasText);
    overflow-wrap: break-word;
}
body[data-frb-editor] .frb-apphead-sub {
    margin: var(--frb-space-2, 8px) 0 0;
    font-family: var(--frb-ed-mono, 'JetBrains Mono', ui-monospace, monospace);
    font-size: 12.5px;
    line-height: 1.7;
    color: var(--text-secondary, rgba(127, 127, 127, .95));
    max-width: 78ch;
}
/* The editor pages are not `data-frb-shell`, so they do not inherit the token
   the shell sets on its own body — they set the same one here. */
body[data-frb-editor] {
    --frb-app-title-font: var(--frb-ed-display, 'Syne', 'Outfit', system-ui, sans-serif);
    --frb-app-title-tracking: -.035em;
}
/* Offered, not imposed — see the note in editor.html about the tour opening
   itself over the form. Quiet, because it is an offer. */
body[data-frb-editor] .frb-apphead-tour {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-top: var(--frb-space-3, 12px);
    padding: 6px 12px;
    font: inherit;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
    border-radius: 999px;
    border: 1px solid var(--glass-border-color, rgba(127, 127, 127, .3));
    background: transparent;
    color: var(--text-secondary, rgba(127, 127, 127, .95));
    transition: color .15s, border-color .15s;
}
body[data-frb-editor] .frb-apphead-tour:hover {
    color: var(--accent-text, var(--accent-primary));
    border-color: var(--accent-glow);
}
/* THE NAME IS NOT WRITTEN TWICE. The controls row keeps its controls; its copy
   of the title now sits in the head above it, at the size the rest of the
   product uses. Hidden rather than removed — every one of these pages has a
   script that writes a version or a state into that node on load. */
body[data-frb-editor] .editor-header .frb-title-moved { display: none; }
/* And a row that has lost its only left-hand item stops reserving the space.

   IT MUST ASK ABOUT THE ROW, NOT ABOUT THE TITLE'S WRAPPER. This was
   `:has(.frb-title-moved:only-child)`, and `:only-child` is relative to the
   title's OWN parent — so on any page that wraps its title in a div, the test
   passed the moment that div held nothing else, no matter what else was in the
   row beside it.

   Resume Architect wraps its title in a `div.d-flex`. Measured across all six
   editors at 1440: it was the only page where this rule matched, and it took
   down the whole row — the account chip, `templateSelector`, `marginSelector`
   and Minimize Editor — at every width, on a desktop, with the phone rules
   nowhere near it. "I can never set margin in there" is this rule: the control
   is in the markup, correct, and was never on screen.

   The intent is "nothing left but the hidden title", so that is what is now
   written: the row carries the moved title AND carries no control of any kind.
   A row with a select in it is not an empty row however its title is nested. */
body[data-frb-editor] .editor-header > :first-child:has(.frb-title-moved):not(:has(select, button, input, textarea, a)) {
    display: none;
}

/* ── THE HEAD WRAPS RATHER THAN CUTTING ITSELF OFF ───────────────────────── */
/* `.editor-header` is `nowrap` in all five, written before the form column had
   a shared width. At 640 it does not fit: Resume Architect's row measures 427px
   of title and 268px of controls against 590px of usable header, so "Margins:
   Standard" is drawn past the panel's right edge and clipped — on a DESKTOP,
   at 1440, with the phone rules nowhere near it.
   The phone block below already wraps this row. There was never a reason for
   the two widths to answer it differently: a row that does not fit wraps, and
   the column it is in is 640px wide on both. */
body[data-frb-editor] .editor-header,
body[data-frb-editor] .editor-header-row {
    flex-wrap: wrap;
    row-gap: 8px;
    column-gap: 10px;
    min-width: 0;
    white-space: normal;
}
body[data-frb-editor] .editor-header > *,
body[data-frb-editor] .editor-header-row > * { max-width: 100%; }
/* WIDTH, NOT FLEX-BASIS, and this is the whole reason the rule reads this way.
   Resume Architect's `.editor-header` is `flex-direction: COLUMN` — the other
   four are rows — and in a column `flex-basis: 100%` means 100% of the HEIGHT,
   so `flex: 1 1 100%` on its children did not give them a line each, it made
   the container wrap into a second COLUMN: the notice bar drawn beside the
   title row at x=388, on a 390px screen, 364px past the edge.
   `width: 100%` is the same instruction in both directions — a full line in a
   wrapping row, and what a column child already is. */
body[data-frb-editor] .editor-header > .editor-header-row,
body[data-frb-editor] .editor-header > .editor-msg {
    flex: 0 0 auto;
    width: 100%;
}
/* A select never shrinks below its own widest option, at any width. */
body[data-frb-editor] .editor-header select,
body[data-frb-editor] .editor-header-row select { flex: 0 0 auto; }

/* ── STICKY CHROME NEEDS A GROUND, NOT A TINT ────────────────────────────── */
/* `.editor-header` is `position: sticky; top: 0; background: inherit`, and
   what it inherits is `.editor-panel`'s `--panel-bg` — measured at
   rgba(10,10,10,.8) on all five. Eighty percent is right for a glass panel
   floating over a page and wrong for a bar the whole form scrolls underneath:
   on CV at 1440 the section labels SHEET and MARGINS read straight through
   "CV Architect v0.42", and every field in the form does the same on its way
   past. A blur does not fix it — blurring text at 20% opacity gives smeared
   text at 20% opacity.

   AND THE GROUND IS THE PANEL'S OWN, NOT THE PAGE HEADER'S. `--frb-header-bg`
   is declared nowhere in this product, so this always fell through to the
   literal — a near-black bar. On Midnight that happens to match the panel and
   the fault is invisible; on Sunset the panel is rgba(30,30,30,.85) over a grey
   gradient and the bar reads as a black block sitting on top of the form, and
   in light mode it would be a black block on white. What it should be is the
   colour the panel ALREADY APPEARS TO BE, made opaque, which is exactly what
   two layers give: the theme's solid ground, and the panel's own translucent
   colour composited over it. Both come from tokens the theme sets, so it
   follows every theme and both modes without a second list to maintain.

   `background-color` and `background-image` named separately, never the
   `background` shorthand — the shorthand resets the half you did not write,
   which is how 63 selects lost their arrow. */
body[data-frb-editor] .editor-header {
    background-color: var(--bg-solid, #0a0a0a);
    background-image: linear-gradient(var(--panel-bg), var(--panel-bg));
    /* AND IT STOPS BEING MOSTLY PADDING. 20px top and bottom around a 34px row
       of controls is a 75px bar, 40px of which is nothing — the band of empty
       above the form. The row keeps its breathing space; it stops having twice
       the form's. */
    padding-top: var(--frb-space-3, 12px);
    padding-bottom: var(--frb-space-3, 12px);
}

/* ── ONE CONTROL FOLDS THE FORM, NOT TWO ─────────────────────────────────── */
/* All five pin a `.restore-editor-btn` inside the preview at top-left, which is
   the only way back they used to have. It is now the second one, and it is the
   worse of the two: measured on a phone it sits at 20,137 over a sheet that
   starts at 24,127 — the words "Back to Editor" printed across the top of the
   document, which is the one thing on that screen the reader folded the form
   away to see. The fold control says "Edit" in the dock, in the same place in
   both states, and a control that stays put is the point of putting it there.

   Hidden rather than removed, for the same reason as the account chip above:
   each app's own script reads and writes this node on load. */
body[data-frb-editor] .preview-panel .restore-editor-btn { display: none !important; }

/* ── A PHONE FILLS THE FORM, THEN PUTS IT AWAY ───────────────────────────── */
/* The first pass stacked the form above the page, which is the right order to
   read in and the wrong one to work in: the form is 2400-5500px tall, so the
   page it renders is several screens below everything the reader types and
   they never see the two together. It folds instead. Open, the form is the
   screen; folded, the page is — and the control that switches them is the same
   one the desktop uses, moved somewhere a thumb can reach. */
@media (max-width: 620px) {
    body[data-frb-editor] .app-container,
    body[data-frb-editor] .app-container.panel-collapsed {
        display: flex;
        flex-direction: column;
        /* The column starts BELOW the header and the trail band, so what is
           left is the window minus both — and minus the 1px hairline under
           them, which is why the container measured 845 against an 844px
           window. One pixel is not a defect anybody sees; it is a scrollbar
           the page did not need. */
        height: calc(100dvh - var(--frb-editor-top, 44px) - 1px);
        overflow: hidden;
        /* ROOM FOR THE DOCK, WHICH IS ABSOLUTE AND THEREFORE OUT OF FLOW.
           The strip below is placed `position: absolute; bottom: 8px` against
           this container, so the column laid its last child out as if the
           strip were not there and the strip was then painted on top of it.
           The last child on these pages is the enrolment bar, and measured at
           390px its code box and Join button were underneath the dock at every
           scroll position — not merely hard to reach, unreachable, with no
           scroll that would free them.

           44px of strip plus its own 8px inset plus 8px of air. An absolute
           element's offsets resolve against the PADDING box, so the strip
           stays exactly where it was and this reserves the space beneath the
           flow rather than moving anything. */
        padding-bottom: 60px;
    }
    body[data-frb-editor] .editor-panel {
        width: 100%;
        min-width: 0;
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
    }
    /* Folded, the form is gone entirely rather than squeezed — a 40px sliver of
       a form is neither a form nor room for a page. */
    body[data-frb-editor] .app-container.panel-collapsed .editor-panel {
        display: none;
    }
    body[data-frb-editor] .preview-panel {
        flex: 0 0 auto;
        /* A PEEK, NOT A SPLIT. The page below the form is there so a reader
           can see their typing land — it is not the reading view, which is
           what folding is for. 42vh was the first number and it left the form
           390px on an 844px phone: a trail, a header and two fields. 36 gives
           the form back 50px and still shows the top third of the sheet. */
        height: 36vh;
        min-height: 200px;
        padding: 10px 10px 64px;
        border-top: 1px solid var(--glass-border-color, rgba(127, 127, 127, .22));
        /* A page fitted to the panel's WIDTH is taller than the panel, which
           is correct — a document is read down. `overflow: visible` was left
           over from the first pass, where the two panes stacked and the window
           did the scrolling; with the panel a fixed height it meant the sheet
           simply hung out of the bottom of its own box and down the page. */
        overflow: auto;
        overscroll-behavior: contain;
    }
    body[data-frb-editor] .app-container.panel-collapsed .preview-panel {
        flex: 1 1 auto;
        height: auto;
        border-top: 0;
        /* The desktop widens the stage's inset when the form folds away,
           because there it has 800px of new room and a sheet flush against a
           panel edge reads as clipped. A phone has 390px and no such surplus:
           that rule is more specific than the phone's own padding, so it won
           here too and took 28px off a sheet already scaled to fit. */
        padding-left: 10px;
        padding-right: 10px;
    }
    /* ACROSS THE BOTTOM, NOT DOWN THE SIDE. The strip was `position: fixed`,
       which is why the controls "pop out" — it sat over whatever the reader
       was typing. But a vertical strip is the DESKTOP's answer: it spends
       width, and on a 390px phone the 62px it costs is 62px the sheet does not
       get, on the one screen where the sheet has none to spare. Laid across
       the bottom instead: it spends 46px of height, and height is what folding
       the form has just made plentiful. */
    body[data-frb-editor] .toolbar {
        position: absolute;
        top: auto;
        right: 8px;
        left: 8px;
        bottom: 8px;
        width: auto;
        max-width: none;
        max-height: none;
        /* STATED, not inherited. This row is placed by all four edges, so a
           translate on top of them can only move it off one — and one did, for
           a whole round, from a block that had already been replaced. */
        transform: none;
        flex-direction: row;
        align-items: center;
        justify-content: flex-start;
        gap: 5px;
        padding: 5px 7px;
        border-radius: 999px;
        overflow-x: auto;
        overflow-y: hidden;
    }
    body[data-frb-editor] .preview-panel { padding-right: 10px; }
    /* AND THE SHEET KEEPS ITS SHAPE. A max-width constrained the wrapper's
       width while its 297mm height stayed, so the page was not merely too big
       — it was the wrong SHAPE, a squashed column of A4. Measured at 390: the
       sheet 318x1123 inside a 390x354 panel, running 780px past the bottom of
       the screen. The width is left alone and the whole sheet is scaled to fit
       by frb-editor.js, which uses `zoom` so the reader's own +/- transform
       still composes on top of it. */
    body[data-frb-editor] .paper-wrapper {
        max-width: none;
        margin: 0 auto;
    }
    body[data-frb-editor] .toolbar .frb-tb-sep {
        width: 1px; height: 18px; margin: 0 2px;
    }
    body[data-frb-editor] .toolbar .frb-tb-btn,
    body[data-frb-editor] .toolbar button,
    body[data-frb-editor] .toolbar .btn-b {
        width: auto; padding: 6px 8px; white-space: nowrap;
    }
    /* ── WHAT COMES OUT OF THE ROW WHEN IT DOES NOT FIT ──────────────────── */
    /* The row scrolls, so nothing here is lost — but what sits past its right
       edge at rest is the primary download, which is the reason the row is on
       the page. Resume Architect is the widest of the five: eleven controls,
       523px of them in 372px of phone.
       Four of those are not actions. The page-split guide draws a line on a
       sheet rendered at 47% — a hint about pagination nobody can read at that
       size — the zoom readout says "100%" beside the two buttons that change
       it, the fit is automatic down here, and printing from a phone is a trip
       through the browser menu anyway. Dropping those, and 2px of padding from
       each of the rest, brings all five back to a row that ends on screen. */
    body[data-frb-editor] .toolbar .frb-tb-pages,
    body[data-frb-editor] .toolbar .frb-tb-z,
    body[data-frb-editor] .toolbar .frb-tb-fit,
    body[data-frb-editor] .toolbar .frb-tb-print { display: none; }
    body[data-frb-editor] .toolbar .frb-tb-btn > i {
        display: inline-block;
        margin: 0 5px 0 0;
        font-size: 12px;
    }
    body[data-frb-editor] .toolbar .frb-tb-word { display: inline; font-size: 12px; }
    /* Back to a row down here: the dock is one horizontal strip on a phone, so
       an icon over its word is two lines of height for no reason. */
    body[data-frb-editor] .toolbar .frb-tb-btn,
    body[data-frb-editor] .toolbar button,
    body[data-frb-editor] .toolbar .btn-b {
        flex-direction: row;
        row-gap: 0;
    }
    /* Info keeps its icon and drops its word: it is the only glyph in a row of
       words, so it is already the one nobody has to read, and the row has no
       55px to spare — Resume Architect's runs 70px long with it. */
    body[data-frb-editor] .toolbar .frb-tb-info .frb-tb-word { display: none; }
    body[data-frb-editor] .toolbar .frb-tb-info > i { margin: 0; }
    /* Over the sheet from the bottom, where the row that opened it is — a
       panel pinned to the right edge of a 390px screen is the whole screen
       anyway, and anchoring it to the control keeps the two connected. */
    body[data-frb-editor] .frb-meta-pop {
        right: 10px;
        left: 10px;
        top: auto;
        bottom: 62px;
        transform: none;
        width: auto;
        max-height: calc(100dvh - 210px);
    }
    /* ── THE PLAN CHIP DOES NOT STEP UP HERE ANY MORE ────────────────────── */
    /* This used to say `body[data-frb-editor] .frb-plan-float { bottom: 62px }`
       — the chip lifted by one row so it would not sit on the toolbar that
       runs across the foot of an editor at this width.

       That was right when the phone chip was a wide badge in the BOTTOM-RIGHT
       CORNER, which is where the toolbar is. It is now a tall tab on the left
       edge, and it carries its own constant inset that already clears this
       row. All this rule did was override that inset on the five editor pages
       and leave the tab sitting 54px lower than on the other 129 — which is
       exactly the "its place is different in each page" that was reported.

       The clearance did not move into a different rule; it moved into the
       tab's own `bottom`, which is one number for every page. Deleting the
       special case IS the fix. */

    /* ── THE APP'S NAME AND ITS CONTROLS STOP FIGHTING ───────────────────── */
    /* `.editor-header` is `justify-content: space-between; white-space: nowrap`
       with 25px of padding each side, written for a 640px column. At 390 the
       two groups measure 132 and 208 against 340px of usable width, so they
       meet in the middle and the title — which overflows its own box, being
       nowrap — is printed underneath the engine badge. On the phone it read as
       a circle stamped across "Lite v1.0".

       IT WRAPS. It does not squeeze. The first version of this rule spent the
       padding and the save stamp's words — both right, a green check already
       says saved — and then went on to cap the account select at 128px and
       truncate the title, which is the wrong instrument twice over. Measured
       afterwards: Resume Architect's template select 76px wide holding
       "Architect (Adaptive)", which needs 163; its margins select 68px for
       "Margins: Standard"; Menu Genie Lite's 122px for a 201px option. A
       dropdown crushed to less than half its shortest label is not a tighter
       layout, it is a control whose value cannot be read — and the value is
       the entire reason a select is on the screen.

       A phone has one axis going spare and it is not the horizontal one. Two
       lines: the app's name, then its controls, each on a row wide enough for
       what is in it. It costs about 30px of height, and nothing is cut. */
    /* BOTH LEVELS, because the row is not always the header. Four of the five
       put their two groups directly in `.editor-header`; Resume Architect
       wraps them in an `.editor-header-row` and keeps a notice bar as the
       header's second child. Wrapping only the outer element there did nothing
       for the row inside it — still `nowrap` and 427px wide in 390 — and
       pushed the notice onto a second line starting at x=449, thirty pixels
       past the right edge of the screen. A rule about a row has to name the
       element that IS the row. */
    body[data-frb-editor] .editor-header,
    body[data-frb-editor] .editor-header-row {
        flex-wrap: wrap;
        justify-content: flex-start;
        align-items: center;
        row-gap: 8px;
        column-gap: 10px;
        min-width: 0;
        /* The apps write `white-space: nowrap` here for a row they expected
           never to wrap, and every descendant inherits it. */
        white-space: normal;
    }
    body[data-frb-editor] .editor-header {
        padding-left: 12px;
        padding-right: 12px;
    }
    /* Natural width, and never wider than the line it is on. Nothing in here
       shrinks: a select crushed below its own label is a control whose value
       cannot be read, which is the whole reason it is on the screen. */
    body[data-frb-editor] .editor-header > *,
    body[data-frb-editor] .editor-header-row > * {
        flex: 0 0 auto;
        max-width: 100%;
    }
    /* The app's name takes the first line; the controls follow on the next.
       `width`, not `flex-basis` — see the note above about Resume Architect's
       column-direction header. */
    body[data-frb-editor] .editor-header > :first-child,
    body[data-frb-editor] .editor-header-row > :first-child {
        flex: 0 0 auto;
        width: 100%;
    }
    /* A notice is a paragraph, not a chip — its own line, full width. */
    body[data-frb-editor] .editor-header > .editor-msg {
        flex: 0 0 auto;
        width: 100%;
    }
    body[data-frb-editor] .editor-header .save-stamp { font-size: 0; gap: 0; }
    body[data-frb-editor] .editor-header .save-stamp i { font-size: 13px; }
    /* AND THE SELECTS THEMSELVES REFUSE TO SHRINK. The rules above reach the
       header's own children; a select sits one level further in, inside the
       controls group, and as a flex item of THAT it was still being squeezed —
       Resume Architect's to 147px for a label needing 163. A select's natural
       width IS its widest option, and that is the width it keeps. */
    body[data-frb-editor] .editor-header select,
    body[data-frb-editor] .editor-header-row select { flex: 0 0 auto; }
    /* And a group that still cannot fit scrolls sideways rather than clipping
       — the last resort, and it keeps every control at its real width. */
    body[data-frb-editor] .editor-header > :last-child,
    body[data-frb-editor] .editor-header-row > :last-child {
        overflow-x: auto;
        scrollbar-width: none;
    }
    body[data-frb-editor] .editor-header > :last-child::-webkit-scrollbar,
    body[data-frb-editor] .editor-header-row > :last-child::-webkit-scrollbar { display: none; }
    body[data-frb-editor] .frb-dock-info { display: none; }
    /* The split strip has no meaning in one column, so the fold control moves
       to the bottom of the window where a thumb is.

       COLLAPSED, NOT HIDDEN. `display: none` on the strip was the obvious way
       to take it out of a single-column layout, and it took the button with
       it: a `position: fixed` child of a `display: none` parent is not
       rendered at all, so the fold control measured 0x0 on every phone and the
       form could not be put away — the one thing this whole layout exists to
       do. Zero height with the overflow left open takes the strip out of the
       column and leaves its child on the screen. */
    body[data-frb-editor] .resizer-bar {
        flex: 0 0 0;
        height: 0;
        min-height: 0;
        padding: 0;
        border: 0;
        background: none;
        overflow: visible;
    }
    body[data-frb-editor] .resizer-bar > .grip-handle { display: none; }
    /* IN THE ROW NOW, not floating over it — see placeFold(). The fold pill
       and the export row both wanted the bottom of a 390px screen and landed
       on top of each other: the pill parked across the middle of the row with
       its left half unreachable behind it. Stacking them costs 110px of a
       354px panel, which is a third of the page. So on a phone they are one
       strip, with the fold as its first control, where it reads as what it is
       — another thing you can do to the view.

       Static, because a positioned pill inside a scrolling row does not scroll
       with it, which is how it ended up parked over the buttons behind it. */
    body[data-frb-editor] .frb-split-fold,
    body[data-frb-editor].panel-collapsed .frb-split-fold {
        position: static;
        transform: none;
        width: auto;
        height: auto;
        gap: 7px;
        padding: 6px 11px;
        border-radius: 999px;
        font-size: 12px;
        font-weight: 700;
        color: var(--text-primary);
    }
    body[data-frb-editor] .frb-split-fold .frb-split-word { display: inline; }
}
body[data-frb-editor] .frb-split-word { display: none; }


/* ═══════════════════════════════════════════════════════════════════════════
   FOLDING THE HEAD HANDS THE SCREEN TO THE PREVIEW — SAY SO

   On a phone the head is about a fifth of the window, and folding it is the
   one gesture that gives that fifth to the sheet. But the sheet is a white
   page on a dark ground that was already on screen, so nothing about it
   changes when it grows: the reader presses the control, the page moves, and
   there is no signal that the thing they wanted more of is the thing that got
   it.

   So the preview takes a ring for a moment. It is an animation rather than a
   permanent outline because the point is the TRANSITION — a border that stays
   is decoration, and decoration around the working surface is the opposite of
   what folding the head was for.

   Phone only. On a desktop the two panes are side by side and the fold moves
   the boundary by a few percent, which announces itself.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 900px) {
    body[data-frb-editor] .frb-preview-lit {
        animation: frb-preview-focus 1.25s var(--frb-ease, ease-out) 1;
    }
    @keyframes frb-preview-focus {
        0%   { box-shadow: 0 0 0 0 var(--accent-primary, #d4af37),
                           0 0 0 0 var(--accent-glow, transparent); }
        22%  { box-shadow: 0 0 0 2px var(--accent-primary, #d4af37),
                           0 0 26px 4px var(--accent-glow, transparent); }
        100% { box-shadow: 0 0 0 0 rgba(0, 0, 0, 0),
                           0 0 0 0 rgba(0, 0, 0, 0); }
    }
}
@media (prefers-reduced-motion: reduce) {
    body[data-frb-editor] .frb-preview-lit { animation: none; }
}
