/* Editor shell */
.editor-main { padding: 0; max-width: none; }
/* The editor keeps its mirrored layout but wears PDFwerk's own palette: a
   calm teal accent on soft neutrals, warm paper tones for the action bar. */
.editor {
  min-height: calc(100vh - var(--header-h));
  --sk-chrome: var(--canvas);
  --sk-chrome-line: var(--line);
  --sk-blue: var(--accent);
  --sk-blue-line: var(--accent-line);
  --sk-blue-soft: var(--accent-soft);
  --sk-active-bg: var(--accent-soft);
  --sk-tool-line: var(--accent-line);
  --sk-ink: var(--ink);
  --sk-sheet: var(--white);
  /* The workspace behind the sheets. Sejda's editor is not white to the edges:
     the sheet sits on a slightly grey canvas, which is what gives a page its
     margin. With a white canvas the sheet and the space around it were the same
     colour, so there was no room to be seen — the complaint that started this.
     Same grey as the preview page's stage, so the two viewers look related. */
  --sk-canvas: var(--canvas);
  --sk-footer: var(--amber-bg);
  --sk-footer-line: var(--amber-line);
  --sk-save: var(--accent);
  --sk-save-dark: var(--accent-dark);
}

/* document title strip above the toolbar */
.doc-header {
  background: var(--sk-chrome); border-bottom: 1px solid var(--sk-chrome-line);
  text-align: center; padding: 9px 16px 7px;
}
.doc-header .dh-title { margin: 0; font-weight: 600; color: #4b5257; font-size: .92rem; }
.doc-header .dh-sub { margin: 2px 0 0; color: #7d848a; font-size: .8rem; }

.start-screen { max-width: 720px; margin: 0 auto; text-align: center; padding: 60px 20px; }
.start-screen .dropzone { margin-top: 26px; }

/* Refresh continuation ("refresh shiuld not leave the editor"): while the
   document reopens, the editor chrome itself is on screen — toolbar, page
   area — with only a quiet spinner where the page will land. The tool page
   never paints and nothing looks like a separate screen. The reopen-prep
   class comes from an inline script in editor.html; editor.js removes it when
   the document opens (buildPages wipes the viewer, spinner and all) or when
   there is nothing to reopen. */
.reopen-screen { display: none; }
/* 🚨 app.css has a site-wide `[hidden] { display: none !important; }` — the
   workspace's [hidden] must be beaten with !important or refresh shows a
   bare blue page (the client's "blue screen"). */
#editor.reopen-prep #workspace { display: flex !important; flex-direction: column; pointer-events: none; }
#editor.reopen-prep .ed-back-wrap { display: none; }
#editor.reopen-prep #start-screen { display: none; }
#editor.reopen-prep .reopen-screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 64px 20px;
  text-align: center;
}
/* No label, no full-page state — just a quiet spinner in the page area, so a
   refresh reads as the editor reloading itself ("nothinh it will refresh the
   editor only no opening anything"). */
.reopen-screen .processing-spin { width: 38px; height: 38px; border-width: 4px; }
/* The back control, in the page's STANDARD container (1160px + 22px gutter,
   same as .container in app.css) so it lands in exactly the same corner as
   every other tool page's .page-back (client: "fix the back button
   position"). Hidden once a document loads — see load() in editor.js. */
.ed-back-wrap { max-width: 1160px; margin: 0 auto; padding: 26px 22px 0; text-align: left; }
/* "or start with a blank document" — the start screen's second way in,
   matching their upload page. */
/* The start screen's closing trio — "or start with a blank document", the
   Resume button and the privacy note — used to clump: the link had no space
   below it (the note sat right on its baseline) and the button carried no
   margins at all. Client: "needs gap between or start to this 3". */
.ed-blank { margin: 20px 0 16px; }
.ed-blank + .ed-blank { margin-top: 0; }   /* blank document · stamp paper doors stack tight */
.ed-blank a {
  color: var(--ink); font-weight: 500; text-decoration: none;
}
.ed-blank a:hover { color: var(--brand); }
#resume-last { margin: 0 0 16px; }

.workspace {
  display: flex; flex-direction: column; position: relative;
  height: calc(100vh - var(--header-h)); height: calc(100dvh - var(--header-h));
  transition: height .22s ease;
}

/* The editor is an APPLICATION, not a page: the site masthead is hidden — that
   big navigation is what made the editor read as "a browser tab" (client:
   "thats tab one not desktop") — and the workspace takes the whole window,
   with the brand and the tool index riding at the head of the tool strip
   instead, the way Sejda's editor carries its logo and "All Tools". */
body.app-shell .site-header { display: none; }
body.app-shell .workspace { height: 100vh; height: 100dvh; }

/* Strip brand chips + the start screen's compact top row. */
.tb-homebar { padding: 2px 2px 2px 3px; gap: 2px; }
.tb-home {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--accent); color: #fff; text-decoration: none;
  font-weight: 800; font-size: .78rem; letter-spacing: -.02em;
  border-radius: 4px; padding: 6px 9px;
}
.tb-home span { font-weight: 600; opacity: .88; }
.tb-home:hover { background: var(--accent-dark); color: #fff; text-decoration: none; }
.tb-nav {
  display: inline-flex; align-items: center; padding: 6px 10px;
  border-radius: 4px; font-size: 12.5px; font-weight: 600;
  color: #5d6771; text-decoration: none; white-space: nowrap;
}
.tb-nav:hover { background: var(--sk-blue-soft); color: var(--sk-blue); text-decoration: none; }
.ed-topnav { display: flex; gap: 8px; justify-content: center; margin-bottom: 18px; }
/* The tool strip floats the way Sejda's does (recorded live on their editor):
   at rest it sits below the site header; the moment the document is scrolled
   the header slides away and the strip climbs to the very top of the screen,
   the sheet sliding under it. The header keeps its box via a negative margin so
   the workspace glides up on the same transition, and the workspace grows to
   full height so nothing jumps at the bottom edge. The class is toggled by
   syncHeaderFloat() in editor.js from the viewer's scroll position. */
.site-header { transition: margin-top .22s ease, transform .22s ease, box-shadow .25s ease; }
body.ed-scrolled .site-header { margin-top: calc(-1 * var(--header-h)); transform: translateY(-100%); }
body.ed-scrolled .workspace { height: 100vh; height: 100dvh; }
/* The pinned tool strip (z 80) and page-controls row (z 78) float over the
   workspace with a higher z than the site header (z 50). That made the
   header's dropdown panels open UNDERNEATH the two white bars: on a narrow
   editor, tapping the hamburger showed the menu with its first rows (the pill,
   Pricing) hidden behind the chrome — leaving a stray "Help / Log in / Sign
   up" fragment floating over the grey workspace (the client's screenshot).
   The header now paints above the chrome while it rests, so an opened menu
   covers the bars like any dropdown should. When the header is sliding away
   on scroll (ed-scrolled), it drops back BELOW the strip so the strip's
   ride-to-top is never painted over. */
body.app-shell .site-header { z-index: 120; }
body.app-shell.ed-scrolled .site-header { z-index: 40; }
/* chrome bars keep their natural height; the page area takes the rest and
   scrolls internally, like Sejda, instead of scrolling the whole window */
.toolbar, .page-controls { flex: 0 0 auto; }
/* Short windows: drop the title strip and tighten the chrome so the page
   keeps a usable amount of height. Phones in portrait are under 720px tall too,
   so this fires there as well — but it only trims padding, never the 40px touch
   targets the 900px block sets. A shorter target would hand the sheet back ~10px
   and cost the finger 6, which is the wrong trade. */
@media (max-height: 720px) {
  .page-controls { padding: 4px 14px; }
  .toolbar { padding: 6px 10px 4px; }
  .viewer-wrap { padding-top: calc(var(--ed-chrome-h, 134px) + 4px); }
}

/* toolbar: a centred floating strip, like Sejda's web editor — and it FLOATS
   over the sheet the way theirs does: the recording of their fixed bar is that
   it stays pinned at the top while the document scrolls beneath its edge, so
   the strip and the page-controls row are absolutely pinned to the top of the
   workspace and the viewer scrolls under them. JS (syncChromeMetrics in
   editor.js) writes --ed-toolbar-h / --ed-chrome-h from the live heights, so
   the viewer can reserve the resting space above the first sheet. */
.toolbar {
  display: flex; justify-content: center; align-items: center;
  /* Checked live on their editor: the tool strip is a TRANSPARENT band pinned
     to the top of the workspace — the buttons float on it in their own white
     groups — inset 12px from the edges. It never rides away: the header's own
     slide carries it to the viewport edge when the document scrolls, and the
     page-controls pill below it floats with it (client: "need to float trash
     rotate bar too"). */
  background: transparent; border-bottom: 0;
  padding: 10px 0 8px; gap: 8px; flex-wrap: wrap;
  position: absolute; top: 0; left: 12px; right: 12px; z-index: 80;
  border-radius: 0; box-shadow: none;
}
.toolbar-group {
  display: flex; align-items: center; gap: 0; flex-wrap: wrap;
  background: var(--white); border: 1px solid var(--sk-tool-line);
  border-radius: 5px; padding: 2px;
  /* Their groups carry a soft glow so the strip reads as a floating layer on
     the page (measured: rgb(158,202,237) 0 0 10px — in our accent). */
  box-shadow: 0 0 10px rgba(196, 226, 218, .95);
}
.toolbar-group.right { margin-left: 0; gap: 0; flex-shrink: 0; }
/* Feedback sits in the right group: a word with its bubble, teal like the site
   accent so it reads as "talk to us" rather than another tool. */
.tb-feedback .mi { color: var(--accent); }
.menu { position: relative; }
/* Do NOT add overflow (e.g. overflow-x: auto) to .toolbar-group: setting one
   axis makes the other compute to hidden, which clips the .menu-panel dropdown
   and the menus silently stop appearing. Keep the row wrapping instead. */
/* Flat buttons inside the strip, with hairline dividers between them. */
.menu-btn, .tool-btn {
  background: none; border: 1px solid transparent; border-radius: 3px;
  padding: 4px 7px; font: inherit; font-size: 13px; line-height: 1.45;
  cursor: pointer; color: var(--sk-ink); vertical-align: middle;
  box-shadow: none;
}
/* Hairline dividers between the segments of the strip: Sejda's toolbar reads as
   one connected control. Covers any mix of dropdowns, split buttons and direct
   tool buttons — the first segment never gets one. */
.toolbar-group > .menu + .menu > .menu-btn,
.toolbar-group > .menu + .tool-btn,
.toolbar-group > .tool-btn + .menu > .menu-btn,
.toolbar-group > .tool-btn + .tool-btn {
  border-left: 1px solid var(--sk-tool-line); border-radius: 0 3px 3px 0;
}
/* Split button, as Sejda's Text: the label half arms the tool, the caret half
   opens the options without disarming it. */
.menu.split { display: inline-flex; align-items: stretch; }
.menu.split > .menu-btn { border-radius: 3px 0 0 3px; }
.menu-caret {
  position: relative; width: 17px; padding: 0; background: none;
  border: 1px solid transparent; border-left: 1px solid var(--sk-tool-line);
  border-radius: 0 3px 3px 0; cursor: pointer; color: var(--sk-ink);
}
.menu-caret::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 4px solid currentColor;
}
.menu-caret:hover { background: var(--sk-blue-soft); color: var(--sk-blue); }
.menu-btn:hover, .tool-btn:hover { background: var(--sk-blue-soft); color: var(--sk-blue); }
.menu-btn.active, .tool-btn.active {
  background: var(--sk-active-bg); border-color: var(--sk-blue); color: var(--sk-blue);
  box-shadow: none;
}
.caret { display: inline-block; width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid currentColor; margin-left: 3px; vertical-align: middle; }
.menu-panel {
  display: none; position: absolute; top: 38px; left: 0; background: var(--white);
  border: 1px solid rgba(0, 0, 0, .15); border-radius: 4px;
  box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
  min-width: 160px; margin-top: 2px; padding: 5px 0; z-index: 70; font-size: 14px;
}
.menu.open .menu-panel { display: block; }
.menu:has(.menu-grid) .menu-panel { width: 340px; }
.menu-item {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: 6px 18px 6px 12px; border-radius: 0; font: inherit; font-size: 14px;
  line-height: 1.35; cursor: pointer; color: #333; white-space: nowrap;
}
/* Menu rows are mostly buttons; the "All tools" row is a plain link and must not
   look like one. */
a.menu-item { text-decoration: none; }
.mc {
  width: 12px; height: 12px; padding: 0; border-radius: 50%; cursor: pointer;
  background: var(--c); border: none;
  /* Sejda's circles have no border; a hairline inset keeps pale yellows and
     whites visible without the heavy outline that made them look boxy. */
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
  transition: transform .12s ease, box-shadow .12s ease;
}
.mc:hover {
  transform: scale(1.25);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12), 0 0 0 3px rgba(31, 122, 109, .18);
}
.mc[aria-pressed="true"] {
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12), 0 0 0 2px #fff, 0 0 0 4px var(--c);
}
.mc[data-color="#000000"], .mc[data-color="#000"] { box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .55); }

/* Sejda's Forms menu: field types two abreast, each with its own icon.
   Section links and grid cells use Sejda's `.forms-opts section a` metrics. */
.menu-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 2px 10px;
  padding: 2px 5px 2px 10px;
}
.menu-grid .menu-item-tool {
  display: flex; align-items: center; gap: 0; width: auto; min-width: 35px;
  padding: 5px 10px 5px 5px; border-radius: 5px; white-space: nowrap;
  color: #000; font-size: 14px;
}
.menu-grid .menu-item-tool .mi { width: 17px; height: 17px; opacity: 1; flex: 0 0 auto; margin: 0 7px 0 0; }
.menu-item > .mi { width: 15px; height: 15px; margin-right: 8px; opacity: .75; vertical-align: -2px; }
/* Sejda's Forms section icons are solid black, not muted. */
.menu:has(.menu-grid) .menu-item > .mi { width: 17px; height: 17px; opacity: 1; }
/* "Add text and symbols": an icon-only text tool, then the three glyph
   buttons in the right half of the row, like Sejda's two-column section. */
.menu-row-tools { display: flex; align-items: center; gap: 8px; padding: 2px 5px 2px 8px; }
.menu-row-tools .menu-item-tool {
  display: inline-flex; align-items: center; gap: 0; min-width: 35px;
  padding: 5px 10px 5px 5px; background: none; border: none; border-radius: 5px;
  cursor: pointer; font: inherit; font-size: 14px; color: #000;
}
.menu-row-tools .menu-item-tool .mi { width: 17px; height: 17px; opacity: 1; margin-right: 7px; }
.menu-grid .menu-item-tool:hover { background-color: #f6f6f6; }
.menu-row-tools .menu-item-tool:hover { background-color: #f6f6f6; }
/* The glyph buttons: Sejda draws them as 20px icons with real spacing, the
   text tool sits alone in the left half and the glyphs centre in the right. */
.menu-row-tools .menu-item-tool { width: auto; }
.menu-row-tools .menu-item-tool[data-tool="text"] { flex: 0 0 50%; }
.menu-row-tools .menu-item-tool[data-tool^="sym-"] {
  margin-left: 0; padding: 2px 8px; min-width: 0; justify-content: center;
  font-size: 20px; line-height: 1; color: #000;
}
.menu-row-tools .menu-item-tool[data-tool="text"] .mi { margin-right: 0; width: 26px; height: 20px; }
.menu-row-tools .menu-item-tool[data-tool="sym-cross"] { margin-left: auto; }
.menu-row-tools .menu-item-tool[data-tool="sym-dot"] { margin-right: auto; }

/* Sign dropdown, Sejda's panel: the saved signature shown large with a grey ×
   beside it, a hairline under it, then a blue-outlined "New Signature". With
   nothing saved the panel is just that button — Sejda has no placeholder text. */
.menu:has(.sign-gallery) .menu-panel { width: 268px; padding: 0 0 14px; }
.sign-gallery { max-height: 240px; overflow-y: auto; padding: 10px 10px 0; }
.sign-gallery:empty { display: none; }
.sign-entry { display: flex; align-items: flex-start; gap: 4px; margin: 0; }
.sign-entry-pick {
  flex: 1 1 auto; padding: 4px; background: #fff; cursor: pointer;
  border: 1px solid transparent; border-radius: 3px; text-align: center;
}
.sign-entry-pick img { max-height: 92px; max-width: 100%; }
.sign-entry-pick:hover { border-color: var(--sel); background: var(--sk-blue-soft); }
.sign-entry-delete {
  flex: 0 0 auto; width: 30px; height: 30px; padding: 0; border: 0;
  background: none; color: var(--muted); border-radius: 3px;
  font-size: 24px; line-height: 1; cursor: pointer;
}
.sign-entry-delete:hover { color: #333; background: #f0f0f0; }
.sign-new {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin: 14px auto 0; padding: 0 20px; min-width: 200px; height: 44px;
  background: #fff; color: var(--sk-blue); border: 1px solid var(--sk-blue);
  border-radius: 3px; font: inherit; font-size: 16px; cursor: pointer;
}
.sign-new:hover { background: var(--sk-blue-soft); }
.sign-new .mi { width: 16px; height: 16px; margin: 0; }
/* The divider only makes sense when a signature sits above it. */
.menu-panel:has(.sign-gallery:empty) .menu-sep { display: none; }

.menu-item:hover { background-color: #f5f5f5; color: #262626; }
.menu-sep { height: 1px; background: var(--line); margin: 5px 0; }
/* Sejda's section label (their `li.text-divider` in the current build): plain
   uppercase 11px grey text on the panel background, no filled band. */
.menu-head {
  background: none; font-size: 11px; line-height: 1; margin: 15px 0 5px;
  padding: 0 12px; text-align: left; text-transform: uppercase;
  color: #8b9298; font-weight: 600; letter-spacing: .6px;
}
.menu-head:first-child { margin-top: 7px; }

/* "Show annotations" is a real checkbox row inside the panel, not a menu item. */
.menu-check {
  display: flex; align-items: center; gap: 9px; padding: 6px 12px;
  font-size: 14px; color: #333; cursor: pointer; white-space: nowrap;
}
.menu-check input[type=checkbox] {
  width: 15px; height: 15px; margin: 0; cursor: pointer; accent-color: var(--sk-blue);
}
.menu-check:hover { background: #f5f5f5; }

/* Images panel: Sejda previews the picked image, then stacks three full-width
   buttons under it — the new-image one outlined in blue. */
.menu:has(.menu-images) .menu-panel { width: 236px; padding: 9px 0 11px; }
.menu-thumb { margin: 0 10px 10px; }
.menu-thumb img {
  display: block; width: 100%; max-height: 88px; object-fit: contain;
  border: 1px solid var(--line); border-radius: 3px; background: #fff;
}
.menu-thumb figcaption { margin-top: 5px; font-size: 12px; color: #6f787e; }
.menu-thumb[hidden] { display: none; }
.menu-cta { display: flex; flex-direction: column; gap: 8px; padding: 0 10px; }
.menu-cta .menu-item, .menu-cta button {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  width: 100%; padding: 8px 10px; font: inherit; font-size: 14px; cursor: pointer;
  background: #fff; color: #333; white-space: nowrap;
  border: 1px solid #ccc; border-radius: 3px;
}
.menu-cta .menu-item:hover { background: #f5f5f5; }
.menu-cta .mi { width: 15px; height: 15px; margin: 0; opacity: 1; }
.menu-cta .cta-primary { color: var(--sk-blue); border-color: var(--sk-blue); }
.menu-cta .cta-primary:hover { background: var(--sk-blue-soft); }

/* Sejda puts a colour palette on the annotation rows themselves. The label
   column is a fixed 120px wide (their `span{width:120px}`), so every row's
   circles start at the same x — the 3-circle rows are not right-aligned. */
.menu-row { display: flex; align-items: center; gap: 0; padding-right: 10px; }
.menu-row .menu-item-tool { flex: 0 0 auto; width: 110px; padding: 6px 6px 6px 12px; }
/* Sejda spaces the annotation swatches well apart so a misfire is unlikely,
   and keeps them small: five of them plus the label still fit a 215px panel. */
.menu-colors { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* "Show annotations" hides the marking-up tools' output without deleting it. */
/* "Show annotations" off: the text markup the Annotate menu lays down and the
   freehand ink go away, so the page can be read. Shapes stay — a Rectangle or a
   Line belongs to the Shapes menu, not to the annotations. Hiding the whole
   `.pg-svg` layer, as this used to, took every line and arrow off the page with
   it; verified against Sejda, whose toggle leaves a drawn rectangle alone. */
.hide-annots .el-highlight, .hide-annots .el-underline,
.hide-annots .el-strikeout, .hide-annots .pg-svg path.annot-ink { display: none; }

/* side panels: attachments and change history */
.side-panel {
  position: absolute; right: 14px; bottom: 74px; width: min(300px, 86vw);
  background: var(--white); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: 0 10px 34px rgba(0, 0, 0, .16); padding: 14px; z-index: 95;
  max-height: 60vh; overflow: auto;
}
.side-head { display: flex; align-items: center; justify-content: space-between; }
.side-head strong { font-size: .92rem; }
.attach-list, .history-list { list-style: none; margin: 10px 0; padding: 0; }
.attach-list li, .history-list li {
  display: flex; align-items: center; gap: 8px; padding: 6px 8px;
  border: 1px solid var(--line); border-radius: 8px; margin-bottom: 6px;
  font-size: .85rem;
}
.attach-list li span:first-child { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attach-list .tool-btn { padding: 2px 7px; }
@media (max-width: 900px) {
  .attach-list .tool-btn { min-height: 34px; min-width: 34px; }
}

/* page thumbnails: hidden by default, Sejda shows them on demand */
.thumbs {
  display: none;
  width: 176px; flex-shrink: 0; background: var(--white);
  border-right: 1px solid var(--line); padding: 12px 10px; overflow-y: auto;
}
.thumbs.open { display: block; }
.thumbs h4 {
  font-size: .8rem; text-transform: uppercase; color: var(--muted);
  display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;
}
/* Every page now carries an insert row, so the thumbs need less air between
   them to keep the list compact. */
.thumbs-list { display: flex; flex-direction: column; gap: 8px; }
.thumb {
  position: relative; border: 1px solid var(--line); border-radius: 8px;
  padding: 5px; background: var(--white); cursor: grab; transition: opacity .15s ease;
}
.thumb canvas { display: block; width: 100%; height: auto; border-radius: 4px; background: #fff; }
.thumb.deleted { opacity: .4; }
.thumb.deleted canvas { filter: grayscale(1); }
/* A page marked for deletion must LOOK it on the sheet itself: a deleted
   blank page was pixel-identical to a live one, so the client read the
   unchanged sheet as "it can't delete the inserted page". Dim it like the
   thumbnail and hang a clear tag on it. */
.page-wrap.deleted { opacity: .45; filter: grayscale(1); }
.page-wrap.deleted::after {
  content: "Marked for deletion";
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  background: rgba(220, 38, 38, .92); color: #fff;
  font-size: 13px; font-weight: 600; padding: 6px 14px; border-radius: 999px;
  pointer-events: none; z-index: 3; white-space: nowrap;
}
.thumb.dragging { opacity: .45; }
.thumb.drop-before { box-shadow: 0 -3px 0 var(--green); }
.thumb.drop-after { box-shadow: 0 3px 0 var(--green); }
.thumb-num { font-size: .72rem; color: var(--muted); text-align: center; margin-top: 4px; }
.thumb-actions { display: flex; justify-content: center; gap: 4px; margin-top: 4px; }
.thumb-actions button {
  border: 1px solid var(--line); background: var(--white); border-radius: 6px;
  font-size: .72rem; line-height: 1; padding: 3px 6px; cursor: pointer;
}
.thumb-actions button:hover { background: var(--bg); }
.thumb-actions .thumb-del:hover { background: #fdecec; border-color: #f3b4b4; }
@media (min-width: 1025px) { .thumbs.collapsed { display: none; } }

/* recovery banner */
.recovery-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--amber-bg); border-bottom: 1px solid var(--amber-line);
  padding: 8px 14px; font-size: .88rem;
}
.recovery-bar #recovery-text { margin-right: auto; color: var(--amber-ink); }

/* browser-zoom warning (see the guard in editor.js) */
.zoom-warning {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  background: var(--red-bg); border-bottom: 1px solid #f3b4b4;
  color: var(--red); padding: 8px 14px; font-size: .88rem;
}
.zoom-warning > span { margin-right: auto; }
.zoom-warning kbd {
  background: #fff; border: 1px solid #e6c6c2; border-radius: 3px;
  padding: 0 4px; font: inherit; font-size: .82em;
}

/* body layout */
.editor-body { display: flex; flex: 1 1 auto; min-height: 0; }
/* The canvas: grey around the sheet, with real padding on every side. The
   padding is part of the scrollable area, so at high zoom you can always scroll
   until the page edge has room around it instead of sitting flush against the
   window. Sejda's editor reads the same way. */
/* The viewer starts at the top of the workspace and its sheets scroll UNDER the
   floating chrome, so the resting space above the first sheet is the chrome's
   live height (--ed-chrome-h, written by syncChromeMetrics()) plus a gap. The
   bottom padding is just air around the last sheet (it used to clear the beige
   Apply tray, which is gone — the canvas now runs to the bottom edge). */
.viewer-wrap { flex: 1; overflow: auto; background: var(--sk-canvas); padding: calc(var(--ed-chrome-h, 146px) + 8px) 32px 48px; }
/* With the chrome floating, a visible recovery banner is the first in-flow row
   of the workspace and must start below it. */
.recovery-bar { margin-top: var(--ed-chrome-h, 146px); }
/* …but then the scroller must NOT reserve the chrome height a second time
   below that banner, or the two reserves stack into a giant dead hole
   (client: "it hs huge gap or space" — band→sheet was 380px on a 640px
   pane). With the banner present, the reserve is already covered; the first
   sheet just needs its normal air. */
.workspace:has(#recovery-bar:not([hidden])) .viewer-wrap { padding-top: 8px; }
/* The scanned-document notice is the OTHER first-in-flow banner, and it never
   got the same clearance: opened on a PDF whose first page has no text layer,
   it drew as a cream strip UNDER the floating toolbar — words clipped at the
   tool buttons and "Open OCR" stranded among them (client: "glitch grey bar
   over there"). Same pattern as the recovery banner: clear the chrome, and
   the scroller parks its own reserve while a banner is up. Two banners at
   once stack cleanly — only the first carries the clearance. */
.scan-notice { margin-top: var(--ed-chrome-h, 146px); }
.workspace:has(#scan-notice:not([hidden])) .viewer-wrap { padding-top: 8px; }
#scan-notice:not([hidden]) + #recovery-bar:not([hidden]) { margin-top: 0; }
/* The gap has to be tall enough to hold the divider row that floats in it. */
.viewer { display: flex; flex-direction: column; align-items: center; gap: 46px; min-width: min-content; }
.page-wrap {
  position: relative; background: #fff;
  box-shadow: 0 1px 3px rgba(20, 30, 35, .10), 0 6px 20px rgba(20, 30, 35, .10);
}
.page-wrap canvas { display: block; }
/* Page divider, as Sejda draws it: a row in the gap between sheets carrying a
   page number and a button to insert a blank page. Ours hangs below its own
   sheet, because it inserts the new page straight after that one — the divider
   above the first sheet used to push that page down when clicked. The row itself
   has no height, so the divider floats in the space between sheets without
   making them drift apart. */
.page-gap { position: relative; width: 100%; height: 0; flex: 0 0 auto; }
.page-gap.deleted { opacity: .4; }
/* The divider above the FIRST sheet needs real height: between two sheets the
   zero-height divider floats in the gap the sheets already have, but above
   page 1 there is only the chrome reserve — the button would sit half over
   the sheet's top corner. A real strip puts it in its own air, like Sejda's
   marker above page 1. */
.page-gap.page-gap-top { height: 44px; }
.page-badge {
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  min-width: 2.4em; padding-right: 10px; text-align: right;
  font-size: 22px; font-weight: 600; line-height: 1;
  color: #b9cbe0; user-select: none; pointer-events: none;
}
.page-gap .insert-here {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
}
/* The insert button is shared by the canvas divider and the thumbnail list, so
   every page carries one in both places. */
.insert-here {
  display: inline-flex; align-items: center; gap: 8px; white-space: nowrap;
  background: var(--white); color: var(--sk-blue);
  border: 1.5px solid var(--sk-blue); border-radius: 4px;
  padding: 5px 16px; font: inherit; font-size: 14px; cursor: pointer;
}
.insert-here:hover { background: rgba(37, 99, 235, .07); }
.ih-plus {
  display: inline-flex; align-items: center; justify-content: center;
  width: 21px; height: 21px; flex: 0 0 auto;
  border: 1.5px solid currentColor; border-radius: 50%;
  font-size: 17px; font-weight: 700; line-height: 1;
}
/* In the thumbnail list it is compact: the panel is only 176px wide — but the
   mark still has to read as a + to aim at (client: "plus icon bit small",
   then "make it big a bit"). */
.thumb-insert { display: flex; justify-content: center; }
.thumb-insert .insert-here { gap: 5px; padding: 3px 8px; font-size: 11px; }
.thumb-insert .ih-plus { width: 18px; height: 18px; font-size: 14px; }
/* Editor selection chrome. Sejda selects objects in blue; the brand green belongs
   to navigation and buttons, not to objects on the page — a green ring read as a
   stray dot parked on the sheet. Scoped variables so the brand stays green. */
:root { --sel: var(--accent); --sel-ink: var(--accent-dark); --sel-soft: rgba(31, 122, 109, .08); }
.text-layer { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
/* Existing form fields, rendered live like Sejda's fill mode: the container
   stays out of the pointer path, each control is interactive on its own.
   STYLING IS SEJDA'S, MEASURED FROM THEIR EDITOR: a flat pale-blue wash
   (rgba(239,242,255,.95)) with NO border, on every field including checkboxes and
   radios. That wash is the answer to "why is there a blue box on forms" — it is
   how a fillable area is made visible, and it is a colour, not a frame. The
   previous rule was a 5%-blue tint PLUS a dashed blue border, and the dashed
   border was the "box type thing"; the checkbox had no styling at all, so it was
   invisible until you happened to click it. */
.fill-layer { position: absolute; inset: 0; pointer-events: none; }
.fw-field {
  position: absolute; pointer-events: auto;
  /* Site text face on the form controls too (client: "use that font for all
     text whole site") — against the UA default, controls would render the
     system sans instead. */
  font-family: inherit;
  /* The overlay is a LATER sibling of the fill layer and covers the whole
     sheet with pointer-events: auto, so without a z-index every control sits
     under it and a real mouse click can never reach a field — the press landed
     on the overlay and could even start a text edit under the cursor. z-index 1
     lifts the fields above the overlay; `.el` (z-index 2) keeps placed objects
     above the fields so a signature dropped on one stays selectable. */
  z-index: 1;
  background: rgba(231, 243, 240, .95);
  border: 0;
  padding: 0 2px; font: inherit; color: inherit;
}
.fw-field:hover { background: rgba(216, 238, 233, .95); }
/* With a tool armed the sheet is a placement surface, so the fields step aside
   — otherwise you could not drop a signature ONTO a signature field, because
   the field would swallow the press. */
.viewer.tool-armed .fw-field { pointer-events: none; }
/* Form Edit mode: an existing field, drawn as an outline with its name and no
   control, so the fields stay on the sheet while the mode is on. */
.fw-box {
  position: absolute; pointer-events: none;
  border: 1px dashed rgba(31, 122, 109, .8); background: rgba(255, 255, 255, .35);
  color: var(--muted); font-size: 10px; line-height: 1.2; overflow: hidden;
  padding: 1px 3px; box-sizing: border-box; border-radius: 2px;
}
.fw-field:focus { background: var(--accent-soft); outline: 1px solid var(--sel); }
.fw-field[type=checkbox], .fw-field[type=radio] {
  padding: 0; border: 0; background: rgba(231, 243, 240, .95);
}
.fw-field[type=checkbox]:focus, .fw-field[type=radio]:focus { outline: 1.5px solid var(--sel); }
.text-layer .tspan {
  position: absolute; color: transparent; white-space: pre; line-height: 1;
  font-family: "Atkinson Hyperlegible", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  transform-origin: 0 0; cursor: text;
  pointer-events: auto; border: 2px dashed transparent;
}
.text-layer .tspan::selection { background: rgba(31, 122, 109, .22); }
/* Hovering page text must not grow a box around the run (client: "this box
   type still exist"). A faint wash shows the text is clickable without
   framing it; the caret cursors already say the rest. */
.text-layer .tspan:hover { background: rgba(31, 122, 109, .10); }
/* The overlay is always the page's input surface: with a tool armed it places
   objects, and with no tool it sweeps a selection rectangle. It used to take
   pointer events only while a tool was armed, which is why a sweep could never
   start — the press went straight past it to the canvas. Items inside it keep
   their own events (.el is pointer-events: auto). touch-action is still only
   disabled while a tool is armed, so a finger can scroll the page otherwise. */
/* Page-bounded, exactly like the saved file: viewers clip a page's content
   at the sheet edge, so a box or blanket that reaches the margin must stop
   there on screen too. Without the clip a moved line's blanket (or any item
   dragged half off the sheet) stuck out as a white notch on the grey desk
   (client: "it leaves blank white stroke" — the blot sat past the page
   edge). */
.overlay { position: absolute; inset: 0; pointer-events: auto; overflow: hidden; }
.overlay.editing { cursor: crosshair; touch-action: none; }

/* editable items */
/* z-index 2 keeps placed objects above the (z-index 1) fill controls, the same
   way they already sat above the overlay. */
.el { position: absolute; touch-action: none; pointer-events: auto; z-index: 2; }
.el-text { min-width: 8px; min-height: 1em; white-space: pre; outline: none; cursor: move; }
/* line-height matches the leading the exporter uses for text
   (services/editor.py: leading = size * 1.25, baseline = top + size), so a
   multi-line text box is spaced on screen exactly as it is in the saved PDF and
   the height-based auto-fit in editor.js predicts the right size. */
.el-text { line-height: 1.25; }
/* While EMPTY the box must hold the hint's full line box. min-height: 1em
   (the rule above) is shorter than line-height x font, so a box that was not
   being edited — every box coming back from a RESTORED draft — drew the hint
   half outside its own frame (client: "the text box is odd size half out half
   in"). 1.25em matches the 1.25 line-height exactly. */
.el-text.is-empty { min-height: 1.25em; }
/* A new text box starts EMPTY and shows this grey prompt until something is
   typed. A box pre-filled with real text arrived with that word selected, which
   painted a blue block over the sheet — the "blue background" the client
   objected to — and had to be cleared before typing. A pseudo-element is not
   part of the content, so the item stays empty for the exporter and for the rule
   that drops a box nobody typed into. The wording is Sejda's own, taken from
   their editor: their empty box reads "Type your text". */
/* The hint is driven by .is-empty (set by syncEmpty in editor.js), not just
   :empty: the box stops being :empty the moment a <br> lands in it or the
   handles are appended — and the preview vanished exactly then (client: "it
   dont have the preview text annymore"). Kept OUT OF FLOW (absolute) so the
   caret for offset 0 stays at the left edge where typing starts; as inline
   ::before content it pushed the caret to the hint's far end. */
.el-text:empty::before,
.el-text.is-empty::before,
.el-text:has(> br:only-child)::before {
  content: "Type your text"; color: var(--muted);
  position: absolute; left: 0; top: 0; white-space: nowrap;
  pointer-events: none;
}
/* Editing a text box no longer paints its own green outline: the selection
   outline is already there, and a second coloured box around the type read as a
   background painted behind the text. */
.el-text:focus { outline: none; }
/* Tighter than the default object outline (and specific enough to beat
   .el.selected further down), so a text box reads as selected type rather than
   as a filled panel. */
.el.el-text.selected { outline-width: 1px; outline-offset: 0; }
/* The native image drag has to be off on every engine, not just the ones that
   honour the draggable attribute: with it on, dragging the picture by its body
   became a browser drag operation and only the corner handle moved it. */
.el-image, .el-signature, .el-stamp {
  cursor: move;
  -webkit-user-drag: none; user-drag: none; user-select: none;
  -webkit-user-select: none; -webkit-touch-callout: none;
}
/* The picture is a CHILD of its .el wrapper, because an <img> is a replaced
   element and will not render children — the resize handle appended to an <img>
   laid out at 0x0, so images, signatures and stamps could be moved but never
   resized. The inner image fills the wrapper and takes no pointer events, so the
   wrapper owns every gesture (which also kills the native image drag for good). */
.el-image > img, .el-signature > img, .el-stamp > img {
  display: block; width: 100%; height: 100%;
  pointer-events: none;
  object-fit: fill;
  -webkit-user-drag: none; user-drag: none; user-select: none;
}
/* A recorded edit over existing page text: bare type by default (Sejda leaves
   an edited line looking like page content), with a faint hairline the moment
   the pointer is over it so it is findable. An emptied record is invisible
   until hovered/selected, and the bar's delete/undo is the way back. */
/* An edited line must stay ONE full line, exactly like the run it replaces:
   without white-space: pre the replacement wrapped at its spaces whenever it
   was longer than the original box ("Generated royalties / August / 2026"),
   and the wrap both looked broken and disagreed with the export. The box
   shrink-wraps (see positionEl) so the whole text fits. line-height matches
   the exporter's leading (size * 1.25). */
.el-edittext { cursor: move; white-space: pre; line-height: 1.25; }
/* The blank left over the run's original spot once its record was dragged:
   scenery that mirrors the redaction the server will make (see makeMoveCover
   in editor.js). Never takes pointer events — the record itself is the target. */
.el-movecover { pointer-events: none; }
/* Delete-existing-image: the marker frames an image that is still on the page and
   carries a cross; clicking it withdraws the removal. */
.el-deleteimage {
  display: flex; align-items: center; justify-content: center;
  outline: 2px dashed var(--red); outline-offset: -2px;
  color: #fff; font-size: 22px; font-weight: 700; line-height: 1;
  background: rgba(179, 38, 30, .35); cursor: pointer; border-radius: 3px;
}
.img-hit {
  position: absolute; pointer-events: auto; cursor: pointer; z-index: 5;
  outline: 2px dashed var(--sk-blue); outline-offset: -2px;
}
.img-hit:hover { background: rgba(31, 122, 109, .18); }
.overlay.hitting { pointer-events: auto; }
.ext-link {
  position: absolute; pointer-events: auto; cursor: pointer; z-index: 5;
  outline: 2px dashed var(--sk-blue); outline-offset: -2px;
}
.ext-link:hover { background: rgba(31, 122, 109, .18); }
.el-editlink {
  display: flex; align-items: center; justify-content: center;
  outline: 2px dashed var(--sk-blue); outline-offset: -2px;
  color: #fff; font-size: 22px; font-weight: 700; line-height: 1;
  background: rgba(31, 122, 109, .35); cursor: pointer; border-radius: 3px;
}
.el-rect, .el-ellipse, .el-whiteout, .el-highlight, .el-underline, .el-strikeout { cursor: move; }
.el-ellipse { border-radius: 50%; }
.el-whiteout { background: #fff; }
.el-highlight { background: rgba(255, 212, 0, .4); }
.el-underline { border-bottom: 1.5px solid var(--red); }
/* Through the middle of the box, not along its top edge: the exporter draws the
   strike at y + h/2 (services/editor.py: _draw_markup), so a border-top put the
   preview line at the top and the saved line ~8pt lower. The colour arrives from
   styleEl as a custom property because a pseudo-element cannot be styled from JS
   directly. */
.el-strikeout { position: relative; }
.el-strikeout::after {
  content: ""; position: absolute; left: 0; right: 0; top: 50%;
  border-top: var(--el-line, 1.5px) solid var(--el-strike, var(--red));
}
.el-link { border: 1px dashed var(--sel); background: var(--sel-soft); cursor: move; }
/* Form fields carry a LIVE control, Sejda's `.ff-editable` model: a real input,
   dropdown, checkbox or textarea.
   The treatment is Sejda's own, measured from their editor on a real form: a
   soft blue wash with NO border and no handles — their overlay is
   `background: rgba(239,242,255,.95)`, `border: 0`, always on, and the class is
   literally `existing` because the wash is there to show WHERE YOU MAY TYPE.
   That is the answer to "why the blue box on forms": it is discoverability, not
   a frame. What read as a box in our version was the blue BORDER plus the eight
   square handles around a field, both of which are gone; a flat tint is not a
   box. */
.el-form {
  border: 0; background: rgba(231, 243, 240, .95);
  border-radius: 2px; cursor: default; overflow: visible;
}
.el-form:hover { background: rgba(216, 238, 233, .95); }
.el-form .ff-ctl {
  width: 100%; height: 100%; box-sizing: border-box; margin: 0;
  border: 0; background: transparent; font: inherit; font-size: 12px;
  color: var(--ink); padding: 0 4px;
}
.el-form .ff-ctl:focus { outline: none; background: rgba(255, 255, 255, .75); }
/* Sejda's empty fields read "Text" in grey (their placed input carries
   placeholder="Text"). opacity is pinned because Firefox fades placeholders on
   its own and the prompt came out too faint to notice. */
.el-form .ff-ctl::placeholder { color: var(--muted); opacity: 1; }
/* A field with a chosen text colour: the hint follows the ink, so the colour
   shows on an empty field too (the ::placeholder grey would otherwise mask it
   until something is typed — the "T does nothing" report). */
.el-form .ff-ctl.has-ink::placeholder { color: inherit; opacity: .85; }
.el-form select.ff-ctl { cursor: pointer; }
.el-form textarea.ff-ctl { resize: none; }
.el-form input[type=checkbox].ff-ctl,
.el-form input[type=radio].ff-ctl {
  padding: 0; margin: 0; width: 100%; height: 100%;
  accent-color: var(--sk-blue); cursor: pointer;
}
.el-form .ff-signbox {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; color: var(--muted); font-size: 11px;
  border: 1px dashed rgba(31, 122, 109, .6); background: rgba(255, 255, 255, .6);
}
/* While Form Edit mode is on, a click means "cycle this field's type", so the
   live controls must not also type or toggle underneath the click. */
.form-editing .el-form .ff-ctl { pointer-events: none; }
/* Ghost of a repeated field on its other pages: shows placement, cannot be
   clicked or typed into. */
.repeat-ghost { opacity: .5; pointer-events: none; }
.el.selected { outline: 1.5px dashed var(--sel); outline-offset: 2px; }
/* Live feedback while a box is being drawn. A colourless whiteout has no fill
   at all, so dragging one out showed NOTHING until the release added the
   selection frame (client: "whiteout never shows the outline mark when u use
   it it blank selection then the brofder are appiled"). The same frame draws
   from the first press and simply keeps going once the item is selected.
   Scenery only — it never reaches the file. */
.el.el-drawing { outline: 1.5px dashed var(--sel); outline-offset: 2px; }
/* A picked-up TEXT RECORD rides on top of its neighbours. Two records stacked
   "text on text" both sit at z-index 2, so a press on the pile grabbed
   whichever happened to be later in the DOM — the client moved a record,
   placed it on another, went to move it again and DRAGGED THE OTHER ONE
   instead. The lift is scoped to edittext records ALONE: as a blanket
   ".el.selected" rule it made a selected WHITEOUT (or any box) jump above
   text sitting ON it — the whiteout then stole every press aimed at the
   text above and covered it visually (client: "whiteout vs add text … it
   glitches … cant change fonts style"). Deselect, press again, and the
   next one down answers. */
.el-edittext.selected { z-index: 3; }
/* …but the page's own words are not a placed object. An edited line wears
   Sejda's thin blue hairline — the only way to SEE which text was picked up
   (client: "select a text… if you can't see a text how can you edit?"). It is
   a hover/selection cue and the live editor's box; the heavy 2px object frame
   stays for objects you placed, and the record itself goes bare once the edit
   ends and nothing is selected. */
.el-edittext:hover { outline: 1px solid rgba(47, 111, 237, .35); outline-offset: 0; }
.el.el-edittext.selected,
.el.el-text.inline-edit, .el.el-text.inline-edit.selected {
  outline: 1px solid rgba(47, 111, 237, .55); outline-offset: 0;
  box-shadow: none;
}
/* A selected text box used to be painted with the same pale blue wash a form
   field carries, borrowed from Sejda. On a form field the wash says "you may
   type here"; on a text box you have just placed it just reads as a blue block
   dropped on the sheet — and it was what the client kept objecting to. A text
   box now shows only its selection outline, never a fill. */
/* Sejda frames a selected object with eight handles: four corners plus four edge
   midpoints. They exist in the DOM always but only DRAW while the object is
   selected (see the rule further down), so an unselected page has no squares. */
/* The sweep rectangle for selecting several objects at once (Sejda's marquee).
   Drawn on the overlay, so it must not take pointer events of its own. */
.marquee {
  position: absolute; z-index: 5; pointer-events: none;
  border: 1px solid var(--sel); background: var(--sel-soft);
}
/* Sejda's selection affordance, measured off their editor (client: "need
   same selection func … requirement" — their screenshots show a thin dashed
   frame and a single "+" at the corner, not a row of square grips). The
   handles are INVISIBLE hit zones now — the same eight-direction resize
   behaviour — and only the south-east one draws the "+" marker. */
.handle {
  position: absolute; width: 14px; height: 14px; background: none;
  border: 0; border-radius: 0; z-index: 6;
}
/* Sejda only frames the SELECTED object; unselected fields and shapes stay
   clean. The handles exist in the DOM but are hidden until selection. */
.el > .handle { display: none; }
.el.selected > .handle { display: block; }
/* The rotate grip (the Shapes tools): a ↻ dot riding the selection — MOVE
   (drag) it and the shape turns freely through 360° about its centre. Box
   shapes carry it ON the top edge (half overlapping, so it reads as a point
   on the shape); a line/arrow gets the single floating twin (.stroke-rot)
   that the floatbar's update loop keeps parked. Solid blue + white icon,
   because a white chip on a white sheet was invisible ("isnt not work").
   Restored 2026-10-12 — it shipped 2026-10-06 and was lost in the 10-09
   editor rework (client: "the shapes got removed the 360 rotated button"). */
.rot-handle {
  display: none; position: absolute; left: 50%; margin-left: -13px; top: -13px;
  width: 26px; height: 26px; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--sel); border: 2px solid #fff;
  color: #fff; cursor: grab; box-shadow: 0 2px 8px rgba(15, 23, 30, .35);
  z-index: 6; touch-action: none;
}
.el.selected > .rot-handle { display: inline-flex; }
.rot-handle svg { display: block; pointer-events: none; }
.rot-handle.stroke-rot { position: fixed; z-index: 141; }
.rot-handle.stroke-rot:not([hidden]) { display: inline-flex; }
/* A triangle/diamond/star/wave paints inside this inline svg — stretched by
   the box (preserveAspectRatio=none) so the geometry stays true at any size,
   pointer-events off so every gesture still lands on the .el node exactly
   like a rectangle's. */
.el .shape-svg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: block; pointer-events: none; overflow: visible;
}
/* Eight-position frame: four corners (the north-west one is the move grip for
   fields) plus four edge handles. */
.handle.se { right: -8px; bottom: -8px; cursor: nwse-resize; }
.handle.sw { left: -8px; bottom: -8px; cursor: nesw-resize; }
.handle.ne { right: -8px; top: -8px; cursor: nesw-resize; }
.handle.nw { left: -8px; top: -8px; cursor: nwse-resize; }
.handle.n { left: 50%; margin-left: -7px; top: -8px; cursor: ns-resize; }
.handle.s { left: 50%; margin-left: -7px; bottom: -8px; cursor: ns-resize; }
.handle.e { top: 50%; margin-top: -7px; right: -8px; cursor: ew-resize; }
.handle.w { top: 50%; margin-top: -7px; left: -8px; cursor: ew-resize; }
/* The "+" corner marker, drawn like theirs. */
.handle.se::before, .handle.se::after {
  content: ""; position: absolute; left: 50%; top: 50%; background: var(--sel);
}
.handle.se::before { width: 11px; height: 1.6px; transform: translate(-50%, -50%); }
.handle.se::after { width: 1.6px; height: 11px; transform: translate(-50%, -50%); }
/* z-index 2 (same as .el) is what makes the per-stroke wrappers stack in
   ITEM ORDER with the boxes: a line or arrow drawn after a whiteout sits on
   top of it and stays visible (client: "shapes doesnt fiot on whiteout,
   annotate too"), and a whiteout drawn after a stroke still covers it. */
.pg-svg { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.pg-svg path { pointer-events: stroke; cursor: move; touch-action: none; }
.pg-svg path.selected { filter: drop-shadow(0 0 3px var(--sel)); }

/* props */
.props {
  width: 250px; flex-shrink: 0; background: var(--white);
  border-left: 1px solid var(--line); padding: 16px; overflow-y: auto;
}
.props h4 {
  font-size: .8rem; text-transform: uppercase; color: var(--muted);
  display: flex; align-items: center; justify-content: space-between;
}
.props-close {
  display: none; background: none; border: none; font-size: 1.3rem;
  line-height: 1; cursor: pointer; color: var(--muted); padding: 0 4px;
}
.props-toggle { display: none; }
.props .field { margin-bottom: 14px; }
.props input[type=text], .props input[type=number], .props select, .props textarea {
  border: 1px solid var(--line); border-radius: 7px; padding: 7px 9px; font: inherit; width: 100%;
  background: var(--white); color: var(--ink);
}
.props .inline { flex-direction: row; gap: 8px; }
.style-toggle {
  width: 34px; height: 32px; border: 1px solid var(--line); border-radius: 7px;
  background: #fff; font-weight: 700; cursor: pointer;
}
.style-toggle.active { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-dark); }

/* No status bar: Sejda's editor has none, the page number lives in the row under
   the toolbar and zoom has its own buttons. This is just the little line in
   that row that says "Applying…" while a save is running (and the fallback
   download link, should one ever land here). */
.ab-status { display: flex; align-items: center; margin-left: 6px; font-size: 13px; }
.ab-status a { color: var(--green-dark); font-weight: 600; }
/* ...but the finished-file link arrives as .btn.btn-primary, whose background is
   dark green (app.css), and the rule above then wrote dark green text onto it:
   dark on dark, which is why the button read as disabled — "this is not
   touchable". White on the primary green, like every other primary button. */
.ab-status a.btn { color: #fff; }
.ab-status a.btn:hover { color: #fff; opacity: .92; }
.ab-status:empty { display: none; }

/* modals */
.modal { position: fixed; inset: 0; background: rgba(15,23,30,.5); display: flex; align-items: center; justify-content: center; z-index: 100; }
.modal-box {
  position: relative; background: var(--white); border-radius: 5px; padding: 22px;
  width: min(520px, 92vw); box-shadow: var(--shadow);
  max-height: calc(100dvh - 24px); overflow-y: auto;
}
.modal-box h3 { margin: 0 0 18px; font-size: 17px; font-weight: 500; color: #333; }
.modal-box input[type=text], .modal-box input[type=password], .modal-box select, .modal-box textarea {
  border: 1px solid var(--line); border-radius: 8px; padding: 9px 11px; font: inherit; width: 100%; margin-bottom: 12px;
  background: var(--white); color: var(--ink);
}
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; }
.link-kinds { display: flex; flex-wrap: wrap; gap: 8px 16px; margin-bottom: 10px; }
.link-kinds label { display: flex; align-items: center; gap: 6px; font-size: .9rem; cursor: pointer; }
.link-kinds input[type=radio] { accent-color: var(--accent); }
/* Sejda's stamp dialog: two labelled columns, the colour circles on the same
   row as the date, then the stamp itself centred and one green Save. */
/* Specificity matters: the @media (max-width:1024px) block further down sets
   `.modal-box { width: 94vw }`, which would otherwise stretch these two. */
.modal-box.stamp-box { width: min(500px, 94vw); padding: 20px 26px 24px; border-radius: 4px; }
.stamp-box h3 { margin: 0 0 20px; font-size: 17px; font-weight: 500; color: #333; }
.stamp-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px 26px; }
.stamp-grid > div { min-width: 0; }
.stamp-grid label { display: block; margin-bottom: 6px; font-size: 14px; color: #333; }
.stamp-grid input[type=text], .stamp-grid select { margin: 0; height: 38px; border-radius: 3px; min-width: 0; }
.stamp-grid .stamp-inks { margin: 7px 0 0; gap: 14px; }
.stamp-preview-wrap { border: 0; padding: 0; margin: 24px 0 26px; background: none; display: flex; justify-content: center; }
.stamp-preview-wrap img { max-width: 100%; max-height: 66px; }
.stamp-actions { justify-content: center; margin-top: 0; }
.stamp-actions .btn, .sign-save-btn {
  min-width: 150px; justify-content: center; padding: 10px 34px;
  background: var(--sk-save); border: 1px solid var(--sk-save); color: #fff;
  border-radius: 3px; font-size: 14px; cursor: pointer;
}
.stamp-actions .btn:hover, .sign-save-btn:hover {
  background: var(--sk-save-dark); border-color: var(--sk-save-dark);
}
.modal-box input[type=color] { width: 52px; height: 36px; padding: 2px; margin-bottom: 12px; }
.modal-box input[type=number] {
  border: 1px solid var(--line); border-radius: 8px; padding: 9px 11px; font: inherit; width: 100%; margin-bottom: 12px;
  background: var(--white); color: var(--ink);
}
/* Sejda's tab strip: icon + label in Sejda's green, the selected one sitting in
   a light grey box, with a hairline under the whole row. */
.tabs { display: flex; gap: 4px; margin: 0; }
.tab {
  display: inline-flex; align-items: center; gap: 8px; padding: 7px 12px;
  background: none; border: 1px solid transparent; border-radius: 3px;
  cursor: pointer; font: inherit; font-size: 14px; color: var(--sk-save);
}
.tab .ti { width: 17px; height: 17px; }
.tab:hover { background: #f5f5f5; }
.tab.active { background: #eee; border-color: #ccc; color: #333; }
.modal-box.sign-box { width: min(760px, 94vw); padding: 0 0 20px; border-radius: 4px; }
.sign-box h3 { margin: 20px 26px 16px; font-size: 17px; font-weight: 500; color: #333; }
.sign-close {
  position: absolute; top: 12px; right: 14px; padding: 0 4px; font-size: 24px;
  line-height: 1; background: none; border: 0; cursor: pointer; color: #8b9298;
}
.sign-close:hover { color: #333; }
.sign-tabsrow {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 0 26px 12px; border-bottom: 1px solid var(--line);
}
.tab-body { padding: 22px 26px 0; }
.sign-remember { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 14px; color: var(--ink); }
#sign-typed {
  display: block; width: min(340px, 100%); margin: 0 auto 20px;
  height: 38px; border-radius: 3px;
}
.sign-inks { display: flex; justify-content: center; gap: 12px; margin: 0 0 20px; }
.sign-ink {
  width: 16px; height: 16px; padding: 0; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(0, 0, 0, .2); background: var(--c);
}
.sign-ink[aria-pressed="true"] { box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--c); }
.sign-legal { text-align: center; margin: 16px 26px 0; color: var(--muted); }
.sign-actions { display: flex; justify-content: center; margin-top: 14px; }
#sign-canvas { border: 1px dashed #cfd8dd; border-radius: 8px; width: 100%; height: auto; background: #fff; touch-action: none; }
.sign-tools { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 10px; }
.sign-tools label, .sign-color-row { display: flex; align-items: center; gap: 6px; font-size: .85rem; }

/* Type tab: the handwriting faces laid out on the dialog itself, like Sejda —
   no frame around them, only the chosen face sits in a light box. */
.sign-styles {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0;
  max-height: 240px; overflow-y: auto; padding: 0;
}
.sign-style {
  min-width: 54px; padding: 2px 10px; background: none; cursor: pointer;
  border: 1px solid transparent; border-radius: 6px; color: var(--ink);
  line-height: 1.1;
}
.sign-style:hover { background: var(--bg); }
.sign-style.active { border-color: #d5d9dd; background: #f4f6f8; }
.sign-remember {
  display: flex; align-items: center; gap: 8px; margin-top: 12px;
  font-size: .85rem; color: var(--ink);
}
#sign-video {
  width: 100%; max-height: 240px; border-radius: 8px; background: #111;
  object-fit: cover; display: block;
}
#sign-shot { width: 100%; height: auto; border-radius: 8px; margin-top: 10px; }
.sign-tools input[type=color], .sign-color-row input[type=color] {
  width: 34px; height: 28px; padding: 0; border: 1px solid var(--line); border-radius: 6px; background: var(--white);
}
.sign-color-row { margin-top: 4px; }

.toast {
  position: fixed; bottom: 96px; left: 50%; transform: translateX(-50%);
  background: var(--ink); color: #fff; padding: 11px 18px; border-radius: 9px;
  z-index: 200; font-size: .9rem; box-shadow: var(--shadow); max-width: 90vw;
  /* The toast is a notice, not a surface: without this it swallowed every
     click and right-click that landed on it for its 2.4s life — the whiteout
     warning sits over the very patch you just drew, so the next press on the
     object beneath (selection, right-click, a second drag) died on the pill. */
  pointer-events: none;
}
@media (max-width: 900px) {
  .toolbar { padding: 6px 8px; gap: 4px; }
  /* Touch sizing. On a phone the icon-only controls measured 20-29px tall,
     well under the ~40px that is comfortable to hit with a finger. */
  .menu-btn, .tool-btn { padding: 6px 8px; font-size: .84rem; min-height: 40px; }
  .menu-item { padding: 11px 20px; }
  .thumb-actions button { min-width: 38px; min-height: 38px; padding: 6px; }
  .props-close { min-width: 40px; min-height: 40px; }
  .menu-panel { max-width: calc(100vw - 24px); }
}

/* Below this the labels make the strip too wide, so fall back to Sejda's
   icon-only toolbar; the tooltip keeps the name discoverable.
   Measured: tool strip 817 + right group 130 + 8 gap + 28 padding ≈ 987px for
   the labelled layout to sit on one line, so the old 1150px hid the labels a
   good 160px before they were in the way.
   The rule used to target .menu-btn only. Links and Whiteout are plain
   .tool-btn (no dropdown) and Undo was bare text, so on a phone three random
   words survived in a row of icons and pushed the strip to two ragged lines.
   Every label is a .mi-label span now — including Undo, which has to stay a
   span rather than raw text for this to work. */
@media (max-width: 920px) {
  .menu-btn .mi-label, .tool-btn .mi-label { display: none; }
  .menu-btn { padding: 6px 10px; }
  .menu-btn .mi, .tool-btn .mi { margin-right: 0; }
  .menu-btn .caret { margin-left: 2px; }
}

@media (max-width: 1024px) {
  .props-toggle { display: inline-block; }
  .props {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: auto; max-height: 70vh; border-left: none;
    border-top: 1px solid var(--line); border-radius: 16px 16px 0 0;
    box-shadow: 0 -8px 30px rgba(0,0,0,.18); z-index: 90;
    transform: translateY(105%); transition: transform .2s ease;
    padding-bottom: calc(16px + env(safe-area-inset-bottom));
  }
  .props.open { transform: translateY(0); }
  .props-close { display: block; }
  .thumbs {
    display: block; position: fixed; top: 64px; bottom: 0; left: 0; width: min(240px, 82vw);
    z-index: 88; box-shadow: 4px 0 24px rgba(0,0,0,.18);
    transform: translateX(-105%); transition: transform .2s ease;
  }
  .thumbs.open { transform: translateX(0); }
  /* The bottom padding used to clear the beige Apply tray that floated over the
     canvas here; the tray is gone (Apply and Save live in the page row now), so
     only the air the last sheet's "Insert page here" divider needs is left.
     The side padding is smaller here than on a desktop, but it stays — a phone
     pinned the sheet to both edges, which is the cramped look this fixes.
     The TOP padding is the floating chrome's reserve (--ed-chrome-h), the same
     as the base rule: the tool strip and page-controls row now float over the
     viewer, so without it the sheet's top edge sits UNDER them. */
  .viewer-wrap { padding: calc(var(--ed-chrome-h, 132px) + 8px) 20px calc(28px + env(safe-area-inset-bottom)); }
  /* The zoom controls stay here. They were hidden below 1024px while the zoom was
     locked at actual size, and the client has since asked for them back on
     phones and tablets with a 90%-150% range — see ZOOM_MIN_COMPACT in
     editor.js, which is the other half of this breakpoint. */
  .modal-box { width: 94vw; padding: 16px; }
  #sign-canvas { height: 150px; }
}

@media (max-width: 480px) {
  .menu-btn, .tool-btn { padding: 6px 7px; font-size: .8rem; }
  .toolbar-group.right { gap: 4px; }
  /* A phone cannot spare much, but the sheet still needs air around it. */
  .viewer-wrap { padding-left: 12px; padding-right: 12px; }
}

/* toolbar icons */
.menu-btn .mi, .tool-btn .mi, .btn .mi {
  width: 15px; height: 15px; margin-right: 5px; vertical-align: -2px;
  opacity: .9; flex: 0 0 auto;
}
.menu-btn, .tool-btn, .btn { display: inline-flex; align-items: center; }

/* page controls: "1 / 1", one bordered cluster of page actions, a zoom rocker
   around its readout, then the two file verbs. The row has NO band of its own
   (client: "remove the grey background from 2nd bar.. client dont want") — the
   white clusters float on the workspace exactly like the tool strip above.
   Measured off their editor: group buttons 40x29 with hairline dividers.
   Desktop keeps it on ONE line — the row used to wrap and strand Save / Save
   and Continue on a second line — narrower windows wrap instead. */
.page-controls {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  /* No background band (client: "remove the grey background from 2nd bar..
     client dont want") — transparent lets the workspace and the sheets show
     through. The earlier sizing rule still holds: max-content + auto side
     margins keep the row SIZED TO THE CONTROLS, never a wide strip (client:
     "you can see the grey background on trash rotate? that's the issue — fix
     the size around those, not wide"). It FLOATS pinned under the strip
     (client: "need to float trash rotate bar too"; see syncHeaderFloat — the
     scroll sync owns the row's transform). */
  background: transparent; border-bottom: 0;
  padding: 6px 12px;
  position: absolute; top: var(--ed-toolbar-h, 56px); left: 0; right: 0;
  width: max-content; max-width: calc(100% - 16px); margin: 0 auto;
  z-index: 76;
  border-radius: 12px;
  box-shadow: none;
}
/* "1 / 3" — their band leads with the current page as a big light numeral
   (their total lives separately), so ours is sized up and quieted to read the
   same way: the page you are on, not a label for the buttons. Line-height is
   pinned to the 29px control height so the numeral sits level with the bin /
   rotate / zoom groups beside it. */
.page-controls .pc-of {
  font-size: 22px; font-weight: 300; color: #b7bfc4; line-height: 29px;
  min-width: 0; margin-right: 8px;
}
/* One white strip with hairline dividers — the same visual language as the tool
   strip above, so the row reads as a group. */
.page-controls .pc-group {
  display: inline-flex; align-items: center;
  background: var(--white); border: 1px solid var(--sk-tool-line);
  border-radius: 4px; padding: 0;
}
.page-controls .pc-group .tool-btn {
  width: 40px; height: 29px; padding: 0; justify-content: center;
  color: #5d6771; border-radius: 0;
}
.page-controls .pc-group .tool-btn + .tool-btn {
  border-left: 1px solid var(--sk-tool-line);
}
.page-controls .pc-group .tool-btn:first-child { border-radius: 4px 0 0 4px; }
.page-controls .pc-group .tool-btn:last-child { border-radius: 0 4px 4px 0; }
.page-controls .pc-group .tool-btn:hover { background: var(--sk-blue-soft); color: var(--sk-blue); }
/* Accidentally deleting the page you are on is the one mistake this row can
   make: the bin gets a red voice of its own. */
.page-controls .pc-danger:hover { background: var(--red-bg); color: var(--red); }
.page-controls .pc-group .mi { width: 17px; height: 17px; }
.page-controls .tool-btn .mi { margin-right: 0; }
/* Zoom rocker: the readout lives INSIDE the group, between − and +. It used to
   float loose beside a five-icon cluster, with the zooms far from the number
   they change. 100% is actual A4 size, so the readout still doubles as the way
   back there. */
.page-controls .pc-zoom {
  min-width: 58px; height: 29px; padding: 0 8px; border: 0; background: none;
  border-left: 1px solid var(--sk-tool-line); border-right: 1px solid var(--sk-tool-line);
  font: inherit; font-size: 12.5px; font-weight: 600; color: #4b5257;
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
}
.page-controls .pc-zoom:hover { background: var(--sk-blue-soft); color: var(--sk-blue); }
/* the insert button is the same control as the one under each sheet */
.page-controls .insert-here { padding: 0 14px; height: 29px; }
/* Desktop: everything on one line (client: "make the delete rotaed toolbar in
   center on desktop mode" — the pill's auto margins keep the row itself
   centred, and the groups line up inside it). Narrower windows keep the
   wrapped row, where centred groups would just look ragged. */
@media (min-width: 901px) {
  .page-controls { flex-wrap: nowrap; padding: 6px 14px; gap: 12px; justify-content: center; }
}

/* The file verbs close the page-controls row (client: "place save and save
   and continue beside zoom in"): Save keeps the edit as a draft and you stay
   in the editor; Save and Continue finishes — processing, then the ready
   page. Sized like the strip's own buttons so the row reads as one language,
   and they keep the tool strip's white-pill wrapper they arrived in.
   Colour history: the pair wore these blue verbs; a round of "the colour is
   odd" took them low-key — then the client asked for the colour BACK and the
   ICONS gone instead ("save and save download restore the color and remove
   the icon its had with text"), so: original palette, no marks, text only. */
.pc-verb {
  display: inline-flex; align-items: center; gap: 6px;
  height: 29px; padding: 0 12px; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 600; border-radius: 4px;
}
.pc-apply {
  background: var(--white); color: var(--accent-dark);
  border: 1.5px solid var(--accent);
}
.pc-apply:hover { background: var(--accent-soft); }
.pc-save {
  background: var(--accent); color: #fff; border: 1.5px solid var(--accent);
}
.pc-save:hover { background: var(--accent-dark); border-color: var(--accent-dark); }
.tb-verbs { padding: 2px; gap: 4px; }

/* scanned-document notice */
.scan-notice {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  background: var(--amber-bg); border-bottom: 1px solid var(--amber-line);
  color: var(--amber-ink); padding: 9px 14px; font-size: .88rem;
}
.scan-notice span { margin-right: auto; }
.scan-notice .scan-close {
  background: none; border: 0; padding: 2px 6px; margin-left: -4px;
  font-size: 22px; line-height: 1; color: inherit; opacity: .75; cursor: pointer;
}
.scan-notice .scan-close:hover { opacity: 1; }

/* Sejda's text toolbar: ONE white rounded bar with a hairline border, controls
   packed tight, no per-button boxes and no word captions — the icon and the
   tooltip carry the meaning (their stamp/signature bar looks the same). */
.float-bar {
  position: fixed; z-index: 140; display: flex; align-items: center; gap: 1px;
  /* A narrow window must not push the controls off-screen. The bar used to be
     one rigid row (nowrap, no cap): at a mobile/tablet width — or a narrow
     desktop window, which is how the client checks — it measured ~411px and
     ran past the right edge, the last controls unreachable (client: "fix the
     floatbar for mobile and tab"). With wrapping on it folds into as many
     rows as the viewport needs, and JS pins it inside the edges. The touch
     dock block below overrides this: a docked bar is ONE sideways-scrolling
     row so it keeps the page-controls row's single-line height. */
  flex-wrap: wrap; max-width: calc(100vw - 16px);
  background: #fff; border: 1px solid #d8d8d8; border-radius: 4px;
  padding: 2px 3px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, .16);
}
.float-bar[hidden] { display: none !important; }
.float-bar::before { content: none; }
.float-bar .fb-cap { display: none; }
.fb-btn,
.fb-font-btn,
.float-bar select,
.float-bar input[type=color],
.float-bar input[type=number],
.float-bar input[type=text] {
  background: none; border: 0; border-radius: 3px; color: #333;
}
.fb-btn {
  min-width: 28px; height: 28px; cursor: pointer; font: inherit; font-size: 13px;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 5px; box-shadow: none;
}
.fb-btn .mi { width: 15px; height: 15px; margin: 0; }
.fb-btn:hover { background: #f0f0f0; }
.fb-btn.active { background: #e6f0fb; color: var(--sk-blue); }
.fb-del { color: #d9534f; }
.fb-del:hover { background: var(--red-bg); color: var(--red); }
/* #Lock#: the padlock beside the bin (client: "add lock and unlock icon … it
   wont go anywhere if u lock it"). Open padlock = it can move; closed padlock
   on the lit button = locked in place. */
.fb-lock .fb-lock-on { display: none; }
.fb-lock.active .fb-lock-on { display: inline-flex; }
.fb-lock.active .fb-lock-off { display: none; }
/* The alignment buttons in the field-properties panel keep their boxes: that
   panel is a form, not Sejda's flat toolbar. */
.field-props .fb-btn { background: #fff; border: 1px solid var(--line); padding: 0 9px; }
.field-props .fb-btn:hover { background: #f5f5f5; }
.field-props .fb-btn.active { background: var(--sk-active-bg); color: var(--sk-blue); }
/* Sejda's dropdown buttons carry the control icon, a small palette and a caret. */
.fb-btn .fb-t { font-weight: 700; font-size: 13px; line-height: 1; }
.fb-btn .mi-pal { width: 13px; height: 13px; margin-left: 2px; }
.fb-btn .mi + .mi-pal { margin-left: 2px; }
.fb-btn .fb-caret {
  width: 0; height: 0; margin-left: 3px; opacity: .75;
  border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor;
}
.float-bar input[type=number] { width: 48px; height: 28px; padding: 0 5px; font: inherit; font-size: 12px; }
/* The thickness slider: a short bar in the pill, thin track, round knob, so it
   reads as a size control beside the colour swatch rather than as a form field. */
.float-bar input[type=range] {
  width: 78px; height: 28px; padding: 0 2px; margin: 0;
  appearance: none; -webkit-appearance: none; background: transparent; cursor: pointer;
}
.float-bar input[type=range]::-webkit-slider-runnable-track {
  height: 4px; border-radius: 2px; background: rgba(0, 0, 0, .18);
}
.float-bar input[type=range]::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none; width: 14px; height: 14px; margin-top: -5px;
  border: 0; border-radius: 50%; background: var(--sk-blue);
}
.float-bar input[type=range]::-moz-range-track {
  height: 4px; border-radius: 2px; background: rgba(0, 0, 0, .18);
}
.float-bar input[type=range]::-moz-range-thumb {
  width: 14px; height: 14px; border: 0; border-radius: 50%; background: var(--sk-blue);
}
.float-bar select { max-width: 104px; }
/* The font control is a custom PREVIEW picker (client: "this fonts should be
   preview type which font which style"): the native <select> popup cannot
   render each option in its own face on macOS, so the visible control is a
   button (built in editor-extras.js) that opens .fp-pop — every family shown
   in its own typeface, the current one highlighted, "More fonts…" pinned at
   the bottom for the searchable sheet. The <select> keeps the value and all
   change bindings, hidden via .fb-font-native. */
.float-bar select.fb-font-native { display: none; }
.fb-font-btn {
  height: 28px; padding: 0 6px; font-size: 13px; cursor: pointer;
  display: inline-block; max-width: 104px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  vertical-align: middle;
}
.fb-font-btn::after {
  content: ""; display: inline-block; margin-left: 4px;
  border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor; opacity: .75;
}
.fb-font-btn:hover { background: #f0f0f0; }
.fp-pop {
  position: fixed; z-index: 300; background: #fff;
  border: 1px solid rgba(0, 0, 0, .15); border-radius: 4px;
  box-shadow: 0 6px 12px rgba(0, 0, 0, .176);
  width: 230px; max-height: 320px; overflow-y: auto; padding: 4px 0;
}
.fp-row {
  display: block; width: 100%; text-align: left; border: 0; background: none;
  padding: 8px 12px; font-size: 15px; line-height: 1.3; color: #111; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fp-row:hover { background: #eef4fd; }
.fp-row.fp-current { background: #e3f0fb; }
.fp-row.fp-more {
  border-top: 1px solid #e5e9ee; color: #1f7a6d; font-weight: 600;
  font-family: inherit; position: sticky; bottom: 0; background: #fff;
}
.fp-row.fp-add { color: #1f7a6d; font-weight: 600; border-bottom: 1px solid #e5e9ee; }
/* "+ Add font…" (sheet and picker) opens the COMPACT "Add a font" dialog —
   computer, Dropbox, Google Drive, OneDrive, web address — instead of
   stretching the sheet with a full-width upload box. Centred under the
   search field, client's ask. See editor-extras.js. */
.font-add-btn { display: block; margin: 10px auto 2px; }
.font-add-box { max-width: 520px; }
.font-add-box .cloud-sources { margin-top: 14px; }
.font-add-url { display: flex; gap: 8px; margin-top: 10px; }
.font-add-url input {
  flex: 1 1 auto; min-width: 0; padding: 8px 10px; font: inherit; font-size: .92rem;
  border: 1px solid var(--line); border-radius: 6px; color: var(--ink); background: #fff;
}
.float-bar input[type=color] { width: 30px; height: 28px; padding: 2px; }
.float-bar input[type=text] { width: 190px; height: 28px; padding: 0 7px; font: inherit; font-size: 12px; }
/* Sejda's colour chip carries a caret: it opens the palette, while the circle
   shows the value. Drawn in CSS so nothing has to be added to the DOM. */
.fb-color::after {
  content: ""; width: 0; height: 0; margin-left: 3px;
  border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
  border-top: 4px solid #6f787e;
}
/* The float bar's colour control is a round swatch that opens the colour plate
   instead of the browser's own colour panel. The input stays in the DOM
   (hidden) so every existing value binding keeps working. */
.fb-color { position: relative; display: inline-flex; align-items: center; }
.fb-color input[type=color] { display: none; }
.fb-sw {
  width: 26px; height: 26px; border-radius: 50%; padding: 0; cursor: pointer;
  border: 1px solid rgba(15, 23, 30, .25);
  box-shadow: inset 0 0 0 2px #fff;
  background: #000;
  flex: 0 0 auto;
}
.fb-sw:hover { box-shadow: inset 0 0 0 2px #fff, 0 0 0 2px rgba(31, 122, 109, .35); }
/* No colour chosen yet (fresh whiteout): the chip shows the classic faint
   transparent checker instead of a solid colour, so it reads as a plain
   selector waiting for the visitor's pick — nothing looks pre-selected. */
.fb-sw.is-none {
  background-color: #fff;
  background-image:
    linear-gradient(45deg, #dfe5ea 25%, transparent 25%, transparent 75%, #dfe5ea 75%),
    linear-gradient(45deg, #dfe5ea 25%, transparent 25%, transparent 75%, #dfe5ea 75%);
  background-size: 10px 10px;
  background-position: 0 0, 5px 5px;
}

/* The colour plate lives in the SHARED color-plate.css (loaded right after
   this file on the editor page): .sp-pop, the .sp-* swatches, .cp-modal and
   the body.doc-picking crosshair. It used to be duplicated here. */
/* .color-head is kept: the tab-order dialog borrows this header row. The old
   in-page "Choose a color" modal (and its .color-hex/.color-body/.color-hue
   rules) is gone — the shared color-plate.css provides the cp-modal instead. */
.color-head { display: flex; align-items: center; justify-content: space-between; }
.color-head h3 { margin: 0; }
/* Field properties panel (Sejda's props-opts dropdown). */
.field-props {
  position: fixed; z-index: 320; width: 250px; background: #fff;
  border: 1px solid rgba(0, 0, 0, .15); border-radius: 4px;
  box-shadow: 0 6px 12px rgba(0, 0, 0, .176); padding: 10px 12px;
}
.field-props label { display: block; font-size: 12px; color: #555; margin: 6px 0 3px; }
.field-props input[type=text], .field-props textarea {
  width: 100%; box-sizing: border-box; border: 1px solid var(--line); border-radius: 4px;
  padding: 6px 8px; font: inherit; font-size: 13px; background: #fff; color: var(--ink);
}
.field-props .fp-check {
  display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink); margin-top: 8px;
}
.field-props .fp-check input[type=number] {
  width: 60px; padding: 3px 6px; border: 1px solid var(--line); border-radius: 4px; font: inherit; font-size: 13px;
}
.field-props .fp-align { display: flex; gap: 6px; margin-top: 4px; }
.field-props .fp-warn { font-size: 11.5px; line-height: 1.35; color: #b45309; margin: -2px 0 8px; }
/* Tab order dialog (Sejda's TabOrder prompt): numbered chips for the current
   order, sort buttons, and a drag-and-drop list. */
.taborder-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 12px 0; }
.taborder-chip {
  min-width: 26px; height: 26px; padding: 0 6px; border: 1px solid var(--sel);
  border-radius: 4px; color: var(--sel); font-size: 13px; font-weight: 600;
  display: inline-flex; align-items: center; justify-content: center; background: #fff;
}
.taborder-sorts { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.taborder-list { list-style: none; margin: 6px 0 0; padding: 0; max-height: 220px; overflow-y: auto; }
.taborder-item {
  display: flex; align-items: center; gap: 10px; padding: 7px 10px; margin-bottom: 4px;
  border: 1px solid var(--line); border-radius: 5px; background: #fff; cursor: grab;
  font-size: 13px; color: var(--ink);
}
.taborder-item.dragging { opacity: .45; }
.taborder-item.over { border-color: var(--sel); box-shadow: 0 0 0 1px var(--sel); }
.taborder-num {
  width: 20px; height: 20px; border-radius: 4px; background: var(--sel-soft);
  color: var(--sel); font-size: 12px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.taborder-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.taborder-type { color: var(--muted); font-size: 12px; flex: 0 0 auto; }
/* Stamp colour circles, Sejda's seven inks. */
.stamp-inks { display: flex; gap: 14px; margin: 7px 0 0; }
.stamp-ink {
  width: 16px; height: 16px; border-radius: 50%; cursor: pointer; padding: 0;
  border: 1px solid rgba(0, 0, 0, .2); background: var(--c);
}
.stamp-ink[aria-pressed=true] { box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--c); }
.fb-sep { flex: 0 0 auto; width: 0; height: 1px; background: none; margin: 0 2px; }
.float-bar .mi { width: 14px; height: 14px; }
@media (hover: none) {
  /* Docked onto the page-controls row — chrome, so it never lies over the sheet.

     TOUCH DEVICES ONLY (hover: none) — phones AND tablets. On any touch screen
     the floating bar lies over the very text being edited, and there is no
     hover to move it away; a tablet has the same problem a phone has, only
     bigger. On a narrow DESKTOP window the bar stays with the item like every
     other width — a strip pinned under the toolbar, far from the selected
     object, reads as a glitch ("it shouldn't be stuck on top bar, it should
     be with the whiteout"). Match the JS condition in updateFloatBar()
     ('hover: none').
  /* Docked onto the page-controls row — chrome, so it never lies over the sheet.

     updateFloatBar() positions the bar from the selected item's rect, and on a
     phone that used to drop a ~350px strip across the MIDDLE of the document,
     and run past the right edge whenever a row of controls would not fit. The
     client's report: "it floats over the page and covers my text while editing".

     Three placements worth not repeating:
     - Following the item (the desktop rule) covers the very thing being edited.
     - Docking to the BOTTOM made it vanish. .workspace is 100dvh and Android
       Chrome does not shrink the layout viewport for the keyboard by default,
       so anything anchored to the bottom (the bar AND the Apply tray) sits
       behind the keyboard. Fixed separately with interactive-widget in base.html.
     - Docking at the top *in flow* shifted the whole document down by the bar's
       height the moment anything was selected — measured a 66px jump — which
       slid the item out from under the pointer mid-drag and made dragging feel
       broken. It must overlay, not participate in layout.

     So: fixed position; JS writes left/right and pins the top to the tool
     strip's bottom edge — the row's slot, which never moves now that the row
     floats too. The row sits above the sheet, the document keeps every
     pixel, and the bar hugs its one row of controls (height auto — see
     editor.js; stretching it to the wrapped row's height made a tall
     half-empty card). Because BOTH left and right are set the width is the
     viewport's, so a row can never overflow — what does not fit scrolls
     sideways. */
  .float-bar {
    left: 8px;
    right: 8px;
    max-width: none;
    /* Wrap, like the floating bar — NOT one sideways-scrolling row. On a very
       narrow phone the sideways version hid the last controls past the right
       edge with nothing showing there was more (client: "fix mobile one");
       wrapping keeps every control visible, one tap away, and still a compact
       one-row strip at normal phone widths once the controls are tightened.
       Two rows cannot spill onto the canvas the way the old comment feared:
       the bar's height is auto and it overlays chrome, and the page-controls
       row underneath is hidden (body.fb-docked). */
    flex-wrap: wrap;
    justify-content: safe center;
  }
  .float-bar::-webkit-scrollbar { display: none; }
  /* While the docked bar sits over the page-controls row, the row underneath
     is out of sight: hide it properly instead of letting slivers peek around
     the bar's edges (or a wrapped second line of the row showing below it).
     The bar IS the row until the selection is dropped; JS toggles the class
     only at this same breakpoint. */
  body.fb-docked .page-controls { visibility: hidden; }
  /* Fewer controls. The captions say nothing the controls do not already show —
     the select displays the family and the swatch displays the colour — and the
     0-width separators still carry 4px of margin each. Dropping them keeps the
     docked bar to ONE compact row instead of two. Hiding .fb-cap is safe even
     though editor.js owns those nodes: it sets style.display = "" for controls
     in the current layout, and an empty inline value lets this rule win. */
  /* One compact row SHOULD hold the full text set at a normal phone width
     (390) once the controls below are tightened — at the old desktop sizes a
     third of them sat past the edge and the row read as broken. What still
     overflows on a very narrow screen wraps to a second row rather than
     hiding behind the sideways scroll. */
  .float-bar { gap: 0; }
  .float-bar .fb-btn { padding: 0 4px; min-width: 26px; }
  .float-bar .fb-cap,
  .float-bar .fb-sep { display: none; }
  .float-bar input[type=number] { width: 38px; padding: 0 3px; }
  .float-bar input[type=text] { width: 84px; }
  .float-bar select, .fb-font-btn { max-width: 86px; }
}

/* ------------------------------------------------------------------ phones
   Phones. The page row and tool strip were each wrapping into two or three
   lines of desktop-sized controls, which on a 320x568 screen left the sheet
   about 100px of height — the document was a sliver under the chrome.

   This block sits at the END of the file on purpose. An identical block placed
   up with the other width queries lost the cascade: it came before the base
   .page-controls rules at the bottom half of the stylesheet, which then
   overrode every value back to the desktop size, so the row never changed.
   Media queries do not add specificity — source order decides. Verify any edit
   here against the measured height, not by reading the rule. */
/* NOTE: the tool strip must never get `flex-wrap: nowrap` with a shrinkable
   main group again. The two file verbs moved INTO the strip (client: "it
   should toolbar"), which added ~250px of unshrinkable width; under the old
   nowrap rules that was past the tipping point at every phone width — the main
   group was crushed to its 6px minimum and every tool icon stacked into a
   vertical column down the left of the sheet (the client's "fix the floatbar
   for mobile and tab"). The base wrapping layout — groups wrap to rows,
   buttons wrap inside a group — is the model at EVERY width; the tightening
   below only shrinks the strip's biggest fixed pieces. */
@media (max-width: 560px) {
  /* One line, always — but the sheet comes FIRST. This row needs about 344px with
     the zoom pair back on phones (page numeral, "1 / 2", five page buttons, the
     zoom readout, the insert button), and at the old 40px buttons it wrapped onto
     a second line on anything under ~360px, spending 35px of a small phone's
     height on chrome instead of the document. Narrower buttons rather than a
     hidden control or a sideways-scrolling row: nothing disappears and nothing
     has to be discovered by swiping. Apply and Save joined the row by client
     request, so the tightening below matters more than it used to: with the word
     "changes" dropped they measure in at about 495px, still one line at 543px;
     a true 320-360px phone wraps to a second line, which beats hiding the two
     file verbs. */
  .page-controls { gap: 6px; padding: 0 8px 6px; }
  /* The strip's home group: the brand alone is enough at this width — "All
     PDF tools" travels in the More menu's link. */
  .tb-nav { display: none; }
  .page-controls .pc-group .tool-btn { width: 30px; min-width: 30px; }
  .page-controls .pc-zoom { min-width: 38px; }
  .page-controls .pc-group .tool-btn { width: 34px; min-width: 34px; }
  .page-controls .pc-group .mi { width: 16px; height: 16px; }
  /* Icon-only: the "+" carries the meaning and the tooltip still names it. */
  .page-controls .insert-here { margin-left: 0; padding: 0 10px; gap: 0; }
  .page-controls .insert-here .ih-text { display: none; }
  .page-controls .pc-zoom { min-width: 42px; font-size: 12px; }
  /* The verbs exist twice — in the page row and (client's ask) in the tool
     strip itself — and both tighten the same way. Both drop the "Save and "
     words: "Continue" alone still reads as the finish button beside Save. */
  .pc-verb { padding: 0 9px; gap: 5px; font-size: 12px; }
  .page-controls .pc-apply { margin-left: 4px; }
  .pc-verb .pc-w { display: none; }
}

/* ----------------------------------------------------------------- Sejda
   parity extras: redo state, the page-of-total readout, the right-click object
   menu, the numeric properties panel and the two sheets (fonts, shortcuts). */
.tool-btn.is-off { opacity: .4; cursor: default; }
.tool-btn.is-off:hover { background: none; border-color: var(--sk-tool-line); color: var(--sk-ink); }
/* Zoom buttons at their floor or ceiling: the editor disables them, so they must
   look spent rather than broken. */
.tool-btn:disabled { opacity: .4; cursor: default; }
.tool-btn:disabled:hover { background: none; border-color: var(--sk-tool-line); color: var(--sk-ink); }
.pc-of { margin: 0 6px 0 0; font-size: .82rem; color: var(--muted); }

/* What a click would place, while a tool is in hand. */
/* (There is no ghost preview: Sejda previews nothing under the cursor, it just
   shows a crosshair — .overlay.editing above — and draws the object as you
   drag. The dashed blue box that used to follow the pointer read as a stray
   selection frame, so it is gone.) */

/* Right-click object menu. */
.ctx-menu {
  position: fixed; z-index: 320; min-width: 210px; padding: 5px 0;
  background: #fff; border: 1px solid rgba(0, 0, 0, .15); border-radius: 4px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, .2);
}
.ctx-menu button {
  display: flex; align-items: center; gap: 9px; width: 100%; padding: 7px 14px;
  background: none; border: 0; font: inherit; font-size: 14px; color: #333;
  text-align: left; white-space: nowrap; cursor: pointer;
}
.ctx-menu button:hover { background: #f5f5f5; }
.ctx-menu button:disabled { opacity: .45; cursor: default; }
.ctx-menu button:disabled:hover { background: none; }
.ctx-menu .mi { width: 15px; height: 15px; margin: 0; }
.ctx-sep { height: 1px; margin: 5px 0; background: var(--line); }
.ctx-menu .ctx-del { color: #c9302c; }

/* The font sheet behind "More fonts…". */
.modal-box.font-box { width: min(620px, 94vw); }
#font-search { margin: 2px 0 0; }
.font-list {
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px 10px;
  margin-top: 12px; max-height: 46vh; overflow-y: auto;
}
.font-row {
  display: block; width: 100%; padding: 9px 10px; font-size: 22px;
  line-height: 1.15; color: #333; text-align: left; cursor: pointer;
  background: none; border: 1px solid transparent; border-radius: 4px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.font-row:hover { background: var(--sk-blue-soft); border-color: var(--sk-blue); }

/* The "?" shortcut sheet. */
.modal-box.shortcut-box { width: min(520px, 94vw); }
.shortcut-list { list-style: none; margin: 4px 0 0; padding: 0; }
.shortcut-list li {
  display: flex; align-items: baseline; gap: 14px;
  padding: 7px 0; border-bottom: 1px solid var(--line);
}
.shortcut-list li:last-child { border-bottom: 0; }
.shortcut-list kbd {
  flex: 0 0 124px; padding: 3px 6px; font: inherit; font-size: 12px;
  text-align: center; color: #333; background: var(--bg);
  border: 1px solid var(--line); border-radius: 4px;
}
.shortcut-list span { font-size: 14px; color: #333; }

/* The toolbar guide sheet (the ⓘ info mark beside the zoom rocker). Rows are
   read off the live strip by editor-extras.js — icon, name and how it works. */
.modal-box.guide-box { width: min(560px, 94vw); }
.guide-intro { margin: -8px 0 2px; }
.guide-list { max-height: 58vh; overflow-y: auto; margin-top: 6px; }
.guide-sec {
  margin: 14px 0 2px; font-size: 11.5px; font-weight: 600;
  letter-spacing: .05em; text-transform: uppercase; color: #8a9199;
}
.guide-sec:first-child { margin-top: 4px; }
.guide-row {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 8px 2px; border-bottom: 1px solid var(--line);
}
.guide-row:last-child { border-bottom: 0; }
.guide-ic {
  flex: 0 0 22px; min-width: 22px; display: flex; align-items: center;
  justify-content: center; padding-top: 2px;
}
.guide-ic img { width: 18px; height: 18px; }
.guide-ic svg { width: 16px; height: 16px; color: #55606c; }
.guide-tx { min-width: 0; }
.guide-name { font-size: 13.5px; font-weight: 600; color: #222; }
.guide-desc { margin-top: 1px; font-size: 13px; color: #55606c; line-height: 1.45; }

/* ---------------------------------------------------------------------------
   Fill & Sign mirrors Sejda's sign-pdf screen: their toolbar is Text, Forms,
   Sign and Undo only — Links, Images, Whiteout, Annotate and Shapes are the
   editor proper's tools, and no Redo or More menu rides along. The Forms menu
   is reduced in the template (see editor.html). Their Create signature dialog
   also has no "Save signature" tick — keep it out of this tool's dialog (still
   ticked, so signatures keep joining the gallery by themselves).
   --------------------------------------------------------------------------- */
#editor[data-tool="sign-pdf"] .menu-links,
#editor[data-tool="sign-pdf"] .menu-images,
#editor[data-tool="sign-pdf"] .menu-whiteout,
#editor[data-tool="sign-pdf"] .menu-annotate,
#editor[data-tool="sign-pdf"] .menu-shapes,
#editor[data-tool="sign-pdf"] .menu-more,
#editor[data-tool="sign-pdf"] .toolbar .tool-btn[data-action="redo"],
/* The Table button arrived with the spreadsheet sheet, after this rule was
   written — the sheet is editor-proper furniture like Shapes, so it must not
   leak onto the sign strip either (the toolbar guide reads the live strip
   and surfaced the leak). */
#editor[data-tool="sign-pdf"] .toolbar .tool-btn[data-action="sheet-open"] { display: none; }

#editor[data-tool="sign-pdf"] .sign-remember { display: none; }

/* The Publish dialog: one link, one Copy. */
.modal-box.publish-box { width: min(520px, 94vw); }
.publish-row { display: flex; gap: 8px; margin-top: 10px; }
.publish-row .btn { flex: 0 0 auto; white-space: nowrap; }
.publish-row input {
  flex: 1 1 auto; min-width: 0; margin-bottom: 0; padding: 9px 10px; font: inherit; font-size: 13px;
  color: #333; background: var(--bg); border: 1px solid var(--line); border-radius: 8px;
}

/* Create Forms wears Sejda's builder strip (see the template) — the More menu
   is editor chrome their builder screen does not carry. */
#editor[data-tool="pdf-forms"] .menu-more { display: none; }
