@layer views {
  /* ── scanner: a full-viewport dialog, always dark regardless of site theme, so
     colors here are hardcoded rather than pulled from tokens.css. Three rows: the top bar, a stage
     (one page, or the crop editor), and the bottom bar; on the camera step the video fills all of
     it, under the bars. data-step on the dialog says which step is showing (core/scanner.js). ── */
  dialog.scanner {
    position: fixed; inset: 0; width: 100vw; height: 100dvh; max-width: none; max-height: none;
    margin: 0; padding: 0; border: 0; overflow: hidden; background: #000; color: #fff;
  }
  dialog.scanner[open] { display: grid; grid-template-rows: auto minmax(0, 1fr) auto; grid-template-columns: minmax(0, 1fr); }
  dialog.scanner::backdrop { background: #000; }

  dialog.scanner .scanner-video {
    position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000;
  }
  dialog.scanner:not([data-step="camera"]) .scanner-video { visibility: hidden; }
  dialog.scanner .scanner-error {
    position: absolute; inset: 0; z-index: 1;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 4px; text-align: center; padding: 32px;
  }
  dialog.scanner .scanner-error-msg { font-size: var(--fs-md); line-height: 1.5; max-width: 40ch; color: rgb(255 255 255 / .92); margin: 0 0 14px; }
  dialog.scanner .scanner-error .pill { background: #fff; color: #000; }

  dialog.scanner .scanner-flash {
    position: absolute; inset: 0; z-index: 3; background: #fff; opacity: 0; pointer-events: none;
  }
  dialog.scanner .scanner-flash.active { animation: scanner-flash-pop 160ms ease-out; }
  @keyframes scanner-flash-pop { from { opacity: .85; } to { opacity: 0; } }
  @media (prefers-reduced-motion: reduce) {
    dialog.scanner .scanner-flash.active { animation: none; }
  }

  dialog.scanner .scanner-top {
    grid-row: 1; position: relative; z-index: 2;
    display: grid; grid-template-columns: 40px minmax(0, 1fr) 40px; align-items: center; gap: 12px;
    padding: calc(10px + env(safe-area-inset-top, 0px)) calc(12px + env(safe-area-inset-right, 0px)) 28px calc(12px + env(safe-area-inset-left, 0px));
    background: linear-gradient(to bottom, rgb(0 0 0 / .55), transparent);
  }
  dialog.scanner:not([data-step="camera"]) .scanner-top { padding-bottom: 10px; }
  dialog.scanner .scanner-title {
    grid-column: 2; text-align: center; font-size: var(--fs-body-sm); font-weight: 700; color: rgb(255 255 255 / .82);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  dialog.scanner .scanner-close,
  dialog.scanner .scanner-flip {
    grid-column: 1; width: 40px; height: 40px; border-radius: 50%; border: 0;
    display: grid; place-items: center; background: rgb(255 255 255 / .14); color: #fff;
    transition: background 180ms;
  }
  dialog.scanner .scanner-flip { grid-column: 3; }
  dialog.scanner .scanner-close:hover,
  dialog.scanner .scanner-flip:hover { background: rgb(255 255 255 / .24); }
  dialog.scanner :is(.scanner-close, .scanner-flip) svg { width: 18px; height: 18px; }

  /* the stage: one page, or the crop editor; nothing on the camera step */
  dialog.scanner .scanner-stage { grid-row: 2; position: relative; min-height: 0; overflow: hidden; }
  dialog.scanner[data-step="camera"] .scanner-stage { display: none; }
  dialog.scanner .scanner-page {
    position: absolute; inset: 12px 16px; width: calc(100% - 32px); height: calc(100% - 24px); object-fit: contain;
  }

  dialog.scanner .scanner-crop { position: absolute; inset: 0; touch-action: none; user-select: none; -webkit-user-select: none; }
  dialog.scanner .crop-view { position: absolute; }
  /* a 44px target around a small ring; translate() from scanner.js centers it on its corner */
  dialog.scanner .crop-handle {
    position: absolute; left: -22px; top: -22px; width: 44px; height: 44px; padding: 0; border: 0;
    border-radius: 50%; background: transparent; touch-action: none; cursor: grab;
    &::before {
      content: ""; position: absolute; inset: 12px; border-radius: 50%;
      border: 2px solid #fff; background: rgb(255 255 255 / .22); box-shadow: 0 0 0 1px rgb(0 0 0 / .4);
    }
    &:focus-visible { outline: 2px solid #fff; outline-offset: -4px; }
  }
  dialog.scanner .scanner-crop.dragging .crop-handle { cursor: grabbing; }
  dialog.scanner .crop-loupe {
    position: absolute; left: 0; top: 0; z-index: 1; width: 112px; height: 112px; border-radius: 50%;
    border: 2px solid #fff; box-shadow: 0 4px 18px rgb(0 0 0 / .5); pointer-events: none;
  }

  dialog.scanner .scanner-bar {
    grid-row: 3; position: relative; z-index: 2;
    display: grid; gap: 10px; justify-items: center;
    padding: 14px 14px calc(14px + env(safe-area-inset-bottom, 0px));
    background: linear-gradient(to top, rgb(0 0 0 / .7), transparent);
  }

  dialog.scanner .scanner-strip {
    position: relative; display: flex; gap: 8px; width: 100%; max-width: 640px; min-height: 68px;
    overflow-x: auto; padding: 2px;
  }
  dialog.scanner .scanner-thumb {
    position: relative; flex: none; width: 52px; height: 68px; border-radius: var(--r-item); overflow: hidden;
    border: 1.5px solid rgb(255 255 255 / .35);
  }
  dialog.scanner .scanner-thumb[aria-current="true"] { border-color: #fff; }
  dialog.scanner[data-step="page"] .scanner-thumb:not([aria-current="true"]) img { opacity: .55; }
  dialog.scanner .thumb-open { display: block; width: 100%; height: 100%; padding: 0; border: 0; background: none; }
  dialog.scanner .thumb-open:focus-visible { outline: 2px solid #fff; outline-offset: -3px; }
  dialog.scanner .scanner-thumb img { width: 100%; height: 100%; object-fit: cover; }
  dialog.scanner .thumb-num {
    position: absolute; left: 3px; bottom: 3px; font-size: var(--fs-label); font-weight: 700; line-height: 1.4;
    color: #fff; background: rgb(0 0 0 / .6); border-radius: 5px; padding: 0 5px;
  }
  dialog.scanner .thumb-remove {
    position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; border-radius: 50%; border: 0;
    display: grid; place-items: center; padding: 0; background: rgb(0 0 0 / .6); color: #fff;
  }
  dialog.scanner .thumb-remove:hover { background: rgb(0 0 0 / .82); }
  dialog.scanner .thumb-remove svg { width: 12px; height: 12px; }

  /* page tools: turn and crop · move · retake and remove */
  dialog.scanner .scanner-tools { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 10px; }
  dialog.scanner .tool-group { display: flex; gap: 2px; }
  dialog.scanner .scanner-tool {
    width: 40px; height: 40px; color: rgb(255 255 255 / .86);
    & svg { width: 18px; height: 18px; }
    &:hover:not(:disabled) { color: #fff; background: rgb(255 255 255 / .14); }
    &:disabled { opacity: .35; }
  }

  dialog.scanner .scanner-controls {
    display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px;
    width: 100%; max-width: 640px;
  }
  dialog.scanner .scanner-slot { grid-column: 1; justify-self: start; }
  dialog.scanner .scanner-shutter {
    grid-column: 2; justify-self: center; position: relative; flex: none; width: 68px; height: 68px;
    border-radius: 50%; border: 3px solid #fff; background: rgb(255 255 255 / .16);
    transition: background 180ms;
  }
  dialog.scanner .scanner-shutter::before {
    content: ""; position: absolute; inset: 6px; border-radius: 50%; background: #fff;
    transition: inset 120ms;
  }
  dialog.scanner .scanner-shutter:hover { background: rgb(255 255 255 / .28); }
  dialog.scanner .scanner-shutter:active::before { inset: 11px; }
  dialog.scanner .scanner-done { grid-column: 3; justify-self: end; background: #fff; color: #000; }
  dialog.scanner .scanner-done:hover:not(:disabled) { background: rgb(255 255 255 / .85); }
  dialog.scanner .scanner-whole { color: #fff; border-color: rgb(255 255 255 / .35); }
  dialog.scanner .scanner-whole:hover:not(:disabled) { background: rgb(255 255 255 / .14); }
  @media (prefers-reduced-motion: reduce) {
    dialog.scanner :is(.scanner-shutter, .scanner-close, .scanner-flip),
    dialog.scanner .scanner-shutter::before { transition: none; }
  }

  /* ── generic drop-target highlight: set by dropZone() in core/upload.js ── */
  [data-drag="1"] {
    outline: 1.5px dashed var(--grad-b);
    outline-offset: -1.5px;
    background: color-mix(in oklch, var(--grad-b) 8%, transparent);
    transition: background var(--t-fast), outline-color var(--t-fast);
  }

  /* ── upload progress: same shape as .meter, just thinner and faster to react; plain ink, since
     blue means memory ── */
  .upload-progress { position: relative; height: 4px; border-radius: 2px; background: var(--track); overflow: hidden; }
  .upload-progress > i {
    position: absolute; inset: 0 auto 0 0; border-radius: 2px; background: var(--fg);
    width: var(--v, 0%); transition: width var(--t-fast) var(--ease-liquid);
  }
}

@layer views {
  /* ── share to study (views/share.js): what was shared, then the class it goes to ── */
  .page.share { display: grid; gap: 24px; align-content: start; }
  .share-what { display: grid; gap: 14px; }
  .share-words {
    display: grid; gap: 6px; max-width: 64ch; padding: var(--pad-card);
    border: 1px solid var(--line); border-radius: var(--r-card); background: var(--surface);
  }
  .share-title { font-weight: 700; color: var(--fg); overflow-wrap: anywhere; }
  .share-body {
    color: var(--fg-2); font-size: var(--fs-body-sm); white-space: pre-wrap; overflow-wrap: anywhere;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 8; line-clamp: 8; overflow: hidden;
  }
  .share-to { display: grid; gap: 14px; max-width: 420px; }
  .share-actions { display: flex; flex-wrap: wrap; gap: 10px; }
  .share-back { justify-self: start; }
}
