/* The Scripture Workshop — styling. Offline-first: the font is bundled (fonts/),
   nothing loads from the network. Warm-paper palette suite-wide (locked decision,
   carried from the Markup Editor). The --ui variable scales the reading surface,
   --chrome the toolbars/menus — independent, both tablet-first. */

@font-face{
  font-family:'JetBrains Mono';font-style:normal;font-weight:100 800;
  src:url('fonts/jbm-normal.woff2') format('woff2');font-display:swap;
}
@font-face{
  font-family:'JetBrains Mono';font-style:italic;font-weight:100 800;
  src:url('fonts/jbm-italic.woff2') format('woff2');font-display:swap;
}

:root{
  --ui:1;
  --chrome:1;
  /* The reading column's cap (INBOX 2026-07-15: "Add option to change max verse text
     width, instead of hard-coded cap"). 900px was the hardcoded value and is still the
     default; the tool bars' ↔ pair steps it and core/reading-width.js publishes it here.
     This is the pre-JS fallback — every consumer keeps the literal in its var() default
     too, so a bare stylesheet renders at exactly today's width. */
  --reading-w:900px;
  --bg:#fbfaf7;
  --surface:#ffffff;
  --ink:#22201c;
  --muted:#8a8578;
  --line:#e6e2d8;
  /* A strong grid line for the Bracketing table — --line is a hairline that vanishes on
     a projector or e-ink; this reads clearly on both. BLACK (Daniel 2026-07-17) for the boldest
     possible grid on the projector / e-ink. */
  --bracketing-border:#000000;
  --accent:#2e6ea8;
  --quote-accent:#c2a25a;
  /* Help/instruction text. --muted is a light warm grey that washes to a muddy "off gold" on colour
     e-ink; this is a solid, saturated brown that stays a nice brown AND reads clearly there
     (Daniel 2026-07-17). */
  --help-ink:#7a5a24;
  /* Bracketing relation-group stroke colors (mirror bracketing-model.js). */
  --relation-coordinate:#2e6ea8;
  --relation-restatement:#2e8c3a;
  --relation-distinct:#b06a1f;
  --relation-contrary:#9a3b6e;
  /* Semantic fills extracted from scattered literals so dark mode can flip them in
     one place (INBOX 2026-07-14). Light values below are the originals verbatim. */
  --fill-warm:#f4f1ea;       /* secondary button / toolbar-button warm fill */
  --chip-fill:#f7f5f0;       /* chips + option pills */
  --pill-fill:#efece4;       /* note origin pill */
  --accent-soft:#eef4fa;     /* hover / active / selected blue tint */
  --bracketing-selected:#cfe3f7;   /* light-blue fill on the rows of the relationship whose picker is open */
  --accent-soft-2:#d8e9f6;   /* stronger selection tint (range-start, poem-selected) */
  --accent-press:#cfe4f5;    /* :active press tint */
  --inverse-surface:#22201c; /* dark bars + toasts that sit on the light page */
  --danger:#b23c3c;          /* error text / destructive accents */
  --danger-soft:#faf3f2;     /* destructive-button fill */
  --danger-soft-border:#e0c9c9;
  --quote-ink:#5b5346;       /* in-quote italic word color */
  --font-body:'JetBrains Mono','Cascadia Mono',Consolas,monospace;
  --font-ui:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  /* Scripture prose: a proportional serif reads far better than monospace on a
     projector and on the Boox e-ink target (Georgia/Iowan are legibility-tuned).
     System fonts only — nothing to bundle, works offline. */
  --font-reading:Georgia,'Iowan Old Style','Palatino Linotype','Times New Roman',serif;
}

/* ── Dark theme (INBOX 2026-07-14: "Add dark mode") ──────────────────────────────
   The whole surface flips, reading area included (Daniel's call). shell.js resolves
   Auto/Light/Dark to a concrete data-theme='dark' on <html>, so this single block is
   the only override needed — every rule already paints through the vars above.
   Study marks are TRANSLUCENT (markup-model.js tints at ~0.16–0.32 alpha), so over a
   dark page they darken and the light ink stays readable — no per-mark recolor needed.
   A warm-dark palette keeps the paper brand; exports/print stay light (export.js bakes
   the paper look on purpose). */
:root[data-theme="dark"]{
  --bg:#17150f;
  --surface:#211d16;
  --ink:#ece7db;
  --muted:#a49d8d;
  --line:#3a352b;
  --help-ink:#d4b268;   /* solid brown help text, dark theme (see the light-mode note) */
  --bracketing-border:#d5cfc1;   /* strong grid line, dark theme — the dark-mode analog of black (see the light-mode note) */
  --accent:#5a9bd4;
  --quote-accent:#d0b978;
  --relation-coordinate:#5a9bd4;
  --relation-restatement:#4bbf63;
  --relation-distinct:#d08a3a;
  --relation-contrary:#c85f96;
  --fill-warm:#2b2620;
  --chip-fill:#262119;
  --pill-fill:#2b2620;
  --accent-soft:#22313f;
  --bracketing-selected:#2a3f57;   /* light-blue-equivalent selection fill, dark theme */
  --accent-soft-2:#2c4459;
  --accent-press:#35536b;
  --inverse-surface:#33302a;
  --danger:#e88b84;
  --danger-soft:#3a2624;
  --danger-soft-border:#5c3d3b;
  --quote-ink:#c9b79a;
}

*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{
  background:var(--bg);color:var(--ink);
  font-family:var(--font-ui);
  -webkit-text-size-adjust:100%;
}

/* ── App header ──────────────────────────────────────────────────────────── */
#app-header{
  position:sticky;top:0;z-index:20;
  display:flex;flex-wrap:wrap;align-items:center;gap:6px;
  padding:8px 12px;background:var(--surface);border-bottom:1px solid var(--line);
}
#app-title{
  font-weight:700;color:var(--ink);
  font-size:calc(16px * var(--chrome));
}
/* The STAGING badge (chat 2026-07-15: "visible which app I'm using… any screen"). The header
   is the only chrome on every screen, so the badge lives here rather than on the Workbench
   heading. Amber, not the brand accent: it pairs with the staging app's amber icon, and it
   must read as "this is the test app", which a blue chip that matches the UI does not.
   Rendered ONLY off a /…-staging URL — :empty keeps live free of it entirely, so a live
   header is exactly what it was. */
#app-channel{
  flex:0 0 auto;white-space:nowrap;user-select:none;
  padding:2px 7px;border-radius:999px;
  background:#d97706;color:#fff;
  font-size:calc(10px * var(--chrome));font-weight:800;text-transform:uppercase;letter-spacing:0.04em;
}
/* The badge carries the build code now, so it is a date-length pill rather than one word. The
   header already wraps (flex-wrap), so a narrow phone drops the Sync button to a second line
   rather than truncating the one thing you came to read. Tabular figures keep the digits from
   dancing between builds, and the tightened letter-spacing above buys back the width the date
   costs — 0.06em over a date is a lot of air for no legibility. */
#app-channel{font-variant-numeric:tabular-nums;}
#app-channel:empty{display:none;}   /* live (and any dev path) ⇒ no badge, no gap */
/* "Am I on the latest?" answered by the app rather than by comparing a stamp against
   something someone told you (Daniel 2026-07-16: "do you have a better way to tell that we're
   on the latest?"). Silent when current — an app with nothing to say should say nothing — so
   :empty hides it and a healthy header is exactly what it was.
   The READY state is the brand accent and tappable: it is an offer, not a warning. The
   UNCHECKED state is muted and inert: it reports the absence of an answer, which is not an
   error and not something you can act on. */
#app-update{
  flex:0 0 auto;white-space:nowrap;
  padding:3px 10px;border-radius:999px;border:1px solid transparent;
  font-family:var(--font-ui);font-size:calc(11px * var(--chrome));font-weight:700;
  cursor:pointer;
}
#app-update:empty{display:none;}
#app-update.is-ready{background:var(--accent);color:#fff;border-color:var(--accent);}
#app-update.is-unchecked{background:transparent;color:var(--muted);border-color:var(--line);cursor:default;font-weight:600;}
/* F1 — the always-available Sync button. margin-left:auto pushes it to the
   header's right edge. */
#app-sync-btn{
  margin-left:auto;
  display:inline-flex;align-items:center;gap:5px;
  font-family:var(--font-ui);font-size:calc(12px * var(--chrome));
  min-height:calc(30px * var(--chrome));padding:3px 12px;
  border:1px solid var(--line);border-radius:8px;
  background:var(--surface);color:var(--ink);cursor:pointer;
}
#app-sync-btn:disabled{opacity:0.55;cursor:default;}
.app-sync-glyph{display:inline-block;}
#app-sync-btn.syncing .app-sync-glyph{animation:app-sync-spin 0.8s linear infinite;}
@keyframes app-sync-spin{to{transform:rotate(360deg);}}

/* F1 — the shell-level toast (header Sync feedback); sibling of .notes-toast. */
.app-toast{
  position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(8px);z-index:90;
  background:var(--inverse-surface);color:#fff;font-family:var(--font-ui);font-size:calc(13px * var(--chrome));
  padding:10px 16px;border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,0.28);opacity:0;pointer-events:none;transition:opacity 0.15s,transform 0.15s;
}
.app-toast.is-visible{opacity:1;transform:translateX(-50%) translateY(0);}

/* ── Workbench (project library) ─────────────────────────────────────────── */
/* 900px is the column for the CHROME screens — the library, New project, About. Those are
   menus and cards, already scaled by Menu size, and nobody reads a settings row wide.
   `.is-reading` (shell.js stamps it for the project screen, tools included) is where verse
   text lives, and that column is the user's to set. */
#workbench{max-width:900px;margin:0 auto;padding:14px 16px 40vh;}
#workbench.is-reading{max-width:var(--reading-w,900px);}
.workbench-heading{
  font-size:calc(24px * var(--chrome));font-weight:800;color:var(--ink);
  margin:10px 0 6px;
}
/* The heading shares its row with the build code (INBOX 2026-07-15: "to the way right of
   the heading, and small"). margin-left:auto on the stamp pins it to the far edge, and
   `baseline` sits it on the heading's own baseline rather than floating it mid-cap-height.
   It WRAPS, deliberately: with nowrap, a 360px phone at Menu size X-Large overflowed the
   column by 79px (measured) — neither child can shrink, so the row simply ran off the page
   and took a horizontal scrollbar with it. The alternatives at that one extreme were to
   clip "Workbench" to an ellipsis or to let the stamp fall to its own line; the stamp is
   the small, quiet thing here, so it yields, still right-aligned. Every ordinary
   phone/Menu-size pairing keeps it on the heading's line. */
.workbench-heading-row{display:flex;flex-wrap:wrap;align-items:baseline;gap:12px;}
/* Small + muted so it reads as a serial number, not a subtitle. No cursor:pointer,
   deliberately — it carries the 7-tap sync reveal, and a pointer would advertise it. */
.workbench-version{
  margin-left:auto;flex:0 0 auto;white-space:nowrap;user-select:none;
  color:var(--muted);font-size:calc(10px * var(--chrome));
}

.workbench-empty-hint{color:var(--muted);line-height:1.6;font-size:calc(15px * var(--chrome));}

/* ── The install offer (Daniel 2026-07-19) ─────────────────────────────────────
   A card, not a floating toast: it sits in the page above "Workbench" and scrolls
   with it, so it can't cover a control or need dismissing to get past. --accent-soft
   tints it enough to read as an offer rather than as a project card, with no border
   colour or shadow beyond the ones the rest of the chrome already uses.
   It WRAPS (like .workbench-heading-row): the copy is a whole sentence and the
   actions are two buttons, which on a 360px phone at Menu size X-Large cannot share
   a line with the icon. The icon is the only thing that never yields. */
.install-banner{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px;
  margin:10px 0 4px;padding:calc(10px * var(--chrome)) calc(12px * var(--chrome));
  background:var(--accent-soft);border:1px solid var(--line);border-radius:10px;
}
.install-banner-mark{flex:0 0 auto;width:calc(36px * var(--chrome));height:calc(36px * var(--chrome));}
/* flex:1 with a 200px basis — the copy is what absorbs slack, and what folds to its
   own line first when there isn't any. */
.install-banner-copy{display:flex;flex-direction:column;gap:2px;flex:1 1 200px;min-width:0;}
.install-banner-title{font-weight:700;color:var(--ink);font-size:calc(14px * var(--chrome));}
.install-banner-hint{color:var(--ink);line-height:1.45;font-size:calc(13px * var(--chrome));}
.install-banner-actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-left:auto;}
.install-banner-install{background:var(--accent);color:#fff;border-color:var(--accent);}
/* The way out is quiet on purpose — visible enough to find, never competing with
   Install. Transparent so it reads as a link sitting on the card's tint. */
.install-banner-dismiss{
  background:transparent;border-color:transparent;color:var(--muted);
  font-size:calc(13px * var(--chrome));
}
.install-banner-dismiss:hover{border-color:var(--line);}
/* The permanent copy in About & settings (so "Not now" is a deferral, not a locked
   door). Matches .about-esv-save, which is the other primary action in that panel. */
.about-install-btn{
  background:var(--accent);color:#fff;border:1px solid var(--accent);border-radius:8px;
  padding:8px 14px;font-family:var(--font-ui);font-size:calc(13px * var(--chrome));cursor:pointer;
}

/* ── Buttons (shared) ──────────────────────────────────────────────────────────
   The box (min-height + padding) scales with --chrome alongside the font, so a
   button shrinks WITH its text at a smaller Menu size instead of keeping a chunky
   fixed box around small type (INBOX 2026-07-14). At Menu size Normal (--chrome:1)
   these calc()s equal the old fixed 40px / 8px·14px — unchanged by default. */
button{
  font-family:var(--font-ui);font-size:calc(14px * var(--chrome));
  min-height:calc(40px * var(--chrome));padding:calc(8px * var(--chrome)) calc(14px * var(--chrome));cursor:pointer;
  background:var(--surface);color:var(--ink);
  border:1px solid var(--line);border-radius:8px;
}
button:hover{border-color:var(--accent);}
button:disabled{opacity:.5;cursor:default;}
input,select,textarea{
  font-family:var(--font-ui);font-size:calc(14px * var(--chrome));
  min-height:calc(40px * var(--chrome));padding:calc(8px * var(--chrome)) calc(10px * var(--chrome));
  background:var(--surface);color:var(--ink);
  border:1px solid var(--line);border-radius:8px;
}
.workbench-new-btn{margin:10px 0 4px;background:var(--accent);color:#fff;border-color:var(--accent);}
.workbench-about-btn,.workbench-import-btn{margin:10px 0 4px;font-family:var(--font-ui);font-size:calc(14px * var(--chrome));padding:calc(9px * var(--chrome)) calc(16px * var(--chrome));border:1px solid var(--line);border-radius:8px;background:var(--fill-warm);cursor:pointer;}

/* ── About & settings panel (Stage 7 branding) ────────────────────────────── */
/* About + New project are their own focused screens (INBOX 2026-07-14) — a back bar
   above the body, nothing else from the Workbench stacked around them. */
.about-screen,.new-project-screen{padding-top:4px;}
.about-topbar,.new-project-topbar{display:flex;align-items:center;gap:10px;margin:2px 0 6px;}
.about-panel{
  margin:12px 0;padding:18px 20px;background:var(--surface);
  border:1px solid var(--line);border-radius:12px;max-width:640px;
  border-left:4px solid var(--quote-accent);
}
.about-brand-head{display:flex;align-items:center;gap:12px;margin-bottom:4px;}
.about-mark{flex:0 0 auto;width:56px;height:56px;border-radius:12px;box-shadow:0 1px 3px rgba(30,58,92,0.25);}
.about-title{margin:0;font-size:calc(22px * var(--chrome));font-weight:800;color:var(--ink);letter-spacing:0.01em;}
.about-tagline{margin:0 0 14px;color:var(--muted);line-height:1.6;font-size:calc(15px * var(--chrome));}
.about-tool-list{list-style:none;margin:0 0 12px;padding:0;display:flex;flex-direction:column;gap:8px;}
.about-tool{display:flex;flex-direction:column;gap:1px;}
.about-tool-name{font-weight:700;color:var(--accent);font-size:calc(14px * var(--chrome));}
.about-tool-blurb{color:var(--ink);line-height:1.5;font-size:calc(13px * var(--chrome));}
.about-version{margin:0 0 14px;color:var(--muted);font-size:calc(12px * var(--chrome));}
.about-settings{border-top:1px solid var(--line);padding-top:12px;}
.about-settings-heading{margin:0 0 3px;font-size:calc(15px * var(--chrome));font-weight:700;color:var(--ink);}
.about-settings-hint{margin:0 0 8px;color:var(--muted);line-height:1.5;font-size:calc(12px * var(--chrome));}
.about-settings-row{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.about-esv-token{flex:1 1 220px;min-width:0;font-family:var(--font-body);font-size:calc(13px * var(--chrome));padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:var(--bg);}
.about-esv-save{background:var(--accent);color:#fff;border:1px solid var(--accent);border-radius:8px;padding:8px 14px;font-family:var(--font-ui);font-size:calc(13px * var(--chrome));cursor:pointer;}
.about-settings-status{color:var(--muted);font-size:calc(12px * var(--chrome));}
/* Support & links (INBOX 2026-07-14) — outreach anchors styled as accent-outline
   buttons; box scales with --chrome like the rest of the button family. */
.about-links-row{gap:10px;}
.about-link-btn{
  display:inline-flex;align-items:center;text-decoration:none;
  font-family:var(--font-ui);font-size:calc(13px * var(--chrome));
  min-height:calc(40px * var(--chrome));padding:calc(8px * var(--chrome)) calc(14px * var(--chrome));
  border:1px solid var(--accent);border-radius:8px;background:var(--bg);color:var(--accent);cursor:pointer;
}
.about-link-btn:hover{background:var(--accent);color:#fff;}
/* Menu size (INBOX 2026-07-14) — preset buttons that drive --chrome; the active one
   is filled in the accent. Sized generously so they stay easy tablet targets. */
.about-menu-size-btn,.about-theme-btn{
  font-family:var(--font-ui);font-size:calc(13px * var(--chrome));
  min-height:calc(34px * var(--chrome));padding:7px 15px;
  border:1px solid var(--line);border-radius:8px;background:var(--bg);color:var(--ink);cursor:pointer;
}
.about-menu-size-btn.is-active,.about-theme-btn.is-active{background:var(--accent);color:#fff;border-color:var(--accent);}
/* The "Get a free ESV key" help link sits under its explainer, above the token box. */
.about-esv-help{margin:2px 0 10px;}
/* Device sync (A4) — reuses the ESV settings look; own classes for spacing + input. */
.about-sync{margin-top:12px;}
.about-sync-key{flex:1 1 220px;min-width:0;font-family:var(--font-body);font-size:calc(13px * var(--chrome));padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:var(--bg);}
.about-sync-now{background:var(--accent);color:#fff;border:1px solid var(--accent);border-radius:8px;padding:8px 14px;font-family:var(--font-ui);font-size:calc(13px * var(--chrome));cursor:pointer;}
.about-sync-now:disabled{opacity:0.6;cursor:default;}

/* ── New-project form ──────────────────────────────────────────────────────── */
.new-project-form{
  display:flex;flex-direction:column;gap:8px;
  margin:12px 0;padding:14px;background:var(--surface);
  border:1px solid var(--line);border-radius:10px;max-width:520px;
  position:relative;   /* anchors the book picker under the passage field */
}
.new-project-custom{min-height:120px;resize:vertical;font-family:var(--font-body);}
.new-project-error{color:var(--danger);font-size:calc(13px * var(--chrome));min-height:1em;}
.new-project-submit{align-self:flex-start;background:var(--accent);color:#fff;border-color:var(--accent);}

/* ── Search + ★ filter (INBOX 2026-07-15) ─────────────────────────────────── */
.workbench-search-row{display:flex;align-items:center;gap:8px;margin-top:14px;}
/* The input takes the row; min-width:0 lets it actually shrink inside the flex row
   rather than hold its default size and push the ★ chip off a narrow phone. */
.workbench-search-input{flex:1 1 auto;min-width:0;}
.workbench-star-chip{
  flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  min-width:calc(40px * var(--chrome));padding:0 10px;color:var(--muted);
}
.workbench-star-chip.is-on{color:var(--accent);border-color:var(--accent);}
.star-icon{width:calc(17px * var(--chrome));height:calc(17px * var(--chrome));display:block;}

/* ── Folders (INBOX 2026-07-15) ───────────────────────────────────────────── */
.workbench-folder-section{display:flex;flex-direction:column;gap:10px;}
.workbench-folder-header{
  display:flex;align-items:center;gap:8px;width:100%;
  background:none;border:none;border-bottom:1px solid var(--line);border-radius:0;
  padding:2px 0 5px;min-height:0;text-align:left;color:var(--muted);
}
.workbench-folder-caret{font-size:calc(11px * var(--chrome));transition:transform 120ms;}
.workbench-folder-caret.is-collapsed{transform:rotate(-90deg);}
.workbench-folder-name{font-size:calc(13px * var(--chrome));font-weight:700;letter-spacing:0.04em;text-transform:uppercase;}
.workbench-folder-count{margin-left:auto;font-size:calc(12px * var(--chrome));color:var(--muted);}

/* ── Project list ──────────────────────────────────────────────────────────── */
.workbench-project-list{display:flex;flex-direction:column;gap:10px;margin-top:12px;}
.workbench-project-card{
  padding:12px 14px;background:var(--surface);
  border:1px solid var(--line);border-radius:10px;
}
/* The star rides the title's row, pushed to the far edge. */
.project-card-title-row{display:flex;align-items:flex-start;gap:10px;}
.project-star-btn{
  flex:0 0 auto;margin-left:auto;display:inline-flex;align-items:center;justify-content:center;
  background:none;border:none;padding:2px;min-height:0;color:var(--muted);
}
.project-star-btn.is-on{color:var(--accent);}
.project-open-link{
  background:none;border:none;padding:0;min-height:0;
  font-size:calc(17px * var(--chrome));font-weight:700;color:var(--accent);text-align:left;
}
.project-card-sub{color:var(--muted);font-size:calc(13px * var(--chrome));margin:4px 0 8px;}
/* Per-tool "worked-on" chips (Daniel 2026-07-17): a chip per enabled tool, solid when that
   tool has work on this passage, faint (muted outline) when untouched — see which tools
   you've used at a glance. Each chip is a BUTTON that opens the project straight into that
   tool, so it's tappable (cursor + press feedback). */
.project-card-work{display:flex;gap:6px;flex-wrap:wrap;margin:0 0 10px;}
.card-work-chip{
  appearance:none;-webkit-appearance:none;cursor:pointer;
  font-family:var(--font-ui);font-size:calc(11px * var(--chrome));font-weight:600;line-height:1.5;
  padding:calc(2px * var(--chrome)) calc(9px * var(--chrome));border-radius:999px;white-space:nowrap;
  border:1px solid var(--line);color:var(--muted);background:transparent;
}
.card-work-chip.is-worked{border-color:var(--accent);background:var(--accent-soft);color:var(--ink);font-weight:700;}
.card-work-chip:active{transform:translateY(1px);}
.project-card-actions{display:flex;gap:6px;flex-wrap:wrap;}
.project-card-action{font-size:calc(13px * var(--chrome));min-height:34px;padding:5px 10px;}

/* ── Presentation mode ─────────────────────────────────────────────────────── */
/* Camp meeting 2026-07-21: projecting to a hall, Daniel's scarcest resource is VERTICAL
   space ("it's the vertical space I desperately need") — at Menu size Huge the app header,
   the slim project bar, the mode toolbar and the fixed bottom palette together cost
   ~110–140px before a single verse is drawn, on top of whatever the browser's own chrome
   takes. He teaches in alternating stretches — mark for a while, TALK for a while — and
   during the talking every one of those controls is pure overhead.

   So this is an explicit toggle, never automatic: nothing may move or reappear on its own
   while he is mid-sentence in front of a room. `is-presenting` goes on <body>, not on the
   app root, because #app-header lives OUTSIDE #workbench and the palettes are position:fixed
   children of the tool — the body is the only ancestor of all three.

   display:none (not visibility/opacity) is deliberate: the toolbars are sticky and in flow,
   so removing them from flow is what actually hands the space to the passage.

   The way back is ALWAYS on screen (.presentation-exit below) plus Esc — a mode you can
   enter but not obviously leave is a trap in front of an audience. */
body.is-presenting #app-header,
body.is-presenting .project-topbar,
body.is-presenting .markup-toolbar,
body.is-presenting .phrasing-toolbar,
body.is-presenting .bracketing-toolbar,
body.is-presenting .markup-palette,
body.is-presenting .phrasing-palette,
body.is-presenting .bracketing-hint,
body.is-presenting .bracketing-divide-help,
body.is-presenting .history-row,
body.is-presenting .project-file-row,
body.is-presenting .backup-row{display:none;}
/* The passage no longer clears a sticky header, and the bottom palette no longer eats the
   last lines, so the reading column can start higher and stop reserving that strip. */
body.is-presenting #workbench{padding-top:4px;}
body.is-presenting{--app-header-h:0px;}
/* The way back. Small, dim and out of the reading column (the passage is centred; this
   rides the top-right corner), it brightens on touch/hover so it is findable without being
   part of the projected image. Sits above the palettes' z-30 so it can never be buried. */
.presentation-exit{
  display:none;position:fixed;top:6px;right:6px;z-index:95;
  min-height:auto;padding:calc(4px * var(--chrome)) calc(9px * var(--chrome));
  font-family:var(--font-ui);font-size:calc(12px * var(--chrome));
  background:var(--surface);border:1px solid var(--line);border-radius:999px;
  color:var(--muted);opacity:0.35;
}
body.is-presenting .presentation-exit{display:inline-flex;align-items:center;gap:4px;}
.presentation-exit:hover,.presentation-exit:active{opacity:1;color:var(--ink);}

/* ── Project screen ────────────────────────────────────────────────────────── */
.project-screen{display:flex;flex-direction:column;gap:14px;}
/* The slim project bar ("‹ Workbench · Title · switcher · ✓ · ⋯") is decoupled from the
   ↔ text-width setting (Daniel 2026-07-17: "the top Workbench links should not shrink with
   the text; for all tools"). Same break-out-then-recentre as the mode toolbars: full-bleed
   out of the reading column, then re-inset to the FIXED --top-bar-w — so changing --reading-w
   moves only the verse text, never this bar. The math cancels #workbench's 16px padding the
   same way the toolbars' does. */
.project-topbar{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-inline:calc(50% - var(--viewport-w, 100vw) / 2);
  padding-inline:max(16px, calc((var(--viewport-w, 100vw) - var(--top-bar-w)) / 2));
}
.project-title{font-size:calc(20px * var(--chrome));font-weight:800;}
.project-save-status{margin-left:auto;color:var(--muted);font-size:calc(13px * var(--chrome));}
.passage-bar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;position:relative;}
.passage-ref-input{flex:1 1 220px;}
/* Book picker (core/bookpicker.js) — canonical-order autocomplete floated under a
   passage-reference input; chips + group heads borrow the Bracketing relationship
   picker's idiom. The input's container anchors it (position:relative above and
   on .new-project-form). */
.book-picker-list{
  position:absolute;z-index:30;background:var(--surface);border:1px solid var(--line);
  border-radius:12px;box-shadow:0 12px 40px rgba(0,0,0,0.25);padding:10px;
  display:flex;flex-direction:column;gap:10px;max-height:min(340px,50vh);overflow-y:auto;
  max-width:min(560px,94vw);
}
.book-picker-group{display:flex;flex-wrap:wrap;gap:6px;}
.book-picker-group-head{flex-basis:100%;font-family:var(--font-ui);font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:2px;color:var(--muted);}
.book-picker-chip{font-family:var(--font-ui);font-size:13px;padding:5px 10px;min-height:36px;border:1px solid var(--line);border-radius:8px;background:var(--chip-fill);cursor:pointer;}
.book-picker-chip.active{background:var(--accent);color:#fff;border-color:var(--accent);}
.passage-bar-status{color:var(--muted);font-size:calc(13px * var(--chrome));flex-basis:100%;}
.passage-bar-egw .passage-ref-input{flex-basis:100%;}
.passage-egw-paste{flex:1 1 100%;min-height:80px;resize:vertical;font-family:var(--font-body);}
/* A2 — the tool switcher sits at the very top of the project screen and stays put:
   it scrolls away with the page (NOT sticky), so it never follows you down. The
   active tool's toolbar still follows on scroll, docked directly under the sticky
   #app-header (z-20) now that no sticky switcher sits between them. Opaque bg so the
   passage doesn't show through; z below the header/palettes/modals/note panels but
   above the dot overlay (z-5). The toolbar docks at --app-header-h, the header's
   MEASURED height (shell.js publishes it; see trackHeaderHeight). It was a hardcoded
   45px calibrated by eye on one device, so any header that stood taller — a bigger
   Menu size, a wrapped header on a narrow phone — clipped the toolbar's top behind
   it once you scrolled (INBOX 2026-07-15). The 45px fallback is the measured height
   at Menu size Normal, used only before the first measurement lands. */
.tool-switcher{
  display:flex;gap:8px;flex-wrap:wrap;
  padding:6px 0;
}
.tool-switch-chip{color:var(--muted);}
/* FULL-BLEED (INBOX 2026-07-15 follow-up: "the top bar is limited to the width of the
   text, unlike the bottom bar — make it more like the bottom bar, centered, but using as
   much as possible of the width"). The bar lives inside #workbench's 900px reading
   column, so it stopped at 868px on a 1024px screen and packed controls into "More ▾"
   while a third of the window sat empty. The negative margin cancels the column (and
   #workbench's own 16px padding) to span the window; the padding then re-centres the
   controls in a 1100px column — the same max-width the fixed bottom .palette-inner uses,
   so the two bars' contents line up. Under 1132px the padding floors at 16px and the bar
   is exactly the window, so phones are unchanged. --viewport-w is the window WITHOUT the
   scrollbar (shell.js publishes it; see trackViewportWidth) — plain 100vw includes it and
   would give the page a horizontal scroll.

   Standardised (Daniel 2026-07-17): full-bleed, then the controls re-centre in a FIXED --top-bar-w
   column — NOT the --reading-w text-width setting, which only sizes the verse text. Full width up to
   that cap (so smaller screens use the whole width), centred beyond it (so a huge monitor doesn't
   stretch the controls edge to edge). The same cap drives the tool chrome + bottom palette. */
:root{--top-bar-w:1100px;}
.markup-toolbar,.phrasing-toolbar,.bracketing-toolbar{
  position:sticky;top:var(--app-header-h,45px);z-index:14;background:var(--bg);
  margin-inline:calc(50% - var(--viewport-w, 100vw) / 2);
  padding-inline:max(16px, calc((var(--viewport-w, 100vw) - var(--top-bar-w)) / 2));
}
.passage-verses{
  background:var(--surface);border:1px solid var(--line);border-radius:10px;
  padding:12px 14px;line-height:1.7;font-family:var(--font-body);
}
.passage-verse{margin:0 0 6px;}
.passage-verse-ref{color:var(--quote-accent);font-weight:700;margin-right:8px;font-size:calc(12px * var(--chrome));}
.passage-verse-text{color:var(--ink);}
.history-row,.backup-row,.project-file-row{display:flex;flex-direction:column;gap:8px;align-items:flex-start;}
.project-file-status{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));color:var(--muted);}
.history-panel{display:flex;flex-direction:column;gap:4px;max-width:520px;}
.history-entry{text-align:left;font-size:calc(13px * var(--chrome));}
.history-empty{color:var(--muted);font-size:calc(13px * var(--chrome));}
.tool-switch-chip.active{border-color:var(--accent);color:var(--ink);background:var(--accent-soft);font-weight:700;}

/* ══ Markup tool (Stage 2 — ported from the Markup Editor) ═══════════════════ */
.markup-tool{position:relative;padding-bottom:96px;}

/* Section title. */
.markup-section-title{
  outline:none;padding:4px 2px;border-radius:4px;
  font-family:var(--font-reading);font-size:calc(22px * var(--ui));font-weight:700;color:var(--ink);
}
.markup-section-title:empty:before{content:attr(data-placeholder);color:var(--muted);font-weight:400;}

/* Mode toolbar. The side padding is 8px, not 12 (INBOX 2026-07-15): the bar needed
   ~890px for one row and a tablet gives 768, and 4px off each side of every mode +
   tool button is 88px of that back for nothing — the words stay whole and the touch
   height is untouched. */
.markup-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-block:8px 2px;}
.markup-mode-btn,.markup-tool-btn{
  font-family:var(--font-ui);font-size:calc(14px * var(--chrome));min-height:calc(40px * var(--chrome));padding:calc(8px * var(--chrome)) calc(8px * var(--chrome));
  background:var(--fill-warm);color:var(--ink);border:1px solid var(--line);border-radius:8px;cursor:pointer;
}
.markup-mode-btn.is-active{background:var(--accent);color:#fff;border-color:var(--accent);}
.markup-tool-btn:disabled{opacity:0.4;cursor:default;}
.markup-toolbar-sep{width:1px;height:26px;background:var(--line);margin:0 2px;}
/* Zone divider (top-bar rethink 2026-07-17): splits tool MODES (left) from shared CHROME (right).
   margin-left:auto absorbs the free space so chrome hugs the right edge; a taller, slightly bolder
   rule than a plain sep marks the seam. Shared by all three tools.
   The element also carries its tool's `*-toolbar-sep` class (equal single-class specificity), and those
   rules set `margin:0 2px` LATER in the file for phrasing/bracketing — which would override this auto.
   So this selector is qualified with the sep class (specificity 0,0,2,0) to win regardless of order. */
.markup-toolbar-sep.tool-zone-divider,
.phrasing-toolbar-sep.tool-zone-divider,
.bracketing-toolbar-sep.tool-zone-divider{margin-left:auto;height:30px;width:2px;border-radius:1px;background:var(--muted);opacity:0.55;}
.markup-hint{color:var(--ink);font-weight:600;font-size:calc(14px * var(--chrome));min-height:18px;margin:2px 0 6px;}

/* Passage reading surface. */
/* --mark-scale: marks are drawn at the size of the text they mark (Daniel 2026-07-21). Declared
   HERE rather than on :root so it reaches the passage's words and nothing else — the palette
   chips and the export key paint the same decorations at chip size and must stay at 1, or a
   style sample would read as "thick" instead of as which style it is. */
.markup-passage{font-family:var(--font-reading);font-size:calc(19px * var(--ui));line-height:var(--markup-line-h, 1.9);color:var(--ink);--mark-scale:var(--ui, 1);}
.markup-empty-hint{color:var(--muted);font-family:var(--font-ui);padding:20px 0;line-height:1.6;}
.markup-tool .passage-paragraph{margin:0 0 14px;}
/* A heading must never be the last thing on a printed page (INBOX 2026-07-15 follow-up:
   "widows/orphans bother me"). break-after is the standard way to say exactly that, and
   core/export.js's print banding READS it off the computed style — so this one line is
   both the declaration and the mechanism, and no tool class names leak into core. Inert
   on screen; it only means anything in a paged context. */
.markup-tool .chapter-heading,
.markup-tool .group-heading,
.markup-tool .theme-heading{break-after:avoid;}
.markup-tool .chapter-heading{
  font-family:var(--font-ui);margin:26px 0 10px;cursor:pointer;
  display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
}
.markup-tool .chapter-heading-title{font-size:calc(21px * var(--ui));font-weight:800;color:var(--ink);}
.markup-tool .chapter-heading-theme{font-size:calc(16px * var(--ui));font-weight:600;color:var(--ink);}
.markup-tool .chapter-heading-theme.chapter-theme-empty{color:var(--accent);font-weight:500;font-style:italic;border-bottom:1px dashed var(--accent);}
.markup-tool .group-heading{
  font-family:var(--font-ui);font-weight:700;font-size:calc(17px * var(--ui));color:var(--ink);
  margin:22px 0 8px;padding-bottom:3px;border-bottom:2px solid var(--line);
  cursor:pointer;   /* tap to edit, like a chapter heading (Daniel 2026-07-21) */
}
.markup-tool .theme-heading{cursor:pointer;}
.markup-tool .group-heading-level-2{font-size:calc(15px * var(--ui));margin-left:calc(18px * var(--ui));margin-top:18px;border-bottom-width:1px;}
.markup-tool .group-heading-level-3{font-size:calc(14px * var(--ui));font-weight:600;margin-left:calc(36px * var(--ui));margin-top:16px;border-bottom-style:dashed;border-bottom-width:1px;}
.markup-tool .group-heading-level-4{font-size:calc(13px * var(--ui));font-weight:600;font-style:italic;margin-left:calc(54px * var(--ui));margin-top:14px;border-bottom:none;}
.markup-tool .theme-heading{font-family:var(--font-ui);font-style:italic;font-size:calc(14px * var(--ui));color:var(--muted);margin:0 0 8px;}
.markup-tool .theme-heading-level-2{margin-left:calc(18px * var(--ui));}
.markup-tool .theme-heading-level-3{margin-left:calc(36px * var(--ui));}
.markup-tool .theme-heading-level-4{margin-left:calc(54px * var(--ui));}
.markup-tool .context-summary{font-family:var(--font-ui);margin:12px 0 20px;}
.markup-tool .context-summary-following{margin-top:26px;}
.markup-tool .context-summary-label{font-size:calc(11px * var(--ui));font-weight:700;color:var(--muted);letter-spacing:0.06em;text-transform:uppercase;margin-bottom:4px;}
.markup-tool .context-summary-input{
  font-size:calc(14px * var(--ui));line-height:1.5;color:var(--ink);
  border:1px dashed var(--line);border-radius:8px;padding:9px 11px;background:var(--surface);outline:none;min-height:20px;
}
.markup-tool .context-summary-input:focus{border-color:var(--accent);border-style:solid;}
.markup-tool .context-summary-input:empty:before{content:attr(data-placeholder);color:var(--muted);}
.markup-tool .verse-number{color:var(--accent);font-weight:700;font-size:0.62em;vertical-align:super;margin-right:2px;user-select:none;position:relative;}
.markup-tool .verse-number-band{position:absolute;left:0;width:calc(100% + 2px);z-index:-1;pointer-events:none;}
/* EGW page.paragraph refs ("84.1") are longer than a bare verse number, so they read
   as a muted pill/chip set off from the prose instead of a cramped superscript. The
   compound selectors out-specify the base .verse-number / .phrasing-verse-number rules. */
.markup-tool .verse-number.verse-number-egw,
.phrasing-verse-number.phrasing-verse-number-egw{
  display:inline-block;vertical-align:baseline;font-size:0.66em;line-height:1;font-weight:700;
  padding:2px 7px;margin-right:7px;border-radius:999px;color:var(--accent);
  background:var(--accent-soft);border:1px solid var(--line);letter-spacing:0.02em;white-space:nowrap;}
/* No horizontal padding: mark decorations (underline/highlight) must hug the word
   ink exactly, and word spacing stays natural. Finger targets come entirely from
   the gap-tap resolution in markup-tool.js — a tap anywhere on the line snaps to
   the nearest word — so short words are reachable without padding them out. */
.markup-tool .word{cursor:default;border-radius:3px;padding:1px 0 8px;position:relative;}
.markup-tool .word-gap{padding:1px 0 8px;}
.markup-tool .word.marked,.markup-tool .word-gap.marked{background-clip:padding-box;}
/* ONE band for the whole passage, pinned to the INK rather than to each face's font box —
   those disagree (Georgia's italic box is 0.38em taller than its roman), which made a mark
   spanning quoted and plain text come out stepped. markup-tool.js measures both faces and
   publishes, per face, how far to pull the paint INSIDE the box (--band-*-trim-*) and how far
   to push it OUTSIDE (--band-*-pad-*, background only — inline padding never moves the line).
   Each marked span picks its face's set below; the trims feed the background layers in
   markup-model.js. See measureBandFaceMetricsInEm() / writeBandGeometryVars(). */
.band-face-ruler{position:absolute;visibility:hidden;white-space:nowrap;pointer-events:none;}
.band-face-strut{display:inline-block;width:0;height:0;}
.markup-tool .word.marked,.markup-tool .word-gap.marked{
  --band-trim-top:var(--band-roman-trim-top, 0px);
  --band-trim-bottom:var(--band-roman-trim-bottom, 0px);
  padding-top:var(--band-roman-pad-top, 1px);padding-bottom:var(--band-roman-pad-bottom, 8px);
}
.markup-tool .word.marked.in-quote,.markup-tool .word.marked.in-quote-speech,
.markup-tool .word-gap.marked.in-quote,.markup-tool .word-gap.marked.in-quote-speech{
  --band-trim-top:var(--band-italic-trim-top, 0px);
  --band-trim-bottom:var(--band-italic-trim-bottom, 0px);
  padding-top:var(--band-italic-pad-top, 1px);padding-bottom:var(--band-italic-pad-bottom, 8px);
}
.markup-tool .word.marked{border-radius:0;}
.markup-tool .word.has-mark-caps{z-index:0;}
/* The caps start where the band's paint starts — see --band-trim-top in markup-tool.js. */
.markup-tool .mark-chip-cap,.markup-tool .mark-arrow-cap{position:absolute;top:var(--band-trim-top, 0px);bottom:var(--band-trim-bottom, 0px);z-index:-1;pointer-events:none;}
.markup-tool .mark-chip-cap{width:calc(7px * var(--ui));border-style:solid;}
.markup-tool .mark-chip-cap-left{left:calc(-7px * var(--ui));border-width:2px 0 2px 2px;border-radius:9px 0 0 9px;}
.markup-tool .mark-chip-cap-right{right:calc(-7px * var(--ui));border-width:2px 2px 2px 0;border-radius:0 9px 9px 0;}
.markup-tool .mark-arrow-cap{width:calc(12px * var(--ui));}
.markup-tool .mark-arrow-cap-left{left:calc(-12px * var(--ui));}
.markup-tool .mark-arrow-cap-right{right:calc(-12px * var(--ui));}
.markup-tool .mark-arrow-cap-point-left{clip-path:polygon(100% 0,0 50%,100% 100%);}
.markup-tool .mark-arrow-cap-point-right{clip-path:polygon(0 0,100% 50%,0 100%);}
.markup-tool .mark-arrow-cap-notch-left{clip-path:polygon(100% 0,0 0,45% 50%,0 100%,100% 100%);}
.markup-tool .mark-arrow-cap-notch-right{clip-path:polygon(0 0,100% 0,55% 50%,100% 100%,0 100%);}
.markup-tool .quote-paragraph{margin-left:22px;padding-left:14px;border-left:3px solid var(--quote-accent);}
.markup-tool .word.in-quote{font-style:italic;color:var(--quote-ink);}
/* The inter-word space inside italic text is italic too, so its painted background box
   uses the same face metrics as the words either side — an upright space among italics
   paints a shorter box and the mark band gets white notches at every gap. */
.markup-tool .word-gap.in-quote,.markup-tool .word-gap.in-quote-speech{font-style:italic;}
/* Speech spans (quote kinds, INBOX 2026-07-13): what someone SAYS stays in the paragraph
   flow — italics only; the indented gold block above is reserved for 'quotation' citations. */
.markup-tool .word.in-quote-speech{font-style:italic;}

/* Poetry mode (INBOX 2026-07-13) — manual poetic line breaks with per-line indent. */
.poem-line-break{display:block;height:0;}
.poem-indent{display:inline-block;user-select:none;}
.markup-tool .word.poem-selected{background:var(--accent-soft-2);outline:2px solid var(--accent);}

/* Quote-kind chips + the poetry ◀▶✕ controls (both swap into the palette bar). */
.markup-palette-label{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));font-weight:700;color:var(--muted);flex:0 0 auto;margin-right:4px;}
.markup-quote-kind-chip{
  font-family:var(--font-ui);font-size:calc(13px * var(--chrome));padding:calc(6px * var(--chrome)) calc(12px * var(--chrome));min-height:calc(38px * var(--chrome));
  border:1px solid var(--line);border-radius:999px;background:var(--chip-fill);cursor:pointer;
}
.markup-quote-kind-chip.active{border-color:var(--accent);box-shadow:0 0 0 2px rgba(46,110,168,0.2);background:var(--surface);}
.markup-tool.mode-active .word{cursor:pointer;}
.markup-tool.mode-active .word:hover{background:var(--accent-soft);}
/* Immediate press feedback — touch has no :hover, and on e-ink the range-start
   outline only lands after a full render(), so a tap felt unregistered → double marks. */
.markup-tool.mode-active .word:active{background:var(--accent-press);}
.markup-tool .word.range-start{background:var(--accent-soft-2);outline:2px solid var(--accent);}

/* Lens palette bar (fixed bottom control + legend). */
.markup-palette{
  position:fixed;left:0;right:0;bottom:0;z-index:30;
  background:var(--surface);border-top:1px solid var(--line);box-shadow:0 -4px 16px rgba(0,0,0,0.06);
  padding:8px 10px calc(8px + env(safe-area-inset-bottom));
}
/* max(1100px, --reading-w): the bottom bar tracks the reading column out past its 1100px
   floor exactly as the sticky top bar does — see the FULL-BLEED comment for why. */
.markup-palette .palette-inner{max-width:var(--top-bar-w, 1100px);margin:0 auto;display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;}
.palette-lens-switch{
  flex:0 0 auto;font-family:var(--font-ui);font-weight:700;font-size:calc(14px * var(--chrome));color:var(--ink);
  background:var(--chip-fill);border:1px solid var(--line);border-radius:999px;padding:calc(6px * var(--chrome)) calc(12px * var(--chrome));min-height:calc(38px * var(--chrome));cursor:pointer;white-space:nowrap;
}
.markup-palette .lens-menu{
  position:absolute;bottom:calc(100% + 6px);left:10px;z-index:35;
  background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 10px 32px rgba(0,0,0,0.18);padding:6px;min-width:250px;
}
.lens-menu-row{display:flex;align-items:center;justify-content:space-between;gap:12px;font-family:var(--font-ui);font-size:calc(15px * var(--chrome));min-height:calc(42px * var(--chrome));padding:calc(8px * var(--chrome)) calc(12px * var(--chrome));border-radius:8px;cursor:pointer;user-select:none;}
.lens-menu-row:hover{background:var(--fill-warm);}
.lens-menu-row.active{background:var(--accent-soft);font-weight:700;}
.markup-palette .palette-group{display:flex;align-items:center;gap:6px;flex-wrap:nowrap;overflow-x:auto;max-width:100%;-webkit-overflow-scrolling:touch;}
/* The lens switch shares the bottom line with the styles (Daniel 2026-07-21) — the switch keeps
   its width, the styles take the rest and scroll inside it. min-width:0 is what lets them: a flex
   item that will not shrink below its content would push the switch off the bar instead. */
.markup-palette .palette-bottom-row{display:flex;align-items:center;gap:8px 14px;width:100%;min-width:0;}
.markup-palette .palette-bottom-row > .palette-lens-switch{flex:0 0 auto;}
.markup-palette .palette-bottom-row > .palette-group{flex:1 1 auto;min-width:0;}
/* Edge hints for a sideways-scrolling strip (core/scroll-hint.js; INBOX 2026-07-21 — "it's
   hard to know that you have to swipe sideways"). Two zero-width sticky flex items that pin
   to the scroller's edges while the chips slide underneath; the visible fade + chevron is
   the ::after, drawn OUTSIDE that zero box so it costs no layout at all.

   Each hint cancels the 6px flex gap on its INNER side only — without that, two extra flex
   items would silently widen the row by 12px whether or not anything overflows. Inner side
   *specifically*: a negative margin on the row's trailing edge is not subtracted from
   scrollable overflow, so an end hint with `margin-right:-3px` left `scrollWidth` reporting
   3px more than the content — enough to light the arrow on a one-chip row with nothing
   behind it, which is the exact "always-on arrow means nothing" failure this guards against.

   Hidden by default: only .is-on (JS: content really is hidden past THIS edge) shows one.
   An arrow that is always there is decoration and stops being read; one that appears only
   when there is something behind it is information. */
.scroll-hint{
  position:sticky;flex:0 0 auto;width:0;align-self:stretch;z-index:2;
  pointer-events:none;opacity:0;transition:opacity 120ms ease;
}
.scroll-hint.is-on{opacity:1;}
.scroll-hint-start{left:0;margin-right:-6px;}
.scroll-hint-end{right:0;margin-left:-6px;}
.scroll-hint::after{
  content:'';position:absolute;top:0;bottom:0;width:calc(30px * var(--chrome));
  display:flex;align-items:center;
  font-family:var(--font-ui);font-size:calc(15px * var(--chrome));font-weight:700;color:var(--muted);
}
/* The fade runs from the palette's own surface out to nothing, so the chips look like they
   pass UNDER the bar's edge rather than stopping at a line. */
.scroll-hint-start::after{
  content:'‹';left:0;justify-content:flex-start;padding-left:2px;
  background:linear-gradient(to right, var(--surface) 45%, rgba(0,0,0,0));
}
.scroll-hint-end::after{
  content:'›';right:0;justify-content:flex-end;padding-right:2px;
  background:linear-gradient(to left, var(--surface) 45%, rgba(0,0,0,0));
}
.color-chip,.style-chip{
  display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;font-family:var(--font-ui);font-size:calc(14px * var(--chrome));
  padding:calc(6px * var(--chrome)) calc(8px * var(--chrome));border:1px solid var(--line);border-radius:999px;background:var(--chip-fill);cursor:pointer;user-select:none;min-height:calc(38px * var(--chrome));
  /* Only the HORIZONTAL axis is a browser gesture here (the palette group scrolls that way). A
     still press-and-hold is left for JS so the long-press-to-edit fires on a tablet — with the
     default (both axes pannable) the browser stole every hold as a scroll and the Edit/Delete
     modal never opened (Daniel 2026-07-16). Same reason as .phrasing-verse-number's pan-x. */
  touch-action:pan-x;
}
.color-chip.active,.style-chip.active{border-color:var(--accent);box-shadow:0 0 0 2px rgba(46,110,168,0.2);background:var(--surface);}
.color-chip.hidden,.style-chip.hidden{opacity:0.4;}
.color-chip .swatch{width:18px;height:18px;border-radius:50%;border:1px solid rgba(0,0,0,0.25);flex:0 0 auto;}
.chip-name{white-space:nowrap;}
/* Hide/show toggle — a real, clearly separated target (divider + a ~30px-WIDE hit area)
   so a fat-finger tap on the chip can't silently blank a whole colour layer. Its height
   is `stretch`, not a min: a 30px min-height set the chip's floor from the inside, making
   every colour/style chip 44px against the lens switch's 38px (30 vs 26 on a phone) —
   the one thing in the palette that wasn't uniform (INBOX 2026-07-15 follow-up). Stretching
   keeps the full separated width and fills whatever height the chip settles at. */
.chip-toggle{display:inline-flex;align-items:center;justify-content:center;min-width:30px;align-self:stretch;
  margin-left:6px;padding:0 4px;border-left:1px solid var(--line);opacity:0.7;color:var(--ink);cursor:pointer;}
.chip-toggle:active{opacity:1;background:var(--accent-soft);border-radius:6px;}
/* The eye SCALES with Menu size (INBOX 2026-07-15: "the chips are still a different height
   than the lens dropdown button… on the phone"). `stretch` above stopped the toggle's own
   box from setting the chip's floor, but the icon INSIDE it was a hardcoded 17px, and a
   stretched flex item still contributes its content height to the row. At Menu size Normal
   that content (17 + padding + border = 25px) squeaked under the phone's 26px floor by 1px
   and looked fixed; at Small the floor drops to 22.1px while a fixed 17px icon does not,
   so the chips stood 24.1px against the lens switch's 22.1 — the same bug one layer down.
   Scaling it keeps the icon under the floor at every Menu size. (Its min-WIDTH deliberately
   stays fixed — the separated tap width is the fat-finger guard, and it isn't a height.) */
.chip-toggle svg{width:calc(17px * var(--chrome));height:calc(17px * var(--chrome));display:block;}
/* Press feedback for all palette/toolbar controls (touch has no :hover). */
.markup-mode-btn:active,.markup-tool-btn:active,.color-chip:active,.style-chip:active,
.markup-quote-kind-chip:active,.palette-lens-switch:active,.add-color-btn:active{filter:brightness(0.93);transform:translateY(1px);}
/* Inline SVG icons (undo/redo, poetry indent/remove) — sized here, colour inherited. */
.markup-icon{display:inline-flex;align-items:center;gap:6px;line-height:0;}
.markup-icon svg{width:18px;height:18px;display:block;}
.markup-icon-label{line-height:1;}

/* The ↔ pair's icons (core/reading-width.js), shared by all three bars. Sized off --chrome
   like the note arm's pencil — these sit ON the toolbar, which is chrome, so they follow
   Menu size rather than the verse text's scale. */
.reading-width-icon,.tab-width-icon,.line-height-icon{display:inline-flex;align-items:center;line-height:0;}
.reading-width-icon svg,.tab-width-icon svg,.line-height-icon svg{width:calc(17px * var(--chrome));height:calc(17px * var(--chrome));display:block;}
/* line-height:1 for the same reason .heading-level-btn has it: this label opens with a
   FULLWIDTH ＋ (U+FF0B), which no UI font in the stack carries, so it falls back to a font
   whose metrics give the line box ~1.37em instead of ~1.2. That box outgrew the chip's
   floor from the inside at Menu size Large and X-Large (32.2 vs 31.2, 38.7 vs 37.7) — the
   one pill that still didn't match the lens switch. Pinning the line box to the font size
   puts the min-height back in charge; the glyph still paints (nothing here clips). */
.add-color-btn{
  flex:0 0 auto;font-family:var(--font-ui);font-size:calc(14px * var(--chrome));padding:calc(6px * var(--chrome)) calc(12px * var(--chrome));min-height:calc(38px * var(--chrome));line-height:1;
  border:1px dashed var(--muted);border-radius:999px;background:transparent;color:var(--muted);cursor:pointer;white-space:nowrap;
}
/* Style samples draw in currentColor; makeStyleChip sets it to the active colour so a chip like
   "Grabbed My Attention" previews the exact highlight you'd get (Daniel 2026-07-16). */
.style-sample{display:inline-block;width:22px;height:14px;flex:0 0 auto;position:relative;color:var(--ink);}
.style-sample-highlight{background:currentColor;border-radius:2px;}
.style-sample::after{content:"";position:absolute;left:0;right:0;bottom:2px;height:2px;}
.style-sample-underline::after{background:currentColor;}
.style-sample-dotted-underline::after{background:repeating-linear-gradient(to right,currentColor 0 2px,transparent 2px 4px);}
.style-sample-striped-underline::after{background:repeating-linear-gradient(to right,currentColor 0 5px,transparent 5px 8px);}
.style-sample-squiggly-underline::after{height:4px;background:currentColor;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='4' viewBox='0 0 8 4'%3E%3Cpath d='M0 3 Q2 0 4 3 T8 3' fill='none' stroke='white' stroke-width='1'/%3E%3C/svg%3E") repeat-x left bottom/8px 4px;
  mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='4' viewBox='0 0 8 4'%3E%3Cpath d='M0 3 Q2 0 4 3 T8 3' fill='none' stroke='white' stroke-width='1'/%3E%3C/svg%3E") repeat-x left bottom/8px 4px;}
.style-sample-chip{border:2px solid currentColor;border-radius:7px;}
.style-sample-arrow-band-left{background:currentColor;clip-path:polygon(25% 0,100% 0,80% 50%,100% 100%,25% 100%,0 50%);}
.style-sample-arrow-band-right{background:currentColor;clip-path:polygon(75% 0,0 0,20% 50%,0 100%,75% 100%,100% 50%);}
.style-sample-arrow-band-both{background:currentColor;clip-path:polygon(25% 0,75% 0,100% 50%,75% 100%,25% 100%,0 50%);}
.hue-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:8px;margin:6px 0 12px;}
.hue-cell{height:40px;border-radius:8px;border:2px solid transparent;cursor:pointer;box-shadow:inset 0 0 0 1px rgba(0,0,0,0.15);}
.hue-cell.selected{border-color:var(--ink);box-shadow:0 0 0 2px #fff,0 0 0 4px var(--ink);}

/* Markup modals + banner. */
.markup-modal{display:none;position:fixed;inset:0;z-index:40;background:rgba(30,28,24,0.35);align-items:center;justify-content:center;}
.markup-modal-box{background:var(--surface);border-radius:12px;padding:18px;width:min(420px,92vw);box-shadow:0 12px 40px rgba(0,0,0,0.25);}
.markup-modal-label{font-weight:700;margin-bottom:10px;font-family:var(--font-ui);}
.markup-modal input{width:100%;font-size:16px;padding:10px;border:1px solid var(--line);border-radius:8px;font-family:var(--font-ui);}
/* Stacked headings (Daniel 2026-07-21: "heading one, two, and three on the same word"). The
   modal edits a LIST, so each heading is a bordered card with its own level caption — the
   level is POSITIONAL (each row one deeper than the one above), and that caption is the only
   thing on screen that says what ＋ just gave you. The rows are the one part of this modal
   that can grow past the screen, so they scroll rather than pushing OK out of reach. */
.markup-heading-rows{display:flex;flex-direction:column;gap:10px;margin-top:12px;max-height:46vh;overflow-y:auto;}
.markup-heading-row{border:1px solid var(--line);border-radius:10px;padding:8px;background:var(--fill-warm);}
.markup-heading-row-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;}
.markup-heading-row-level{font-family:var(--font-ui);font-size:12px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:0.04em;}
.markup-heading-row-remove{
  min-width:30px;min-height:30px;line-height:1;padding:0 6px;
  border:1px solid var(--line);border-radius:8px;background:var(--surface);color:var(--muted);cursor:pointer;
}
.markup-heading-row-remove:hover{color:var(--ink);}
.markup-heading-row-theme{margin-top:6px;font-style:italic;}
.markup-heading-add{
  margin-top:10px;width:100%;font-family:var(--font-ui);font-size:14px;padding:9px 12px;
  border:1px dashed var(--line);border-radius:8px;background:none;color:var(--ink);cursor:pointer;
}
.markup-heading-add:disabled{opacity:0.35;cursor:default;}   /* at level 4 there is nothing deeper to add */
.heading-level-row{display:flex;align-items:center;gap:10px;margin-top:12px;}
.heading-level-caption{font-family:var(--font-ui);font-size:13px;color:var(--muted);}
.heading-level-btn{font-size:15px;min-width:38px;min-height:38px;line-height:1;border:1px solid var(--line);border-radius:8px;background:var(--fill-warm);cursor:pointer;}
.heading-level-btn:disabled{opacity:0.35;cursor:default;}
.heading-level-value{font-family:var(--font-ui);font-size:14px;font-weight:700;min-width:56px;text-align:center;}
.markup-modal-buttons{display:flex;justify-content:flex-end;gap:8px;margin-top:14px;flex-wrap:wrap;}
.markup-modal-buttons button{font-size:15px;padding:9px 16px;border-radius:8px;border:1px solid var(--line);cursor:pointer;background:var(--fill-warm);}
.markup-modal-buttons button:first-child{background:var(--accent);color:#fff;border-color:var(--accent);}
.markup-modal-buttons .modal-remove-btn{color:var(--danger);border-color:var(--danger-soft-border);background:var(--danger-soft);}
/* Add/Save is inert until the colour has a name — no blank "bogus" colours (markup-tool.js). */
.markup-modal-buttons #markup-color-ok:disabled{opacity:0.45;cursor:default;}
/* Reset picker (markup-tool.js): the toolbar's "Reset…" and the verse-number long-press.
   The confirm carries the count and reads solid-danger even as the first button — an id rule
   beats the first-child accent rule so this destructive action never looks like the safe one. */
.markup-modal-note{font-family:var(--font-ui);font-size:calc(13px * var(--chrome));color:var(--muted);line-height:1.5;margin:0 0 10px;}
.markup-reset-list{max-height:min(50vh,340px);overflow-y:auto;margin-bottom:4px;border:1px solid var(--line);border-radius:8px;padding:4px;}
.markup-reset-row{display:flex;align-items:center;gap:10px;font-family:var(--font-ui);font-size:calc(14px * var(--chrome));
  min-height:calc(40px * var(--chrome));padding:0 8px;border-radius:6px;cursor:pointer;user-select:none;}
.markup-reset-row:hover{background:var(--accent-soft);}
.markup-reset-row input{width:18px;height:18px;flex:0 0 auto;}
.markup-reset-all{font-weight:700;border-bottom:1px solid var(--line);border-radius:6px 6px 0 0;}
.markup-modal-buttons #markup-reset-confirm{color:#fff;background:var(--danger);border-color:var(--danger);}
.markup-modal-buttons #markup-reset-confirm:disabled{opacity:0.5;cursor:default;}
/* Verse-number long-press menu — a small popover of tap targets, positioned in the tool root. */
.markup-context-menu{position:absolute;z-index:45;min-width:170px;background:var(--surface);border:1px solid var(--line);
  border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,0.18);padding:6px;display:flex;flex-direction:column;gap:2px;}
.markup-context-item{font-family:var(--font-ui);font-size:calc(14px * var(--chrome));text-align:left;min-height:calc(40px * var(--chrome));
  padding:calc(8px * var(--chrome)) calc(10px * var(--chrome));border:none;border-radius:7px;background:transparent;color:var(--ink);cursor:pointer;}
.markup-context-item:hover{background:var(--accent-soft);}
.markup-context-item.danger{color:var(--danger);}
.markup-banner{
  position:fixed;left:50%;bottom:84px;transform:translateX(-50%);
  background:var(--inverse-surface);color:#fff;font-family:var(--font-ui);font-size:14px;
  padding:10px 18px;border-radius:20px;opacity:0;pointer-events:none;transition:opacity 0.2s;z-index:50;max-width:90vw;
}
.markup-banner.is-visible{opacity:0.95;}

/* Outline / quicknav side panel (Markup, Daniel 2026-07-18). A fixed panel docked to the
   left, hidden until the toolbar's Outline toggle opens it; sits BELOW the sticky toolbar
   (lower z) so the bar stays usable. On a phone it overlays the reading column and a tap on
   an entry closes it (see jumpToOutlineTarget). Sizes follow the Menu-size (--chrome) setting. */
.markup-outline-panel{
  position:fixed;left:0;top:calc(var(--app-header-h,45px) + 96px);z-index:30;
  width:min(300px, 84vw);max-height:70vh;
  display:flex;flex-direction:column;background:var(--surface);
  border:1px solid var(--line);border-radius:12px;
  box-shadow:0 6px 24px rgba(0,0,0,0.22);overflow:hidden;
  /* the resize grip is absolutely positioned against this box */
  position:fixed;
}
.markup-outline-panel[hidden]{display:none;}
/* The head doubles as the DRAG HANDLE (Daniel 2026-07-19: "make it so that it can be moved
   around") — touch-action:none lets a finger drag it without the page scrolling. */
.markup-outline-head{
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:calc(7px * var(--chrome)) calc(12px * var(--chrome));
  border-bottom:1px solid var(--line);background:var(--fill-warm);
  cursor:move;touch-action:none;user-select:none;
}
.markup-outline-title{font-family:var(--font-ui);font-weight:700;font-size:calc(13px * var(--chrome));text-transform:uppercase;letter-spacing:0.04em;}
/* A−/A+ for the outline's own text. Sized on --chrome only: it is chrome, and a control that
   grew with the thing it adjusts would run away from its own panel. */
.markup-outline-sizer{display:flex;gap:3px;margin-left:auto;}
/* The ⤓ export shares the A−/A+ box so the head reads as one row of controls, and stays on
   --chrome with them: it is chrome, not outline text. */
.markup-outline-size-btn,.markup-outline-export-btn{
  font-family:var(--font-ui);font-size:calc(11px * var(--chrome));line-height:1;
  min-width:calc(26px * var(--chrome));min-height:calc(24px * var(--chrome));padding:0 calc(5px * var(--chrome));
  border:1px solid var(--line);border-radius:5px;background:var(--surface);color:var(--ink);cursor:pointer;
}
.markup-outline-export-btn{font-size:calc(13px * var(--chrome));}
.markup-outline-export-btn:disabled{opacity:.45;cursor:default;}
/* Touch-friendly corner grip (the native CSS resizer is mouse-only), matching .note-resize-handle. */
.markup-outline-resize-handle{
  position:absolute;right:0;bottom:0;width:18px;height:18px;cursor:nwse-resize;touch-action:none;
  background:linear-gradient(135deg,transparent 50%,var(--line) 50%,var(--line) 70%,transparent 70%);
}
.markup-outline-close{display:inline-flex;align-items:center;justify-content:center;width:calc(30px * var(--chrome));height:calc(30px * var(--chrome));border:none;background:none;cursor:pointer;color:var(--muted);border-radius:6px;}
.markup-outline-close:hover{background:var(--surface);}
/* Compact by default (Daniel 2026-07-21: "it can be more compact, it doesn't have to have so
   much space in between the lines"). An outline earns its place by letting you see the shape of
   a chapter at once; every row of padding is a heading pushed off the bottom. Row padding drops
   6px -> 2px and the leading tightens, while the TEXT keeps its own scale below. */
/* --outline-box: the row's own box unit (Daniel 2026-07-21: "the spacing should scale too when
   changing the outline font size"). The suite rule is "type scales with --ui, boxes scale with
   --chrome" — but the outline is the one place with a SECOND type scale of its own, so a row
   padded on --chrome alone kept projector-sized gaps around text the A− button had just made
   tiny, and shrinking the text bought no extra headings on screen, which is the only reason to
   shrink it. The panel's own chrome (head, A−/A+, close) deliberately stays on --chrome: a
   control that shrank with the thing it adjusts would shrink itself out of reach. */
.markup-outline-list{
  --outline-box:calc(var(--chrome) * var(--outline-scale, 1));
  overflow-y:auto;padding:calc(5px * var(--outline-box));
  display:flex;flex-direction:column;gap:0;min-height:0;
}
.markup-outline-entry{
  /* flex-shrink:0 is load-bearing (Daniel 2026-07-21: shrinking the panel "sometimes overlaps
     some text"). In a flex COLUMN the shrink step runs BEFORE overflow: with the default
     flex-shrink:1, a panel too short for its rows compressed each row below the height of its
     own text instead of letting the list scroll — so a two-line entry drew straight over its
     neighbour. Rows keep their natural height now, and the list scrolls, which is what the
     overflow-y above was always for. */
  flex:0 0 auto;
  display:flex;align-items:baseline;gap:calc(5px * var(--outline-box, 1));width:100%;text-align:left;
  /* 1.3, not 1.2: the tight leading was chosen when every entry was one line, and a WRAPPED
     entry set that tight reads as one clump of text rather than two lines of one heading. */
  font-family:var(--font-ui);font-size:calc(13px * var(--chrome) * var(--outline-scale, 1));line-height:1.3;
  padding:calc(2px * var(--outline-box, 1)) calc(8px * var(--outline-box, 1));
  /* The row is a <button>, so the shared button rule was handing it a 40px * --chrome floor —
     and THAT, not the padding, is what held the rows apart while A− shrank the words to nothing
     inside them (it also quietly capped how much the "compact" pass could ever buy). Same 40px
     at Menu size Normal, so the default panel is unchanged; it just travels with the outline's
     own scale now, like everything else in this list. */
  min-height:calc(40px * var(--outline-box, 1));
  border:none;border-radius:5px;background:none;cursor:pointer;color:var(--ink);
}
.markup-outline-entry:hover{background:var(--fill-warm);}
.markup-outline-entry-section{font-weight:800;}
.markup-outline-entry-chapter{font-weight:700;}
/* Hierarchy carried by WEIGHT and a rule, not by indent alone (Daniel 2026-07-21: "level two
   headings need to be more distinctly organized"). Indent is a few pixels of whitespace and is
   the first cue to stop reading as structure at a distance; a left rule and a real weight drop
   survive the projector. Level 1 sits flush and bold; every level below gets its own rule so a
   sub-heading is visibly subordinate rather than merely shifted right. */
.markup-outline-level-1{font-weight:700;}
.markup-outline-level-2{
  font-weight:600;font-size:calc(12px * var(--chrome) * var(--outline-scale, 1));
  border-left:calc(2px * var(--outline-box, 1)) solid var(--line);
}
.markup-outline-level-3{
  font-weight:500;font-style:italic;font-size:calc(11.5px * var(--chrome) * var(--outline-scale, 1));
  border-left:calc(2px * var(--outline-box, 1)) dashed var(--line);color:var(--muted);
}
.markup-outline-level-4{
  font-weight:400;font-style:italic;font-size:calc(11px * var(--chrome) * var(--outline-scale, 1));
  border-left:calc(1px * var(--outline-box, 1)) dotted var(--line);color:var(--muted);
}
/* Fold control (Daniel 2026-07-21: "make the headings collapsible in the outline if they have
   multiple children"). A fixed box that every row carries — blank when the row does not fold —
   so the titles keep one left edge instead of jogging sideways as sections gain or lose
   children. Its tap target is padded well past the glyph: this is a 10px triangle that has to
   be hit with a thumb, and missing it jumps the passage instead of folding. */
.markup-outline-twisty{
  flex:0 0 auto;width:calc(13px * var(--outline-box, 1));margin-right:1px;
  color:var(--muted);font-size:0.8em;line-height:1;text-align:center;cursor:pointer;
  padding:calc(3px * var(--outline-box, 1)) calc(2px * var(--outline-box, 1));margin-block:calc(-3px * var(--outline-box, 1));
}
.markup-outline-twisty.is-empty{cursor:inherit;}
.markup-outline-twisty:not(.is-empty):hover{color:var(--ink);}
/* A folded heading keeps its weight but loses its theme to the count — the row is standing in
   for everything under it, so what matters is how much is hidden, not its own subtitle. */
.markup-outline-entry-count{
  flex:0 0 auto;color:var(--muted);font-size:0.8em;font-variant-numeric:tabular-nums;
  border:1px solid var(--line);border-radius:999px;padding:0 5px;
}
/* Edge arrows for the TALL sideways canvases — Phrasing's canvas and Bracketing's rows
   (core/scroll-hint.js; Daniel 2026-07-21: "you can have a little arrow there to indicate that
   you can actually go that direction"). position:fixed and placed by script, because these
   have to track the PAGE scroll: an arrow pinned to the middle of a 2000px canvas is off
   screen almost the whole time you are reading it.

   A round chip rather than the palette's flat chevron: on the palette the fade does the
   explaining and the chevron only points, while out here the arrow floats over verse text
   with nothing behind it, so it needs an edge of its own to read as a control. Deliberately
   quiet until touched — it sits over the passage Daniel is teaching from. */
.scroll-edge-arrow{
  display:none;position:fixed;z-index:12;transform:translateY(-50%);
  width:calc(30px * var(--chrome));height:calc(30px * var(--chrome));
  align-items:center;justify-content:center;
  border:1px solid var(--line);border-radius:50%;background:var(--surface);
  box-shadow:0 2px 8px rgba(0,0,0,0.14);
  font-family:var(--font-ui);font-size:calc(17px * var(--chrome));line-height:1;color:var(--muted);
  opacity:0.55;pointer-events:none;   /* a hint, not a control: never eats a tap meant for a word */
}
.scroll-edge-arrow.is-on{display:flex;}

/* Outline numbering — I. / a. / 1. / i. by level (Daniel 2026-07-21). Tabular figures and a
   min-width so a column of them lines up instead of ragging with each label's width; it never
   shrinks, because a number that ellipsised would be worse than no number. */
.markup-outline-entry-number{
  flex:0 0 auto;min-width:1.6em;color:var(--muted);font-variant-numeric:tabular-nums;font-weight:700;
}
.markup-outline-entry-text{flex:0 1 auto;min-width:0;overflow-wrap:anywhere;}
/* The reference rides next to the heading it belongs to — tabular so a column of them lines up,
   and never the thing that gets ellipsised when a long theme runs out of room. */
.markup-outline-entry-ref{flex:0 0 auto;color:var(--muted);font-variant-numeric:tabular-nums;font-size:0.85em;}
.markup-outline-entry-theme{flex:1 1 auto;min-width:0;color:var(--muted);font-style:italic;font-size:0.88em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.markup-outline-empty{padding:14px 10px;color:var(--muted);font-family:var(--font-ui);font-size:calc(13px * var(--chrome));}
.outline-flash{animation:outlineFlash 1.1s ease-out;border-radius:4px;}
@keyframes outlineFlash{0%{background:var(--accent-soft);}100%{background:transparent;}}

/* ══ Phrasing tool (Stage 3 — reworked from the Phrasing Editor onto word anchors) ══ */
.phrasing-tool{position:relative;padding-bottom:96px;}

/* Mode toolbar (shares the markup toolbar look). */
.phrasing-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-block:8px 2px;}
.phrasing-mode-btn,.phrasing-tool-btn{
  font-family:var(--font-ui);font-size:calc(14px * var(--chrome));min-height:calc(40px * var(--chrome));padding:calc(8px * var(--chrome)) calc(8px * var(--chrome));
  background:var(--fill-warm);color:var(--ink);border:1px solid var(--line);border-radius:8px;cursor:pointer;
}
.phrasing-mode-btn.is-active{background:var(--accent);color:#fff;border-color:var(--accent);}
.phrasing-tool-btn:disabled{opacity:0.4;cursor:default;}
.phrasing-toolbar-sep{width:1px;height:26px;background:var(--line);margin:0 2px;}
.phrasing-hint{color:var(--muted);font-size:calc(13px * var(--chrome));min-height:18px;margin:2px 0 6px;}
/* The List gesture's Done / Cancel. They live in the PALETTE bar, which is fixed to the
   bottom of the viewport with a safe-area inset — picking members means scrolling down the
   passage, so anything anchored to the top of the tool is gone by the time you need it
   (Daniel 2026-07-16, on an iPhone: "i can't see a done button when you're done selecting").
   Sized for a thumb, matching the palette's own chips. */
.phrasing-list-done,.phrasing-list-cancel{
  flex:0 0 auto;font-family:var(--font-ui);font-size:calc(13px * var(--chrome));font-weight:700;
  padding:calc(6px * var(--chrome)) calc(16px * var(--chrome));min-height:calc(38px * var(--chrome));
  border-radius:8px;cursor:pointer;}
.phrasing-list-done{background:var(--accent);color:#fff;border:1px solid var(--accent);}
.phrasing-list-done:disabled{opacity:0.45;cursor:default;}
.phrasing-list-cancel{background:var(--fill-warm);color:var(--ink);border:1px solid var(--line);}
.phrasing-list-remove{
  flex:0 0 auto;font-family:var(--font-ui);font-size:calc(13px * var(--chrome));font-weight:700;
  padding:calc(6px * var(--chrome)) calc(16px * var(--chrome));min-height:calc(38px * var(--chrome));
  border-radius:8px;cursor:pointer;
  background:var(--danger-soft);color:var(--danger);border:1px solid var(--danger-soft-border);}
/* A picked member, and the leader it will hang under — visible while the gesture is open. */
.phrasing-row.list-picked{background:var(--accent-soft-2);box-shadow:inset 0 0 0 1px var(--accent);}
.phrasing-row.list-leader{box-shadow:inset 0 0 0 1px var(--accent);}
.phrasing-list-pick-num{display:inline-block;margin-left:10px;min-width:20px;padding:1px 6px;border-radius:10px;text-indent:0;
  background:var(--accent);color:#fff;font-family:var(--font-ui);font-size:calc(11px * var(--chrome));
  font-weight:700;text-align:center;user-select:none;}

/* Reading / diagram surface (monospace so indent columns align; relative for the arrow layer). */
/* Scripture prose in the READING serif, like .markup-passage — Phrasing was the last
   place the passage was still monospace (DECISIONS 2026-07-14 switched Markup and left
   this one behind). It had to stay mono while a line's column was an integer on a
   character grid; Round 20 made every position a MEASURED column off the live word
   rects, so the grid — and with it the reason for the mono font — is gone. Georgia is
   also narrower than JetBrains Mono at the same size, so phrased lines wrap LESS, and
   wrapping is what actually destroys phrasing (the indent structure IS the analysis). */
.phrasing-passage{
  position:relative;
  font-family:var(--font-reading);font-size:calc(17px * var(--ui));line-height:var(--phrasing-line-h, 2.3);color:var(--ink);
  /* Breaks out to the WHOLE window (like the toolbars / bracketing) so the phrasing canvas can use
     all of it and scroll sideways when it's wider (Daniel 2026-07-17). The side gutter lives on the
     CANVAS, not here, so #phrasing-passage's left edge stays at the canvas's — arrows and words share
     one origin. */
  margin-inline:calc(50% - var(--viewport-w, 100vw) / 2);
  overflow-x:auto;
}
/* The rows + arrow SVG live here; its width IS the study width the ↔ sets, so lines wrap at that
   width and the passage scrolls when it exceeds the window (Daniel 2026-07-17). position:relative
   for the absolute arrow SVG; the top room is for arrows that curve above the first row. */
.phrasing-canvas{position:relative;width:var(--reading-w, 900px);padding-top:44px;padding-inline:clamp(14px, 4vw, 60px);box-sizing:border-box;}
.phrasing-empty-hint{color:var(--muted);font-family:var(--font-ui);padding:20px 0;line-height:1.6;}
/* Hanging indent so a WRAPPED (overflow) line reads as a continuation, not a manually divided
   line (Daniel 2026-07-19). The padding-left ADDS 2ch and text-indent pulls the first line back
   by the same 2ch, so the first line — and every word position the arrow/indent layout measures —
   stays exactly where it was; only wrapped lines inset. */
.phrasing-row{position:relative;padding:2px 6px;border-radius:6px;padding-left:calc(6px + 2ch);text-indent:-2ch;}
.phrasing-row.selected{background:var(--accent-soft);box-shadow:inset 0 0 0 1px rgba(46,110,168,0.4);}
.phrasing-row.extracted{border-left:2px solid var(--line);}
.phrasing-line{white-space:normal;}
.phrasing-verse-number{margin-right:5px;user-select:none;}
.phrasing-verse-number-main{font-weight:700;color:var(--ink);}
.phrasing-verse-number-sub{font-weight:600;font-size:0.66em;color:var(--muted);vertical-align:super;margin-left:1px;}
.phrasing-verse-number-egw .phrasing-verse-number-main{color:inherit;}
.phrasing-tool .word{cursor:default;border-radius:3px;padding:1px 1px;}
/* Off-layout probe for measuring the passage's TRUE ch (passageChWidth in phrasing-tool.js).
   It must measure the font and NOTHING else — hence no padding, no border, no wrapping.
   The .word above is exactly why this exists: its 1px side padding made rect.width/length
   report a ch inflated by 2/length px, which is what walked list members off their leader. */
.phrasing-ch-probe{position:absolute;visibility:hidden;white-space:pre;padding:0;border:0;}
.phrasing-tool.mode-active .word{cursor:pointer;}
.phrasing-tool.mode-active .word:hover{background:var(--accent-soft);}
.phrasing-tool .word.range-start{background:var(--accent-soft-2);outline:2px solid var(--accent);}
.phrasing-type-tag{
  font-family:var(--font-ui);font-size:calc(11px * var(--ui));color:var(--muted);
  margin-left:10px;font-style:italic;user-select:none;
}
/* List mode (P3) — an inline tab-width spacer rendered before the leader word so it
   (and the members aligned under it via alignToCol) hang one tab further in. */
.phrasing-inline-tab{display:inline-block;user-select:none;}
/* The … an extraction leaves behind, marking the hole in the source line AND acting as the
   handle that pulls the extracted line back into it. Muted, because it is a mark about the
   text rather than part of it — but a real touch target, since it is the only way back.
   Sized on --ui (the READING scale): it sits inline among the words, so it must grow and
   shrink with them, not with the menu chrome. */
.phrasing-extract-hole{
  display:inline-block;color:var(--muted);cursor:pointer;user-select:none;font-weight:700;
  padding:0 calc(5px * var(--ui));border-radius:5px;letter-spacing:0.08em;
}
.phrasing-extract-hole:hover{background:var(--accent-soft);color:var(--accent);}
.phrasing-extract-hole:focus-visible{outline:2px solid var(--accent);outline-offset:1px;}

/* Insertable block rows (F5) — word-less structural rows keyed off phrase.type. */
.phrasing-block{padding-top:2px;padding-bottom:2px;}
.phrasing-block-empty .phrasing-empty-line{display:inline-block;min-height:1.2em;}
.phrasing-block-divider{padding:6px 6px;}
.phrasing-divider-line{border:0;border-top:2px solid var(--line);margin:0;width:100%;}
/* break-after:avoid for the same reason Markup's headings carry it (see .markup-tool
   .chapter-heading above): an inserted heading must never be the last thing on a printed
   page, with the phrases it introduces starting overleaf. core/export.js's print banding
   reads it off the computed style, so this one line is both declaration and mechanism. */
.phrasing-block-heading .phrasing-heading-text{
  font-family:var(--font-ui);font-weight:800;font-size:calc(18px * var(--ui));color:var(--ink);
  line-height:1.4;padding:2px 4px;border-radius:6px;outline:none;min-width:2ch;min-height:1.4em;
  break-after:avoid;
}
/* P4 — no blue focus box on a heading (the reported "yellow border"); typing is
   still obvious from the caret and the bold heading styling. */
.phrasing-block-heading .phrasing-heading-text:focus{box-shadow:none;background:transparent;}
.phrasing-block-heading .phrasing-heading-text:empty:before{content:attr(data-placeholder);color:var(--muted);font-weight:600;}

/* Row context menu (long-press → insert above/below → block type). */
.phrasing-context-menu{
  position:absolute;z-index:45;min-width:170px;background:var(--surface);border:1px solid var(--line);
  border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,0.18);padding:6px;display:flex;flex-direction:column;gap:2px;
}
.phrasing-context-heading{
  font-family:var(--font-ui);font-size:calc(11px * var(--chrome));font-weight:700;text-transform:uppercase;
  letter-spacing:0.05em;color:var(--muted);padding:4px 8px 2px;
}
.phrasing-context-item{
  font-family:var(--font-ui);font-size:calc(14px * var(--chrome));text-align:left;min-height:calc(40px * var(--chrome));
  padding:calc(8px * var(--chrome)) calc(10px * var(--chrome));border:none;border-radius:7px;background:transparent;color:var(--ink);cursor:pointer;
}
.phrasing-context-item:hover{background:var(--accent-soft);}
.phrasing-context-item.danger{color:var(--danger);}
/* The reset picker's verse list (phrasing-tool.js): a checkbox per verse plus Select all.
   Rows are tap targets, not mouse targets — this is reached from a long-press on a tablet. */
.phrasing-modal-note{font-family:var(--font-ui);font-size:calc(13px * var(--chrome));color:var(--muted);
  line-height:1.5;margin:0 0 10px;}
.phrasing-reset-list{max-height:min(50vh,340px);overflow-y:auto;margin-bottom:12px;
  border:1px solid var(--line);border-radius:8px;padding:4px;}
.phrasing-reset-row{display:flex;align-items:center;gap:10px;font-family:var(--font-ui);
  font-size:calc(14px * var(--chrome));min-height:calc(40px * var(--chrome));padding:0 8px;
  border-radius:6px;cursor:pointer;user-select:none;}
.phrasing-reset-row:hover{background:var(--accent-soft);}
.phrasing-reset-row input{width:18px;height:18px;flex:0 0 auto;}
.phrasing-reset-all{font-weight:700;border-bottom:1px solid var(--line);border-radius:6px 6px 0 0;}
.phrasing-context-sep{height:1px;background:var(--line);margin:4px 6px;}

/* Arrow layer — an SVG overlaid on the diagram, re-anchored from DOM boxes each render. */
.phrasing-arrows{position:absolute;top:0;left:0;overflow:visible;pointer-events:none;z-index:2;}
.phrasing-arrows .arrow-path{pointer-events:stroke;cursor:pointer;fill:none;}
/* The VERSE INDICATOR is each line's grip (Daniel 2026-07-16: the ‖ handle is gone — "the
   user should be able to select the verse indicator instead"). Tap it to select the line,
   drag it up/down to move the line (wired in phrasing-tool.js). It shows in every mode, so
   the grip never moves and never has to share its slot. It keeps its own colour (it is a
   verse number first, a control second); the row's selected/dragging states show on the ROW.
   touch-action:pan-x because only the VERTICAL axis is a gesture now (Round 20 removed the
   sideways re-indent) — a horizontal scroll may still start on the indicator, which on a
   phone is how you reach the right-hand end of a deeply indented line. */
/* touch-action:auto so a quick vertical drag on a verse number SCROLLS the page; a row is only
   picked up by a deliberate press-and-hold (Daniel 2026-07-17: reordering must be clear + stable). */
.phrasing-verse-number{cursor:grab;touch-action:auto;}
.phrasing-row.reorder-dragging .phrasing-verse-number{cursor:grabbing;}
/* The picked-up row reads as clearly LIFTED — an elevated shadow + accent wash, not a faint ghost;
   it follows the finger, and an accent edge on the row below/above marks the drop slot. */
.phrasing-row.reorder-dragging{background:var(--accent-soft);z-index:5;box-shadow:0 6px 18px rgba(0,0,0,0.28);border-radius:6px;}
.phrasing-row.reorder-drop-before{box-shadow:inset 0 2px 0 0 var(--accent);}
.phrasing-row.reorder-drop-after{box-shadow:inset 0 -2px 0 0 var(--accent);}
/* Relationship chips at the line end — one per labeled yellow/blue arrow, ported from
   the source Phrasing app's .arrow-label (gold = logical, teal = genitive). Sized for
   tablet reading + tapping (INBOX 2026-07-13), text darkened for e-ink contrast. */
/* text-indent:0 — the row's hanging-indent text-indent is INHERITED, and on an inline-block pill
   it would re-indent the chip's OWN text, shoving the relationship name's start out of the pill
   and up against the verse text (Daniel 2026-07-19). Reset it here (and on the pick-num pill). */
.phrasing-rel-chip{display:inline-block;border-radius:12px;padding:2px 10px;margin-left:10px;text-indent:0;
  font-family:var(--font-ui);font-size:calc(13px * var(--ui));line-height:1.5;vertical-align:middle;
  cursor:pointer;opacity:0.9;user-select:none;}
.phrasing-rel-chip:hover{opacity:1;}
.phrasing-rel-chip-yellow{background:#e8b840;color:#4a3000;}
.phrasing-rel-chip-blue{background:#3ab8cc;color:#073a45;}

/* Palette bar (fixed bottom; type chips, or arrow-color chips in Arrow mode). */
.phrasing-palette{
  position:fixed;left:0;right:0;bottom:0;z-index:30;
  background:var(--surface);border-top:1px solid var(--line);box-shadow:0 -4px 16px rgba(0,0,0,0.06);
  padding:8px 10px calc(8px + env(safe-area-inset-bottom));
}
.phrasing-palette .palette-inner{max-width:var(--top-bar-w, 1100px);margin:0 auto;display:flex;flex-wrap:wrap;align-items:center;gap:8px 10px;overflow-x:auto;}
/* The relationship palette's label is a plain label — that bar never collapses, so it has
   no toggle to be (see renderPalette). */
.phrasing-palette-label{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));font-weight:700;color:var(--muted);flex:0 0 auto;margin-right:4px;}
/* The TYPE palette's label IS its collapse toggle, so folding the bar away costs no extra
   chrome. Styled as the label it replaces (not as a button) — it must read as a heading
   that happens to be tappable, not as another chip competing with the ones below it. */
.phrasing-palette-toggle{
  font-family:var(--font-ui);font-size:calc(12px * var(--chrome));font-weight:700;color:var(--muted);
  flex:0 0 auto;margin-right:4px;background:none;border:0;padding:calc(4px * var(--chrome)) calc(6px * var(--chrome));
  min-height:calc(28px * var(--chrome));cursor:pointer;white-space:nowrap;border-radius:6px;
}
.phrasing-palette-toggle:hover{background:var(--chip-fill);border-color:transparent;}
/* Collapsed: the bar shrinks to its toggle row, handing the passage back its vertical
   room. The fixed bar's own padding tightens too, or the strip keeps a chip-sized gap
   under a row that no longer has chips in it. */
.phrasing-palette.is-collapsed{padding-bottom:calc(4px + env(safe-area-inset-bottom));padding-top:4px;}
.phrasing-palette.is-collapsed .palette-inner{gap:0;}
.phrasing-color-chip,.phrasing-type-chip{
  display:inline-flex;align-items:center;gap:6px;flex:0 0 auto;font-family:var(--font-ui);font-size:calc(13px * var(--chrome));
  padding:calc(5px * var(--chrome)) calc(8px * var(--chrome));border:1px solid var(--line);border-radius:999px;background:var(--chip-fill);cursor:pointer;user-select:none;min-height:calc(36px * var(--chrome));
}
.phrasing-color-chip.active,.phrasing-type-chip.active{border-color:var(--accent);box-shadow:0 0 0 2px rgba(46,110,168,0.2);background:var(--surface);}
.phrasing-type-chip.disabled{opacity:0.5;}
.phrasing-color-swatch{width:16px;height:16px;border-radius:50%;border:1px solid rgba(0,0,0,0.25);flex:0 0 auto;}
.phrasing-type-swatch{width:14px;height:14px;border-radius:3px;flex:0 0 auto;}

/* Arrow-label picker modal + banner. */
.phrasing-modal{display:none;position:fixed;inset:0;z-index:40;background:rgba(30,28,24,0.35);align-items:center;justify-content:center;}
.phrasing-modal-box{background:var(--surface);border-radius:12px;padding:18px;width:min(460px,92vw);max-height:80vh;display:flex;flex-direction:column;box-shadow:0 12px 40px rgba(0,0,0,0.25);}
.phrasing-modal-label{font-weight:700;margin-bottom:10px;font-family:var(--font-ui);font-size:calc(14px * var(--chrome));}
.phrasing-label-list{overflow-y:auto;display:flex;flex-wrap:wrap;gap:6px;padding:2px;}
/* The label picker is a chip grid, so it scales with Menu size like the palette chips do.
   It needs saying explicitly: .phrasing-label-option out-specifies the base `button` rule
   (0,1,0 vs 0,0,1), so raw px here silently overrode the scaling AND the phone trim, and
   the whole picker sat at Normal-desktop size at every Menu size and on every phone. */
.phrasing-label-group{flex-basis:100%;font-family:var(--font-ui);font-size:calc(12px * var(--chrome));font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;margin:8px 0 2px;}
.phrasing-label-option{font-family:var(--font-ui);font-size:calc(13px * var(--chrome));padding:calc(6px * var(--chrome)) calc(10px * var(--chrome));min-height:calc(34px * var(--chrome));border:1px solid var(--line);border-radius:8px;background:var(--chip-fill);cursor:pointer;}
.phrasing-label-option.active{background:var(--accent);color:#fff;border-color:var(--accent);}
.phrasing-label-none{color:var(--muted);font-family:var(--font-ui);font-size:calc(13px * var(--chrome));padding:8px 0;}
.phrasing-modal-buttons{display:flex;justify-content:flex-end;gap:8px;margin-top:14px;flex-wrap:wrap;}
.phrasing-modal-buttons button{font-size:14px;padding:8px 14px;border-radius:8px;border:1px solid var(--line);cursor:pointer;background:var(--fill-warm);}
.phrasing-modal-buttons .modal-remove-btn{color:var(--danger);border-color:var(--danger-soft-border);background:var(--danger-soft);}
.phrasing-banner{
  position:fixed;left:50%;bottom:84px;transform:translateX(-50%);
  background:var(--inverse-surface);color:#fff;font-family:var(--font-ui);font-size:14px;
  padding:10px 18px;border-radius:20px;opacity:0;pointer-events:none;transition:opacity 0.2s;z-index:50;max-width:90vw;
}
.phrasing-banner.is-visible{opacity:0.95;}

/* ══ Shared notes (Stage 4 — one layer over every tool, filterable by origin) ══ */
/* The tool host anchors the dot overlay (the tool sets its own .{tool}-tool class,
   which is also position:relative — see each tool block). */
.tool-host{position:relative;}
.notes-arming .word{cursor:crosshair;}

/* Dots: word/row-anchored markers painted over whichever tool's passage is mounted. */
.notes-overlay{position:absolute;inset:0;pointer-events:none;z-index:7;}
/* The note ring passes BEHIND the words (Daniel 2026-07-21: "the dot note ring is covering the
   text, we need a better solution"). The ring has to sit ON its word to point at it, and at
   34px it crosses the glyphs — so the fix is z-order, not size: the words are raised above the
   ring layer, and where a letter crosses the ring, the LETTER wins. The ring reads as a mark
   drawn on the paper underneath the type, which is what it is.

   Raised on the word spans rather than by pushing the ring layer to a negative z-index: a
   marked word carries its highlight as its own background, so a layer below the whole passage
   would sit under the mark bands too and vanish on any highlighted word — precisely where
   notes get made. The gaps come along so a band stays continuous across them.

   PAINT and TOUCH are separated (see drawDots): the ring layer is under the words and inert,
   while .notes-overlay keeps an invisible hit circle ABOVE them. Leaving the target underneath
   would have made a note reachable only through the leading between lines — the very space
   that disappears at the tight line spacing used for projection. */
.markup-passage .word,.markup-passage .word-gap,
.phrasing-passage .word,.phrasing-passage .word-gap{position:relative;z-index:6;}
.notes-ring-layer{position:absolute;inset:0;pointer-events:none;z-index:4;}
.notes-ring-layer .note-dot{pointer-events:none;}
/* The hit circle: same box as the ring, no ink of its own. */
.note-dot-target{background:none !important;border-color:transparent !important;box-shadow:none;}
/* Sized as a MARKER, not as a character (Daniel 2026-07-21, projected to a hall: "it was
   very hard to even see at all, I needed to literally walk up close to the screen to see
   it, and point to where it was"). At 20px against 19px verse text the dot came out the
   size of one letter — proportionate, and invisible as a signpost from the back of a room.
   34px reads as a deliberate mark at a glance while still sitting inside a line of text.

   The ring and shadow now scale too. They were flat 1px/1px at 20% black, which is exactly
   the thin low-contrast detail a projector washes out first — so the dot lost its edge
   against warm paper at precisely the distance it most needed one. */
.note-dot{
  position:absolute;width:calc(34px * var(--ui));height:calc(34px * var(--ui));
  border-radius:50%;transform:translate(-50%,-50%);
  /* A RING, not a disc (Daniel 2026-07-21, on the bigger dot: "this is a huge improvement,
     but the text is hard to read inside the circle"). The dot sits ON the word it marks, so
     the two requirements pull against each other: opaque enough to carry across a hall, and
     clear enough to read the word through. A disc cannot do both — at 34px a 55% colour wash
     covers the whole word. A ring can: the colour lives at the edge where it does the
     signalling, and the middle stays nearly clear where the word needs to be legible.
     Border colour is set per-note in notes.js (it carries the note's own colour); the dark
     halo underneath is what keeps the ring's edge from washing out on a projector. */
  border:calc(3px * var(--ui)) solid rgba(0,0,0,0.55);
  pointer-events:auto;cursor:pointer;
  box-shadow:0 0 0 calc(1px * var(--ui)) rgba(0,0,0,0.4), 0 calc(1px * var(--ui)) calc(4px * var(--ui)) rgba(0,0,0,0.3);
  touch-action:none;
}
.note-dot.open{outline:2px solid var(--accent);outline-offset:1px;}
.note-dot.parked{transform:none;border-style:dashed;}

/* ── The tool bars' "More ▾" overflow (INBOX 2026-07-15; core/toolbar-overflow.js) ──
   Shown only when packing the row into ONE line needs it. The controls are the real
   elements, re-parented in — so they keep the mode-button look they had in the bar,
   just laid out down the menu instead of across it. */
.toolbar-more{position:relative;display:inline-flex;align-items:center;}
.toolbar-more-btn{display:inline-flex;align-items:center;gap:4px;}
.toolbar-more-caret{font-size:0.8em;opacity:0.7;}
/* Hangs from the button's RIGHT edge, like the notes origin-filter menu beside it. The
   More button is always at the end of the bar (the note arm's margin-left:auto pins that
   cluster to the right edge), so a left-anchored menu ran off-screen at every width
   measured — 88px of it past a 390px phone's edge. */
.toolbar-more-menu{
  display:none;position:absolute;top:calc(100% + 4px);right:0;z-index:46;min-width:170px;
  background:var(--surface);border:1px solid var(--line);border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,0.18);
  padding:6px;flex-direction:column;gap:4px;
}
.toolbar-more.open .toolbar-more-menu{display:flex;}
.toolbar-more-row{display:flex;flex-direction:column;gap:4px;}
.toolbar-more-row.inline{flex-direction:row;align-items:center;flex-wrap:wrap;}
/* A moved control fills the menu's width and reads left-aligned, like a menu item —
   in the bar the same button is a snug, centred chip. */
.toolbar-more-row > button{width:100%;text-align:left;}
.toolbar-more-row.inline > button{width:auto;text-align:center;}
.toolbar-more-row-label{font-family:var(--font-ui);font-size:calc(11px * var(--chrome));color:var(--muted);flex-basis:100%;}
/* The slot marking a moved control's home position in the bar, and a separator left
   dangling by a group that moved out. Neither may draw OR count as a flex item (a
   visible-but-empty item would still take a gap). */
.toolbar-overflow-slot,.toolbar-overflow-hidden{display:none;}
/* Held for the length of a fit pass only (never across a paint): forcing the row onto
   ONE non-shrinking line and sizing the bar to its content makes the bar's own box
   report exactly what one row would need. */
.toolbar-measuring{flex-wrap:nowrap !important;width:max-content !important;}
.toolbar-measuring > *{flex:0 0 auto !important;}

/* A3 — the ✎ note arm + origin-filter caret, hosted in each tool's mode bar. The arm
   is the PENCIL ALONE (INBOX 2026-07-15): as "✎ Note" / "✎ Anteckning" it was the bar's
   widest control at 95px (en) / 137px (sv) and sat last in the row, which is why the
   note arm was the item always pushed onto a second line, and why it happened in every
   language but hit Swedish hardest. Its instruction now arrives as a toast on arming. */
.notes-toolbar-control{position:relative;display:inline-flex;align-items:center;margin-left:auto;}
.notes-arm-btn{
  font-family:var(--font-ui);font-size:calc(13px * var(--chrome));min-height:calc(38px * var(--chrome));padding:calc(6px * var(--chrome)) calc(10px * var(--chrome));
  background:var(--fill-warm);color:var(--ink);border:1px solid var(--line);border-radius:8px 0 0 8px;cursor:pointer;
}
.notes-arm-btn.armed{background:var(--accent);color:#fff;border-color:var(--accent);}
.notes-arm-label{display:inline-flex;align-items:center;}
.notes-arm-label svg{width:calc(17px * var(--chrome));height:calc(17px * var(--chrome));display:block;}
.notes-caret-btn{
  font-family:var(--font-ui);font-size:calc(12px * var(--chrome));min-height:calc(38px * var(--chrome));padding:calc(6px * var(--chrome)) calc(8px * var(--chrome));
  background:var(--fill-warm);color:var(--muted);border:1px solid var(--line);border-left:0;border-radius:0 8px 8px 0;cursor:pointer;
}
.notes-filter-menu{
  position:absolute;top:calc(100% + 4px);right:0;z-index:46;min-width:150px;
  background:var(--surface);border:1px solid var(--line);border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,0.18);
  padding:6px;display:flex;flex-direction:column;gap:3px;
}
.notes-filter-chip{
  font-family:var(--font-ui);font-size:calc(12px * var(--chrome));padding:calc(6px * var(--chrome)) calc(10px * var(--chrome));min-height:calc(34px * var(--chrome));text-align:left;
  border:1px solid var(--line);border-radius:8px;background:var(--chip-fill);color:var(--muted);cursor:pointer;user-select:none;
}
.notes-filter-chip.active{border-color:var(--accent);color:var(--ink);background:var(--accent-soft);font-weight:700;}

/* A1 — floating note panels (position:fixed on the body, anchored to their dot). */
#note-connector-svg{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;z-index:60;}
/* Sizes scale with --ui, not --chrome (Daniel 2026-07-21, camp meeting): a note is a
   little document, so its body tracks the PASSAGE text the way .markup-passage does,
   while its header/footer chrome stays on --chrome like every other control. Projected
   to a room at --ui 2.5 the note text stayed at chrome size and was unreadable from the
   back, so every note had to be read aloud — it throttled the one segment that was
   working. Scaling the MINIMUMS (rather than the stored width/height) means notes
   written before this change grow too, with no stored-view migration. */
.note-panel{
  position:fixed;z-index:70;background:var(--surface);
  /* Capped against the viewport as well as scaled: at the top of the --ui range a bare
     170px * 6 would demand a wider panel than the screen it floats on. */
  min-width:min(calc(170px * var(--ui)), 90vw);min-height:min(calc(100px * var(--ui)), 70vh);
  border:2px solid var(--accent);border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,0.22);
  display:flex;flex-direction:column;overflow:hidden;
}
.note-header{
  display:flex;align-items:center;gap:8px;padding:6px 8px;cursor:move;touch-action:none;user-select:none;
  font-family:var(--font-ui);font-size:calc(12px * var(--chrome));
}
.note-origin{
  font-weight:700;text-transform:uppercase;letter-spacing:0.05em;font-size:calc(9px * var(--chrome));
  color:var(--muted);background:var(--pill-fill);border-radius:5px;padding:1px 6px;flex:0 0 auto;
}
.note-word{color:var(--ink);flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.note-close{cursor:pointer;color:var(--muted);font-size:calc(18px * var(--chrome));line-height:1;padding:0 2px;flex:0 0 auto;}
.note-orphan{font-family:var(--font-ui);font-size:calc(11px * var(--chrome));color:var(--danger);background:var(--danger-soft);padding:4px 8px;}
.note-text{
  flex:1 1 auto;font-family:var(--font-ui);font-size:calc(19px * var(--ui) * var(--note-scale, 1));line-height:1.3;color:var(--ink);
  /* Padding is chrome, so it tracks --chrome and NOT the text it surrounds — the same
     mistake the bars made. Scaled on --ui it went to 32px/40px at the top of the range and
     the note spent more of itself on margin than on words (Daniel 2026-07-21: "make the
     text inside the notes hug the text, now it has a lot of padding inside"). */
  padding:calc(3px * var(--chrome)) calc(6px * var(--chrome));outline:none;overflow:auto;
  white-space:pre-wrap;word-break:break-word;   /* keep the user's line breaks (stored as '\n') */
}
.note-text:empty:before{content:attr(data-placeholder);color:var(--muted);}
.note-footer{display:flex;align-items:center;gap:8px;padding:6px 8px;border-top:1px solid var(--line);flex-wrap:wrap;}
.note-colors{display:flex;gap:5px;flex-wrap:wrap;}
.note-swatch{width:18px;height:18px;border-radius:50%;border:1px solid rgba(0,0,0,0.2);cursor:pointer;}
/* A−/A+ for the note body only. Sized on --chrome like every other control: it is chrome,
   and it must not grow with the thing it is adjusting or it runs away from the panel. */
.note-sizer{display:flex;gap:4px;}
.note-size-btn{
  font-family:var(--font-ui);font-size:calc(12px * var(--chrome));line-height:1;
  min-width:calc(28px * var(--chrome));min-height:calc(26px * var(--chrome));padding:0 calc(6px * var(--chrome));
  border:1px solid var(--line);border-radius:6px;background:var(--fill-warm);color:var(--ink);cursor:pointer;
}
.note-swatch.active{outline:2px solid var(--accent);outline-offset:1px;}
.note-delete{
  margin-left:auto;font-family:var(--font-ui);font-size:calc(12px * var(--chrome));padding:5px 10px;min-height:32px;
  color:var(--danger);border:1px solid var(--danger-soft-border);background:var(--danger-soft);border-radius:7px;cursor:pointer;
}
.note-resize-handle{
  position:absolute;right:0;bottom:0;width:18px;height:18px;cursor:nwse-resize;touch-action:none;
  background:linear-gradient(135deg,transparent 50%,var(--line) 50%,var(--line) 70%,transparent 70%);
}

/* M1 — the "which marking is this note about?" chooser (2+ highlight colors). */
.note-marking-chooser-overlay{position:fixed;inset:0;z-index:80;background:rgba(20,18,14,0.35);display:flex;align-items:center;justify-content:center;}
.note-marking-chooser-box{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:14px;min-width:240px;display:flex;flex-direction:column;gap:8px;box-shadow:0 12px 40px rgba(0,0,0,0.28);}
.note-marking-chooser-label{font-family:var(--font-ui);font-weight:700;font-size:calc(14px * var(--chrome));color:var(--ink);}
.note-marking-chooser-row{display:flex;align-items:center;gap:10px;min-height:44px;padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:var(--bg);cursor:pointer;text-align:left;}
.note-marking-chooser-swatch{width:20px;height:20px;border-radius:50%;border:1px solid rgba(0,0,0,0.2);flex:0 0 auto;}
.note-marking-chooser-name{font-family:var(--font-ui);font-size:calc(13px * var(--chrome));color:var(--ink);}
.note-marking-chooser-cancel{font-family:var(--font-ui);font-size:calc(13px * var(--chrome));min-height:40px;border:1px solid var(--line);border-radius:8px;background:var(--fill-warm);color:var(--ink);cursor:pointer;}

/* M1 — the gentle "highlight first" prompt. */
.notes-toast{
  position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(8px);z-index:90;
  background:var(--inverse-surface);color:#fff;font-family:var(--font-ui);font-size:calc(13px * var(--chrome));
  padding:10px 16px;border-radius:10px;box-shadow:0 8px 28px rgba(0,0,0,0.28);opacity:0;pointer-events:none;transition:opacity 0.15s,transform 0.15s;
}
.notes-toast.is-visible{opacity:1;transform:translateX(-50%) translateY(0);}

/* ══ Bracketing tool (Stage 5 — rows + relations forest + brackets/arcs) ═══════ */
.bracketing-tool{position:relative;padding-bottom:96px;}

/* Mode toolbar (shares the markup/phrasing toolbar look). */
.bracketing-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-block:8px 2px;}
.bracketing-mode-btn,.bracketing-tool-btn{
  font-family:var(--font-ui);font-size:calc(14px * var(--chrome));min-height:calc(40px * var(--chrome));padding:calc(8px * var(--chrome)) calc(8px * var(--chrome));
  background:var(--fill-warm);color:var(--ink);border:1px solid var(--line);border-radius:8px;cursor:pointer;
}
.bracketing-mode-btn.is-active{background:var(--accent);color:#fff;border-color:var(--accent);}
.bracketing-tool-btn.is-active{border-color:var(--accent);box-shadow:0 0 0 2px rgba(46,110,168,0.2);}
.bracketing-tool-btn:disabled{opacity:0.4;cursor:default;}
/* Brackets│Arcs segmented VIEW control (top-bar rethink 2026-07-17): the two toggles read as one
   joined pill on the chrome side. margin-left:-7px cancels the 6px flex gap plus a 1px border overlap
   so they share a seam. Active segment fills accent (clearer "on" than the tool-btn glow) and rises a
   z-layer so its border wins over the neighbour. Scoped to DIRECT toolbar children, so if the pair
   folds into "More ▾" the buttons render as normal menu items. */
.bracketing-toolbar > .bracketing-view-seg{border-radius:0;}
.bracketing-toolbar > .bracketing-view-seg.view-seg-first{border-top-left-radius:8px;border-bottom-left-radius:8px;}
.bracketing-toolbar > .bracketing-view-seg.view-seg-last{border-top-right-radius:8px;border-bottom-right-radius:8px;margin-left:-7px;}
.bracketing-toolbar > .bracketing-view-seg.is-active{background:var(--accent);color:#fff;border-color:var(--accent);box-shadow:none;position:relative;z-index:1;}
.bracketing-toolbar-sep{width:1px;height:26px;background:var(--line);margin:0 2px;}
.bracketing-hint{color:var(--help-ink);font-weight:600;font-size:calc(13px * var(--chrome));min-height:18px;margin:2px 0 6px;}

/* Main-point thesis line. */
.bracketing-main-point-wrap{display:flex;align-items:baseline;gap:8px;margin:4px 0 10px;}
.bracketing-main-point-label{font-family:var(--font-ui);font-size:calc(11px * var(--chrome));font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:0.05em;flex:0 0 auto;}
.bracketing-main-point{
  /* font-size never below 16px: an editable field under 16px makes iOS Safari zoom in on focus and
     not zoom back out (Daniel 2026-07-17). */
  flex:1 1 auto;font-family:var(--font-ui);font-size:max(16px, calc(14px * var(--ui)));color:var(--ink);
  padding:5px 8px;border:1px solid var(--line);border-radius:7px;background:var(--surface);outline:none;min-height:32px;
}
.bracketing-main-point:focus{border-color:var(--accent);}
.bracketing-main-point:empty:before{content:attr(data-placeholder);color:var(--muted);}
/* The bracketing chrome (hint + main-point) lines up with the toolbar: full-bleed, re-centred in the
   same fixed --top-bar-w column, decoupled from the ↔ text-width setting (Daniel 2026-07-17). */
.bracketing-hint,.bracketing-main-point-wrap,.bracketing-status,.bracketing-divide-help{margin-inline:calc(50% - var(--viewport-w, 100vw) / 2);padding-inline:max(16px, calc((var(--viewport-w, 100vw) - var(--top-bar-w)) / 2));}
/* Completeness readout (item 3): muted while in progress, green when one bracket covers all. */
.bracketing-status{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));font-weight:600;color:var(--muted);min-height:16px;margin:0 0 6px;}
.bracketing-status:empty{display:none;}
.bracketing-status.is-done{color:#2e8c3a;}
/* Divide-mode grammar reference (item 1): shown only in Divide mode; body opens on tap. */
.bracketing-divide-help{display:none;margin-bottom:8px;}
.bracketing-tool[data-mode="divide"] .bracketing-divide-help{display:block;}
.bracketing-divide-help-toggle{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));font-weight:700;color:var(--accent);background:none;border:none;padding:2px 0;cursor:pointer;}
.bracketing-divide-help-body{display:none;margin-top:4px;padding-left:10px;border-left:2px solid var(--bracketing-border,var(--line));flex-direction:column;gap:4px;max-width:640px;}
.bracketing-divide-help.open .bracketing-divide-help-body{display:flex;}
.bracketing-divide-help-rule{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));color:var(--help-ink);line-height:1.4;}

/* Rows surface (relative for the diagram SVGs; reading serif). It breaks out to the FULL WINDOW
   width (margin-inline, like the toolbars) so the arcs can use all the space beside the table; the
   ↔ setting sizes the TABLE within it, not the window (Daniel 2026-07-17). overflow-x:auto so it
   SCROLLS sideways when the table + arcs exceed the window rather than squishing. Height stays auto,
   so there's no vertical scroll and nothing above a row gets clipped. */
.bracketing-rows{position:relative;font-family:var(--font-reading);font-size:calc(16px * var(--ui));color:var(--ink);overflow-x:auto;margin-inline:calc(50% - var(--viewport-w, 100vw) / 2);}
.bracketing-empty-hint{color:var(--muted);font-family:var(--font-ui);padding:20px 0;line-height:1.6;}
/* Table-grid cell bounds (INBOX 2026-07-13): the received + MINE columns read as
   table cells even when MINE is empty, so brackets/arcs never look like they float
   in the air. (No overflow:hidden — the merge ✕ hangs above its row's box.) The grid is the FULL
   study width the ↔ control sets (--reading-w, minus the diagram gutter so paddingLeft+grid = the
   set width exactly); when that exceeds the screen, #bracketing-rows scrolls sideways instead of
   squishing the columns (Daniel 2026-07-17). */
.bracketing-grid{display:flex;flex-direction:column;gap:0;border:2px solid var(--bracketing-border);border-radius:6px;width:var(--reading-w, 900px);}
.bracketing-header-row{
  display:grid;grid-template-columns:minmax(34px,auto) minmax(0,1fr) minmax(0,1fr);
  align-items:end;gap:10px;padding:4px 0;
  font-family:var(--font-ui);font-size:calc(11px * var(--ui));font-weight:700;
  letter-spacing:0.04em;text-transform:uppercase;color:var(--muted);
}
.bracketing-header-cell{white-space:nowrap;user-select:none;}
.bracketing-header-mine{color:var(--muted);}
/* padding:0 + align-items:stretch so the column dividers (border-left below) run the FULL height of
   every row and meet the row separators (border-top) with no gap — a grid connected all the way
   (Daniel 2026-07-17). The per-row vertical breathing moves into the cells. */
.bracketing-row{
  position:relative;display:grid;grid-template-columns:minmax(34px,auto) minmax(0,1fr) minmax(0,1fr);
  align-items:stretch;gap:10px;padding:0;border-top:2px solid var(--bracketing-border);
}
.bracketing-row.pending-join{background:var(--accent-soft);box-shadow:inset 0 0 0 1px rgba(46,110,168,0.4);}
/* Main-point lens (item 2): the passage main-point proposition(s), traced down the ★. A soft
   accent wash + a bold gold label so it reads as "this is where the passage lands". */
.bracketing-row.is-main-point{background:var(--accent-soft);}
.bracketing-row.is-main-point .bracketing-label{color:var(--accent);}
.bracketing-row.is-main-point .bracketing-label::after{content:"★";margin-left:3px;font-size:0.85em;}
/* The AREA the open relationship's bracket encloses — a light-blue fill so you can see which one
   is selected (Daniel 2026-07-17). Drawn behind the bracket lines in the diagram SVG. */
.bracket-selected-fill{fill:var(--bracketing-selected);}
.bracketing-label{
  font-family:var(--font-ui);font-size:calc(12px * var(--ui));font-weight:700;color:var(--ink);
  text-align:right;user-select:none;white-space:nowrap;padding:4px 0;cursor:text;min-width:20px;
}
.bracketing-label.is-override{font-style:italic;}
.bracketing-label.is-editing{outline:1px solid var(--accent);border-radius:4px;background:var(--surface);color:var(--ink);}
.bracketing-cell{line-height:var(--bracketing-line-h, 1.7);}
.bracketing-received{color:var(--ink);border-left:2px solid var(--bracketing-border);padding:4px 4px 4px 10px;}
/* MINE is contenteditable, so its font never drops below 16px — under that, iOS Safari zooms in on
   focus and won't zoom back out (Daniel 2026-07-17). The RECEIVED source keeps the ↔/text-size scale. */
.bracketing-mine{color:var(--ink);min-height:1.7em;border-left:2px solid var(--bracketing-border);padding:4px 4px 4px 10px;}
/* Phone: MINE is a tappable field that opens the focused editor. */
.bracketing-mine-editable{cursor:pointer;}
/* MINE editor overlay (phone): source verse + a 16px textarea. STACKED by default (portrait) so the
   source stays above your input; SIDE-BY-SIDE in landscape (Daniel 2026-07-17). */
.bracketing-mine-modal{position:fixed;inset:0;z-index:60;background:rgba(0,0,0,0.45);display:flex;align-items:center;justify-content:center;padding:14px;}
.bracketing-mine-box{background:var(--surface);border:1px solid var(--line);border-radius:12px;padding:12px;width:min(680px,96vw);max-height:92vh;display:flex;flex-direction:column;gap:10px;box-shadow:0 12px 40px rgba(0,0,0,0.35);}
.bracketing-mine-pane{display:flex;flex-direction:column;gap:5px;min-height:0;}
.bracketing-mine-pane-label{font-family:var(--font-ui);font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:var(--muted);}
.bracketing-mine-source-text{font-family:var(--font-reading);font-size:17px;line-height:1.55;color:var(--ink);padding:8px 10px;background:var(--fill-warm);border-radius:8px;overflow-y:auto;max-height:34vh;}
.bracketing-mine-input{font-family:var(--font-reading);font-size:17px;line-height:1.55;color:var(--ink);width:100%;box-sizing:border-box;min-height:110px;padding:8px 10px;border:1px solid var(--line);border-radius:8px;background:var(--bg);resize:vertical;}
.bracketing-mine-input:focus{outline:none;border-color:var(--accent);}
.bracketing-mine-buttons{display:flex;justify-content:flex-end;gap:8px;}
.bracketing-mine-btn{font-family:var(--font-ui);font-size:15px;padding:9px 16px;border-radius:8px;border:1px solid var(--line);cursor:pointer;background:var(--fill-warm);color:var(--ink);}
.bracketing-mine-done{background:var(--accent);color:#fff;border-color:var(--accent);}
@media (orientation:landscape){
  .bracketing-mine-box{flex-direction:row;flex-wrap:wrap;align-items:stretch;}
  .bracketing-mine-source,.bracketing-mine-input-wrap{flex:1 1 0;min-width:0;}
  .bracketing-mine-source-text{max-height:60vh;}
  .bracketing-mine-input{height:100%;}
  .bracketing-mine-buttons{flex-basis:100%;}
}
.bracketing-mine[contenteditable="true"]:focus{outline:none;border-left-color:var(--accent);}
.bracketing-received .cell-word{border-radius:3px;padding:0 1px;}
.bracketing-tool[data-mode="divide"] .bracketing-received .cell-word{cursor:pointer;}
.bracketing-tool[data-mode="divide"] .bracketing-received .cell-word:hover{background:var(--accent-soft);}
.bracketing-tool[data-mode="join"] .bracketing-row{cursor:pointer;}

/* Merge ✕ — only in divide mode. */
.bracketing-merge-btn{
  position:absolute;left:calc(-1 * var(--merge-inset,0px));top:-9px;display:none;
  width:22px;height:22px;border-radius:50%;border:1px solid var(--danger-soft-border);background:var(--danger-soft);color:var(--danger);
  font-size:13px;line-height:1;cursor:pointer;z-index:6;
}
.bracketing-tool[data-mode="divide"] .bracketing-merge-btn{display:block;}

/* Diagram SVGs (brackets left margin, arcs right margin). */
.bracketing-diagram{position:absolute;top:0;left:0;overflow:visible;pointer-events:none;z-index:3;}
.bracketing-diagram .bracket-path,.bracketing-diagram .arc-path{fill:none;stroke-linejoin:round;stroke-linecap:round;}
.bracketing-diagram .bracket-hit-path,.bracketing-diagram .arc-hit-path{pointer-events:stroke;cursor:pointer;}
.bracketing-diagram .untyped-joint-handle{pointer-events:auto;cursor:pointer;}
.bracketing-type-label{font-family:var(--font-ui);font-size:calc(12px * var(--ui));font-weight:700;pointer-events:none;}

/* Relationship picker — a NON-modal side panel docked beside the edited relationship
   (Daniel 2026-07-16): pointer-events:none on the overlay so the passage + rows behind
   stay tappable (endpoint editing taps land on them); only the box catches input. The
   dock side + passage shift are driven by classes on the tool (the exact shift is set inline
   from the panel's measured width — see positionPickerPanel — so it sits right against it). */
.bracketing-modal{display:none;position:fixed;inset:0;z-index:40;pointer-events:none;}
/* Compact enough to sit beside the passage on a PHONE (Daniel 2026-07-17). overflow:hidden + the
   list's min-height:0 below keep the whole box within max-height (the type list scrolls INSIDE it),
   so the panel never grows off the screen. */
.bracketing-modal-box{position:fixed;pointer-events:auto;background:var(--surface);border:1px solid var(--bracketing-border,var(--line));border-radius:10px;padding:8px;width:min(240px,88vw);max-height:82vh;overflow:hidden;display:flex;flex-direction:column;gap:5px;box-shadow:0 8px 26px rgba(0,0,0,0.3);}
.bracketing-tool.picker-docked-left .bracketing-modal-box{left:8px;}
.bracketing-tool.picker-docked-right .bracketing-modal-box{right:8px;}
/* The passage shift is set entirely inline by positionPickerPanel (it hugs the selected bracket at
   its real on-screen position and shifts only as far as needed), so there is no fixed-margin CSS
   here — a fixed shift left a panel-width gap in the centred reading column (Daniel 2026-07-17). */
.bracketing-modal-title{font-weight:700;font-family:var(--font-ui);font-size:calc(14px * var(--chrome));}
.bracketing-modal-def{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));color:var(--help-ink);font-weight:600;line-height:1.4;}
.bracketing-picker-distinctions{display:flex;flex-direction:column;gap:3px;}
.bracketing-picker-distinction{font-family:var(--font-ui);font-size:calc(11px * var(--chrome));color:var(--muted);line-height:1.35;padding-left:9px;border-left:2px solid var(--bracketing-border,var(--line));}
.bracketing-picker-main{display:flex;flex-wrap:wrap;align-items:center;gap:5px;}
.bracketing-picker-label{font-family:var(--font-ui);font-size:calc(11px * var(--chrome));font-weight:700;color:var(--muted);}
.bracketing-main-chip{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));padding:calc(3px * var(--chrome)) calc(8px * var(--chrome));min-height:calc(26px * var(--chrome));border:1px solid var(--line);border-radius:7px;background:var(--chip-fill);cursor:pointer;}
.bracketing-main-chip.active{background:var(--accent);color:#fff;border-color:var(--accent);}
/* Filter-by-conjunction input (ported from be-relations) — narrows the type list live. */
.bracketing-picker-filter{
  font-family:var(--font-ui);font-size:16px;padding:6px 9px;border:1px solid var(--line);
  border-radius:7px;background:#fdfcf9;color:var(--ink);width:100%;
}
.bracketing-picker-filter:focus{outline:none;border-color:var(--accent);}
/* flex:1 + min-height:0 so the type list is the part that SCROLLS inside the height-capped box —
   the panel stays on screen instead of growing too tall (Daniel 2026-07-17). */
.bracketing-picker-list{overflow-y:auto;flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:6px;padding:1px;}
.bracketing-picker-group{display:flex;flex-wrap:wrap;gap:4px;}
.bracketing-picker-group-head{flex-basis:100%;font-family:var(--font-ui);font-size:calc(11px * var(--chrome));font-weight:700;text-transform:uppercase;letter-spacing:0.04em;margin-bottom:1px;}
/* Course taxonomy (item 5): the "Subordinate" parent over its three subgroups, and the two
   Distinct clusters (logical basis / tying two points). */
.bracketing-picker-parent-head{font-family:var(--font-ui);font-size:calc(10px * var(--chrome));font-weight:800;text-transform:uppercase;letter-spacing:0.08em;color:var(--muted);margin:3px 0 1px;}
.bracketing-picker-subgroup-head{flex-basis:100%;font-family:var(--font-ui);font-size:calc(10px * var(--chrome));font-style:italic;color:var(--muted);margin:2px 0 0;}
/* Compact chips: symbol + name only, filled with the group colour + white text (Daniel 2026-07-17).
   The fill/border/ink are set inline per group; the ACTIVE type keeps its fill and gains a dark ring. */
.bracketing-type-chip{display:inline-flex;align-items:baseline;gap:5px;font-family:var(--font-ui);font-size:calc(12px * var(--chrome));padding:calc(3px * var(--chrome)) calc(7px * var(--chrome));min-height:calc(26px * var(--chrome));border:1px solid var(--line);border-radius:7px;background:var(--chip-fill);cursor:pointer;}
.bracketing-type-chip.active{box-shadow:0 0 0 1px var(--surface),0 0 0 3px var(--ink);}
.bracketing-type-abbr{font-weight:800;}
.bracketing-type-name{font-size:calc(11px * var(--chrome));}
.bracketing-modal-buttons{display:flex;justify-content:flex-end;gap:8px;margin-top:6px;flex-wrap:wrap;}
/* Action row (Flip / Clear type / Save / Cancel) follows the Menu-size (--chrome) setting
   like every other UI button — at Normal (--chrome:1) the calc() equals the old fixed px, so
   no visual change at default (Daniel 2026-07-17 backlog: picker buttons should scale). */
.bracketing-modal-buttons button{font-size:calc(14px * var(--chrome));padding:calc(8px * var(--chrome)) calc(14px * var(--chrome));min-height:calc(40px * var(--chrome));border-radius:8px;border:1px solid var(--line);cursor:pointer;background:var(--fill-warm);}
.bracketing-modal-buttons .modal-remove-btn{color:var(--danger);border-color:var(--danger-soft-border);background:var(--danger-soft);}

/* Banner. */
.bracketing-banner{
  position:fixed;left:50%;bottom:84px;transform:translateX(-50%);
  background:var(--inverse-surface);color:#fff;font-family:var(--font-ui);font-size:14px;
  padding:10px 18px;border-radius:20px;opacity:0;pointer-events:none;transition:opacity 0.2s;z-index:50;max-width:90vw;
}
.bracketing-banner.is-visible{opacity:0.95;}

/* ── Stage 6: export control (project topbar) ── */
.export-control{position:relative;display:inline-flex;align-items:center;gap:6px;}
.export-btn{font-family:var(--font-ui);font-size:calc(13px * var(--chrome));padding:6px 12px;border:1px solid var(--line);border-radius:8px;background:var(--fill-warm);cursor:pointer;}
.export-menu{position:absolute;top:100%;right:0;margin-top:4px;z-index:30;background:var(--surface);border:1px solid var(--line);border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,0.18);display:flex;flex-direction:column;min-width:120px;overflow:hidden;}
.export-menu-item{font-family:var(--font-ui);font-size:14px;text-align:left;padding:9px 14px;border:none;background:none;cursor:pointer;}
.export-menu-item:hover{background:var(--fill-warm);}
/* Project slim-bar ⋯ actions menu (2026-07-17 top rethink): change/extend passage + the exports,
   grouped off the top into one dropdown. Mirrors the export-menu look. */
.project-actions{position:relative;display:inline-flex;align-items:center;gap:6px;}
.project-actions-btn{font-family:var(--font-ui);font-size:calc(18px * var(--chrome));line-height:1;min-width:calc(38px * var(--chrome));min-height:calc(38px * var(--chrome));padding:0 10px;border:1px solid var(--line);border-radius:8px;background:var(--fill-warm);cursor:pointer;}
.project-actions-menu{position:absolute;top:100%;right:0;margin-top:4px;z-index:30;background:var(--surface);border:1px solid var(--line);border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,0.18);display:flex;flex-direction:column;min-width:210px;overflow:hidden;}
.project-actions-item{font-family:var(--font-ui);font-size:15px;text-align:left;padding:11px 15px;border:none;background:none;cursor:pointer;color:var(--ink);}
.project-actions-item:hover{background:var(--fill-warm);}
.project-actions-sep{height:1px;background:var(--line);margin:4px 0;}
/* Compact in-tool switcher on the slim bar (segmented chips, sized to sit inline with the title). */
.tool-switcher-compact{display:inline-flex;gap:4px;flex-wrap:nowrap;}
.tool-switcher-compact .tool-switch-chip{font-size:calc(13px * var(--chrome));padding:calc(5px * var(--chrome)) calc(10px * var(--chrome));min-height:calc(34px * var(--chrome));border:1px solid var(--line);border-radius:7px;background:var(--fill-warm);cursor:pointer;}
/* App-level popup (passage change/extend) — a centered modal card over the whole app. */
.app-modal{position:fixed;inset:0;z-index:50;background:rgba(30,28,24,0.4);display:flex;align-items:center;justify-content:center;padding:16px;}
.app-modal-box{background:var(--surface);border:1px solid var(--line);border-radius:12px;box-shadow:0 14px 44px rgba(0,0,0,0.32);padding:16px;width:min(560px,96vw);max-height:92vh;overflow-y:auto;display:flex;flex-direction:column;gap:10px;}
.app-modal-title{font-family:var(--font-ui);font-size:calc(17px * var(--chrome));font-weight:700;color:var(--ink);}
.app-modal-note{font-family:var(--font-ui);font-size:calc(13px * var(--chrome));color:var(--muted);line-height:1.5;}
.app-modal-buttons{display:flex;justify-content:flex-end;gap:8px;margin-top:4px;}
.app-modal-buttons button{font-family:var(--font-ui);font-size:15px;padding:9px 16px;border-radius:8px;border:1px solid var(--line);background:var(--fill-warm);color:var(--ink);cursor:pointer;}
/* Workbench-card export: same menu, sourced from a project summary. The image formats
   render the Markup view into an off-screen host (inline-styled in shell.js since
   renderInto overwrites the className) purely to be serialized, then it's removed. */
.project-card-export{vertical-align:middle;}
.project-card-export .export-menu{min-width:150px;}
/* Multi-lens export scope chooser (INBOX 2026-07-14) — a centered modal card. */
/* ── The app's own dialogs (core/dialog.js) ────────────────────────────────────
   Every native confirm/prompt/alert is gone: prompt() doesn't work in an installed
   iOS PWA, and Safari's "suppress further dialogs" would silently turn every guard
   in the app into a no-op (Daniel's iPhone, 2026-07-15). */
.app-dialog-backdrop{position:fixed;inset:0;z-index:120;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.4);padding:16px;}
.app-dialog-card{background:var(--surface);color:var(--ink);border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 50px rgba(0,0,0,0.35);padding:18px;width:min(380px,100%);display:flex;flex-direction:column;gap:8px;max-height:calc(100vh - 32px);overflow-y:auto;}
.app-dialog-title{margin:0 0 4px;font-family:var(--font-ui);font-size:calc(16px * var(--chrome));font-weight:700;}
.app-dialog-message{margin:0 0 6px;font-family:var(--font-ui);font-size:calc(14px * var(--chrome));color:var(--ink);line-height:1.45;}
.app-dialog-label{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));font-weight:700;color:var(--muted);}
/* ≥16px or iOS zooms the page when the field takes focus. */
.app-dialog-input{width:100%;font-size:max(16px, calc(15px * var(--chrome)));}
.app-dialog-opt{text-align:left;background:var(--fill-warm);color:var(--ink);border:1px solid var(--line);border-radius:8px;}
.app-dialog-opt:hover{border-color:var(--accent);}
.app-dialog-opt.is-current{border-color:var(--accent);color:var(--accent);font-weight:700;}
/* The confirming action sits RIGHT, where the thumb is, and cancel is the quiet one. */
.app-dialog-buttons{display:flex;justify-content:flex-end;gap:8px;margin-top:6px;}
.app-dialog-ok{background:var(--accent);color:var(--inverse-surface);border:1px solid var(--accent);border-radius:8px;font-weight:700;}
.app-dialog-ok.is-danger{background:var(--danger);border-color:var(--danger);}
.app-dialog-cancel{background:none;color:var(--muted);border:1px solid var(--line);border-radius:8px;}
/* Export key / legend (INBOX 2026-07-14) — appended below the passage at export time so
   each image explains its marks; the full-lens variant is the blank print sheet's key.
   Samples are real .word marks (painted by the tool) so print matches on-screen colors. */
/* Two panels either side of a rule (Daniel 2026-07-15, from BibleArc's printed key): the
   lens and its STYLES on the left, drawn on their own names; its COLOURS on the right, a
   swatch each. The key prints ABOVE the passage now, so its own rule sits UNDER it — a
   border-top here would draw a line across the very top of the sheet. */
.markup-legend{display:flex;align-items:stretch;margin-bottom:16px;padding-bottom:10px;border-bottom:2px solid var(--line);font-family:var(--font-ui);break-inside:avoid;}
/* min-width:0 on both panels: they are flex items, and a flex item's default min-width:auto
   refuses to shrink below its CONTENT — at Daniel's projector type scale (--ui 5) a colour name
   like "Frivilligt kastade i havet" is wider than the key's whole column, so the key spilled past
   its own right edge and the export cut it off (Daniel 2026-07-21: "the legend is out of view"). */
.markup-legend-lens{flex:0 0 auto;max-width:45%;min-width:0;padding-right:18px;border-right:1px solid var(--line);}
.markup-legend-title{font-weight:800;font-size:calc(15px * var(--ui));margin-bottom:8px;color:var(--accent);}
.markup-legend-styles{display:flex;flex-direction:column;align-items:flex-start;gap:3px;}
/* align-self keeps the mark the width of the NAME it illustrates, not of the column. */
.markup-legend-style-row{break-inside:avoid;}
.markup-legend-style{display:inline-block;font-family:var(--font-ui);font-size:calc(13px * var(--ui));overflow-wrap:anywhere;}
/* The colours use the width the styles don't: as many columns as fit (INBOX 2026-07-14). */
/* min(190px, 100%) rather than a flat 190px: the track floor must never exceed the space there
   IS, or the grid lays out wider than its container and the overflow is what gets clipped. */
.markup-legend-colors{flex:1 1 auto;min-width:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(190px,100%),1fr));gap:3px 24px;align-content:start;padding-left:18px;}
/* flex-start, not center: once a long name wraps to two lines the swatch belongs beside the
   FIRST line, the way it reads on a printed key. */
.markup-legend-color-row{display:flex;align-items:flex-start;gap:10px;break-inside:avoid;min-width:0;}
.markup-legend-dot{flex:0 0 auto;width:calc(10px * var(--ui));height:calc(10px * var(--ui));border-radius:50%;}
/* A colour name is a phrase Daniel typed, not a word we chose — at large type a single long one
   has to be allowed to break, or it sets a min-content floor nothing can shrink under. */
.markup-legend-label{font-size:calc(13px * var(--ui));color:var(--ink);min-width:0;overflow-wrap:anywhere;}
.export-status{font-family:var(--font-ui);font-size:calc(12px * var(--chrome));color:var(--muted);}

.workbench-actions{display:flex;align-items:center;gap:10px;flex-wrap:wrap;}

/* ── Bars hug their text (Daniel 2026-07-21, camp meeting) ───────────────────────
   "Shrink the top and bottom button and chip sizes, to save space, but not shrinking
   the text size. It can basically hug the text."

   The bars were tall for one reason: min-height multiplied by --chrome alongside the
   font. That pairing is right at Normal — 38px is a touch target, not a text box — but
   it compounds badly at the top of the range: at Huge (1.75) a 38px floor becomes 66px
   around ~23px text, so the projector spent its scarce vertical space on padding rather
   than on the passage.

   So the floor stops scaling and the TEXT sets the height: an unscaled min-height keeps
   the tap target honest on a tablet at --chrome 1, while vertical padding scales gently
   enough that a control at Huge is only a little taller than its own line. Font sizes are
   deliberately untouched — every rule here sets box, never type. Horizontal padding is
   left alone too; the complaint was vertical, and pills need their waist.

   Phones override this below (they trim further still); this is the everywhere rule. */
.markup-mode-btn,.markup-tool-btn,.phrasing-mode-btn,.phrasing-tool-btn,
.bracketing-mode-btn,.bracketing-tool-btn,.notes-arm-btn,.notes-caret-btn,
.markup-quote-kind-chip,.palette-lens-switch,.color-chip,.style-chip,.add-color-btn,
.phrasing-color-chip,.phrasing-type-chip{
  /* max(30px, 26px * --chrome), not a flat 34px: the floor must still WIN over the
     content at every menu size, or the pills stop matching each other (a swatch and a
     bare label grow at different rates, and markup-palette-chips-uniform rightly fails).
     26 * --chrome stays comfortably under the old 38 — 45px at Huge where it used to be
     66 — while never dropping under 30px for a fingertip at the small end. */
  min-height:max(30px, calc(26px * var(--chrome)));
  padding-top:calc(3px * var(--chrome));padding-bottom:calc(3px * var(--chrome));
}
/* The bars' own padding shrinks with the same reasoning — a fixed strip whose chips have
   tightened should not keep its old margins around them. */
.markup-palette,.phrasing-palette{padding-top:calc(5px * var(--chrome));padding-bottom:calc(5px * var(--chrome) + env(safe-area-inset-bottom));}

/* ── Responsive: tighter chrome on phones (INBOX 2026-07-14) ─────────────────────
   "Especially on phones it takes up too much space." Buttons/inputs already track
   the Menu-size setting via --chrome; on narrow phone viewports we ALSO trim the
   shared box + the chunkiest chrome so controls stop eating horizontal space and
   wrapping. The Menu-size setting still multiplies on top (calc · --chrome). Verse
   text (its own --ui scale) is untouched. 640px ≈ large-phone / small-tablet edge. */
@media (max-width:640px){
  /* Phones are vertically tight — trim the button box below the 40px touch default so a
     column of controls doesn't dominate the screen; Small (--chrome .85) then shrinks it
     further still (INBOX 2026-07-14: "even on small font size the buttons are too tall"). */
  button{min-height:calc(30px * var(--chrome));padding:calc(5px * var(--chrome)) calc(11px * var(--chrome));font-size:calc(13px * var(--chrome));}
  .workbench-new-btn,.workbench-about-btn,.workbench-import-btn{margin:6px 0 2px;padding:calc(5px * var(--chrome)) calc(12px * var(--chrome));}
  /* Tool chrome (mode/tool buttons + notes button + menu rows) — same phone trim as the
     Workbench buttons so a mode bar doesn't dominate a small screen. */
  .markup-mode-btn,.markup-tool-btn,.phrasing-mode-btn,.phrasing-tool-btn,.bracketing-mode-btn,.bracketing-tool-btn,
  .notes-arm-btn,.notes-caret-btn,.notes-filter-chip,.lens-menu-row,.phrasing-context-item{
    min-height:calc(30px * var(--chrome));padding:calc(4px * var(--chrome)) calc(10px * var(--chrome));
  }
  /* Bottom palette pills sit in a fixed bar (space is even tighter there) — trim them a
     touch more, and shrink the bar's own padding so the strip hugs its chips. */
  .markup-quote-kind-chip,.palette-lens-switch,.add-color-btn,.color-chip,.style-chip,
  .phrasing-color-chip,.phrasing-type-chip,.phrasing-label-option,.bracketing-main-chip,.bracketing-type-chip{
    min-height:calc(26px * var(--chrome));padding:calc(3px * var(--chrome)) calc(9px * var(--chrome));
  }
  /* The hide/show eye narrows with everything else here; its height is `stretch` (see the
     base rule), so it follows the chip rather than setting its floor. */
  .chip-toggle{min-width:calc(22px * var(--chrome));}
  .color-chip .swatch{width:calc(15px * var(--chrome));height:calc(15px * var(--chrome));}
  /* Phrasing's palette dots trim with Markup's (same 18→15 / 16→13 ratio). Their base
     rules stay raw px, exactly like .color-chip .swatch above — parity, not an oversight. */
  .phrasing-color-swatch{width:calc(13px * var(--chrome));height:calc(13px * var(--chrome));}
  .phrasing-type-swatch{width:calc(12px * var(--chrome));height:calc(12px * var(--chrome));}
  .markup-palette,.phrasing-palette{padding:calc(4px * var(--chrome)) 8px calc(4px * var(--chrome) + env(safe-area-inset-bottom));}
  /* keep inputs ≥16px so focusing them never triggers iOS's zoom, but still grow with --chrome */
  input,select,textarea{min-height:calc(36px * var(--chrome));padding:calc(6px * var(--chrome)) calc(10px * var(--chrome));font-size:max(16px, calc(13px * var(--chrome)));}
  #app-header{padding:6px 10px;}
  #app-title{font-size:calc(15px * var(--chrome));}
  .workbench-heading{font-size:calc(21px * var(--chrome));}
  .about-panel{padding:14px 15px;}
}
