/*
 * First tab stop on the page, off-screen until focused. It does not navigate:
 * the hash is the tool router, so a real jump to #panels would be rewritten to
 * a tool id by selectFromHash. app.js moves focus instead.
 */
.skip-link {
  position: fixed; top: 1rem; left: 1rem; z-index: 20;
  padding: .6rem 1rem; border-radius: 10px;
  background: var(--accent); color: #fff; text-decoration: none;
  transform: translateY(-300%);
}
.skip-link:focus { transform: none; }

/* Live regions that carry a message for a screen reader and nothing for the eye. */
.sr-only {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.app { padding-block: clamp(24px, 5vw, 56px); }
.nav-title { font-family: var(--font-mono); color: var(--ink-mute); font-size: .9rem; }

.dropzone {
  border: 2px dashed var(--glass-brd);
  border-radius: var(--radius);
  padding: clamp(20px, 4vw, 36px);
  text-align: center;
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.dropzone.is-over { border-color: var(--accent); background: var(--surface-2); }

.file-list { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .4rem; }
.file-list li {
  display: flex; align-items: center; gap: .6rem;
  font-family: var(--font-mono); font-size: .82rem;
  background: var(--surface-1); border-radius: 10px; padding: .45rem .7rem;
}
.file-list button { margin-left: auto; }

.app-body { display: grid; grid-template-columns: 240px 1fr; gap: 1.5rem; margin-top: 1.5rem; }
/*
 * A grid item defaults to min-width: auto, which lets it grow past the grid to
 * fit its widest content — so the sign panel's page preview widened the whole
 * column and made the document scroll sideways, and every `max-width: 100%`
 * inside it resolved against the grown width and never bound. Measured at
 * 390 px: 386 px of horizontal overflow before this line.
 */
.app-body > * { min-width: 0; }

.tool-nav { display: flex; flex-direction: column; gap: .35rem; }
.tool-btn {
  text-align: left; padding: .7rem .9rem; border-radius: 14px;
  border: 1px solid transparent; background: transparent;
  color: var(--ink-soft); cursor: pointer; font: inherit;
}
.tool-btn:hover { background: var(--surface-2); }
.tool-btn[aria-selected="true"] {
  background: var(--glass-fill-strong); border-color: var(--glass-brd);
  color: var(--ink); font-weight: 600;
}

.panel { padding: clamp(18px, 3vw, 28px); }
.panel-sub { color: var(--ink-mute); margin-bottom: 1.2rem; }
/* pre-line: the batch panels report one failure per line. */
.panel-error { color: var(--danger); margin-top: 1rem; font-size: .9rem; white-space: pre-line; }
/*
 * `role="alert"` only announces when the region is already in the accessibility
 * tree at the moment it fills, so this element is never `hidden`. An empty
 * block generates no line box, so collapsing the margin is all it takes to make
 * it invisible.
 */
.panel-error:empty { margin-top: 0; }
.panel-toolbar { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center; margin-bottom: 1rem; }

.thumb-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 1rem; }
.thumb {
  background: var(--glass-fill-strong); border: 1px solid var(--glass-brd);
  border-radius: 14px; padding: .6rem; text-align: center;
}
.thumb canvas, .thumb .thumb-ph {
  width: 100%; height: auto; aspect-ratio: 1 / 1.414;
  background: var(--surface-2); border-radius: 8px; display: block;
}
.thumb-label { font-family: var(--font-mono); font-size: .75rem; color: var(--ink-mute); margin-top: .4rem; }
.thumb-actions { display: flex; justify-content: center; gap: .3rem; margin-top: .4rem; }
.thumb.is-dragging { opacity: .4; }
/* set by renderGrid when a page render fails; the label says why. */
.thumb-failed { border-color: var(--danger); }
.thumb.cut-after { box-shadow: 4px 0 0 var(--accent); }

/*
 * Signature placement depends on this block. The stage is the overlay's
 * offsetParent, and the canvas sits at its origin, so `display: block` on the
 * canvas (no inline baseline gap) is what makes overlay.offsetLeft/Top mean
 * "pixels from the top-left of the page". `max-width` stops a 700 px preview
 * overflowing a phone; sign.js converts the resulting CSS pixels back to canvas
 * pixels via getBoundingClientRect, so scaling here is safe — but only because
 * it does. `height: auto` must stay, or the canvas keeps its attribute height
 * and the page is squashed.
 */
.sign-stage { position: relative; display: block; width: max-content; max-width: 100%; touch-action: none; }
.sign-stage canvas { display: block; max-width: 100%; height: auto; }
.sign-overlay {
  position: absolute; cursor: move; outline: 2px solid var(--accent);
  background: rgba(59, 91, 219, .06);
}
.sign-handle {
  position: absolute; right: -7px; bottom: -7px; width: 14px; height: 14px;
  background: var(--accent); border-radius: 50%; cursor: nwse-resize;
}
/*
 * `body` has `overflow-x: hidden`, so anything wider than the column is clipped
 * rather than scrollable: at 390 px the 480 px pad lost its right-hand 135 px
 * with no way to reach it. `height: auto` is not optional — without it the
 * bitmap is squashed horizontally on display while makePad's pos() keeps
 * mapping y at full scale, so the exported signature would not match the
 * shape that was drawn.
 */
.sign-pad {
  border: 1px solid var(--glass-brd); border-radius: 12px; background: #fff;
  touch-action: none; max-width: 100%; height: auto;
}

.busy { opacity: .6; pointer-events: none; }

@media (max-width: 768px) {
  .app-body { grid-template-columns: 1fr; }
  .tool-nav { flex-direction: row; overflow-x: auto; padding-bottom: .4rem; }
  .tool-btn { white-space: nowrap; }
}
