theseus/bundled-addons/pdf-editor/editor.css
Local Dev 8ed051c2da feat(pdf-editor): edit the document's own text on the page, and a line means the whole line
Three complaints, one cause between the first two.

A line of a PDF is rarely one run. pdf.js splits it wherever the file does
— a font change, a kerning adjustment, a colour change — so a heading can
be three spans and an invoice line ten. Replacing the span under the
cursor covered a fragment and left the rest of the line standing, which is
exactly what a replacement that looks like a copy laid over the original
is. A run is now the whole visual line: the spans that share its baseline
and sit close enough to be spacing rather than a second column, with the
spaces the geometry implies put back between them.

And that line is edited on the page. The dialog that used to hold a copy
of the words is gone: the cover goes down first, carrying the line's own
words at the page's own size and colour, and the caret opens on it. The
cover keeps its words hidden while you type, so the original never shows
through the thing covering it. Escape with nothing changed lifts the cover
again and leaves the page as it was found — no mark, no undo step.

The rotate grip was a square like the resize handles, wearing the open
hand that means drag-the-page. It is a disc with a turning arrow now, and
a cursor drawn to match, since no standard cursor means turn. The inline
style that was defeating the stylesheet is gone with it.

Ctrl and the wheel zoom, about the pointer rather than the top-left, so
the words you were reading stay where they were. A plain wheel still
scrolls.
2026-09-27 19:24:28 +02:00

383 lines
23 KiB
CSS

/* PDF Editor — full-tab. Same palette and control shapes as the screenshot
editor so the two add-ons read as one family; loaded AFTER pdf.js's
pdf_viewer.css so the overrides at the bottom win. */
:root { color-scheme: light dark;
--bg:#0e131c; --panel:#141a24; --panel2:#191f2b; --line:rgba(255,255,255,.09);
--ink:#e7eaf1; --mut:#8b98a9; --dim:#5e6678; --acid:#d6ff3d;
--danger:#ff5b5b; --board:#0a0d13; --shadow:rgba(0,0,0,.45); }
@media (prefers-color-scheme: light) {
:root { --bg:#f8faff; --panel:#ffffff; --panel2:#eff3fb; --line:rgba(0,0,0,.10);
--ink:#1a1f2b; --mut:#5c6577; --dim:#8a93a5; --acid:#0AC18E;
--board:#dde3ee; --shadow:rgba(20,30,50,.20); }
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body { background: var(--bg); color: var(--ink);
font: 13px/1.5 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
display: flex; flex-direction: column; overflow: hidden; }
/* pdf.js's stylesheet sets display on some of the nodes we toggle, so plain
[hidden] loses the cascade fight. */
[hidden] { display: none !important; }
/* ---- bars ----------------------------------------------------------- */
.topbar, .toolbar { display: flex; align-items: center; gap: 4px; padding: 6px 8px;
border-bottom: 1px solid var(--line); background: var(--panel);
user-select: none; }
.topbar { flex-wrap: nowrap; }
.toolbar { align-items: flex-start; gap: 8px; row-gap: 6px; flex-wrap: wrap; }
.tool-cluster { display: flex; flex-wrap: wrap; align-items: center;
gap: 10px; row-gap: 6px; flex: 1 1 auto; justify-content: center; }
.tgroup { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; }
.tgroup.zoomgroup { align-self: center; }
.spacer { flex: 1; }
.sep { width: 1px; height: 20px; background: var(--line); margin: 0 4px; flex: 0 0 auto; }
.docname { color: var(--mut); font-size: 12.5px; overflow: hidden; min-width: 0;
text-overflow: ellipsis; white-space: nowrap; max-width: 46vw; }
.docname.dirty::after { content: " •"; color: var(--acid); font-weight: 700; }
.btn, .tool {
border: 1px solid var(--line); background: var(--panel2); color: var(--ink);
width: 30px; height: 30px; border-radius: 6px; cursor: pointer;
display: inline-flex; align-items: center; justify-content: center;
padding: 0; transition: background 100ms, border-color 100ms;
font: inherit; line-height: 0; flex: 0 0 auto;
}
.btn:hover, .tool:hover { border-color: rgb(from var(--acid) r g b / .5); }
.btn:disabled, .tool:disabled { opacity: .4; cursor: default; }
.btn:disabled:hover { border-color: var(--line); }
.btn svg, .tool svg { width: 16px; height: 16px; display: block; }
.btn.wide { width: auto; padding: 0 10px; gap: 6px; line-height: 1; }
.btn.wide span { font-size: 12px; }
.btn.primary { background: var(--acid); color: #101418; border-color: transparent; font-weight: 600; }
.btn.primary:hover { filter: brightness(1.06); border-color: transparent; }
.btn.primary:disabled:hover { filter: none; }
.btn.danger { color: var(--danger); }
.btn.danger:hover { border-color: rgba(255,91,91,.55); }
.tool.active { background: rgb(from var(--acid) r g b / .15);
border-color: rgb(from var(--acid) r g b / .7); color: var(--acid); }
/* Redact is destructive; it reads red when armed rather than acid. */
.tool[data-tool="redact"].active { background: rgba(255,91,91,.16);
border-color: rgba(255,91,91,.7); color: var(--danger); }
.swatch { width: 22px; height: 22px; border-radius: 50%; padding: 0;
border: 2px solid var(--line); cursor: pointer; flex: 0 0 auto;
/* The near-black ink swatch would otherwise disappear into a dark
toolbar; a faint inner ring keeps every colour countable. */
box-shadow: inset 0 0 0 1px rgb(from var(--ink) r g b / .28); }
.swatch.active { border-color: var(--acid);
outline: 1px solid rgb(from var(--acid) r g b / .35); outline-offset: 1px; }
.width { border: 1px solid var(--line); background: var(--panel2); color: var(--ink);
width: 30px; height: 30px; border-radius: 6px; cursor: pointer;
display: inline-flex; align-items: center; justify-content: center; padding: 0; }
.width.active { border-color: rgb(from var(--acid) r g b / .7);
background: rgb(from var(--acid) r g b / .15); }
.width .dot { border-radius: 50%; background: currentColor; color: var(--ink); }
.width.active .dot { color: var(--acid); }
select, input[type="text"], textarea {
border: 1px solid var(--line); background: var(--panel2); color: var(--ink);
border-radius: 6px; padding: 5px 7px; font: inherit; height: 30px;
}
textarea { height: auto; resize: vertical; width: 100%; line-height: 1.45; }
select:focus, input:focus, textarea:focus { outline: none; border-color: rgb(from var(--acid) r g b / .6); }
/* ---- body: viewer + rail -------------------------------------------- */
.body { flex: 1; display: flex; min-height: 0; }
.viewerContainer { position: absolute; inset: 0; overflow: auto; background: var(--board); }
.body { position: relative; }
.viewerContainer { position: relative; flex: 1 1 auto; min-width: 0; }
.rail { flex: 0 0 176px; border-left: 1px solid var(--line); background: var(--panel);
display: flex; flex-direction: column; min-height: 0; }
body[data-rail="off"] .rail { display: none; }
.rail-head { display: flex; align-items: center; gap: 6px; padding: 7px 10px;
border-bottom: 1px solid var(--line); color: var(--dim); font-size: 11.5px; }
.rail-list { flex: 1; overflow: auto; padding: 8px; display: flex;
flex-direction: column; gap: 8px; }
.thumb { position: relative; border: 1px solid var(--line); border-radius: 6px;
background: var(--panel2); padding: 6px; cursor: grab; }
.thumb.current { border-color: rgb(from var(--acid) r g b / .75); }
.thumb.dragging { opacity: .45; }
.thumb.dropbefore { box-shadow: 0 -3px 0 0 var(--acid); }
.thumb.dropafter { box-shadow: 0 3px 0 0 var(--acid); }
.thumb.deleted { opacity: .38; }
.thumb.deleted .tcanvas { filter: grayscale(1); }
.thumb .tcanvas { display: block; width: 100%; height: auto; background: #fff;
border-radius: 3px; box-shadow: 0 1px 4px var(--shadow); }
.thumb .tfoot { display: flex; align-items: center; gap: 3px; margin-top: 5px; }
.thumb .tnum { color: var(--dim); font-size: 11px; font-variant-numeric: tabular-nums;
flex: 1 1 auto; min-width: 0; }
.thumb .tbtn { width: 20px; height: 20px; border-radius: 4px; border: 1px solid var(--line);
background: var(--panel); color: var(--ink); cursor: pointer; padding: 0;
display: inline-flex; align-items: center; justify-content: center; }
.thumb .tbtn svg { width: 12px; height: 12px; display: block; }
.thumb .tbtn:hover { border-color: rgb(from var(--acid) r g b / .55); }
.thumb .tbtn.del:hover { border-color: rgba(255,91,91,.6); color: var(--danger); }
.fbtn { border: 1px solid var(--line); background: var(--panel2); color: var(--ink);
border-radius: 6px; cursor: pointer; padding: 3px 7px; font: inherit;
font-size: 11px; line-height: 1.4; }
.fbtn:hover { border-color: rgb(from var(--acid) r g b / .55); }
/* ---- footer --------------------------------------------------------- */
.footer { display: flex; align-items: center; gap: 8px; padding: 5px 10px;
border-top: 1px solid var(--line); background: var(--panel); font-size: 11.5px; }
.fstat { color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fstat.err { color: var(--danger); }
/* ---- drop zone ------------------------------------------------------ */
.dropzone { position: absolute; inset: 0; display: none; align-items: center;
justify-content: center; pointer-events: none; z-index: 20; }
body[data-empty="1"] .dropzone { display: flex; pointer-events: auto; }
body[data-dragover="1"] .dropzone { display: flex; pointer-events: none;
background: rgb(from var(--acid) r g b / .08); }
.dz-inner { text-align: center; color: var(--dim); border: 2px dashed var(--line);
border-radius: 14px; padding: 40px 54px; background: var(--panel);
max-width: 82%; }
body[data-dragover="1"] .dz-inner { border-color: rgb(from var(--acid) r g b / .8); color: var(--ink); }
.dz-inner svg { width: 48px; height: 48px; margin-bottom: 10px; }
.dz-title { font-size: 15px; color: var(--ink); font-weight: 600; }
.dz-sub { margin-top: 5px; max-width: 42ch; margin-left: auto; margin-right: auto; }
/* Start-screen tiles. Hidden while a drag is in flight: during a drop the
drop zone is the whole target, and a row of buttons under the cursor
reads as somewhere to aim, which it is not. */
.dz-actions { display: flex; gap: 10px; margin-top: 22px; justify-content: center;
flex-wrap: wrap; }
body[data-dragover="1"] .dz-actions { visibility: hidden; }
.tile { display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
width: 176px; padding: 13px 14px; text-align: left; cursor: pointer;
background: var(--bg); color: var(--ink); border: 1px solid var(--line);
border-radius: 10px; font: inherit; }
.tile:hover { border-color: rgb(from var(--acid) r g b / .55);
background: rgb(from var(--acid) r g b / .06); }
.tile:focus-visible { outline: 2px solid var(--acid); outline-offset: 2px; }
.tile svg { width: 22px; height: 22px; margin: 0 0 5px; color: var(--acid); }
.tile-t { font-weight: 600; font-size: 13px; }
.tile-s { font-size: 11.5px; color: var(--dim); line-height: 1.35; }
.linkish { background: none; border: none; padding: 0; font: inherit; cursor: pointer;
color: var(--acid); text-decoration: underline; }
/* ---- the three main tools ------------------------------------------- */
/* Point at it, write on it, sign it. Wider, labelled, and set apart from the
drawing kit, because they are the things most people open a PDF to do and
they used to be four identical squares in a row of thirteen. */
.tgroup.primary { gap: 4px; }
.tool.big { width: auto; padding: 0 10px; gap: 6px; }
.tool.big .tlabel { font-size: 12px; line-height: 1; }
.tool.big svg { width: 15px; height: 15px; }
/* Five names take room. Below this the toolbar would start pushing the zoom
and colour controls off the end, so the names go and the icons stay. */
@media (max-width: 1180px) { .tool.big .tlabel { display: none; } .tool.big { width: 30px; padding: 0; } }
/* ---- typing on the page --------------------------------------------- */
/* Positioned by lib/inline.js against the page's own coordinates, so it sits
where the text will sit. No background: the page is the background, which
is the whole point — you see the words against what they are going on. */
.inline-text { position: absolute; z-index: 24; margin: 0; padding: 0;
min-width: 1px; white-space: pre; outline: none;
caret-color: currentColor;
box-shadow: 0 0 0 1px rgb(from var(--acid) r g b / .55),
0 0 0 4px rgb(from var(--acid) r g b / .14); }
.inline-text:empty::before { content: ""; display: inline-block; width: 1px; }
/* ---- signature library ---------------------------------------------- */
.siglist { list-style: none; margin: 0 0 12px; padding: 0; display: grid;
gap: 8px; max-height: 40vh; overflow: auto; }
.sigrow { display: flex; align-items: center; gap: 10px; padding: 8px 10px;
border: 1px solid var(--line); border-radius: 9px; background: var(--bg); }
.sigrow .sigpreview { flex: 0 0 132px; height: 44px; }
.sigrow .sigpreview svg { width: 100%; height: 100%; }
.sigrow .signame { flex: 1 1 auto; min-width: 0; font-size: 13px; color: var(--ink);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* flex: none on the buttons — without it they shrink below their own labels
and the words run into each other. The name gives way instead. */
.sigrow .sigacts { display: flex; gap: 6px; flex: 0 0 auto; }
/* width: auto matters most — .btn is a 30px square by default, made for an
icon, and a word does not fit in one. */
.sigrow .sigacts .btn { flex: none; width: auto; height: 26px; white-space: nowrap;
padding: 0 10px; font-size: 11.5px; line-height: 1; }
/* Ink for the pad. Dark by default: a signature is not a highlight. */
.sigink { display: flex; gap: 6px; margin: 0 0 8px; }
.sigink .swatch { width: 22px; height: 22px; border-radius: 50%;
border: 2px solid var(--line); cursor: pointer; padding: 0; }
.sigink .swatch.active { border-color: var(--acid); }
/* ---- modals --------------------------------------------------------- */
.scrim { position: fixed; inset: 0; background: rgba(4,7,12,.62); z-index: 50;
display: flex; align-items: center; justify-content: center; padding: 24px; }
.modal { background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
box-shadow: 0 18px 60px rgba(0,0,0,.5); padding: 18px 20px; max-width: 560px;
width: 100%; max-height: 90vh; overflow: auto; }
.modal h2 { margin: 0 0 10px; font-size: 15px; }
.modal p { margin: 0 0 10px; color: var(--mut); }
.modal p strong { color: var(--ink); }
.mhint { font-size: 12px; }
.mhint.warn { color: var(--danger); }
.mrow { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
.mfield { display: inline-flex; align-items: center; gap: 6px; color: var(--mut); font-size: 12px; }
.mfield.block { display: flex; flex-direction: column; align-items: stretch; gap: 5px; }
.check { display: flex; align-items: flex-start; gap: 8px; margin: 12px 0 0;
color: var(--ink); font-size: 12.5px; cursor: pointer; }
.check input { margin: 2px 0 0; accent-color: var(--acid); }
#sign-pad { width: 100%; height: auto; aspect-ratio: 640 / 220; background: #fff;
border: 1px solid var(--line); border-radius: 8px; cursor: crosshair;
touch-action: none; display: block; }
.summary { margin: 10px 0 0; padding-left: 18px; color: var(--mut); font-size: 12px; }
.summary li { margin: 2px 0; }
.summary li.warn { color: var(--danger); }
.toast { position: fixed; left: 50%; bottom: 44px; transform: translateX(-50%) translateY(20px);
padding: 8px 14px; border-radius: 6px; background: rgba(11,14,20,.95);
color: #e7eaf1; font-size: 12.5px; border: 1px solid var(--line);
opacity: 0; pointer-events: none; transition: opacity 180ms, transform 180ms;
z-index: 100; max-width: 80vw; text-align: center; }
.toast.on { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.err { border-color: rgba(255,91,91,.6); color: #ffb3b3; }
/* ---- pdf.js layer overrides ----------------------------------------- */
/* A real gap between pages plus a drop shadow, instead of pdf.js's
transparent-border trick. */
/* overflow-anchor off: pages change height as they take their real size and
again on every zoom, and scroll anchoring "helpfully" moves the scroll
position to compensate — which walks the reader away from the page they
were on and, worse, can park the whole document outside the viewport. */
.viewerContainer { overflow-anchor: none; }
.pdfViewer { --page-margin: 0 auto 14px; --page-border: none; padding: 14px 0 4px; }
.pdfViewer .page { box-shadow: 0 2px 14px var(--shadow); }
/* Our overlay sits above the canvas but below the annotation (form) layer, so
a form field stays clickable even with marks drawn around it. */
/* Marks are clipped to the page by a clipPath on the marks group, not by the
SVG's own overflow — the selection handles sit just outside a mark's bounds
and a mark flush with the page edge still has to be grabbable. */
.pdfViewer .page .smOverlay { position: absolute; inset: 0; z-index: 3;
pointer-events: none; overflow: visible; }
.pdfViewer .page .annotationLayer { z-index: 4; }
.pdfViewer .page .textLayer { z-index: 2; }
/* Tool modality. When a drawing tool is armed the text and form layers stop
taking pointer events, so a drag paints instead of selecting text or
landing in a field. Highlight is the exception: it works BY selecting
text, so the text layer stays live for it. */
body[data-mode="draw"] .textLayer,
body[data-mode="draw"] .annotationLayer { pointer-events: none !important; }
body[data-mode="draw"] .pdfViewer .page { cursor: crosshair; }
body[data-mode="stamp"] .textLayer,
body[data-mode="stamp"] .annotationLayer { pointer-events: none !important; }
body[data-mode="stamp"] .pdfViewer .page { cursor: copy; }
body[data-mode="highlight"] .annotationLayer { pointer-events: none !important; }
body[data-mode="highlight"] .pdfViewer .page { cursor: text; }
/* Marks are only hit-testable with the select tool, so a drawing drag is
never stolen by something already on the page. */
body[data-mode="select"] .smOverlay { pointer-events: none; }
body[data-mode="select"] .smOverlay .mark { pointer-events: stroke; cursor: move; }
body[data-mode="select"] .smOverlay .mark[data-fill="1"] { pointer-events: all; }
.smOverlay .mark.sel { outline: none; }
/* ...with one exception: the SELECTED mark stays grabbable whatever tool is
armed. Its handles already were, so a shape you had just drawn could be
stretched but not shifted. Only the selected one opts in, so a press
anywhere else still draws. */
.smOverlay .mark.sel { pointer-events: stroke; cursor: move; }
.smOverlay .mark.sel[data-fill="1"] { pointer-events: all; }
.smOverlay .selbox { fill: none; stroke: var(--acid); stroke-width: 1;
stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; }
/* A group: one solid box round the lot, and a fainter one round each member
so it is obvious WHICH marks are in it — a marquee that caught one more
than you meant is worth seeing before you press Delete. */
.smOverlay .selbox.group { stroke-dasharray: 2 4; stroke-width: 1.5; }
.smOverlay .selbox.member { stroke-dasharray: none; stroke-opacity: .45; }
/* The rubber band itself. */
.smOverlay .marquee { fill: rgb(from var(--acid) r g b / .10); stroke: var(--acid);
stroke-width: 1; stroke-dasharray: 5 3;
vector-effect: non-scaling-stroke; }
.smOverlay .handle { fill: var(--acid); stroke: #101418; stroke-width: 1;
vector-effect: non-scaling-stroke; cursor: pointer; pointer-events: all; }
/* The rotate grip. A turning arrow on a disc, and a cursor that says turn
rather than the open hand the browser gives `grab`, which reads as "drag
the page". No standard cursor means turn, so it is drawn here. */
.smOverlay .handle.rot { fill: none; stroke: none; }
.smOverlay .rotdisc { fill: var(--acid); stroke: #101418; stroke-width: 1;
vector-effect: non-scaling-stroke; }
.smOverlay .rotmark { fill: none; stroke: #101418; stroke-width: 1.4;
stroke-linecap: round; stroke-linejoin: round;
vector-effect: non-scaling-stroke; }
.smOverlay .rotarm { stroke: var(--acid); stroke-width: 1; stroke-dasharray: 2 2;
vector-effect: non-scaling-stroke; }
.smOverlay .handle.rot,
body[data-rotating="1"] { cursor: url("data:image/svg+xml;utf8,\
%3Csvg xmlns='http://www.w3.org/2000/svg' width='26' height='26' viewBox='0 0 26 26'%3E\
%3Cg fill='none' stroke='%23101418' stroke-width='3.6' stroke-linecap='round' stroke-linejoin='round'%3E\
%3Cpath d='M19 13a6 6 0 1 1-1.8-4.2'/%3E%3Cpath d='M17.4 4.2v4.6h-4.6'/%3E%3C/g%3E\
%3Cg fill='none' stroke='%23ffffff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E\
%3Cpath d='M19 13a6 6 0 1 1-1.8-4.2'/%3E%3Cpath d='M17.4 4.2v4.6h-4.6'/%3E%3C/g%3E%3C/svg%3E") 13 13, grab; }
/* pdf.js renders form widgets with a coloured backdrop; keep it, it is the
only cue that a field is fillable. */
.annotationLayer .textWidgetAnnotation input,
.annotationLayer .textWidgetAnnotation textarea,
.annotationLayer .choiceWidgetAnnotation select { font: inherit; }
/* ---- selection bar --------------------------------------------------- */
/* Pinned to the current selection in viewport coordinates, so a page edge
never clips it and moving the selection to another page needs no
re-parenting. It steps aside mid-drag rather than jittering under the
cursor. */
.selbar {
position: fixed; z-index: 40; display: flex; align-items: center; gap: 2px;
padding: 3px; border-radius: 8px; background: var(--panel);
border: 1px solid var(--line); box-shadow: 0 6px 22px var(--shadow);
user-select: none;
}
body[data-dragging="1"] .selbar,
.selbar.offscreen { opacity: 0; pointer-events: none; }
.selbar .sb {
border: 1px solid transparent; background: transparent; color: var(--ink);
min-width: 26px; height: 26px; border-radius: 5px; cursor: pointer; padding: 0 5px;
display: inline-flex; align-items: center; justify-content: center;
font: inherit; font-size: 13px; line-height: 1;
}
.selbar .sb svg { width: 15px; height: 15px; display: block; }
.selbar .sb:hover { background: var(--panel2); border-color: rgb(from var(--acid) r g b / .5); }
.selbar .sb.active { background: rgb(from var(--acid) r g b / .16);
border-color: rgb(from var(--acid) r g b / .7); color: var(--acid); }
.selbar .sb.danger:hover { border-color: rgba(255,91,91,.6); color: var(--danger); }
.selbar .sb-group { display: inline-flex; align-items: center; gap: 1px; }
.selbar .sb-val { min-width: 22px; text-align: center; font-size: 11.5px;
color: var(--mut); font-variant-numeric: tabular-nums; }
.selbar .sb-sep { width: 1px; height: 17px; background: var(--line); margin: 0 3px; }
/* Style toggles inside the text dialog. */
.btn.styl { width: 30px; font-size: 14px; line-height: 1; }
.btn.styl.active { background: rgb(from var(--acid) r g b / .16);
border-color: rgb(from var(--acid) r g b / .7); color: var(--acid); }
/* Resize handles stay grabbable whatever tool is armed, so a shape can be
sized the moment it is drawn without a detour through the select tool.
Safe to leave live because a handle is a 9 px square that exists only while
something is selected — aiming at one is not something you do by accident,
and Escape clears the selection if you want the space back for drawing. */
.smOverlay .handle { pointer-events: all; }
/* The invisible grab area under a thin stroke. `stroke` rather than
`visibleStroke`: the copy is transparent on purpose. */
body[data-mode="select"] .smOverlay .mark .hit { pointer-events: stroke; }
.smOverlay .mark .hit { pointer-events: none; }
/* Picking a run of the document's own text to replace. The text layer stays
live so a click can land on a run; the form layer steps aside so a widget
does not swallow it. The hover outline is the only cue that the invisible
text layer is a target at all. */
body[data-mode="text"] .annotationLayer { pointer-events: none !important; }
body[data-mode="text"] .pdfViewer .page { cursor: text; }
/* The document's own words light up under the cursor, so it is obvious that
clicking them replaces them and clicking anywhere else starts new text. */
body[data-mode="text"] .textLayer span:hover {
outline: 2px solid rgb(from var(--acid) r g b / .9);
outline-offset: 1px;
background: rgb(from var(--acid) r g b / .16);
border-radius: 2px;
}