/* ConceptLine, in the SiteNotePro brand.
   Colour values copied verbatim from
   01_SiteNotePro App Development/.../brand/tokens/brand-tokens.json — do not invent
   new ones here; if the brand file changes, change this block, not the call sites.

   Inter is self-hosted (brand rule: "no third-party font CDN"), which also keeps the
   app working offline. */

@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("./vendor/fonts/inter-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("./vendor/fonts/inter-latin-600-normal.woff2") format("woff2");
}

/* The SiteNotePro wordmark is set in Poppins Bold (brand kit, logo-colour.svg). Used for the
   wordmark and nothing else, so only the one weight ships. */
@font-face {
  font-family: Poppins;
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("./vendor/fonts/poppins-latin-700-normal.woff2") format("woff2");
}

/* Signature faces (OFL). Only loaded because the Sign dialog uses them. */
@font-face {
  font-family: Caveat;
  font-weight: 400;
  font-display: swap;
  src: url("./vendor/fonts/caveat-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Dancing Script";
  font-weight: 400;
  font-display: swap;
  src: url("./vendor/fonts/dancing-script-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Great Vibes";
  font-weight: 400;
  font-display: swap;
  src: url("./vendor/fonts/great-vibes-latin-400-normal.woff2") format("woff2");
}

:root {
  /* --- SiteNotePro brand palette (constant across themes) --- */
  --sn-navy: #263241;
  --sn-navy-deep: #1b2530;
  --sn-orange: #f6921e;
  --sn-orange-deep: #e07d0e;
  --sn-blue: #0e63b3;
  --sn-slate: #5a636e;
  --sn-red: #c8423a;
  --sn-font-body: Inter, system-ui, Arial, sans-serif;
  --sn-radius-lg: 0.5rem;
  --sn-radius-xl: 0.75rem;

  /* Toolbar dimensions — bumped for a more comfortable bar. The "bigger" comes mostly
     from height + icon size (free on width); button width only nudges up, so ~20 buttons
     still fit a normal laptop without horizontal scroll. */
  --bar-h: 54px;
  --btn: 36px;
  --icon-size: 20px;

  --fast: 150ms;
  --ease: cubic-bezier(0.22, 1, 0.36, 1); /* brand motion curve */

  /* --- Motion tokens for the redesign (specs/redesign-studio-and-site.md, R1) ---
     Defined here so the stages that move things (the mark's menu in R3, the rail's armed tile
     and the options bar in R4, the pill in R5, the sheets in R6) all move alike.
     --spring is a critically damped spring (damping 1.0, response 0.35s, the value Apple uses
     for moving things) sampled into linear(), the same curve as mockups/shared.css. It never
     overshoots, because nothing it moves was flicked. A spring has no duration of its own;
     --spring-dur is how long this one takes to settle. --press-dur and --press-scale are the
     push a control gives under a finger or a click, and --hint-delay is how long a pointer rests
     on a rail tool before its label shows. All but the last have a reduced-motion value below. */
  --spring: linear(0, 0.0815, 0.2427, 0.4124, 0.5614, 0.6813, 0.7731, 0.8408, 0.8897, 0.9243, 0.9485, 0.9652, 0.9766, 0.9843, 0.9896, 0.9931, 0.9954, 0.997, 1);
  --spring-dur: 470ms;
  --press-dur: 100ms;
  --press-scale: 0.96;
  --hint-delay: 350ms;
  /* What a move becomes when reduced motion is asked for: a short cross-fade, the spec's rule for
     spring motion. A moving element marked data-fade takes this transition in place of its own
     under reduced motion (the reduced-motion block after the dialogs), so it fades to where it is going
     instead of travelling there. */
  --fade: opacity 120ms linear;

  /* --- ROLE tokens: these are what components use, and what the theme swaps ---
     Light theme. Contrast measured against each token's own background:
       ink #263241 on white ........ 13.0:1
       on-active #263241 on orange .. 5.6:1  (white on orange is 2.3:1 — never used)
       ink-mute #5a636e on white .... 6.1:1
       ring #0e63b3 on white ........ 6.1:1
     Orange is a fill only, never text (2.3:1 on white). */
  color-scheme: light;
  --bar: #fff;
  /* Navy at 7% over white, about #f0f1f2, so a white sheet stands off it (SiteNotePro, 21 October
     2026). A mix of two brand colours rather than a new one. Measured on it: ink 11.5:1, ink-mute
     5.4:1. */
  --canvas: color-mix(in srgb, var(--sn-navy) 7%, #fff);
  --surface: #fff;          /* find bar, dialogs */
  /* The floating material the rail is made of (R4), and R5's pill may be: the surface, a little
     see-through, with the drawing blurred under it. The options bar and the bar at the foot carry
     words, the status line among them, and are the plain surface. Never on anything that
     spans the top edge (#bar). The reduced-transparency rule below makes it the plain surface. */
  --glass: color-mix(in srgb, var(--surface) 82%, transparent);
  --glass-blur: blur(20px) saturate(170%);
  --ink: var(--sn-navy);
  --ink-mute: var(--sn-slate);
  --line: #e5e7eb;
  --hover: rgb(38 50 65 / 0.06);
  --press: rgb(38 50 65 / 0.12);
  --active: var(--sn-orange);
  --on-active: var(--sn-navy);
  --ring: var(--sn-blue);
  --link: #1d5f9e;               /* 5.9:1 on white */
  --danger: var(--sn-red);
  --err-text: var(--sn-red);      /* 4.9:1 on white */
  /* Navy, not the deep orange it was: that is 2.95:1 on the white bar, and orange is never text on
     white. The dark theme keeps the orange, which reads there. */
  --busy-text: var(--ink);
  --scrim: rgb(38 50 65 / 0.45);
  --shadow-card: 0 1px 2px rgb(38 50 65 / 0.04), 0 4px 16px rgb(38 50 65 / 0.06);
  /* The wordmark's "Site" and "Pro" (its "Note" is always orange), and ConceptLine's own red. */
  --wordmark: var(--sn-navy);
  --redline: var(--sn-red);       /* 4.9:1 on white */
  /* A pane's toast is navy in both themes, so what sits on it is the same in both: white ink
     (13:1) and a white wash for hover and press. The orange is for its edge and outlines. */
  --note-ink: #fff;
  --note-hover: rgb(255 255 255 / 0.12);
  /* A resting tint for a chip or the small logo on the first screen: the ink, faintly. Not
     --hover, which means a pointer is over something. */
  --fill: rgb(38 50 65 / 0.055);
  /* A small icon that stands for a promise or a drop, on the first screen. Navy in the light
     theme, where orange would be 2.3 to 2.95:1 on white or the canvas; orange in the dark,
     where it reads at over 6:1. */
  --icon-accent: var(--ink);
  /* The edge of a colour swatch (R4): dark on the light bar, light on the dark one, so a navy or slate
     swatch still shows its edge at 3:1 or more against the bar. */
  --swatch-edge: rgb(38 50 65 / 0.6);
}

/* Dark theme. Applied when the OS asks for it (unless the user forced light), or when
   the user forces dark. Values duplicated across the two selectors on purpose — CSS
   custom properties can't share a value block across a media query and a plain rule.
   Contrast, measured: ink #e7ebf0 on #1b2530 ≈ 13:1; ink-mute #9aa7b4 ≈ 6:1;
   ring #60a5fa on #1b2530 ≈ 5:1; on-active #1b2530 on orange = 5.6:1. Pages stay white. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bar: #1b2530;
    --canvas: #10161f;
    --surface: #232f3d;
    --ink: #e7ebf0;
    --ink-mute: #9aa7b4;    /* the shade the SiteNotePro site uses for dark-hero body text */
    --line: rgb(255 255 255 / 0.12);
    --hover: rgb(255 255 255 / 0.08);
    --press: rgb(255 255 255 / 0.16);
    --on-active: var(--sn-navy-deep);
    --ring: #60a5fa;
    --link: #7cb6e8;        /* 7.4:1 on #232f3d */
    --err-text: #f0a6a0;
    --busy-text: var(--sn-orange);
    --scrim: rgb(0 0 0 / 0.6);
    --shadow-card: 0 2px 8px rgb(0 0 0 / 0.5), 0 12px 32px rgb(0 0 0 / 0.35);
    --wordmark: #ffffff;          /* the brand kit's reversed logo */
    --redline: #ef8a82;           /* 6.4:1 on #1b2530 */
    --fill: rgb(255 255 255 / 0.06);
    --icon-accent: var(--sn-orange);
    --swatch-edge: rgb(255 255 255 / 0.7);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bar: #1b2530;
  --canvas: #10161f;
  --surface: #232f3d;
  --ink: #e7ebf0;
  --ink-mute: #9aa7b4;
  --line: rgb(255 255 255 / 0.12);
  --hover: rgb(255 255 255 / 0.08);
  --press: rgb(255 255 255 / 0.16);
  --on-active: var(--sn-navy-deep);
  --ring: #60a5fa;
  --link: #7cb6e8;
  --err-text: #f0a6a0;
  --busy-text: var(--sn-orange);
  --scrim: rgb(0 0 0 / 0.6);
  --shadow-card: 0 2px 8px rgb(0 0 0 / 0.5), 0 12px 32px rgb(0 0 0 / 0.35);
  --wordmark: #ffffff;
  --redline: #ef8a82;
  --fill: rgb(255 255 255 / 0.06);
  --icon-accent: var(--sn-orange);
  --swatch-edge: rgb(255 255 255 / 0.7);
}

/* How every control in this file answers a pointer. In tools/isolation-matrix.mjs, the
   touchAndTheme suite checks 1 to 3. The designTokens suite checks the light and dark canvas, that
   --spring is critically damped, the motion tokens and the fade under reduced motion, the glass
   under reduced transparency, that nothing at the top edge is see-through, the contrast of the
   placeholders, the status line, the toolbar icons and whatever floats over the drawing or sits on
   the first screen, and that the toast's second button wears the tokens.
   1. Hover is for a mouse only. Every :hover sits inside @media (hover: hover) and (pointer: fine),
      because a phone or an iPad fakes a hover on tap and leaves it on until the next tap.
   2. Every gated :hover has an :active twin with the same selector outside the query, showing at
      least what the hover shows, so a press shows on every kind of pointer, and shows on the
      press, not on the release.
   3. No tap flash (-webkit-tap-highlight-color on html), since the :active twins replace it.
   4. The colours come from tokens: a plain control takes --hover, and --press when pressed; an
      orange button deepens to --sn-orange-deep for both; a button on a pane's toast takes
      --note-hover. A control that pushes in on a press scales to --press-scale over
      --press-dur, so reduced motion turns the push off with the rest.
   5. Things that move use --spring over --spring-dur, and things that float use --glass and
      --glass-blur. Under reduced motion the motion tokens go to nothing, and a moving element
      marked data-fade cross-fades on --fade instead; under reduced transparency the glass is the
      plain surface. Both fallbacks are after the dialogs below. Nothing that spans the top edge
      is see-through (#bar).
   6. Focus shows as a 2px outline in --ring on :focus-visible, except on navy surfaces such as a
      pane's toast, where the outline is --note-ink. Orange is a fill, never the words or a thin
      line on white; the words on it are --on-active. Placeholders are --ink-mute, at full
      opacity. */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
}
/* No grey or blue flash over whatever a finger taps, which phones paint on anything with a click
   handler and which fought the buttons' own pressed look. Every :hover below has an :active twin,
   so a tap still shows. */
html { -webkit-tap-highlight-color: transparent; }
/* The browser's own placeholder grey (#757575 in Chrome) is 4.1:1 on the light canvas and under
   3:1 in the dark fields. The muted ink is 4.5:1 or more on every field here, in both themes, the
   scale dialog's unstyled distance field included (4.6:1 on the browser's dark grey). A PDF's own
   form fields are left as the PDF has them. */
:is(input, textarea):not(.annotationLayer *)::placeholder { color: var(--ink-mute); opacity: 1; }
/* The toolbar wraps to more rows on a narrow screen, so its height is not a constant and
   nothing below it can be positioned against one. Two grid rows solve it with no
   measuring: the bar takes exactly what it needs, and .container, #drop and #findbar all
   share the second row, overlaying each other in DOM order.
   A ResizeObserver publishing the measured height would also work, but it runs AFTER the
   first paint — so every narrow load would start with the page under the toolbar and jump. */
body {
  display: grid;
  grid-template-rows: auto 1fr;
  font: 13px/1.5 var(--sn-font-body);
  background: var(--canvas);
  color: var(--ink);
}

/* ---- toolbar ---- */

#bar {
  grid-row: 1;
  /* Sticky changes nothing here, because the page itself never scrolls. It is for the iPhone.
     Since iOS 26 a home-screen app gets a soft blur over the top of the page, just under the
     status bar, which fogged the top of these buttons. WebKit leaves it off when a fixed or
     sticky box spans the top edge with one solid background, and paints the status bar in
     that colour instead (LocalFrameView::fixedContainerEdges). So: sticky, opaque, and no
     backdrop-filter here, ever. */
  position: sticky;
  top: 0;
  /* One row that scrolls sideways, not a wrapping block. Wrapping did make every button
     reachable without scrolling, but on a phone it grew to five rows and ate 38% of the
     screen — tested on the real device and rejected. A tall toolbar costs you the drawing
     on every screen; the scroll only costs a swipe, and only when you want a far button. */
  min-height: var(--bar-h);
  display: flex;
  align-items: center;
  gap: 2px;
  /* Don't let a flick along the toolbar turn into a scroll of the page behind it. */
  overscroll-behavior-x: contain;
  /* 8px, down from 10, for the Edit tool's button. With the group and status changes below that
     is exactly the 36px the button takes at 1366px, so the toolbar has the same room there as it
     had without it. With the page count's fixed width and the 5px rules below, measured: 3px to
     spare at 1366px with any drawing of up to 999 pages open, and the welcome screen the same. */
  padding: 0 8px;
  background: var(--bar);
  border-bottom: 1px solid var(--line);
  user-select: none;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
}
#bar::-webkit-scrollbar { display: none; }

.group { flex: none; display: flex; align-items: center; gap: 2px; }
/* 5px either side of the rule, down from 10, then 8, then 6: the brand mark, Compress, Download,
   the Edit tool's button and then room for a three-digit page count (#pageTotal) had to come
   from somewhere, and a 1366px laptop has no other room to give. The rule still reads as a gap
   between groups at 5. */
/* The page jobs start the bar that is the foot of the drawing in Studio (.pageBar, stage R4), which
   on a phone is display: contents, so they still follow the colour group there and keep their rule. */
.pageBar { display: contents; }
.group + .group,
.group + .pageBar > .group:first-child { margin-left: 5px; }
.group + .group::before,
.group + .pageBar > .group:first-child::before {
  content: "";
  align-self: center;
  width: 1px;
  height: 24px;
  margin-right: 5px;
  background: var(--line);
}

#bar button {
  flex: none;
  display: grid;
  place-items: center;
  width: var(--btn);
  height: var(--btn);
  padding: 0;
  border: 0;
  border-radius: var(--sn-radius-lg);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
#bar button svg { width: var(--icon-size); height: var(--icon-size); pointer-events: none; }

/* Every :hover in this file sits behind (hover: hover) and (pointer: fine). A finger has no hover,
   so phones and iPads fake one on tap and leave it on until the next tap somewhere else: a
   button stayed grey, and Delete stayed red, after it was used. Each one has an :active twin
   outside the query instead, so a press still shows on every kind of pointer. */
@media (hover: hover) and (pointer: fine) {
  #bar button:hover:not(:disabled) { background: var(--hover); }
}
#bar button:active:not(:disabled) { background: var(--press); }

/* Active tool: the SiteNote orange, with a navy glyph (white on orange fails, both themes). */
#bar button.on {
  background: var(--active);
  color: var(--on-active);
}
@media (hover: hover) and (pointer: fine) {
  #bar button.on:hover:not(:disabled) { background: var(--sn-orange-deep); }
}
#bar button.on:active:not(:disabled) { background: var(--sn-orange-deep); }

@media (hover: hover) and (pointer: fine) {
  #bar button.danger:hover:not(:disabled) { background: var(--danger); color: #fff; }
}
#bar button.danger:active:not(:disabled) { background: var(--danger); color: #fff; }

#bar button:disabled { opacity: 0.35; cursor: not-allowed; }

/* Unsaved changes in the active drawing: a dot at the Save button's top right corner. Placed
   absolutely, inside a button made position: relative for it, because #bar buttons are grids
   and an ::after in the flow would take a cell and push the icon aside; this way the button
   keeps its size and the toolbar its width. ConceptLine's red, which is 4.9:1 on the light bar and
   6.4:1 on the dark one, with a ring of the bar's own colour so it stands clear of the icon. */
#bar button[data-act="save"] { position: relative; }
#bar button.unsaved::after {
  content: "";
  position: absolute;
  top: 4px;
  right: 4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--redline);
  box-shadow: 0 0 0 2px var(--bar);
  pointer-events: none;
}

#bar button:focus-visible,
#colour:focus-visible,
#thick:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}
/* The ring sits on the orange fill for an active tool, where the blue ring vanishes;
   navy reads on orange in both themes (5.6:1). */
#bar button.on:focus-visible { outline-color: var(--sn-navy); outline-offset: -3px; }

/* `hidden` loses to `#bar button { display: grid }`, which outranks the browser's own rule,
   so it is restated. Download hides itself inside the installed app. */
#bar button[hidden],
#drop button[hidden] { display: none; }

/* ---- brand ---- */

/* The <symbol> the mark is drawn from. Out of the grid: body lays its children out in rows. */
.svgDefs { position: absolute; width: 0; height: 0; overflow: hidden; }

/* The logo's own type and colours. A logo is the one place orange is text: the contrast rule
   is for reading, and nobody reads a wordmark letter by letter. */
.wordmark {
  font-family: Poppins, var(--sn-font-body);
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
  color: var(--wordmark);
}
.wordmark span { color: var(--sn-orange); }

/* The product's own name, set the way SiteNotePro sets its own: Poppins Bold, one syllable in
   colour. The <span> tools/naming.mjs puts round its last word ("Line" in ConceptLine) takes the
   brand red, the line the pen in the mark draws. */
.rlword {
  font-family: Poppins, var(--sn-font-body);
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
  color: var(--wordmark);
}
.rlword span { color: var(--redline); }
.by { color: var(--ink-mute); white-space: nowrap; }

/* The logo with its words: the mark, then ConceptLine over "by SiteNotePro". Wherever it is a
   link, it goes to the SiteNotePro site. */
.lockup { display: flex; align-items: center; gap: 10px; color: inherit; text-decoration: none; }
.lockup .names { display: grid; justify-items: start; line-height: 1.1; }
a.lockup:focus-visible { outline: 2px solid var(--ring); outline-offset: 4px; border-radius: 4px; }

/* Read out, never shown: a heading the logo stands in for on screen. */
.srOnly {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* First in the file group, set off by space rather than a rule: a rule costs 17px more. */
#bar button.brandBtn {
  width: auto;
  min-width: var(--btn);
  display: flex;
  gap: 8px;
  padding: 0 4px;
  margin-right: 6px;
}
#bar .brandBtn .mark { width: 28px; height: 28px; flex: none; }
.brandWords { display: none; align-items: baseline; gap: 7px; font-size: 17px; line-height: 1; }
.brandWords .by { font-size: 12px; }
/* Only where the room is spare: on a laptop the mark alone has to say it. */
@media (min-width: 1720px) {
  .brandWords { display: flex; }
  #bar button.brandBtn { padding: 0 10px 0 4px; }
}

/* Download. An arrow where room is short, the word as well where it is not. */
#bar button.install {
  width: auto;
  min-width: var(--btn);
  grid-auto-flow: column;
  gap: 6px;
  padding: 0 8px;
  font: inherit;
  font-weight: 600;
}
#bar button.install .label { display: none; }
@media (min-width: 1500px) {
  #bar button.install { padding: 0 12px 0 9px; box-shadow: inset 0 0 0 1px var(--line); }
  #bar button.install .label { display: inline; }
}

/* ---- colour + thickness ---- */

#colourWrap {
  display: grid;
  place-items: center;
  width: var(--btn);
  height: var(--btn);
  border-radius: var(--sn-radius-lg);
  transition: background var(--fast) var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  #colourWrap:hover { background: var(--hover); }
}
#colourWrap:active { background: var(--press); }

#colour {
  width: 24px;
  height: 24px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: none;
  cursor: pointer;
}
#colour::-webkit-color-swatch-wrapper { padding: 0; }
#colour::-webkit-color-swatch { border: 0; border-radius: 3px; }

#thickWrap { display: flex; align-items: center; padding: 0 6px; }
/* 60px, down from 66, for the page count's fixed width (#pageTotal): still 3px a step over 1 to 20. */
#thick { width: 60px; accent-color: var(--ring); cursor: pointer; }

#colourWrap:has(#colour:disabled),
#thickWrap:has(#thick:disabled) { opacity: 0.35; pointer-events: none; }

/* ---- readouts ---- */

#zoom, #pageinfo {
  flex: none;
  min-width: 44px;
  text-align: center;
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}
#pageinfo { min-width: 62px; }

#status {
  /* Shrinkable, not fixed: the hints run to ~440px, and as an unshrinkable tail on a
     phone they added that much dead scroll AFTER the last button — you could never swipe
     far enough to reach Save. With flex-shrink and min-width 0 it gives up its width
     first, ellipsising and finally collapsing, and the buttons stay the end of the
     scroll. A desktop has the room, so nothing changes there. */
  flex: 0 1 auto;
  min-width: 0;
  margin-left: auto;
  /* Padding does not shrink with the line, so it is width the buttons can never have. 4px,
     down from 16, is the room the Edit tool's button needed at 1366px; margin-left: auto still
     pushes the line to the far end whenever there is space to spare. */
  padding-left: 4px;
  color: var(--ink-mute);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#status.busy { color: var(--busy-text); }
#status.err { color: var(--err-text); }

/* ---- Studio's top bar (redesign R3) ---- */

/* In Studio the toolbar is B's top bar (.topBar in index.html), with the mark's menu, the file name
   and its unsaved dot, then Undo, Redo, Find, Split, Theme, Share and Save. Under it, from R3 to R4,
   was the tool row, the rest of the toolbar as it had been; R4 took it away: the tools are the rail
   on the left of the drawing, their options the bar at its top centre, and the page jobs, the zoom
   and the page the small bar at its foot, until R5 (Studio's rail and bars, below). The groups and
   buttons marked forSite are the phone's, and go. On a phone none of this applies: the toolbar is
   today's, one row that scrolls, until R6. The top bar is part of #bar, so it is as opaque as the
   rest of it, for the iOS 26 reason there. The switch is the one in "the Studio and On site switch"
   below, copied word for word. */
#bar .forStudio { display: none; }
@media (min-width: 700px) and (min-height: 500px), (min-width: 700px) and (pointer: fine), (min-width: 700px) and (pointer: none) {
  #bar {
    flex-wrap: wrap;
    align-content: flex-start;
  }
  #bar .forSite { display: none; }
  /* The full width of the bar, edge to edge. Its own rule under it is the bar's: two rules a pixel
     apart read as one thick line. */
  #bar .topBar {
    display: flex;
    flex: 0 0 calc(100% + 16px);
    align-items: center;
    gap: 2px;
    min-width: 0;
    height: 52px;
    margin: 0 -8px;
    padding: 0 8px;
  }
  #bar button.forStudio:not([hidden]) { display: grid; }
  /* The welcome screen has no drawing to undo, find, split, share, save or draw on, and its one
     orange button is Open a PDF: until a drawing opens, the toolbar is the top bar with the mark
     and Theme, as in the mockup, and the rail and the bars come with the first drawing. The status
     line stays where a screen reader hears it ("Opening…"), out of sight. */
  body:not(.hasDrawing) #bar .docOnly,
  body:not(.hasDrawing) #bar .pageBar > .group { display: none; }
  body:not(.hasDrawing) #bar .topBar .group::before { content: none; }
  #bar { min-height: 0; }
  body:not(.hasDrawing) #status { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip-path: inset(50%); }
}
.topBar .spacer { flex: 1 1 auto; min-width: 8px; }

/* The mark, with the small arrow that says it opens a menu, in the muted ink (6:1 light, 6.5:1 dark). */
#bar button.markBtn {
  width: auto;
  grid-auto-flow: column;
  gap: 2px;
  padding: 0 4px 0 3px;
  color: var(--ink-mute);
}
#bar .markBtn .mark { width: 28px; height: 28px; }
#bar .markBtn i { display: flex; }
#bar .markBtn i svg { width: 14px; height: 14px; }
#bar button.markBtn[aria-expanded="true"] { background: var(--press); }

/* The active drawing's name, cut short with an ellipsis rather than pushing the buttons off, and
   its unsaved dot in ConceptLine's red: 4.9:1 on the light bar, 6.4:1 on the dark one. In Studio the
   dot is here, beside the name, and not on Save as well. */
#fileName:not([hidden]) {
  display: flex;
  flex: 0 1 auto;
  align-items: center;
  gap: 8px;
  min-width: 0;
  padding: 0 8px;
  margin-left: 2px;
}
#fileName .name {
  overflow: hidden;
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: -0.005em;
  white-space: nowrap;
  text-overflow: ellipsis;
}
#fileName .dot { display: none; flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--redline); }
#fileName.unsaved .dot { display: block; }

/* Share, outlined, and Save, the one orange button, both with their words. Navy on the orange
   (5.6:1), and the darker navy on the deeper orange of a hover or a press, where the navy is 4.4:1. */
#bar button.ghost,
#bar button.primary {
  width: auto;
  grid-auto-flow: column;
  gap: 7px;
  padding: 0 14px 0 11px;
  font: inherit;
  font-weight: 600;
  transition: transform var(--press-dur) var(--ease), background-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
#bar button.ghost i,
#bar button.primary i { display: flex; }
#bar button.ghost svg,
#bar button.primary svg { width: 17px; height: 17px; }
#bar button.ghost { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 30%, transparent); }
#bar button.primary {
  margin-left: 6px;
  background: var(--sn-orange);
  color: var(--on-active);
  box-shadow: 0 1px 2px rgb(38 50 65 / 0.2);
}
@media (hover: hover) and (pointer: fine) {
  #bar button.primary:hover:not(:disabled) { background: var(--sn-orange-deep); color: var(--sn-navy-deep); }
}
#bar button.primary:active:not(:disabled) { background: var(--sn-orange-deep); color: var(--sn-navy-deep); }
#bar button.ghost:active:not(:disabled),
#bar button.primary:active:not(:disabled) { transform: scale(var(--press-scale)); }
#bar .topBar button.primary::after { content: none; }

/* ---- Studio's rail and bars (redesign R4) ---- */

/* With a drawing open in Studio the tools are a rail on the left of the drawing (#rail), the armed
   tool's options a pop-out beside its rail button (#opts, with its pointer #optsPtr; redesign R4b,
   SiteNotePro, 1 November 2026), and the page jobs with Compress, the zoom, the page and the status
   line a small bar at the foot of the drawing (.pageBar), until R5's sidebar and pill take those.
   All of them are in #bar, so the matrix and the keys find the same buttons, and are fixed to the
   window rather than laid out in it; #bar sits above the drawings for them (a sticky box is a
   stacking context, so its z-index is theirs).
   The rail and the bottom bar never cover the drawing (SiteNotePro, 31 October 2026): the drawings
   start right of the rail's strip (#main's padding) and end over the bottom bar's strip, which is as
   tall as that bar with Edit's knobs in it (placeFloats() in app.js). There is no strip at the top:
   the options are at the side, so the drawing starts under the top bar, and arming a tool never
   moves it. The pop-out floats over the drawing beside the rail, with a mouse as on a tablet
   (SiteNotePro, 1 November 2026: "the window can pop over the PDF sometimes, that's fine"): it goes
   when drawing starts and comes back with a press on the tool, unless it is pinned (app.js). The
   rail is see-through, over nothing but canvas; the pop-out and the bottom bar carry words and are
   the plain surface, so reduced transparency has nothing to change on them. In split view the rail
   is shared, once, at the left, the pop-out follows the shared tool, and the bottom bar sits in the
   active pane's strip, centred on it. The switch is the R2 one, word for word. */
#rail, #railHint, #opts, #optsPtr { display: none; }
/* Edit's colour and thickness, which join the bar at the foot in Studio (renderOpts() in app.js), are
   never part of a phone's toolbar, which that bar is there. */
.pageBar > .optGroup { display: none; }
@media (min-width: 700px) and (min-height: 500px), (min-width: 700px) and (pointer: fine), (min-width: 700px) and (pointer: none) {
  :root {
    /* The top bar and the rule under it. */
    --bar-studio: 53px;
    --rail-btn: 40px;
    /* The strip of canvas the rail sits in: the rail, its padding, and a gap either side. */
    --rail-room: calc(var(--rail-btn) + 30px);
    /* The rail's box: its buttons, 5 px of padding and a 1 px edge each side. */
    --rail-w: calc(var(--rail-btn) + 12px);
    /* The pop-out (R4b): 116 px holds Measure's "Calibrate…" whole (112 cut it), and two swatches or
       two presets to a row. The gap leaves room for its pointer between it and the rail. It floats
       over the drawing, with no room kept for it, with a mouse as on a tablet: --opts-float says so to
       app.js, which asks the stylesheet rather than the browser. */
    --opts-w: 116px;
    --opts-gap: 14px;
    --opts-x: calc(10px + var(--rail-w) + var(--opts-gap));
    --opts-float: yes;
  }
  body.hasDrawing #bar { z-index: 30; }
  body.hasDrawing #main { padding-left: var(--rail-room); }
  /* The drawing, and the pane's toasts and busy bar with it, from the pane's top (on a 4 px step,
     --opts-strip, placeFloats() in app.js) to the bottom bar's strip. Until placeFloats() has
     measured that, a bar's height and a margin. */
  body.hasDrawing .paneBody > :is(.container, .paneNotes) {
    top: var(--opts-strip, 0px);
    bottom: var(--foot-strip, calc(var(--btn) + 20px));
  }
  /* Find stays under the top bar, at the top right of the drawing. */
  body.hasDrawing #findbar { margin-top: var(--find-top, 0px); }

  /* -- the rail -- */
  body.hasDrawing #rail {
    position: fixed;
    z-index: 1;
    left: 10px;
    top: var(--bar-studio);
    bottom: 0;
    /* Centred down the drawing, and scrolled inside itself on a window too short for it. */
    height: max-content;
    max-height: calc(100% - var(--bar-studio) - 20px);
    margin-block: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 5px;
    border: 1px solid var(--line);
    border-radius: 14px;
    background: var(--glass);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    box-shadow: var(--shadow-card);
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: none;
  }
  #rail::-webkit-scrollbar { display: none; }
  /* A short window with a mouse (a laptop with its browser's bars, under 720 px high): smaller buttons
     and tighter rules, so every tool shows at 1366x600 and 1024x600. Where even that does not fit,
     the rail scrolls, and fades at the edge it scrolls from (placeTile() in app.js marks it). */
  @media (max-height: 720px) and (pointer: fine) {
    :root { --rail-btn: 32px; --rail-w: calc(var(--rail-btn) + 10px); }
    body.hasDrawing #rail { gap: 0; padding: 4px; }
    .railRule { margin: 2px 0; }
    .railTile { left: 4px; }
  }
  #rail.more-below { -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent); }
  #rail.more-above { -webkit-mask-image: linear-gradient(to top, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to top, #000 calc(100% - 28px), transparent); }
  #rail.more-above.more-below { -webkit-mask-image: linear-gradient(#0000, #000 28px, #000 calc(100% - 28px), #0000); mask-image: linear-gradient(#0000, #000 28px, #000 calc(100% - 28px), #0000); }
  #bar #rail button {
    position: relative;
    z-index: 1;
    width: var(--rail-btn);
    height: var(--rail-btn);
    border-radius: 11px;
    transition: transform var(--press-dur) var(--ease), background-color var(--fast) var(--ease), color var(--fast) var(--ease);
  }
  /* The armed tool's orange is the tile under it, which moves; the button itself stays clear. */
  #bar #rail button.on,
  #bar #rail button.on:active:not(:disabled) { background: transparent; color: var(--on-active); }
  #bar #rail button:active:not(:disabled) { transform: scale(var(--press-scale)); }
  .railRule { flex: none; width: 22px; height: 1px; margin: 3px 0; background: var(--line); }
  /* Critically damped, so it settles on the tool without going past it; under reduced motion the
     data-fade rule makes this a cross-fade, and placeTile() fades it out and in at the new tool. */
  .railTile {
    position: absolute;
    left: 5px;
    top: 0;
    width: var(--rail-btn);
    height: var(--rail-btn);
    border-radius: 11px;
    background: var(--active);
    box-shadow: 0 1px 2px rgb(38 50 65 / 0.25), inset 0 1px 0 rgb(255 255 255 / 0.35);
    pointer-events: none;
    transition: transform var(--spring-dur) var(--spring);
  }
  /* What a tool is, beside it, once a mouse has rested on it for --hint-delay, or at once when the
     keyboard reaches it. Navy with white words, 13:1 and 8:1, in both themes. */
  body.hasDrawing #railHint {
    position: fixed;
    z-index: 2;
    left: var(--hint-x, 0px);
    top: var(--hint-y, 0px);
    display: flex;
    gap: 8px;
    align-items: baseline;
    padding: 5px 10px;
    border-radius: 7px;
    background: var(--sn-navy-deep);
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transform: translate(-4px, -50%);
    transition: opacity 120ms var(--ease), transform 160ms var(--ease);
  }
  #railHint span { font-weight: 400; color: rgb(255 255 255 / 0.75); }
  body.hasDrawing #railHint.on { opacity: 1; transform: translate(0, -50%); transition-delay: var(--hint-delay); }
  body.hasDrawing #railHint.on.now { transition-delay: 0ms; }

  /* -- the options: a pop-out at the armed tool (R4b) -- */
  /* Beside the rail, at the armed button's height and as tall as the tool's options, with a pointer
     towards the button. app.js (placeOpts()) sets its top and the pointer's, keeping the pop-out
     inside the window and moving it, never the pointer's target, when the window is short; on a
     window too short for it even then, it scrolls. Between two tools that both have options it
     glides to the new one on the spring; arriving or going, it fades and grows from the pointer. */
  body.hasDrawing #opts {
    position: fixed;
    z-index: 1;
    left: var(--opts-x);
    top: var(--opts-y, calc(var(--bar-studio) + 10px));
    width: var(--opts-w);
    max-height: var(--opts-max, calc(100% - var(--bar-studio) - 20px));
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 7px 6px 8px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface);
    box-shadow: var(--shadow-card);
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: none;
    transform-origin: 0 var(--ptr, 24px);
    transition: opacity 160ms var(--ease), transform var(--spring-dur) var(--spring), top var(--spring-dur) var(--spring);
  }
  #opts::-webkit-scrollbar { display: none; }
  /* Where it scrolls (a very short window), it fades at an edge there is more beyond, as the rail does
     (placeOpts() in app.js marks it). */
  #opts.more-below { -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent); }
  #opts.more-above { -webkit-mask-image: linear-gradient(to top, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(to top, #000 calc(100% - 28px), transparent); }
  #opts.more-above.more-below { -webkit-mask-image: linear-gradient(#0000, #000 28px, #000 calc(100% - 28px), #0000); mask-image: linear-gradient(#0000, #000 28px, #000 calc(100% - 28px), #0000); }
  /* It goes back towards the tool the way it came, and takes no clicks or keys while it is away
     (inert). */
  body.hasDrawing #opts.off { opacity: 0; transform: translateX(-6px) scale(0.97); pointer-events: none; }
  /* A first showing, or a window resized: straight to its place, with no glide (placeOpts()). */
  body.hasDrawing :is(#opts, #optsPtr).placing { transition-property: opacity, transform; }
  /* The pointer: a square turned to a point, the half towards the rail shown, its long edge laid
     over the pop-out's own edge so the two read as one. It fades with the pop-out. */
  body.hasDrawing #optsPtr {
    display: block;
    position: fixed;
    z-index: 2;
    left: calc(var(--opts-x) - 7px);
    top: calc(var(--ptr-y, -100px) - 8px);
    width: 16px;
    height: 16px;
    background: var(--surface);
    border-left: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    transform: rotate(45deg);
    clip-path: polygon(0 0, 0 100%, 100% 100%);
    pointer-events: none;
    transition: opacity 160ms var(--ease), top var(--spring-dur) var(--spring);
  }
  body.hasDrawing #opts.off + #optsPtr { opacity: 0; }
  /* The groups, each named over it in muted words (6:1 and more, never orange), two to a row for
     the shapes, swatches and presets. A group's name is shown, not read out a second time: each
     already has its name for a screen reader (index.html). */
  .optGroup { flex: 0 1 auto; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 2px; max-width: 100%; }
  .optGroup[hidden] { display: none; }
  .swatches { display: flex; align-items: center; gap: 2px; }
  #opts .optGroup { flex: none; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: none; }
  #opts .optGroup[hidden] { display: none; }
  #opts .swatches { display: contents; }
  #bar #opts button, #opts .anyColour { width: auto; }
  #opts .optGroup::before {
    grid-column: 1 / -1;
    padding: 0 4px 3px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 14px;
    color: var(--ink-mute);
  }
  #opts [data-opt="shapes"]::before { content: "Shape"; content: "Shape" / ""; }
  #opts [data-opt="measure"]::before { content: "Measure"; content: "Measure" / ""; }
  #opts [data-opt="colour"]::before { content: "Colour"; content: "Colour" / ""; }
  #opts [data-opt="weight"]::before { content: "Thickness"; content: "Thickness" / ""; }
  #opts [data-opt="size"]::before { content: "Text size"; content: "Text size" / ""; }
  #opts [data-opt="dash"]::before { content: "Line"; content: "Line" / ""; }
  #opts [data-opt="erase"]::before { content: "Eraser size"; content: "Eraser size" / ""; }
  .optRule { flex: none; width: 1px; height: 16px; margin: 0 3px; background: color-mix(in srgb, var(--line) 60%, transparent); }
  #opts .optRule { grid-column: 1 / -1; width: auto; height: 1px; margin: 3px 6px; }
  /* Measure's choices, and the Take-off with them: a list, one to a line, the words left. */
  #opts .optGroup:is([data-opt="measure"], [data-opt="summary"]) { grid-template-columns: minmax(0, 1fr); }
  #opts [data-opt="summary"] { margin-top: -8px; }
  /* The pin at the top right keeps the pop-out up while drawing; the first group's name shares its
     row. Pinned, it is the orange tile with the navy glyph, as an armed tool is (#bar button.on). */
  body.hasDrawing #opts { padding-top: 0; }
  #bar #opts .optsPin {
    display: grid;
    position: absolute;
    top: 0;
    right: 0;
    width: var(--btn);
    height: var(--btn);
    color: var(--ink-mute);
  }
  #bar #opts .optsPin svg { width: 18px; height: 18px; }
  #bar #opts .optsPin.on { color: var(--on-active); }
  #opts > .optGroup:not([hidden]):not(.optGroup:not([hidden]) ~ *)::before { padding: 0 var(--btn) 0 4px; line-height: var(--btn); }
  #bar :is(#opts, .pageBar) button { border-radius: 8px; }
  #bar :is(#opts, .pageBar) button.worded { width: auto; min-width: var(--btn); grid-auto-flow: column; gap: 6px; padding: 0 10px 0 8px; font: inherit; font-weight: 600; }
  #bar :is(#opts, .pageBar) button.worded svg { width: 18px; height: 18px; }
  /* In the pop-out, after the rules both bars share so these win: Measure's words left, close enough
     for "Calibrate…" in 116 px, and Text's sizes centred. */
  #bar #opts button.worded { justify-content: start; gap: 5px; padding: 0 4px 0 7px; font-size: 12.5px; white-space: nowrap; }
  #bar #opts [data-opt="size"] button.worded { justify-content: center; padding: 0; }
  /* A short window with a mouse, as for the rail: the pop-out's buttons a little shorter, so the
     tallest (Shapes) still fits whole at 1366x600 with its pointer on the button (placeOpts()). */
  @media (max-height: 720px) and (pointer: fine) {
    #bar #opts button, #opts .anyColour { height: 28px; }
    body.hasDrawing #opts { gap: 6px; padding-bottom: 5px; }
    #opts .optGroup::before { padding-bottom: 1px; }
    #bar #opts .optsPin { width: 28px; height: 28px; }
    #opts > .optGroup:not([hidden]):not(.optGroup:not([hidden]) ~ *)::before { padding: 0 28px 0 4px; line-height: 28px; }
  }
  /* A choice that is on: the orange tile with the navy glyph, as the armed tool (5.6:1), never orange
     words or a thin orange ring on white. */
  #bar :is(#opts, .pageBar) button[aria-checked="true"]:not(.swatch) { background: var(--active); color: var(--on-active); }
  @media (hover: hover) and (pointer: fine) {
    #bar :is(#opts, .pageBar) button[aria-checked="true"]:not(.swatch):hover:not(:disabled) { background: var(--sn-orange-deep); }
  }
  #bar :is(#opts, .pageBar) button[aria-checked="true"]:not(.swatch):active:not(:disabled) { background: var(--sn-orange-deep); }
  #bar :is(#opts, .pageBar) button[aria-checked="true"]:focus-visible { outline-color: var(--sn-navy); outline-offset: -3px; }
  /* Colour swatches. The chosen one has a ring in the ink, with a gap of the bar's own colour. */
  .swatch span,
  .anyColour span {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--c);
    box-shadow: inset 0 0 0 1.5px var(--swatch-edge);
  }
  #bar :is(#opts, .pageBar) .swatch[aria-checked="true"] span,
  .anyColour.on span { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--ink); }
  /* Any colour: the browser's own picker, over the whole of a swatch that shows a colour wheel, or
     the colour picked from it once that is the one in use. */
  .anyColour {
    position: relative;
    display: grid;
    place-items: center;
    width: var(--btn);
    height: var(--btn);
    border-radius: 8px;
    cursor: pointer;
    transition: background var(--fast) var(--ease);
  }
  .anyColour span { background: conic-gradient(#c8423a, #f6921e, #e5d23a, #3aa55b, #0e63b3, #7a4cc2, #c8423a); }
  .anyColour.on span { background: radial-gradient(circle, var(--c) 0 52%, transparent 56%), conic-gradient(#c8423a, #f6921e, #e5d23a, #3aa55b, #0e63b3, #7a4cc2, #c8423a); }
  @media (hover: hover) and (pointer: fine) {
    .anyColour:hover { background: var(--hover); }
  }
  .anyColour:active { background: var(--press); }
  .anyColour:has(input:focus-visible) { outline: 2px solid var(--ring); outline-offset: 2px; }
  .anyColour:has(input:disabled) { opacity: 0.35; pointer-events: none; }
  #colourAny { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer; }
  .weight { width: 22px; height: var(--w); border-radius: 2px; background: currentColor; }
  /* The eraser's ring, the size picked, in Studio only: a phone's eraser is the point, as it was. */
  :root[data-erase-size] .viewer.erasing { cursor: var(--erase-cursor); }
  .spot { width: var(--d); height: var(--d); border-radius: 50%; background: currentColor; }

  /* -- the bar at the foot: page jobs, zoom and page, until R5 -- */
  body.hasDrawing #bar .pageBar {
    position: fixed;
    z-index: 1;
    bottom: var(--foot-bottom, 12px);
    left: var(--pane-x, 50%);
    width: max-content;
    max-width: var(--float-max, calc(100% - var(--rail-room) - 24px));
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 2px 0;
    padding: 3px 4px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface);
    box-shadow: var(--shadow-card);
    transform: translateX(-50%);
  }
  #bar .pageBar > .group { margin-left: 0; }
  #bar .pageBar > .group::before { content: none; }
  #bar .pageBar > .group:not(.forSite) + .group:not(.forSite) { margin-left: 12px; }
  body.hasDrawing #bar .pageBar > .optGroup:not([hidden]) { display: flex; margin-left: 12px; }
  /* The status line, at the right end of this bar (placeStatus() in app.js; at the side the pop-out
     has no room for it): as wide as its words, up to 28rem, where the bar has the room, and
     narrower where it has not, cut short with its whole text in the title. Its basis is 6rem, so how
     long it is never moves the bar onto another line. */
  body.hasDrawing #bar .pageBar > #status {
    flex: 1 1 6rem;
    min-width: 6rem;
    max-width: 28rem;
    margin-left: 12px;
    padding: 0 8px 0 0;
    line-height: 22px;
    text-align: left;
  }
  /* With nothing to say it takes no room, and the bar is only as wide as its buttons. */
  body.hasDrawing #bar .pageBar > #status:empty { display: none; }
}
/* Fingers on an iPad: the rail's buttons, and the zoom and page fields in the bar at the foot, are
   44 px, as every other target is. The phone's toolbar keeps its fields as they are until R6. */
@media (min-width: 700px) and (min-height: 500px), (min-width: 700px) and (pointer: fine), (min-width: 700px) and (pointer: none) {
  @media (pointer: coarse) {
    :root { --rail-btn: 44px; }
    body.hasDrawing #bar .pageBar :is(#zoom, #pageNum) { height: 44px; min-width: 44px; }
  }
}
/* A tablet: the pop-out 120 px wide, for two 44 px targets to a row. */
@media (min-width: 700px) and (min-height: 500px), (min-width: 700px) and (pointer: fine), (min-width: 700px) and (pointer: none) {
  @media (pointer: coarse) {
    :root { --opts-w: 120px; }
  }
}

/* Tooltips are native `title`: the bar needs overflow-x:auto to survive a half-screen
   window, and per spec that forces overflow-y to clip, so a CSS tooltip could never
   escape the 48px bar. */

/* ---- find bar ---- */

#findbar {
  grid-row: 2;
  grid-column: 1;
  justify-self: end;
  align-self: start;
  margin-right: 12px;
  /* Earlier in the DOM than .container, so it needs the z-index to sit on top of it.
     Grid items honour z-index without being positioned. */
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-top: 0;
  border-radius: 0 0 var(--sn-radius-lg) var(--sn-radius-lg);
  box-shadow: var(--shadow-card);
}
#findbar[hidden] { display: none; }

#findInput {
  width: 220px;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  font: inherit;
  color: var(--ink);
  background: var(--canvas); /* recessed field, in both themes */
}
#findInput:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; border-color: var(--ring); }
#findInput.no-match { border-color: var(--danger); background: rgb(200 66 58 / 0.08); }

#findCount {
  min-width: 62px;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--ink-mute);
}

#findbar button {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 0;
  border-radius: var(--sn-radius-lg);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background var(--fast) var(--ease);
}
#findbar button svg { width: 17px; height: 17px; pointer-events: none; }
@media (hover: hover) and (pointer: fine) {
  #findbar button:hover { background: var(--hover); }
}
#findbar button:active { background: var(--press); }
#findbar button:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }

#findHighlightAll {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--ink-mute);
  white-space: nowrap;
  cursor: pointer;
}
#findAll { accent-color: var(--ring); cursor: pointer; }

/* ---- document canvas ---- */

/* #main lays the panes out; with one pane that is indistinguishable from the single box
   this used to be. Each pane is a column: a slim bar (hidden unless split) over the body
   that holds the scrolling container. */
#main {
  grid-row: 2;
  grid-column: 1;
  min-height: 0; /* or the 1fr row grows to the content instead of scrolling it */
  /* And across: a grid item is as wide as its content at the least, so in split view a very long
     file name on a pane's bar made both panes wider than the window, and the Close button went
     off its right edge. */
  min-width: 0;
  display: flex;
}
/* Orientation is a flex-direction and nothing else. It must never reload a document, so
   there is deliberately no JavaScript that rebuilds anything when this class changes. */
#main.stacked { flex-direction: column; }

/* Class, not id, and that is the whole point: there can be two of these. */
.pane {
  position: relative;
  flex: 1 1 0;
  min-width: 0;  /* flex children default to min-content, which a PDF page would blow out */
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* ---- the slim per-pane bar ---- */

/* Hidden with one pane: there is nothing to tell apart, and a bar over a single drawing
   is just lost height. `.split` goes on #main only while a second pane exists. */
.paneBar { display: none; }
#main.split .paneBar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 6px 0 10px;
  height: 26px;
  flex: 0 0 auto;
  background: var(--bar);
  border-bottom: 1px solid var(--line);
  font-size: 12px;
  color: var(--ink-mute);
}

/* Which pane the toolbar is acting on. A left border rather than a tint: it reads at a
   glance without staining the drawing, and it survives both themes. Orange is a fill
   only — never text — so the NAME does not change colour, the marker does. */
#main.split .pane.active .paneBar {
  color: var(--ink);
  box-shadow: inset 3px 0 0 var(--active);
}
#main.split.stacked .pane.active .paneBar { box-shadow: inset 0 3px 0 var(--active); }

.paneName {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1 1 auto;
}
/* The same dot as on the Save button, before the name of a drawing with unsaved changes. */
.unsavedDot {
  display: none;
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  margin-right: -2px;
  border-radius: 50%;
  background: var(--redline);
}
.paneBar.unsaved .unsavedDot { display: block; }
/* Announced to a screen reader, shown to nobody: the marker and the dot are visual. */
.paneBar .activeWord,
.paneBar .unsavedWord {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

.paneClose {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--sn-radius-lg);
  background: none;
  color: var(--ink-mute);
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .paneClose:hover { background: var(--canvas); color: var(--ink); }
}
.paneClose:active { background: var(--canvas); color: var(--ink); }
.paneClose svg { width: 14px; height: 14px; }

/* ---- the divider ---- */

/* Only ever between two panes, so it lives in the DOM only while split. */
.divider {
  flex: 0 0 10px;
  position: relative;
  background: var(--line);
  cursor: col-resize;
  display: grid;
  place-items: center;
  touch-action: none; /* a finger drags the divider rather than scrolling the page */
}
#main.stacked .divider { cursor: row-resize; }

/* The orientation toggle rides in the divider rather than the toolbar, because
   requirement 22 allows exactly one new toolbar button and Split is it. */
/* Absolutely positioned so it does NOT widen the divider: the divider is a 10px grab
   strip, and a 22px button in the flow made it 22px of dead space between the drawings. */
.dividerBtn {
  position: absolute;
  width: 22px; height: 22px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bar);
  color: var(--ink-mute);
  cursor: pointer;
  padding: 0;
}
@media (hover: hover) and (pointer: fine) {
  .dividerBtn:hover { color: var(--ink); border-color: var(--ring); }
}
.dividerBtn:active { color: var(--ink); border-color: var(--ring); }
.dividerBtn svg { width: 14px; height: 14px; }

/* PDFViewer throws unless its container computes to position: absolute
   (pdf_viewer.mjs:7862), so the container cannot be the flex child that shares the pane
   with the bar. .paneBody takes that job and the container fills it. */
.paneBody {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
}

.container {
  position: absolute;
  inset: 0;
  overflow: auto;
  background: var(--canvas);
}
.container.covering { cursor: crosshair; }

/* The hand is its own tool now, so the cursor follows it and nothing else. */
.container.panning { cursor: grab; }
.container.grabbing, .container.grabbing * { cursor: grabbing; }
.container.covering .textLayer { pointer-events: none; }

/* The page's own text and annotations get in the way of drawing — an AutoCAD export
   carries its text as SHX annotations, so clicking anywhere popped up notes nobody asked
   for — but two tools genuinely need them, and they need DIFFERENT things:

     Select     text AND annotations: picking out a reference, opening a note
     Highlight  text only. Highlighting IS text selection, so switching this off the first
                time stopped the tool working at all.

   Only ever subtractive, so pdf.js's own layering is left as it built it.
   .annotationEditorLayer is never touched: that is where pdf.js's pen, text and stamp
   editors live and they need their pointer events. */
.container:not(.textlive) .textLayer { pointer-events: none; }
.container:not(.annotlive) .annotationLayer,
/* The layer itself is already transparent in pdf.js; the events live on its children —
   .popupTriggerArea and .popup are the ones an AutoCAD export fills a drawing with. */
.container:not(.annotlive) .annotationLayer * { pointer-events: none; }

/* PDF pages stay white in both themes (documents are white). The shadow — deeper in
   dark mode — lifts the white page off the dark canvas. */
.pdfViewer .page {
  border: 0;
  border-image: none;
  margin: 14px auto;
  background: #fff;
  box-shadow: var(--shadow-card);
}

#coverBox {
  position: fixed;
  z-index: 50;
  border: 1px solid var(--sn-blue);
  background: rgb(14 99 179 / 0.12); /* brand blueprint-blue tint */
  pointer-events: none;
}
/* Redact draws the marquee solid black, so it reads as "this will be blacked out". */
#coverBox.redact {
  border-color: #000;
  background: rgb(0 0 0 / 0.55);
}

/* Zoom picker and page field. Both REPLACE an <output> of similar width, so the toolbar
   does not grow — the budget is the reason these aren't extra buttons. */
#zoom, #pageNum {
  height: 26px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink);
  font: inherit;
}
#zoom { width: 4.1rem; padding: 0 2px; }
#pageNum { width: 2.7rem; padding: 0 3px; text-align: right; }
/* The spinners cost width and the field is three characters wide. */
#pageNum::-webkit-inner-spin-button, #pageNum::-webkit-outer-spin-button {
  appearance: none; margin: 0;
}
#pageNum { -moz-appearance: textfield; }
#zoom:focus-visible, #pageNum:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }
/* Wide enough for "/ 999" whatever the drawing, so the toolbar is the same width for a single sheet
   as for a set of 120. It grew with the count before, and at 1366px a set of ten sheets or more
   made the toolbar scroll (by 2px at 12 sheets and 9px at 120). Tabular figures keep "/ 1" and
   "/ 888" from changing width, so this is measured once. A set of a thousand sheets or more still
   widens it. */
#pageTotal { min-width: 4.1ch; color: var(--ink-mute); font-size: 12px; font-variant-numeric: tabular-nums; }

/* ---- shapes flyout ---- */

#shapeMenu,
#measureMenu,
#pagesMenu,
#markMenu {
  position: absolute;
  min-width: 9.5rem;
  z-index: 60;
  display: grid;
  gap: 2px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  box-shadow: var(--shadow-card);
  /* Script caps the height at the window below the button (placeFlyout in app.js), and the
     menu scrolls inside that on a short window or a phone held sideways. It never scrolls
     sideways: where the platform draws a scroll bar, a row can end up a little narrower than
     its words, and they wrap rather than bring a second bar. A flick that reaches either end
     stops there rather than going on to the page behind, as on the toolbar, and the bar is
     the thin one, so it takes as little of the menu as it can. */
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
}
#shapeMenu[hidden],
#measureMenu[hidden],
#pagesMenu[hidden],
#markMenu[hidden] { display: none; }
#shapeMenu button,
#measureMenu button,
#pagesMenu button,
#markMenu button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 6px 12px 6px 8px;
  border: 0;
  border-radius: var(--sn-radius-lg);
  background: transparent;
  color: var(--ink);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
#shapeMenu hr,
#measureMenu hr,
#markMenu hr { width: 100%; height: 1px; margin: 4px 0; border: 0; background: var(--line); }
#shapeMenu button i,
#measureMenu button i,
#pagesMenu button i,
#markMenu button i { display: flex; flex: none; }
#shapeMenu button svg,
#measureMenu button svg,
#pagesMenu button svg,
#markMenu button svg { width: var(--icon-size); height: var(--icon-size); }
@media (hover: hover) and (pointer: fine) {
  #shapeMenu button:hover,
  #measureMenu button:hover,
  #pagesMenu button:hover,
  #markMenu button:hover { background: var(--hover); }
}
#shapeMenu button:active,
#measureMenu button:active,
#pagesMenu button:active,
#markMenu button:active { background: var(--press); }
#pagesMenu button:disabled { opacity: 0.45; cursor: not-allowed; }
#pagesMenu button:focus-visible,
#measureMenu button:focus-visible,
#markMenu button:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
#shapeMenu button:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
/* Orange fill with a navy glyph — white on orange is 2.3:1 and fails. */
#shapeMenu button[aria-checked="true"],
#measureMenu button[aria-checked="true"] { background: var(--active); color: var(--on-active); }
/* The mark's menu. `hidden` loses to the display above, so it is restated: Account is hidden while
   the paid side is off, and Download the app in the installed app. On the welcome screen Download
   the app is the first screen's to say, so the menu leaves it out until a drawing is open. Rows a
   finger can hit, on a touch screen. */
#markMenu button[hidden] { display: none; }
body:not(.hasDrawing) #markMenu [data-act="install"] { display: none; }
#markMenu button { min-height: 36px; }
@media (pointer: coarse) {
  #markMenu button { min-height: 44px; }
}

#shapeHint {
  position: fixed;
  z-index: 70;
  padding: 4px 8px;
  border-radius: 6px;
  background: var(--sn-navy);
  color: #fff;
  font-size: 11px;
  line-height: 1.4;
  white-space: nowrap;
  pointer-events: none;   /* never in the way of the very clicks it describes */
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.25);
}
#shapeHint[hidden] { display: none; }

#notice {
  position: fixed;
  z-index: 71;
  display: grid;
  gap: 2px;
  max-width: 280px;
  padding: 8px 12px;
  border-left: 4px solid var(--sn-orange);
  border-radius: 6px;
  background: var(--sn-navy);
  color: #fff;
  font-size: 12px;
  line-height: 1.4;
  pointer-events: none;   /* never in the way of the next click */
  box-shadow: 0 4px 14px rgb(0 0 0 / 0.3);
}
#notice strong { font-size: 13px; }
#notice[hidden] { display: none; }

/* ---- what a pane is doing, and what came of it (busy() and toast() in app.js) ---- */

/* Over the pane's drawing, beside the container rather than in it: pdf.js needs the container
   absolutely positioned with nothing in it but the viewer. Above the welcome screen (#drop, 10),
   which shares the grid cell, so a file that will not open is said there too; under the find bar
   (40) and the flyouts. Every pointer goes through to the drawing; only the buttons take clicks. */
.paneNotes {
  position: absolute;
  inset: 0;
  z-index: 11;
  overflow: hidden;
  pointer-events: none;
}

/* A thin bar across the top while an operation runs. It and the chip appear only after 300ms,
   so most page operations show nothing; the wait is an animation-delay rather than a timer
   because pdf-lib's save holds the main thread, and both animate only opacity and transform,
   which the browser can go on drawing while it does. */
.paneProgress {
  display: none;
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 3px;
  overflow: hidden;
  background: rgb(246 146 30 / 0.25);
}
.paneNotes.working .paneProgress { display: block; animation: paneWait 150ms var(--ease) 300ms both; }
.paneProgress::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 40%;
  background: var(--sn-orange);
  animation: paneSweep 1.2s linear infinite;
}
@keyframes paneWait { from { opacity: 0; } to { opacity: 1; } }
@keyframes paneSweep { from { transform: translateX(-100%); } to { transform: translateX(250%); } }
@keyframes paneToastIn { from { opacity: 0; transform: translate(-50%, -4px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* The pane's one message: the chip while it works, then a failure, a result or a hint in the same
   place. The same navy and orange as #notice. Empty, the toast stays in the page with nothing to
   show, so its live regions are already there when words arrive in them. */
.paneToast,
.paneChip {
  position: absolute;
  /* Below the find bar while it is open over this pane (placeNotes() in app.js). */
  top: var(--notes-top, 12px);
  /* The middle of the part of the pane that is in the window, which is all of it but on a phone
     with Find open (placeNotes() in app.js). */
  left: var(--notes-x, 50%);
  transform: translateX(-50%);
  width: max-content;
  max-width: min(460px, calc(var(--notes-w, 100%) - 24px));
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 6px 6px 12px;
  border-left: 4px solid var(--sn-orange);
  border-radius: 8px;
  background: var(--sn-navy);
  color: #fff;
  font-size: 13px;
  line-height: 1.4;
  box-shadow: 0 4px 14px rgb(0 0 0 / 0.3);
  animation: paneToastIn 150ms var(--ease) both;
}
.paneToast.off { padding: 0; border: 0; background: none; box-shadow: none; animation: none; }
/* The chip: smaller, and only after the same wait as the bar. Hidden with it while the save
   dialog is open (holdBusy), and shown again after the wait once it closes. */
.paneChip {
  padding: 3px 12px;
  border-left-width: 0;
  border-radius: 999px;
  font-size: 12px;
  animation: paneWait 150ms var(--ease) 300ms both;
}
.paneChip[hidden],
.paneNotes.held .paneProgress,
.paneNotes.held .paneChip { display: none; }
/* The --redline token of the dark theme: it reads on navy in both themes, where the light one does not. */
.paneToast.err { border-left-color: #ef8a82; }
/* pre-line: a Save's result and the one-time line about shapes are two paragraphs (sayWritten()). */
.toastText { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; white-space: pre-line; }
.paneToast button { pointer-events: auto; flex: 0 0 auto; font: inherit; cursor: pointer; }
.paneToast button[hidden] { display: none; }
.toastAction {
  height: 30px;
  padding: 0 12px;
  border: 1px solid var(--sn-orange);
  border-radius: var(--sn-radius-lg);
  background: var(--sn-orange);
  color: var(--sn-navy);
  font-weight: 600;
}
@media (hover: hover) and (pointer: fine) {
  .toastAction:hover { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
}
.toastAction:active { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
/* The second of two (the plan note's See plans): the same size, in outline, so the first reads as
   the one to press. White on the navy is 13:1; the orange is only its outline, never its words. */
.toastAction.also { background: none; color: var(--note-ink); border-color: var(--active); }
@media (hover: hover) and (pointer: fine) {
  .toastAction.also:hover { background: var(--note-hover); border-color: var(--active); }
}
.toastAction.also:active { background: var(--note-hover); border-color: var(--active); }
.toastClose {
  width: 30px; height: 30px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--sn-radius-lg);
  background: none;
  color: var(--note-ink);
}
@media (hover: hover) and (pointer: fine) {
  .toastClose:hover { background: var(--note-hover); }
}
.toastClose:active { background: var(--note-hover); }
.toastClose svg { width: 16px; height: 16px; }
.paneToast button:focus-visible { outline: 2px solid var(--note-ink); outline-offset: 1px; }
/* A finger needs a bigger target than a mouse. */
@media (pointer: coarse) {
  .toastClose { width: 44px; height: 44px; }
  .toastAction { height: 44px; }
}

/* The "new version" note. Bottom centre, clear of the toolbar and of where people draw, in
   the same navy and orange as #notice, but it takes clicks: it has buttons. */
#updateNote {
  position: fixed;
  z-index: 72;
  left: 50%;
  bottom: max(16px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  width: max-content;
  max-width: min(460px, calc(100vw - 24px));
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  padding: 10px 12px 10px 14px;
  border-left: 4px solid var(--sn-orange);
  border-radius: 8px;
  background: var(--sn-navy);
  color: #fff;
  font-size: 13px;
  line-height: 1.4;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.35);
}
#updateNote[hidden] { display: none; }
#updateNote p { flex: 1 1 220px; margin: 0; }
#updateNote .actions { display: flex; gap: 8px; margin-left: auto; }
#updateNote button {
  height: 32px;
  padding: 0 14px;
  border: 1px solid rgb(255 255 255 / 0.35);
  border-radius: var(--sn-radius-lg);
  background: transparent;
  color: #fff;
  font: inherit;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  #updateNote button:hover { background: rgb(255 255 255 / 0.12); }
}
#updateNote button:active { background: rgb(255 255 255 / 0.12); }
#updateNote button.primary { border-color: var(--sn-orange); background: var(--sn-orange); color: var(--sn-navy); font-weight: 600; }
@media (hover: hover) and (pointer: fine) {
  #updateNote button.primary:hover { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
}
#updateNote button.primary:active { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
#updateNote button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* Everything the eraser touches is hit-tested in script — Konva programmatically, pdf.js's
   editors against their own rects — precisely so their layers can stay pointer-transparent
   and pdf.js's handlers never fire underneath us.
   The pointer is an eraser over a ring the size of what it rubs out, its hot spot in the ring.
   It was `cell`, a thick plus, which read as "add" rather than "rub out" (reported). */
/* The eraser with its small ring at the point. In Studio, with a size picked in the options bar, a
   ring that size instead (--erase-cursor, set from app.js, in Studio's rules below). */
.viewer.erasing { cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cg fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg transform='translate(12 0) scale(.8)'%3E%3Cpath d='M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21' stroke='%23fff' stroke-width='5'/%3E%3Cpath d='m5.082 11.09 8.828 8.828' stroke='%23fff' stroke-width='5'/%3E%3Cpath d='M21 21H8a2 2 0 0 1-1.42-.587l-3.994-3.999a2 2 0 0 1 0-2.828l10-10a2 2 0 0 1 2.829 0l5.999 6a2 2 0 0 1 0 2.828L12.834 21' stroke='%231b2530' stroke-width='2'/%3E%3Cpath d='m5.082 11.09 8.828 8.828' stroke='%231b2530' stroke-width='2'/%3E%3C/g%3E%3Ccircle cx='8' cy='24' r='6' stroke='%23fff' stroke-width='3.5'/%3E%3Ccircle cx='8' cy='24' r='6' stroke='%231b2530' stroke-width='1.5'/%3E%3C/g%3E%3C/svg%3E") 8 24, auto; }

/* ---- shape overlay ---- */

/* Last child of .page, so it sits above the text and editor layers. Transparent to the
   pointer until the Shapes tool is armed, or it would swallow text selection and every
   pdf.js markup tool. */
.shapeLayer { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.viewer.shaping .shapeLayer { pointer-events: auto; }
.viewer.shaping .shapeLayer canvas { cursor: crosshair; }

/* ---- Edit tool overlay ---- */

/* One SVG per page, under the shape layer so ConceptLine's own markup stays on top of the outlines.
   It never takes the pointer itself: the Edit tool hit-tests the drawing in script, so pdf.js's
   text and annotation layers underneath are never reached either. Nor do the handles' grips: a
   line's handles are hit-tested in script as well, nearest first where small grips overlap, and
   the pane's scroller takes the pointer that shows which way the one under it pulls, so the
   pointer always says what a press there would do. Pages are white in both themes, so the colours
   are fixed rather than themed. The outlines are cut off at the page's edge, and the handles, in
   an SVG of their own, are not: they stand just outside what is picked, so off the page wherever
   it reaches the edge. That SVG is raised over the pages after its own, which would otherwise
   paint over a handle that reaches into the next page while its grip still took presses there;
   it is raised over ConceptLine's shapes on its own page too, but only the handles are, never the
   outlines. */
.editLayer { position: absolute; inset: 0; pointer-events: none; }
.editLayer svg { position: absolute; inset: 0; display: block; width: 100%; height: 100%; overflow: hidden; }
.editLayer svg.editOver { overflow: visible; z-index: 1; }
.editSel { fill: rgb(14 99 179 / 0.1); stroke: var(--sn-blue); stroke-width: 1.5; stroke-linejoin: round; }
/* The selection being dragged or stretched: an outline only, so the drawing under it shows where
   it will go. Stretched, the outline is scaled by its transform, and its line should not be. */
.editSel.ghost { fill: none; stroke-dasharray: 4 3; vector-effect: non-scaling-stroke; }
.editHover { fill: none; stroke: var(--sn-blue); stroke-width: 1.5; stroke-linejoin: round; }
/* Grey, and dashed as well, so it does not rest on telling two colours apart: 3.9:1 on white. */
.editHover.grey { stroke: #7c8691; stroke-dasharray: 4 3; }
.editBox { fill: rgb(14 99 179 / 0.08); stroke: var(--sn-blue); stroke-width: 1; stroke-dasharray: 4 3; }
.editHandle { fill: #fff; stroke: var(--sn-blue); stroke-width: 1.5; }
.editGrip { fill: transparent; }
/* The pointer a handle shows is set on the scroller (setPull in app.js), and from there on the
   viewer and the pages' canvas wrappers, whatever the pointer is over. Each page's own default
   stops the change at the page: inherited further, it restyled every span of every text layer. */
.viewer.editing, .viewer.editing .page { cursor: default; }
.viewer.editing .canvasWrapper.editHot { cursor: pointer; }
.container.pull-ns, .container.pull-ns > .viewer.editing, .container.pull-ns .viewer.editing .canvasWrapper { cursor: ns-resize; }
.container.pull-ew, .container.pull-ew > .viewer.editing, .container.pull-ew .viewer.editing .canvasWrapper { cursor: ew-resize; }
.container.pull-nwse, .container.pull-nwse > .viewer.editing, .container.pull-nwse .viewer.editing .canvasWrapper { cursor: nwse-resize; }
.container.pull-nesw, .container.pull-nesw > .viewer.editing, .container.pull-nesw .viewer.editing .canvasWrapper { cursor: nesw-resize; }
.container.pull-end, .container.pull-end > .viewer.editing, .container.pull-end .viewer.editing .canvasWrapper { cursor: move; }

/* The edit box a piece of text is retyped in (app.js's placeTyping): laid over the words in the
   scroller's own coordinates, so it scrolls with the page, and turned with them by its transform.
   White with the selection's blue round it, on the white page in both themes. */
.editType {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 5;
  transform-origin: 0 0;
  box-sizing: content-box;
  margin: 0;
  padding: 0 2px;
  border: 0;
  border-radius: 0;
  outline: 1.5px solid var(--sn-blue);
  background: #fff;
  color: #000;
  font-family: Helvetica, Arial, sans-serif;
  box-shadow: 0 1px 6px rgb(0 0 0 / 0.25);
}
/* A paragraph's box (Edit, stage E10): it wraps its words as it is typed into, and grows
   downward to hold them, so it never scrolls or shows a handle of its own. */
.editPara {
  display: block;
  resize: none;
  overflow: hidden;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
/* Done beside a paragraph's box on a phone or tablet, where the return key is a line break. */
.editDone {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 5;
  min-width: 44px;
  min-height: 44px;
  padding: 0 12px;
  border: 0;
  border-radius: 8px;
  background: var(--sn-blue);
  color: #fff;
  font: inherit;
  font-weight: 600;
  box-shadow: 0 1px 6px rgb(0 0 0 / 0.25);
}

/* "Edit every copy" above what is picked, while it is all in one block drawn more than once
   (app.js's placeEvery()): a toggle, its box ticked while it is on. 44 px tall on a phone or tablet. */
.editEvery {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 30px;
  padding: 0 10px 0 8px;
  border: 1px solid var(--sn-blue);
  border-radius: 8px;
  background: #fff;
  color: #000;
  font: inherit;
  font-size: 13px;
  white-space: nowrap;
  box-shadow: 0 1px 6px rgb(0 0 0 / 0.25);
  cursor: pointer;
}
/* `hidden` loses to the display above, so it is restated: without it the control never hid, and stood
   over the drawing after its selection had gone, taking a click meant for the page (found in R4). */
.editEvery[hidden] { display: none; }
.editEvery.coarse { min-height: 44px; padding: 0 14px 0 12px; font-size: 15px; }
.editEvery::before {
  content: "";
  box-sizing: border-box;
  width: 14px;
  height: 14px;
  border: 1.5px solid var(--sn-blue);
  border-radius: 3px;
  background: #fff;
}
.editEvery[aria-pressed="true"] { background: var(--sn-blue); color: #fff; border-color: var(--sn-blue); }
.editEvery[aria-pressed="true"]::before {
  border-color: #fff;
  background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M3 7.5l2.5 2.5L11 4.5' fill='none' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E") center / 12px no-repeat;
}
.editEvery[aria-disabled="true"] { border-color: #999; color: #555; cursor: default; }
.editEvery[aria-disabled="true"]::before { border-color: #999; }
.editEvery:focus-visible { outline: 2px solid var(--sn-blue); outline-offset: 2px; }

/* ---- the Studio and On site switch ---- */

/* Studio (B in specs/redesign-studio-and-site.md) is the layout on computers and tablets, On site (C)
   the layout on phones. This is where one turns into the other, written once here and used by
   every stage that lays out the two differently: the first screen now (R2), the top bar, rail and
   pages sidebar (R3 to R5), and the phone's dock and sheets when R6 settles it on devices.
   Studio is a window 700 px wide or more, unless it has a coarse pointer and is under 500 px high:
   a phone held sideways is wide but short, and is On site. An iPad either way up is Studio.
   Media queries cannot hold a variable, so a stage that needs the switch copies this query exactly,
   word for word: the firstScreen suite finds every rule in this file with a width of 700px and fails if
   one differs from it. In the old comma form rather than with "not", which iPads before iPadOS 16.4
   do not read. --look says which side of the switch a window is on, for anything that needs to ask
   from a script, and for the checks. */
:root { --look: site; }
@media (min-width: 700px) and (min-height: 500px), (min-width: 700px) and (pointer: fine), (min-width: 700px) and (pointer: none) {
  :root { --look: studio; }
}

/* ---- the first screen ---- */

#drop {
  grid-row: 2;
  grid-column: 1;
  /* .container is absolutely positioned (PDFViewer insists), and a positioned element
     paints over a static one whatever the DOM order — so without this the empty state
     sits UNDER the viewer and the terms link stops being clickable. It has been
     unclickable once before; that is why this is measured in the checks. */
  z-index: 10;
  /* For the drawing grid behind the words (::before). */
  position: relative;
  display: grid;
  pointer-events: none;
  transition: background var(--fast) var(--ease);
  /* Without these a welcome taller than the window grew the grid's second row past the foot of
     the window, pane and all, on a phone held sideways. Now the row keeps its size and the
     overlay scrolls instead (see the short-window rules below), logo first. */
  min-height: 0;
  overflow-y: auto;
}
#drop.hide { display: none; }

/* The canvas's faint drawing grid, 8 px squares in 40 px ones, fading out from the picture. Lines
   of the ink at 5%, so it is navy on the light canvas and pale on the dark one, and the words over
   it keep their contrast (the firstScreen suite measures them). */
#drop::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  --grid: color-mix(in srgb, var(--ink) 5%, transparent);
  background:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px),
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 40px 40px, 40px 40px, 8px 8px, 8px 8px;
  background-position: center;
  -webkit-mask-image: radial-gradient(90% 45% at 50% 30%, #000 10%, transparent 90%);
  mask-image: radial-gradient(90% 45% at 50% 30%, #000 10%, transparent 90%);
}

/* On site, the phone's layout, comes first and Studio overrides it inside the switch below. One
   column the height of the window: the logo, the picture taking whatever height is left, then
   the words, and the buttons at the foot where a thumb is. The picture's row gives way first, so
   on a small phone the sheets get smaller rather than the words being pushed off the screen; with
   no room left at all they go (the container rule after .sheet). */
#drop .welcome {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) auto auto auto auto;
  grid-template-areas: "lockup" "art" "headline" "lede" "chips" "finish";
  justify-self: center;
  width: 100%;
  max-width: 30rem;
  min-height: 100%;
  /* Clear of the home indicator once R6 lets the page run under it (viewport-fit=cover); until
     then the inset is 0 and the 16 px is what shows. */
  padding: 10px 16px max(16px, env(safe-area-inset-bottom, 0px));
}
#drop .hero { display: contents; }
#drop .forStudio { display: none; }

/* The logo leads: the screen a client meets says whose tool this is before anything else.
   It is a link to the SiteNotePro site, so it opts back in to the pointer like the buttons. */
/* Its box is at least 44 px tall, as a finger needs, however small the logo is set below. */
#drop .lockup { grid-area: lockup; justify-self: start; min-height: 44px; pointer-events: auto; gap: 9px; padding: 2px 0; }
#drop .lockup .mark { width: 30px; height: 30px; }
#drop .lockup .rlword { font-size: 19px; }
#drop .lockup .by { font-size: 12px; }

#drop .headline {
  grid-area: headline;
  margin: 0 0 8px;
  font-size: 31px;
  line-height: 1.08;
  letter-spacing: -0.028em;
  font-weight: 600;
  color: var(--ink);
  text-wrap: balance;
}
#drop .lede { grid-area: lede; margin: 0; font-size: 16px; line-height: 1.5; color: var(--ink-mute); text-wrap: pretty; }

/* Two chips side by side and the install chip alone on a row under them, centred under the pair
   (SiteNotePro, 31 October 2026). A grid of two columns as wide as their chips, at the start of
   the line like the words above it, with the third chip across both columns: so it is centred
   under the two, not in the window. In the installed app it hides and the two are all there is. */
#drop .chips { grid-area: chips; display: grid; grid-template-columns: auto auto; justify-content: start; justify-items: start; gap: 6px; margin: 14px 0 0; padding: 0; list-style: none; }
#drop .chips li:nth-child(n+3) { grid-column: 1 / -1; justify-self: center; }
#drop .chips li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px 0 8px;
  border-radius: 999px;
  background: var(--fill);
  color: var(--ink);
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
}
/* `hidden` loses to the display above, so it is restated: the install chip goes in the installed app. */
#drop .chips li[hidden] { display: none; }
#drop .chips i { display: flex; color: var(--icon-accent); }
#drop .chips svg { width: 14px; height: 14px; }

#drop .finish { grid-area: finish; display: grid; gap: 10px; margin-top: 18px; }
#drop .actions { display: grid; gap: 10px; }
#drop .actions button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 50px;
  padding: 0 16px;
  border: 0;
  border-radius: 16px;
  background: var(--fill);
  color: var(--ink);
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  pointer-events: auto; /* the overlay lets a drag through; its buttons still take a click */
  transition: transform var(--press-dur) var(--ease), background-color var(--fast) var(--ease);
}
/* `hidden` loses to the display above, so it is restated: Download hides in the installed app. */
#drop .actions button[hidden] { display: none; }
#drop .actions i { display: flex; }
#drop .actions svg { width: 22px; height: 22px; }
/* The one orange button. Orange fill, navy words: white on orange is 2.3:1 and fails. */
#drop .actions button.primary {
  height: 54px;
  font-size: 17px;
  background: var(--sn-orange);
  color: var(--on-active);
  box-shadow: 0 1px 2px rgb(38 50 65 / 0.2);
}
@media (hover: hover) and (pointer: fine) {
  #drop .actions button:hover { background: var(--hover); }
  #drop .actions button.primary:hover { background: var(--sn-orange-deep); color: var(--sn-navy-deep); }
}
#drop .actions button:active { background: var(--press); transform: scale(var(--press-scale)); }
/* The darker navy on the deeper orange: the navy is 4.4:1 there, the darker one 5.3:1. */
#drop .actions button.primary:active { background: var(--sn-orange-deep); color: var(--sn-navy-deep); transform: scale(var(--press-scale)); }
#drop .actions button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

/* The coming feature. Muted ink, never faded with an opacity, which would take it under 4.5:1. */
#drop .soonLine {
  margin: 4px 0 0;
  padding: 0;
  border: 0;
  color: var(--ink-mute);
  font-size: 12px;
  line-height: 1.45;
  text-align: center;
  text-wrap: balance;
}
/* "Soon" is a word on an orange tag, navy on the orange (5.6:1), so it is read out as well. */
.soonTag {
  display: inline-block;
  margin-right: 4px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--sn-orange);
  color: var(--on-active);
  font-size: 10.5px;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  vertical-align: 1px;
}

/* The overlay is pointer-events:none so a drag passes through to the viewer beneath.
   The link has to opt back in, or it is decoration — clicking it does nothing at all. */
/* Padded out to 44 px tall, so a finger can hit a line of 12 px type; the space above it is taken
   back with a negative margin, as the padding is what shows as the gap. */
#drop .terms {
  justify-self: center;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: -8px;
  padding: 0 8px;
  pointer-events: auto;
  color: var(--ink-mute);
  font-size: 12px;
  text-underline-offset: 3px;
}
@media (hover: hover) and (pointer: fine) {
  #drop .terms:hover { color: var(--ink); }
}
#drop .terms:active { color: var(--ink); }
#drop .terms:focus-visible { outline: 2px solid var(--ring); outline-offset: 3px; border-radius: 3px; }

/* The picture: two cards on the canvas, each holding a photo of a drawing on paper, laid as
   R2's two sheets were, sized to the room the column leaves them (cqw and cqh are the art box's
   width and height), never more than a phone's width needs. Each photo is cut to the card's
   proportions, so cover only trims a pixel's rounding; the white is for the moment before the
   photo arrives. Neither photo nor card is tinted or filtered: they are drawings on paper, in
   their own colours in both themes, as pages are. */
#drop .art {
  grid-area: art;
  position: relative;
  container: art / size;
  margin: 6px 0 14px;
  --sheet-shadow: 0 0 0 1px rgb(38 50 65 / 0.06), 0 2px 6px rgb(38 50 65 / 0.06), 0 24px 48px -16px rgb(38 50 65 / 0.22);
}
#drop .sheet {
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(250px, 64cqw, 98cqh);
  height: auto;
  aspect-ratio: 297 / 210;
  border-radius: 2px;
  overflow: hidden;
  background: #fff;
  box-shadow: var(--sheet-shadow);
}
#drop .sheet picture, #drop .sheet img { display: block; width: 100%; height: 100%; }
#drop .sheet img { object-fit: cover; }
#drop .sheet.back { transform: translate(-64%, -62%) rotate(-6deg); }
#drop .sheet.front { transform: translate(-38%, -38%) rotate(3deg); }
@container art (max-height: 52px) {
  #drop .sheet { display: none; }
}

/* A smaller phone, or a short or narrow window: the same screen set tighter, so it fits a phone
   320 px across, or a window 280 by 541, with the picture still in it. */
@media (max-height: 680px), (max-width: 359px) {
  #drop .welcome { padding-top: 6px; padding-bottom: max(12px, env(safe-area-inset-bottom, 0px)); }
  #drop .lockup .mark { width: 26px; height: 26px; }
  #drop .lockup .rlword { font-size: 17px; }
  #drop .lockup .by { font-size: 11px; }
  #drop .art { margin: 4px 0 8px; }
  #drop .headline { margin-bottom: 6px; font-size: 25px; }
  #drop .lede { font-size: 14.5px; line-height: 1.45; }
  #drop .chips { margin-top: 10px; gap: 5px; }
  #drop .chips li { height: 26px; font-size: 12px; }
  #drop .finish { gap: 8px; margin-top: 12px; }
  #drop .actions { gap: 8px; }
  #drop .actions button { height: 44px; font-size: 15px; border-radius: 13px; }
  #drop .actions button.primary { height: 48px; font-size: 16px; }
  #drop .actions svg { width: 20px; height: 20px; }
  #drop .soonLine { margin-top: 2px; font-size: 11.5px; }
}

/* Tighter again for the smallest: a window 280 or 320 px wide and 521 to 560 tall still shows the
   picture, as well as every word and both buttons, with nothing to scroll. */
@media (max-height: 560px) {
  #drop .welcome { padding-top: 4px; padding-bottom: max(10px, env(safe-area-inset-bottom, 0px)); }
  #drop .lockup .mark { width: 22px; height: 22px; }
  #drop .lockup .rlword { font-size: 15px; }
  #drop .lockup .by { font-size: 10.5px; }
  #drop .art { margin: 2px 0 6px; }
  #drop .headline { margin-bottom: 4px; font-size: 22px; }
  #drop .lede { font-size: 13.5px; line-height: 1.4; }
  #drop .chips { margin-top: 8px; gap: 4px; }
  #drop .chips li { height: 24px; padding: 0 7px 0 6px; font-size: 11px; gap: 4px; }
  #drop .chips svg { width: 13px; height: 13px; }
  #drop .finish { gap: 6px; margin-top: 10px; }
  #drop .actions { gap: 6px; }
  /* 44 px is the floor for a finger: the picture gives way before the buttons do. */
  #drop .actions button { height: 44px; font-size: 14.5px; border-radius: 12px; }
  #drop .actions button.primary { height: 44px; font-size: 15px; }
  #drop .actions svg { width: 18px; height: 18px; }
  #drop .soonLine { margin-top: 0; font-size: 11px; line-height: 1.4; }
  /* The link's 44 px is mostly padding, so it may sit a little into the gap above it, which
     leaves the picture more room. */
  #drop .terms { margin-top: -12px; font-size: 11.5px; }
}

/* A phone held sideways, or any short window that is not Studio: two columns, the logo, picture
   and words on the left and the buttons on the right, as there is width to spare and no height.
   Where even that is too tall (568 by 320) the overlay scrolls, so here the overlay takes the
   pointer: a swipe or the wheel over the text scrolls it, and where scroll bars are drawn, as for
   a mouse on Windows, its scroll bar works. Everywhere else the overlay lets the pointer through to
   the pane. A file dropped on it still goes where it always did: the drop is handled on the
   document, for the active pane. */
@media (orientation: landscape) and (max-height: 520px) {
  #drop { pointer-events: auto; }
  #drop .welcome {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto auto auto;
    grid-template-areas: "lockup finish" "art finish" "headline finish" "lede finish" "chips finish";
    column-gap: 24px;
    max-width: 46rem;
    padding-top: 6px;
    /* Clear of the notch and the rounded corners on either side once R6 sets viewport-fit=cover. */
    padding-left: max(16px, env(safe-area-inset-left, 0px));
    padding-right: max(16px, env(safe-area-inset-right, 0px));
  }
  #drop .art { margin: 2px 0 6px; }
  #drop .headline { font-size: 23px; }
  #drop .lede { font-size: 14px; }
  #drop .finish { align-self: center; margin-top: 0; }
}
/* A phone 320 px across or a window 280 px wide: a little less margin and smaller chips, so the
   first two chips still sit side by side, each on one line, which leaves the picture its room. */
@media (max-width: 340px) {
  #drop .welcome { padding-left: 12px; padding-right: 12px; }
  #drop .chips { gap: 4px; }
  #drop .chips li { height: 24px; padding: 0 6px 0 5px; font-size: 10.5px; gap: 4px; }
  #drop .chips svg { width: 12px; height: 12px; }
}
/* At 280 px the two ran 19 px past the column at 10.5 px, so here they are set a size smaller,
   drawn a little closer, with less margin round the block. */
@media (max-width: 300px) {
  #drop .welcome { padding-left: 10px; padding-right: 10px; }
  #drop .chips { gap: 3px; }
  #drop .chips li { padding: 0 5px 0 4px; gap: 3px; font-size: 10px; letter-spacing: -0.01em; }
  #drop .chips svg { width: 11px; height: 11px; }
}
/* A narrow window not much taller (280 or 320 px wide and up to 620 tall) may not hold it either,
   so the overlay takes the pointer there too, for the same reasons. */
@media (max-width: 340px) and (max-height: 620px) {
  #drop { pointer-events: auto; }
}

/* Studio, the layout on computers and tablets: two columns on the drawing grid, the words and
   buttons on the left and, on the right, the two sheets in the outline a PDF can be dropped in.
   The same query as the switch above, word for word. */
@media (min-width: 700px) and (min-height: 500px), (min-width: 700px) and (pointer: fine), (min-width: 700px) and (pointer: none) {
  /* Stronger than the On site rules that lay these out, so it holds whatever they say. */
  #drop .welcome .forSite { display: none; }
  #drop::before {
    -webkit-mask-image: radial-gradient(70% 80% at 65% 50%, #000 10%, transparent 90%);
    mask-image: radial-gradient(70% 80% at 65% 50%, #000 10%, transparent 90%);
  }
  #drop .welcome {
    grid-template-columns: minmax(0, 470px) minmax(0, 520px);
    grid-template-rows: auto;
    /* At least the window's height, and as tall as the words when they need more, so the overlay
       scrolls to them. Left to the overlay's grid, the block stayed the window's height and the
       words ran out of it. */
    height: max-content;
    grid-template-areas: none;
    column-gap: clamp(40px, 5vw, 72px);
    /* Safe, so a block taller than the window starts at the top, logo first, and scrolls, rather
       than being centred up under the toolbar where nothing can reach it. */
    align-items: safe center;
    justify-content: center;
    max-width: none;
    padding: 28px clamp(24px, 4vw, 56px);
  }
  #drop .hero { display: flex; flex-direction: column; align-items: flex-start; }

  /* The logo, small, above the headline. */
  #drop .lockup {
    gap: 7px;
    padding: 4px 11px 4px 5px;
    border-radius: 999px;
    background: var(--fill);
  }
  #drop .lockup .mark { width: 20px; height: 20px; }
  #drop .lockup .names { display: flex; align-items: baseline; gap: 6px; }
  #drop .lockup .rlword { font-size: 13px; }
  #drop .lockup .by { font-size: 12.5px; }

  #drop .headline { display: block; margin: 18px 0 12px; font-size: clamp(34px, 3.3vw, 46px); line-height: 1.06; letter-spacing: -0.03em; }
  #drop .lede { display: block; max-width: 430px; font-size: 16.5px; line-height: 1.55; letter-spacing: -0.005em; }

  #drop .finish { display: flex; flex-direction: column; align-items: flex-start; gap: 0; margin-top: 26px; }
  #drop .actions { display: flex; flex-wrap: wrap; gap: 12px; }
  #drop .actions button {
    height: 46px;
    padding: 0 18px 0 15px;
    border-radius: 12px;
    background: none;
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 24%, transparent);
    font-size: 14px;
  }
  #drop .actions button.primary { height: 46px; padding: 0 20px 0 16px; font-size: 15px; background: var(--sn-orange); box-shadow: 0 1px 2px rgb(38 50 65 / 0.2); }
  #drop .actions svg { width: 19px; height: 19px; }

  /* The key hint is for a mouse and a keyboard. A touch screen has neither to press. */
  #drop .keys { display: none; }
  @media (hover: hover) and (pointer: fine) {
    #drop .keys { display: flex; align-items: center; gap: 4px; margin: 12px 0 0; color: var(--ink-mute); font-size: 12px; }
  }
  #drop kbd {
    display: inline-grid;
    place-items: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    border-radius: 6px;
    background: var(--surface);
    box-shadow: inset 0 0 0 1px var(--line), 0 1px 0 var(--line);
    color: var(--ink-mute);
    font: inherit;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.02em;
  }

  /* Three lines under a rule: the two promises, then the coming feature. */
  #drop .facts {
    display: grid;
    gap: 10px;
    align-self: stretch;
    margin: 28px 0 0;
    padding: 16px 0 0;
    border-top: 1px solid var(--line);
    list-style: none;
  }
  #drop .facts li { display: flex; gap: 10px; align-items: flex-start; color: var(--ink-mute); font-size: 13px; line-height: 1.5; }
  #drop .facts i { display: flex; margin-top: 2px; color: var(--ink); }
  #drop .facts svg { width: 16px; height: 16px; }
  #drop .facts b { color: var(--ink); font-weight: 600; }
  #drop .soonLine { display: flex; gap: 6px; align-items: flex-start; margin: 10px 0 0; font-size: 13px; line-height: 1.5; text-align: left; text-wrap: wrap; }
  #drop .soonTag { flex: none; margin: 1.5px 0 0; }

  #drop .terms { margin-top: 20px; justify-self: auto; }

  /* The picture in the outline a PDF can be dropped in, square-ish, beside the words, and no taller
     than the window leaves it under the toolbar, less the padding round the block. (The overlay
     cannot be the container to measure against: size containment stops its grid growing to
     scroll.) */
  #drop .art {
    grid-area: auto;
    display: block;
    justify-self: center;
    width: min(100%, (100dvh - var(--bar-h) - 56px) * 1.05);
    aspect-ratio: 1.05;
    margin: 0;
    border: 1.5px dashed color-mix(in srgb, var(--ink) 26%, transparent);
    border-radius: 24px;
  }
  /* The sheets stay inside the outline whatever its shape: the back one reaches about 0.56 of its
     width above the middle once turned, the front one 0.42 below it, and the caption takes the
     bottom 60 px. A wide, short outline, as on a tablet held sideways, is limited by its height. */
  #drop .sheet { width: max(0px, min(76cqw, (50cqh - 10px) * 1.75, (50cqh - 62px) * 2.35)); }
  #drop .sheet.back { transform: translate(-60%, -72%) rotate(-5deg); }
  #drop .sheet.front { transform: translate(-44%, -44%) rotate(2.5deg); }
  #drop .cap {
    position: absolute;
    left: 50%;
    bottom: 22px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 14px 7px 10px;
    border-radius: 999px;
    background: var(--glass);
    -webkit-backdrop-filter: var(--glass-blur);
    backdrop-filter: var(--glass-blur);
    box-shadow: var(--shadow-card);
    color: var(--ink);
    font-weight: 600;
    white-space: nowrap;
  }
  #drop .cap i { display: flex; color: var(--icon-accent); }
  #drop .cap svg { width: 17px; height: 17px; }

  /* A short window, a laptop with a bookmarks bar above all (1366 by about 600 shown): the same
     screen set tighter so it still fits. Where even that is too tall the overlay takes the pointer,
     so the wheel, a swipe or a scroll bar scrolls it (see the sideways rule above for why). */
  @media (max-height: 640px) {
    #drop { pointer-events: auto; }
    #drop .welcome { padding-top: 10px; padding-bottom: 10px; }
    #drop .headline { margin: 12px 0 8px; font-size: clamp(30px, 2.6vw, 36px); }
    #drop .lede { font-size: 15.5px; line-height: 1.5; }
    #drop .finish { margin-top: 18px; }
    #drop .keys { margin-top: 8px; }
    #drop .facts { gap: 6px; margin-top: 16px; padding-top: 12px; }
    #drop .soonLine { margin-top: 6px; }
    #drop .terms { margin-top: 12px; }
  }

  /* A tablet held upright, or a narrower window: the picture goes under the words, in the room
     they leave, rather than squeezing both columns. The block may be taller than the window here,
     so the overlay takes the pointer and scrolls. */
  @media (max-width: 959px) {
    #drop { pointer-events: auto; }
    #drop .welcome {
      grid-template-columns: minmax(0, 470px);
      grid-template-rows: auto minmax(0, 1fr);
      row-gap: 32px;
    }
    #drop .art { width: min(100%, 440px); height: 100%; max-height: 420px; aspect-ratio: auto; align-self: center; }
  }
}

#drop.over {
  background: rgb(14 99 179 / 0.08);
  outline: 2px dashed var(--sn-blue);
  outline-offset: -14px;
}

/* A feature that is coming, greyed out but readable, as About shows it (the first screen sets
   its own after the Soon tag, above, and keeps the arrow cursor from here). It is in the muted ink
   rather than faded with an opacity, which would take it under 4.5:1: measured, 6.1:1 in About in
   the light theme and 5.5:1 in the dark. The dashed outline marks it as a placeholder rather than
   a button, and the arrow cursor says the same. Nothing listens for a click on it, on the first
   screen or in About. */
.soonLine {
  text-wrap: balance;
  margin: 8px 0 0;
  padding: 6px 10px;
  border: 1px dashed var(--ink-mute);
  border-radius: var(--sn-radius-lg);
  color: var(--ink-mute);
  font-size: 12px;
  line-height: 1.45;
  cursor: default;
}

/* ---- signature dialog ---- */

#textDlg,
#signDlg {
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-xl);
  background: var(--surface);
  box-shadow: 0 24px 60px -12px rgb(0 0 0 / 0.4), 0 8px 24px -8px rgb(0 0 0 / 0.25);
  color: var(--ink);
}
#textDlg::backdrop,
#signDlg::backdrop { background: var(--scrim); }
#textDlg form,
#signDlg form { display: grid; gap: 10px; padding: 22px; width: min(92vw, 400px); }
#textDlg h2,
#signDlg h2 { margin: 0; font-size: 16px; font-weight: 600; }
#textDlg label,
#signDlg label { font-size: 12px; color: var(--ink-mute); }

#signName {
  height: 62px;
  padding: 0 12px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  font-size: 30px;
  color: var(--ink);
  background: var(--canvas);
}
#signName:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; border-color: var(--ring); }

#signFonts { display: flex; gap: 6px; }
#signFonts button {
  flex: 1;
  height: 46px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  background: var(--canvas);
  color: var(--ink);
  font-size: 19px;
  cursor: pointer;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  #signFonts button:hover { background: var(--hover); }
}
#signFonts button:active { background: var(--press); }
#signFonts button.on { border-color: var(--sn-orange); border-width: 2px; }
#signFonts button:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }

#textDlg menu,
#signDlg menu,
#calDlg menu,
#summaryDlg menu { display: flex; justify-content: flex-end; gap: 8px; margin: 6px 0 0; padding: 0; }
#textDlg menu button,
#signDlg menu button,
#calDlg menu button,
#summaryDlg menu button {
  height: 34px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  #signDlg menu button:hover,
  #calDlg menu button:hover,
  #summaryDlg menu button:hover { background: var(--hover); }
}
#signDlg menu button:active,
#calDlg menu button:active,
#summaryDlg menu button:active { background: var(--press); }
/* Orange fill needs a navy glyph — white on orange is 2.3:1 (both themes). */
/* First in the DOM so Enter submits with it rather than with Cancel, last on screen so the
   buttons still read the way every other dialog's do. */
dialog menu button.primary { order: 1; }
#textDlg menu button.primary,
#signDlg menu button.primary,
#calDlg menu button.primary,
#summaryDlg menu button.primary { background: var(--sn-orange); border-color: var(--sn-orange); color: var(--on-active); font-weight: 600; }
@media (hover: hover) and (pointer: fine) {
  #textDlg menu button.primary:hover,
  #signDlg menu button.primary:hover,
  #calDlg menu button.primary:hover,
  #summaryDlg menu button.primary:hover { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
}
#textDlg menu button.primary:active,
#signDlg menu button.primary:active,
#calDlg menu button.primary:active,
#summaryDlg menu button.primary:active { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
#signDlg menu button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

/* ---- about dialog ---- */

#aboutDlg {
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-xl);
  background: var(--surface);
  box-shadow: 0 24px 60px -12px rgb(0 0 0 / 0.4), 0 8px 24px -8px rgb(0 0 0 / 0.25);
  color: var(--ink);
}
#aboutDlg::backdrop { background: var(--scrim); }
#aboutDlg form { display: grid; gap: 12px; padding: 24px; width: min(92vw, 460px); }
#aboutDlg .lockup .mark { width: 42px; height: 42px; }
#aboutDlg .lockup .rlword { font-size: 22px; }
#aboutDlg .lockup .by { font-size: 12px; }
#aboutDlg h2 { margin: 0; font-size: 17px; font-weight: 600; }
#aboutDlg p { margin: 0; }
#aboutDlg .lede { color: var(--ink); line-height: 1.55; }
#aboutDlg .small { font-size: 12px; color: var(--ink-mute); line-height: 1.5; }

/* The one thing in here that is a real safety point, so it gets the orange rule. */
#aboutDlg .warn {
  padding: 10px 12px;
  border-left: 3px solid var(--sn-orange);
  border-radius: 0 var(--sn-radius-lg) var(--sn-radius-lg) 0;
  background: rgb(246 146 30 / 0.10);
  font-size: 13px;
  line-height: 1.5;
}

#aboutDlg details { font-size: 13px; }
#aboutDlg summary { cursor: pointer; color: var(--ink-mute); padding: 4px 0; }
#aboutDlg summary:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: 4px; }
#aboutDlg ul { margin: 8px 0; padding-left: 20px; display: grid; gap: 4px; line-height: 1.45; }
#aboutDlg code { font-family: ui-monospace, monospace; font-size: 12px; }
#aboutDlg a { color: var(--link); }

#aboutDlg menu { display: flex; justify-content: flex-end; margin: 4px 0 0; padding: 0; }
#aboutDlg menu button {
  height: 34px;
  padding: 0 18px;
  border: 1px solid var(--sn-orange);
  border-radius: var(--sn-radius-lg);
  background: var(--sn-orange);
  color: var(--on-active);          /* navy on orange — white would be 2.3:1 */
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  #aboutDlg menu button:hover { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
}
#aboutDlg menu button:active { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
#aboutDlg menu button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  /* The motion tokens as well as the rule under them: the rule stops CSS transitions and
     animations, but a spring run from script (element.animate() with --spring-dur) is not a CSS
     animation and would still move. With these, it lands at once, and a press no longer pushes. */
  :root {
    --spring: linear(0, 1);
    --spring-dur: 0ms;
    --press-dur: 0ms;
    --press-scale: 1;
  }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  /* The cross-fade the spec asks for in place of a spring: opacity only, so whatever else was
     moving lands at once. It wins over the rule above, which would cut the fade to nothing, by
     being the more specific of the two. */
  [data-fade] {
    animation-duration: 0.01ms !important;
    transition: var(--fade) !important;
  }
  /* The rule above would spin the busy bar's sweep endlessly at a hundredth of a millisecond;
     still is what was asked for, so the bar is simply full while the operation runs. */
  .paneProgress::after { animation: none; width: 100%; }
}

/* Less see-through, when that is asked for: the floating material becomes the plain surface, with
   nothing blurred under it, in both themes. */
@media (prefers-reduced-transparency: reduce) {
  :root {
    --glass: var(--surface);
    --glass-blur: none;
  }
}

/* Text dialog: same shell as the signature one, with a textarea and a size box. The password
   box wears the same field. */
#textBody, #textSize, #passwordInput {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  resize: vertical;
}
#textSize { width: 6rem; }
#textBody:focus-visible, #textSize:focus-visible, #passwordInput:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }

/* A character the saved PDF can't show: said where it is typed, with the swap beside it. A
   wrong password is said the same way, under the box. */
#textWarn,
#wmWarn,
#passwordWrong {
  margin: 0;
  padding: 8px 10px;
  border-left: 3px solid var(--sn-orange);
  border-radius: 0 var(--sn-radius-lg) var(--sn-radius-lg) 0;
  background: rgb(246 146 30 / 0.10);
  color: var(--ink);
  font-size: 13px;
  line-height: 1.5;
}
#textWarn[hidden],
#wmWarn[hidden],
#passwordWrong[hidden] { display: none; }
#textWarn button,
#wmWarn button {
  margin-left: 4px;
  padding: 2px 10px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  #textWarn button:hover,
  #wmWarn button:hover { background: var(--hover); }
}
#textWarn button:active,
#wmWarn button:active { background: var(--press); }
#textWarn button:focus-visible,
#wmWarn button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

/* ---- touch ------------------------------------------------------------------ */

/* A finger has to DRAW, not pan. Nothing here declared touch-action, so the browser
   claimed every drag for scrolling and a stroke started on a page could never begin —
   pdf.js only sets it on its text layer and FreeText editors, not on ink, and our own
   shape/cover/redact/eraser surfaces had nothing at all.
   Select is the one tool where panning IS the intent, so the class follows the armed
   tool. pinch-zoom rather than none, because two fingers zooming is how you read a
   drawing on a tablet. */
.container.drawing { touch-action: pinch-zoom; }

/* Fingers need a bigger target than a mouse pointer. Scoped to coarse pointers on
   purpose: bumping --btn everywhere would push the desktop toolbar back over the
   ~1290px content budget it was just brought under. */
/* A 1366-wide laptop. Two pixels off every button is what keeps the whole toolbar, Download
   included, on screen without scrolling; a mouse does not miss them. */
@media (max-width: 1439px) and (pointer: fine) {
  :root { --btn: 34px; }
}

@media (pointer: coarse) {
  :root { --btn: 44px; }
  /* The knobs are dragged, not clicked, so they need the height more than the buttons. */
  #thick { height: 28px; }
}



/* ---- take-off summary ------------------------------------------------------- */

#summaryBody { max-height: 50vh; overflow-y: auto; margin-bottom: 4px; }
#summaryBody h3 {
  margin: 14px 0 4px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-mute);
}
#summaryBody h3:first-child { margin-top: 0; }
#summaryBody table { width: 100%; border-collapse: collapse; }
#summaryBody th {
  padding: 3px 0;
  font-weight: 400;
  text-align: left;
}
/* The numbers are the point, so they line up in their own column and read as figures. */
#summaryBody td {
  padding: 3px 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
#summaryBody tr + tr th, #summaryBody tr + tr td { border-top: 1px solid var(--line); }

/* The reminders to check a scale and the totals: quieter than the question and the figures, so
   they read as advice and not a warning, but full size and at the muted ink's 6:1. */
.measureCheck { margin: 0 0 4px; font-size: 13px; line-height: 1.45; color: var(--ink-mute); }
#summaryCheck { margin-top: 10px; }

/* ---- compress and download dialogs ---- */

/* The same shell as the signature dialog. */
#compressDlg,
#installDlg,
#stampDlg,
#shareDlg,
#passwordDlg,
#shapesDlg,
#fieldDlg,
#hiddenDlg {
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-xl);
  background: var(--surface);
  box-shadow: 0 24px 60px -12px rgb(0 0 0 / 0.4), 0 8px 24px -8px rgb(0 0 0 / 0.25);
  color: var(--ink);
}
#compressDlg::backdrop,
#installDlg::backdrop,
#stampDlg::backdrop,
#shareDlg::backdrop,
#passwordDlg::backdrop,
#shapesDlg::backdrop,
#fieldDlg::backdrop,
#hiddenDlg::backdrop { background: var(--scrim); }
#compressDlg form,
#installDlg form,
#stampDlg form,
#shareDlg form,
#passwordDlg form,
#shapesDlg form,
#fieldDlg form,
#hiddenDlg form { display: grid; gap: 12px; padding: 22px; width: min(92vw, 440px); }
#compressDlg h2,
#installDlg h2,
#stampDlg h2,
#shareDlg h2,
#passwordDlg h2,
#shapesDlg h2,
#fieldDlg h2,
#hiddenDlg h2 { margin: 0; font-size: 16px; font-weight: 600; }
#stampDlg h3 { margin: 0; font-size: 14px; font-weight: 600; }
#compressDlg p,
#installDlg p,
#stampDlg p,
#shareDlg p,
#passwordDlg p,
#shapesDlg p,
#fieldDlg p,
#hiddenDlg p { margin: 0; line-height: 1.5; }
#compressDlg .small,
#stampDlg .small { font-size: 12px; color: var(--ink-mute); }

#compressLevels { display: grid; gap: 6px; }
#compressLevels label {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  line-height: 1.45;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  #compressLevels label:hover { background: var(--hover); }
}
#compressLevels label:active { background: var(--press); }
#compressLevels label:has(input:checked) { border-color: var(--sn-orange); box-shadow: inset 0 0 0 1px var(--sn-orange); }
#compressLevels label:has(input:focus-visible) { outline: 2px solid var(--ring); outline-offset: 1px; }
#compressLevels input { margin: 3px 0 0; accent-color: var(--sn-orange-deep); }
#compressLevels span { font-size: 12px; color: var(--ink-mute); }
#compressLevels strong { display: block; font-size: 13px; font-weight: 600; color: var(--ink); }

/* The one line that matters in each of these dialogs gets the orange rule. */
#compressResult,
#installSteps {
  padding: 10px 12px;
  border-left: 3px solid var(--sn-orange);
  border-radius: 0 var(--sn-radius-lg) var(--sn-radius-lg) 0;
  background: rgb(246 146 30 / 0.10);
}

#compressDlg menu,
#installDlg menu,
#stampDlg menu,
#shareDlg menu,
#passwordDlg menu,
#shapesDlg menu,
#fieldDlg menu,
#hiddenDlg menu { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin: 4px 0 0; padding: 0; }
#compressDlg menu button,
#installDlg menu button,
#stampDlg menu button,
#shareDlg menu button,
#passwordDlg menu button,
#shapesDlg menu button,
#fieldDlg menu button,
#hiddenDlg menu button {
  height: 34px;
  padding: 0 16px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  #compressDlg menu button:hover,
  #installDlg menu button:hover,
  #stampDlg menu button:hover,
  #shareDlg menu button:hover,
  #passwordDlg menu button:hover,
  #shapesDlg menu button:hover,
  #fieldDlg menu button:hover,
  #hiddenDlg menu button:hover { background: var(--hover); }
}
#compressDlg menu button:active,
#installDlg menu button:active,
#stampDlg menu button:active,
#shareDlg menu button:active,
#passwordDlg menu button:active,
#shapesDlg menu button:active,
#fieldDlg menu button:active,
#hiddenDlg menu button:active { background: var(--press); }
/* Orange fill needs navy text: white on orange is 2.3:1. */
#compressDlg menu button.primary,
#installDlg menu button.primary,
#stampDlg menu button.primary,
#shareDlg menu button.primary,
#passwordDlg menu button.primary,
#shapesDlg menu button.primary,
#fieldDlg menu button.primary,
#hiddenDlg menu button.primary { background: var(--sn-orange); border-color: var(--sn-orange); color: var(--on-active); font-weight: 600; }
@media (hover: hover) and (pointer: fine) {
  #compressDlg menu button.primary:hover,
  #installDlg menu button.primary:hover,
  #stampDlg menu button.primary:hover,
  #shareDlg menu button.primary:hover,
  #passwordDlg menu button.primary:hover,
  #shapesDlg menu button.primary:hover,
  #fieldDlg menu button.primary:hover,
  #hiddenDlg menu button.primary:hover { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
}
#compressDlg menu button.primary:active,
#installDlg menu button.primary:active,
#stampDlg menu button.primary:active,
#shareDlg menu button.primary:active,
#passwordDlg menu button.primary:active,
#shapesDlg menu button.primary:active,
#fieldDlg menu button.primary:active,
#hiddenDlg menu button.primary:active { background: var(--sn-orange-deep); border-color: var(--sn-orange-deep); }
#compressDlg menu button:focus-visible,
#installDlg menu button:focus-visible,
#stampDlg menu button:focus-visible,
#shareDlg menu button:focus-visible,
#passwordDlg menu button:focus-visible,
#shapesDlg menu button:focus-visible,
#fieldDlg menu button:focus-visible,
#hiddenDlg menu button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
#compressResult.err { border-left-color: var(--danger); background: rgb(200 66 58 / 0.08); color: var(--err-text); }
#compressChoose { display: grid; gap: 12px; }
#compressChoose[hidden] { display: none; }

/* ---- after a redaction: the shared-field question, and what Save also takes out ---- */

/* A field's name can be long and have no spaces in it (form1[0].Sheet[0].Client[0]). */
#fieldAsk { overflow-wrap: anywhere; font-size: 15px; }
/* "Keep on pages 2 to 14" can be longer than the dialog is wide on a phone. */
#fieldDlg menu button { height: auto; min-height: 34px; padding: 6px 16px; line-height: 1.3; }
#hiddenDlg label.hiddenOn {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  font-size: 14px;
  font-weight: 600;
  line-height: 1.45;
  cursor: pointer;
}
#hiddenDlg label.hiddenOn:has(input:checked) { border-color: var(--sn-orange); box-shadow: inset 0 0 0 1px var(--sn-orange); }
#hiddenDlg label.hiddenOn:has(input:focus-visible) { outline: 2px solid var(--ring); outline-offset: 1px; }
#hiddenDlg label.hiddenOn input { flex: none; width: 16px; height: 16px; margin: 2px 0 0; accent-color: var(--sn-orange-deep); }
#hiddenWhat { font-size: 13px; color: var(--ink-mute); }
#hiddenWhat ul { margin: 6px 0 0; padding-left: 20px; line-height: 1.5; }

/* ---- stamp dialog ---- */

#stampDlg hr { width: 100%; height: 1px; margin: 4px 0; border: 0; background: var(--line); }
#stampDlg label,
#passwordDlg label { font-size: 12px; color: var(--ink-mute); }
#stampDlg label.check { display: flex; align-items: center; gap: 8px; color: var(--ink); font-size: 13px; }
#stampDlg label.check input { accent-color: var(--sn-orange-deep); }
#stampDlg input[type="text"] {
  height: 34px;
  padding: 0 10px;
  border: 1px solid var(--line);
  border-radius: var(--sn-radius-lg);
  background: var(--canvas);
  color: var(--ink);
  font: inherit;
}
#stampDlg input[type="text"]:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; border-color: var(--ring); }
#stampCustom[hidden] { display: none; }

/* Each choice wears its own ink, the way it will look on the drawing. */
#stampPicks { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
#stampPicks button {
  min-height: 36px;
  padding: 4px 8px;
  border: 2px solid currentColor;
  border-radius: 6px;
  /* Paper in both themes: each choice shows its ink the way it will look on a drawing. */
  background: #fff;
  color: #263241;
  font: 700 12px/1.2 Poppins, var(--sn-font-body);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  #stampPicks button:hover { background: #f1f3f5; }
}
#stampPicks button:active { background: #f1f3f5; }
#stampPicks button[aria-checked="true"] { box-shadow: 0 0 0 3px var(--sn-orange); }
#stampPicks button:focus-visible { outline: 2px solid var(--ring); outline-offset: 3px; }

/* ---- typing on a touch screen ------------------------------------------------ */

/* iOS Safari zooms the whole page into a field whose text is under 16px when it takes focus, and
   leaves it zoomed after: tapping the page number or Find on an iPhone left the toolbar cut off.
   So every field of ConceptLine's own you type in or pick from is 16px where the pointer is a finger.
   Last in the file, and by id where a field has its own rule, so it wins over each field's
   `font: inherit`. #signName is left out: it is already 30px. Form fields inside a PDF are left
   out too, by keeping to dialogs and ids: their size is the PDF's. Zoom stays allowed; the cause
   is what changes. */
@media (pointer: coarse) {
  dialog input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"]),
  dialog select,
  dialog textarea,
  #pageNum,
  #zoom,
  #findInput,
  #textBody,
  #textSize,
  #passwordInput,
  #stampDlg input[type="text"] { font-size: 16px; }
  /* At 16px the zoom readout's longest text, 1000%, needs about 79px with the picker's arrow, and
     its 4.1rem cut the end off even 100%. The toolbar already scrolls sideways on a touch screen,
     so the wider readout costs nothing there; on a computer it keeps its width. */
  #zoom { width: 5rem; }
}
