/*
  Door scanner.

  Terminal aesthetic, matching the receipts and the audit reports: near-black,
  JetBrains Mono, small type, small radii, colour used only where it carries
  meaning. Nothing here is decorative — a steward looks at this for a second at
  a time, in the dark, holding a queue.

  The one deliberate break from the restraint is the verdict screen. It floods
  the whole viewport with a single colour, because the person reading it is not
  reading: they are catching a colour at arm's length while looking at a face.
  A tasteful badge would be the wrong answer to that problem.
*/

:root {
  --bg: #09090b;
  --bg-card: #0c0c0e;
  --bg-elevated: #111114;
  --bg-surface: #161619;
  --border: #1a1a1e;
  --border-hover: #2a2a30;

  --text: #e4e4e7;
  --text-secondary: #a1a1aa;
  --text-muted: #52525b;
  --text-dim: #3f3f46;

  --accent: #3b82f6;
  --accent-dim: rgba(59, 130, 246, 0.12);
  --success: #22c55e;
  --danger: #ef4444;
  --warning: #f59e0b;

  --font: 'JetBrains Mono', 'Fira Code', 'SF Mono', ui-monospace, Menlo, monospace;
  --radius-sm: 3px;
  --radius: 6px;
  --radius-lg: 8px;
  --ease: 0.15s ease;
}

*,
*::before,
*::after { box-sizing: border-box; }

html,
body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 12px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* The camera fills the screen; a bounce would drag it away from the frame. */
  overscroll-behavior: none;
}

::selection { background: var(--accent-dim); color: var(--text); }

/* Screens ------------------------------------------------------------------ */

/*
  Visibility is the `hidden` attribute — that is what app.js toggles. Any rule
  that sets `display` on these elements must therefore say so again for the
  hidden case, because `display` beats the attribute's default styling. Getting
  that wrong hides the whole app and leaves a black page with no error.
*/
.screen {
  display: block;
  min-height: 100dvh;
}

.screen[hidden] { display: none; }

/* Sign in ------------------------------------------------------------------ */

.screen--signin {
  display: grid;
  place-items: center;
  padding: 24px;
}

.screen--signin[hidden] { display: none; }

.signin {
  width: 100%;
  max-width: 340px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 22px 20px;
}

.signin__title {
  margin: 0 0 18px;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.2px;
}

.field { display: block; }
.field + .field { margin-top: 14px; }

/* The kicker treatment from the reports: tiny, spaced, dim. It labels without
   competing with the value underneath. */
.field__label {
  display: block;
  margin-bottom: 6px;
  font-size: 8px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  color: var(--text-dim);
}

.field__input {
  width: 100%;
  padding: 9px 11px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  color: var(--text);
  font-family: var(--font);
  /* 16px stops iOS Safari zooming the viewport on focus, which on a phone at a
     gate leaves the steward panning a magnified page one-handed. */
  font-size: 16px;
  transition: border-color var(--ease);
}

.field__input:focus {
  outline: none;
  border-color: var(--accent);
}

.field__input--mono { letter-spacing: 0.5px; }

.signin__error {
  margin: 14px 0 0;
  color: var(--danger);
  font-size: 11px;
}

/* Buttons ------------------------------------------------------------------ */

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  margin-top: 18px;
  padding: 11px 14px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-family: var(--font);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background var(--ease), border-color var(--ease);
}

.button--primary {
  background: var(--accent);
  color: #fff;
}

.button--primary:hover { background: #2f6fd8; }

.button--ghost {
  width: auto;
  margin: 0;
  background: transparent;
  border-color: var(--border);
  color: var(--text-secondary);
}

.button--ghost:hover { border-color: var(--border-hover); color: var(--text); }

/* Scanner ------------------------------------------------------------------ */

.screen--scanner { position: relative; }

.topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  background: linear-gradient(180deg, rgba(9, 9, 11, 0.92), rgba(9, 9, 11, 0));
  font-size: 10px;
  color: var(--text-secondary);
}

.topbar__event {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 8px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--text-muted);
}

.pill__dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--success);
}

/* Offline is a state the steward must be able to see without reading. The
   values are the ones app.js sets on the pill. */
.pill[data-state='offline'] { color: var(--warning); }
.pill[data-state='offline'] .pill__dot { background: var(--warning); }

.camera {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #000;
}

.reticle {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(62vw, 280px);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border: 1px solid rgba(228, 228, 231, 0.35);
  border-radius: var(--radius-lg);
  /* The dim surround is the aiming aid: it says where the code goes without
     drawing a heavy frame over the picture the camera needs. */
  box-shadow: 0 0 0 100vmax rgba(9, 9, 11, 0.55);
  pointer-events: none;
}

.toolbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px;
  background: linear-gradient(0deg, rgba(9, 9, 11, 0.92), rgba(9, 9, 11, 0));
}

.tally {
  font-size: 10px;
  color: var(--text-muted);
}

/* Verdict ------------------------------------------------------------------ */

/*
  Full bleed and loud, on purpose.

  Everything else on this page is restrained because it is read. This is not
  read — it is caught, at arm's length, by someone looking at a face and not a
  screen. Colour does the work; the words only confirm it.
*/
.result {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 28px;
  text-align: center;
}

.result[hidden] { display: none; }

/*
  Green admits, red refuses — and blue says "you already did this".

  A third colour was deliberately avoided at first, on the grounds that a
  steward reading one has to stop and think. That was right about refusals and
  wrong about repeats: the camera keeps reading after the green screen, so the
  same code comes back seconds later, and painting that red tells the steward
  somebody is cheating when nobody is. Blue is the colour of "no action
  needed", and it is the only screen that does not interrupt the queue.
*/
.result[data-verdict='admit'] { background: var(--success); color: #05130a; }
.result[data-verdict='refuse'] { background: var(--danger); color: #1a0505; }
.result[data-verdict='repeat'] { background: var(--accent); color: #04101f; }
/* No answer from the server: not a refusal of the guest, but nobody goes in on
   it either. Amber, the same colour as the offline pill. */
.result[data-verdict='offline'] { background: var(--warning); color: #1a1205; }

.result__verdict {
  margin: 0;
  font-size: clamp(30px, 11vw, 56px);
  font-weight: 700;
  letter-spacing: -1px;
  line-height: 1.1;
}

/* The ambassador marker. Inverted against the verdict colour so it reads as a
   badge, not as one more line of text: the steward greets this person
   differently. */
.result__badge {
  margin: 0;
  padding: 8px 16px;
  border-radius: var(--radius-lg);
  background: rgba(9, 9, 11, 0.88);
  color: #fafafa;
  font-size: clamp(16px, 5.2vw, 24px);
  font-weight: 700;
  letter-spacing: -0.2px;
}

.result__badge[hidden] { display: none; }

.result__type {
  margin: 0;
  font-size: clamp(14px, 4.5vw, 20px);
  font-weight: 600;
}

/* The attendee's name, under the verdict. Big enough to read at arm's length,
   because "is this you?" is the next thing said out loud. */
.result__name {
  margin: 0;
  font-size: clamp(16px, 5vw, 24px);
  font-weight: 700;
  letter-spacing: -0.3px;
}

.result__detail {
  margin: 0;
  font-size: 12px;
  opacity: 0.75;
}

/* The instruction the screen now needs, since it no longer leaves on its own.
   Pinned to the bottom and dim: read once, then ignored. */
.result__tap {
  position: absolute;
  bottom: calc(24px + env(safe-area-inset-bottom));
  left: 0;
  right: 0;
  margin: 0;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  opacity: 0.6;
}

/* Sheet -------------------------------------------------------------------- */

.sheet {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: flex;
  align-items: flex-end;
  background: rgba(9, 9, 11, 0.7);
}

.sheet[hidden] { display: none; }

.sheet__panel {
  width: 100%;
  padding: 20px 18px calc(20px + env(safe-area-inset-bottom));
  background: var(--bg-card);
  border-top: 1px solid var(--border);
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}

.sheet__actions {
  display: flex;
  gap: 10px;
  margin-top: 16px;
}

.sheet__actions .button { margin-top: 0; }

.hint {
  margin: 0;
  color: var(--text-muted);
  font-size: 11px;
  line-height: 1.7;
}
