/* ══════════════════════════════════════════════════════════════
   Meetback — Scanner (entry screen at /scan)

   Lifted from the pinned reference
   design-system/products/meetback/assets/scanner.html @ 44f4682b,
   which is a signet-transcript, already re-skinned to the Meetback tokens by
   the transcriber. This file finishes that job against the tokens as they
   actually exist in this repo:

     reference :root         → this repo
     --ink       var(--ink-900)     → var(--ink)            (tokens.css)
     --mist      var(--page)     → var(--surface-muted)  (tokens.css; the transcript's
                               "var(--surface2) → var(--mist)" re-skin note)
     --surface               → var(--surface)
     --border / --muted / --faint → the vars cards-home.css declares at :root
     --font-display          → var(--font-display)   ('Fraunces', serif)
     24 / 19 / 13.5 / 15 / 11.5px → --t-xl / --t-lg / --t-sm / --t-base / --t-xs
                               whose clamp() maxima are those same sizes, so the
                               drawing is unchanged at 460px and fluid below it
                               (iOS-first adaptive invariant).

   DEPENDS ON cards-home.css BEING LOADED FIRST, exactly as welcome.css does:
   --border, --muted, --faint and the safe-area vars (--sat/--sab/--sal/--sar)
   are declared there, not in tokens.css. Keep that order in index.html.

   The three radii (18 / 20 / 14px) are kept as the reference's own literals.
   They have no token — --r-md is 16px and --r-lg is 22px, and --r-lg being
   22px is a load-bearing Critic invariant for the passport, so bending the
   panel to it would change the drawing to match a token that is not about it.

   Everything is scoped under `.scanner` because the reference's class names are
   deliberately generic — .wrap, .panel, .primary, .hidden — and unscoped they
   would collide with the other screens' stylesheets in this single-document app.
   ══════════════════════════════════════════════════════════════ */

.scanner {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;                 /* the panel is short; long copy still scrolls */
  /* Explicit, and load-bearing. `overflow-y:auto` alone makes overflow-x
     compute to `auto` as well, and THIS box is the scroll container — so
     `#app{overflow-x:hidden}` cannot help it. A picked filename can be a
     64-char hash (Android picker) or longer (iOS Files), and the screen slid
     sideways: scrollWidth 490 vs clientWidth 375 at SE width, 1304 at 200
     chars. The message now wraps (see .scan-msg) so nothing should reach this,
     which is exactly why it stays: it is the backstop, not the fix. */
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--bg);
}

/* The reference's `.wrap{max-width:460px;margin:0 auto;padding:8px 2px}`.
   The 2px horizontal padding is right for a 460px page centred in a desktop
   window and wrong in the app, where the shell is edge-to-edge on a phone and
   the panel would touch the screen. So the gutter becomes fluid.

   EACH SAFE-AREA INSET IS PAID EXACTLY ONCE, and only the top is paid here:

     left/right — #app already applies var(--sal)/var(--sar) for every screen
       (cards-home.css). Adding max(gutter, --sal) on top charged the inset
       twice: measured at 932×430 with 59px insets, 118px of padding per side
       instead of 59, costing 82px — 19% — of the content width.
     bottom — the tab bar beneath this screen pays calc(8px + var(--sab)), and
       it is an opaque sibling in the flex column, not an overlay, so nothing
       here is ever underneath it. Paying --sab again left 34px of dead
       scrollable space below the panel.
     top — paid HERE, and it must stay: this screen has no chrome above it, so
       without it the title sits under the notch. It is the one inset no
       ancestor of this box is paying. */
.scanner .wrap {
  width: 100%;
  max-width: 460px;
  margin: 0 auto;
  padding: calc(8px + var(--sat)) clamp(14px, 4.5vw, 18px) 8px;
}

.scanner .screen-title {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--t-xl);           /* clamp(20px, 5.5vw, 24px) — 24px at width */
  font-weight: 600;
  letter-spacing: -.01em;
  margin: 6px 0 14px;
}

.scanner .panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: clamp(22px, 6.5vw, 26px) clamp(16px, 5vw, 20px);
  text-align: center;
}

.scanner .glyph {
  width: 64px;
  height: 64px;
  border-radius: 20px;
  background: var(--surface-muted);
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 16px;
}
.scanner .glyph svg { width: 30px; height: 30px; }

.scanner .panel-title {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--t-lg);           /* clamp(17px, 4.6vw, 19px) — 19px at width */
  font-weight: 600;
  margin-bottom: 8px;
}

.scanner .panel-copy {
  font-size: var(--t-sm);           /* clamp(13px, 3.6vw, 14px) — the 13.5px copy */
  color: var(--muted);
  line-height: 1.5;
  margin-bottom: 18px;
}

.scanner .primary {
  width: 100%;
  padding: 15px;
  background: var(--ink);
  color: var(--surface);
  border: none;
  border-radius: 14px;
  font-size: var(--t-base);         /* clamp(15px, 4vw, 16px) */
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: .15s;
}
.scanner .primary:hover { background: var(--slate-850); }
.scanner .primary:active { transform: translateY(1px); }
/* The reference draws no focus ring — a static drawing cannot. This is a real
   button on a touch-and-keyboard surface, so it gets one. */
.scanner .primary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.scanner .footnote {
  font-size: var(--t-xs);           /* clamp(11px, 3vw, 12px) — the 11.5px note */
  color: var(--faint);
  margin-top: 12px;
}

/* The deferral message. Not in the reference — see scanner.js: §6 requires the
   screen to say plainly that reading is not available yet, and the reference
   draws no surface for saying it. Styled as the footnote's sibling so it adds
   no new visual language, and the line is reserved so the panel does not jump
   when it fills. */
.scanner .scan-msg {
  font-size: var(--t-xs);
  line-height: 1.45;
  color: var(--muted);
  margin-top: 10px;
  min-height: 1.45em;
  /* The message quotes the filename the user picked, and a filename has no
     spaces to break at: a 64-char hash off the Android picker, or an iOS Files
     name, is one unbreakable word wider than the phone. `anywhere` (not
     `break-word`) is the one that also shrinks the box's min-content width, so
     the panel cannot be pushed wide in the first place; `word-break` is the
     fallback for engines older than overflow-wrap:anywhere. Nothing is
     truncated — the whole name is still readable, on as many lines as it takes. */
  overflow-wrap: anywhere;
  word-break: break-word;
}
.scanner .scan-msg:empty { margin-top: 0; }

/* Q58 — ONE SLOT, TWO JOBS. This element now carries the resting offline note
   as well as every message, because they were two <p>s and the owner
   photographed them saying opposite things at the same moment. The note is the
   quiet state, so the resting colour is the footnote's --faint; a failure takes
   the page's ink so it reads as something that happened rather than as small
   print. */
.scanner .scan-msg { color: var(--faint); }
.scanner .scan-msg[data-tone="error"] { color: var(--text); }

/* ⚠ `.footnote` IS NO LONGER EMITTED BY scanner.js AND IS KEPT ON PURPOSE. The
   paired fixture in tests/viewport/scanner-says-why.test.mjs rebuilds the
   two-slot panel the owner photographed, and it needs this rule to reproduce
   what that screen looked like. Deleting it would leave the fixture drawing an
   unstyled paragraph and quietly weaken the proof. */

/* Verbatim from the reference: the file input is triggered by the button, so it
   is never shown. Scoped, because `.hidden` unscoped is a landmine. */
.scanner .hidden { display: none; }

@media (prefers-reduced-motion: reduce) {
  .scanner .primary { transition: none; }
  .scanner .primary:active { transform: none; }
}


/* ══ Q44 — THE CAMERA IS THE SCREEN ═══════════════════════════════════════
   Owner, 2026-08-08: "tapping the Scanner TAB opens a live viewfinder with a
   card-shaped frame; dark, edge to edge, one shutter, no chrome." Each of those
   five words is a declaration below.
   ══════════════════════════════════════════════════════════════════════════ */

/* EDGE TO EDGE. The viewfinder ignores the screen's own padding — it is the
   only element in this app that is allowed to, and it is allowed because a
   camera framed by a margin reads as a preview of a camera. */
.scanner .finder {
  position: absolute; inset: 0; z-index: 2;
  background: var(--cam-surround);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.scanner .finder[hidden] { display: none; }
.scanner .finder-video {
  width: 100%; height: 100%; object-fit: cover;
  /* The camera's own frame, not a mirror: this is the BACK camera, and
     mirroring it would make the card's text read backwards. */
  transform: none;
}

/* THE CARD-SHAPED FRAME. 85.6 x 53.98mm is the ISO/IEC 7810 ID-1 card, which
   is what a business card is cut to — so the ratio is the object's, not a
   number that looked right. The dim outside it is one box-shadow rather than
   four overlays. */
.scanner .finder-frame {
  position: absolute; width: min(86vw, 420px); aspect-ratio: 85.6 / 53.98;
  border: 2px solid rgba(255, 255, 255, .92); border-radius: 14px;
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .45);
  pointer-events: none;
}
.scanner .finder-hint {
  position: absolute; left: 0; right: 0;
  top: calc(50% + min(43vw, 210px) * 53.98 / 85.6 + 18px);
  text-align: center; color: rgba(255, 255, 255, .86);
  font-size: var(--t-sm); font-weight: 600; margin: 0; pointer-events: none;
}

/* ONE SHUTTER, NO CHROME. Not a labelled button — the ring is the convention
   every camera app uses, and LANGUAGE.md §2.4's "icon-only controls need a
   name" is met by aria-label rather than by printing "Take the photo" over a
   viewfinder. */
.scanner .shutter {
  position: absolute; bottom: calc(24px + var(--sab, 0px));
  width: 72px; height: 72px; border-radius: 50%;
  background: var(--cam-shutter); border: 4px solid rgba(255, 255, 255, .45);
  background-clip: padding-box;
  cursor: pointer; padding: 0;
  transition: transform .12s ease-out;
}
.scanner .shutter:active { transform: scale(.93); }
.scanner .shutter:disabled { opacity: .5; cursor: default; }
.scanner .shutter:focus-visible { outline: 3px solid var(--cam-shutter); outline-offset: 4px; }
@media (prefers-reduced-motion: reduce) { .scanner .shutter { transition: none; } }

/* ⚠ THE CONTEXT IS THE SCREEN, NOT THE STAGE — measured, after the first
   attempt put it on #scanStage and the viewfinder came out 394px wide on a
   430px phone. `.wrap` carries a max-width, so anything positioned inside it
   inherits that width and "edge to edge" quietly becomes "edge to the wrap".
   `.scanner` is the screen root and is the full width of #app; it stops at the
   tab bar, which is where the viewfinder should stop too. */
.scanner { position: relative; }
