@font-face {
  font-family: 'Apercu';
  src: url("../assets/fonts/apercu.woff2") format('woff2');
  font-weight: 400 800;
}

/* Names referenced inside the GoogleCafe_Badge.svg badge template.
   TEMPORARY: this template actually calls for "Google Sans Flex" (a
   newer, different variable font with more axes — wdth/ROND/slnt on top
   of the usual opsz/wght/GRAD), not the plain "Google Sans" file we have
   embedded here. Standing in with that file for now so text still reads
   as a real sans-serif instead of falling back to serif; swap the src
   below to the real Google Sans Flex file (same SIL OFL setup as before,
   see assets/fonts/GoogleSans-OFL.txt) once it's available, no other
   changes needed. */
@font-face { font-family: 'GoogleSansFlex-Medium'; src: url("../assets/fonts/google-sans-variable.woff2") format('woff2-variations'); font-weight: 100 900; }
@font-face { font-family: 'GoogleSansFlex-Light'; src: url("../assets/fonts/google-sans-variable.woff2") format('woff2-variations'); font-weight: 100 900; }
@font-face { font-family: 'Google Sans Flex'; src: url("../assets/fonts/google-sans-variable.woff2") format('woff2-variations'); font-weight: 100 900; }

/* Bump design system (see the "Bump Check-in redesign" canvas): white
   paper, ink text, one Bump blue for the word that matters, hairlines, small
   radii, no shadows at rest, uppercase button labels. */
:root {
  --ink: #0a0a0a;
  --text: #333333;
  --text-secondary: #444444;
  --muted: #666666;
  --faint: #999999;
  --line: #e6e6e6;
  --wash: #eeeeee;
  --paper: #ffffff;
  --blue: #4f7fff;
  --blue-tint: rgba(79, 127, 255, .12);
  --blue-underline: rgba(79, 127, 255, .28);
  --mint: #00e6a3;
  --mint-tint: #d6f7ec;
  --mint-deep: #00604a;
  --mint-text: #00a37a;
  --pink: #ff0086;
  --ring: rgba(180, 107, 240, .08);
  /* kept for older rules/tests that still name them */
  --border: var(--line);
  --bg: var(--paper);
  --blue-bg: var(--blue-tint);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
[hidden] { display: none !important; }

html, body {
  height: 100%;
  overflow: hidden;
  font-family: 'Apercu', 'Hanken Grotesk', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  color: var(--ink);
}

/* Fluid scaling instead of a fixed-pixel canvas + transform:scale — same
   1080x1920 (portrait) / 1920x1080 (landscape) reference, min()-of-both-
   axes, 1rem = one reference pixel. */
html {
  font-size: min(100vw / 1080, 100vh / 1920);
}
html.orientation-landscape {
  font-size: min(100vw / 1920, 100vh / 1080);
}

a { color: var(--ink); }

.kiosk-viewport {
  position: fixed;
  inset: 0;
  overflow: hidden;
  background: var(--paper);
}

.kiosk {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  position: relative;
  overflow: hidden;
  background: var(--paper);
}

/* Bump's rings (bump.nu's hero): 1px circles every 46px, centred just under
   the top bar and fading out. Only on the start and welcome screens; on the
   start screen they shrink back while the keyboard is open. */
.kiosk-rings {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: repeating-radial-gradient(circle at 50% 104rem, transparent 0 45rem, var(--ring) 45rem 46rem);
  -webkit-mask-image: radial-gradient(ellipse 1300rem 640rem at 50% 104rem, #000 0%, transparent 100%);
  mask-image: radial-gradient(ellipse 1300rem 640rem at 50% 104rem, #000 0%, transparent 100%);
  opacity: 0;
  transition: opacity .3s ease;
}
.kiosk:has(#screen-search:not([hidden])) .kiosk-rings,
.kiosk:has(#screen-success:not([hidden])) .kiosk-rings { opacity: 1; }
.kiosk:has(#kb:not([hidden])) .kiosk-rings {
  -webkit-mask-image: radial-gradient(ellipse 1100rem 240rem at 50% 104rem, #000 0%, transparent 100%);
  mask-image: radial-gradient(ellipse 1100rem 240rem at 50% 104rem, #000 0%, transparent 100%);
}

/* header: logo + CHECK-IN on the left, NEED HELP on the right */

.kiosk-header {
  position: relative;
  z-index: 1;
  flex: none;
  height: 104rem;
  padding: 0 120rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1rem solid var(--line);
  background: rgba(255, 255, 255, .92);
}
.kiosk-brand { display: flex; align-items: center; gap: 18rem; }
.kiosk-logo { height: 36rem; width: auto; display: block; }
.kiosk-brand-label {
  padding-top: 4rem;
  font-size: 18rem;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--blue);
  -webkit-user-select: none;
  user-select: none;
}

.help-btn {
  height: 58rem;
  padding: 0 28rem;
  border: 1.5rem solid var(--ink);
  border-radius: 8rem;
  background: var(--paper);
  color: var(--ink);
  font-family: inherit;
  font-size: 16rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.help-btn:active { background: var(--wash); }
.help-btn:disabled { opacity: .6; cursor: default; }

/* footer: "Creativity by Committee" (also the secret way into Admin) */

.kiosk-footer {
  position: relative;
  z-index: 1;
  flex: none;
  height: 76rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-top: 1rem solid var(--line);
  background: var(--paper);
}
.secret-admin { font-size: 16rem; color: var(--muted); text-decoration: none; -webkit-tap-highlight-color: transparent; }

/* screens */

.kiosk-main { flex: 1; position: relative; min-height: 0; }

.screen {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 36rem;
  padding: 0 80rem;
  text-align: center;
}

/* Shared type: outlined pill above the heading (bump.nu's "A CREATIVE
   AGENCY FOR PUBLIC AFFAIRS"), and the heading with its one blue word. */
.pill {
  display: inline-flex;
  align-items: center;
  height: 46rem;
  padding: 0 24rem;
  border: 1rem solid var(--line);
  border-radius: 999rem;
  background: var(--paper);
  font-size: 15rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
  flex: none;
}
.pill--mint { border: none; background: var(--mint-tint); color: var(--mint-deep); font-weight: 800; }

.headline {
  font-size: 104rem;
  line-height: 1.02;
  font-weight: 800;
  letter-spacing: -.03em;
}
.headline--m { font-size: 72rem; }
.hl {
  color: var(--blue);
  text-decoration: underline;
  text-decoration-color: var(--blue-underline);
  text-decoration-thickness: .1em;
  text-underline-offset: .08em;
}

/* search */

/* padding-bottom is set/cleared from script.js (adjustForKeyboard) to
   reserve exactly as much room as the on-screen keyboard currently takes
   up — see the comment above that method for why. */
.screen-search { gap: 40rem; transition: padding-bottom .15s ease; animation: slideUp .45s ease both; }

/* While typing: the field's label steps aside so welcome pill + name +
   field + results sit together above the keyboard. Heading and field keep
   their size — nothing jumps when the keyboard opens. */
.kiosk:has(#kb:not([hidden])) .screen-search { gap: 20rem; }
/* a little tighter while typing, so two rows of names fit above the keyboard */
.kiosk:has(#kb:not([hidden])) .search-result-row { padding-top: 14rem; padding-bottom: 14rem; }
.kiosk:has(#kb:not([hidden])) .screen-search .field-label { display: none; }
/* No results / no hint yet → no reserved space; the group stays centred
   and only opens up once names actually show. */
.search-results:empty, .search-hint:empty { display: none; }

.search-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12rem;
  flex: none;
}
.field-label {
  font-size: 15rem;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}
.search-box input {
  width: 600rem;
  max-width: 100%;
  height: 84rem;
  padding: 0 28rem;
  border: 2rem solid var(--blue);
  border-radius: 8rem;
  background: var(--paper);
  color: var(--blue);
  font-family: inherit;
  font-size: 28rem;
  font-weight: 600;
  text-align: center;
  outline: none;
}
.search-box input::placeholder { color: var(--blue); opacity: 1; }
/* Once tapped, the prompt goes and just the blinking cursor is left. */
.search-box input:focus::placeholder { color: transparent; }
.search-box input { caret-color: var(--blue); }

.search-results {
  /* shrinks (and scrolls) rather than sliding under the keyboard when
     there are many names, e.g. someone on several live sessions' lists */
  flex: 0 1 auto;
  min-height: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16rem;
  width: 100%;
  max-width: 1760rem;
  max-height: 440rem;
  overflow-y: auto;
}

.search-result-row {
  display: flex;
  align-items: center;
  gap: 22rem;
  padding: 18rem 22rem;
  border-radius: 10rem;
  background: var(--paper);
  border: 1rem solid var(--line);
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  color: var(--ink);
  max-width: 100%;
}
/* Instant "your tap registered" feedback — a guest who can't see that
   anything happened taps again. :active covers the press itself;
   .is-loading (set by selectResult() in script.js) keeps the tapped row
   highlighted with a spinner until the confirm screen appears, and
   .is-busy dims/locks the other rows meanwhile. */
.search-result-row {
  transition: transform .08s ease, background-color .08s ease, border-color .08s ease, opacity .15s ease;
  -webkit-tap-highlight-color: transparent;
}
.search-result-row:active { transform: scale(.985); background: var(--wash); border-color: var(--faint); }
.search-result-row.is-loading { background: var(--blue-tint); border-color: var(--blue); }
.search-result-row.is-loading .result-arrow { display: none; }
.search-result-row.is-loading::after {
  content: "";
  flex: none;
  width: 30rem;
  height: 30rem;
  border-radius: 50%;
  background: conic-gradient(from 0deg, var(--blue), transparent);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 5rem), #000 calc(100% - 5rem));
  mask: radial-gradient(farthest-side, transparent calc(100% - 5rem), #000 calc(100% - 5rem));
  animation: spinRing 1s linear infinite;
}
.search-results.is-busy .search-result-row:not(.is-loading) { opacity: .4; pointer-events: none; }
.search-result-row .name-block { display: flex; flex-direction: column; gap: 8rem; min-width: 0; }
.search-result-row .name { font-size: 30rem; font-weight: 800; letter-spacing: -.01em; }
/* Landscape: at most 4 names (the server sends no more), always on one row
   — a card shrinks and a very long name gets "…" rather than wrapping
   onto a second row. Portrait stacks them (see further down). */
html.orientation-landscape .search-results { flex-wrap: nowrap; }
html.orientation-landscape .search-result-row { flex: 0 1 auto; min-width: 0; }
.search-result-row .name, .search-result-row .session, .search-result-row .meta { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-result-row .meta { display: flex; align-items: center; gap: 12rem; font-size: 19rem; color: var(--text-secondary); white-space: nowrap; }
.search-result-row .company { overflow: hidden; text-overflow: ellipsis; }
.search-result-row .company + .ticket-tag::before {
  content: "";
  width: 4rem;
  height: 4rem;
  margin-right: 4rem;
  border-radius: 999rem;
  background: var(--line);
}
.ticket-tag {
  display: inline-flex;
  align-items: center;
  gap: 8rem;
  font-size: 13rem;
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink);
  flex: none;
}
.ticket-tag-bar { width: 20rem; height: 5rem; border-radius: 5rem; flex: none; }
.result-arrow { font-size: 30rem; font-weight: 700; flex: none; }

.search-hint { font-size: 18rem; color: var(--muted); min-height: 26rem; }
/* "Pieter Maes is already checked in." + ask-staff link (showAlreadyCheckedIn) */
.search-hint:has(.already-line) { display: flex; flex-direction: column; align-items: center; gap: 14rem; }
.already-line { font-size: 26rem; color: var(--ink); }
.already-line strong { font-weight: 800; }
.already-help {
  padding: 0;
  border: none;
  background: none;
  font-family: inherit;
  font-size: 20rem;
  font-weight: 700;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 2rem;
  text-underline-offset: 6rem;
  cursor: pointer;
}
.already-help:disabled { color: var(--muted); text-decoration: none; cursor: default; }

.no-event-notice { font-size: 24rem; color: var(--text); text-align: center; max-width: 720rem; line-height: 1.5; }

.spinner {
  width: 44rem;
  height: 44rem;
  border-radius: 50%;
  background: conic-gradient(from 0deg, var(--ink), transparent);
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 4rem), #000 calc(100% - 4rem));
  mask: radial-gradient(farthest-side, transparent calc(100% - 4rem), #000 calc(100% - 4rem));
  animation: spinRing 1s linear infinite;
}
.spinner--small { width: 24rem; height: 24rem; flex: none; }

/* confirm */

.screen-confirm { animation: slideUp .35s ease both; }

/* The real badge (see renderConfirmBadge() in script.js), or the details
   card as the fallback while it renders. */
.confirm-badge { display: flex; align-items: center; justify-content: center; min-height: 268rem; flex: none; }
.confirm-badge img {
  display: block;
  width: 400rem;
  height: auto;
  border: 1rem solid var(--line);
  border-radius: 10rem;
  animation: popIn .3s cubic-bezier(.2, .8, .2, 1) both;
}

.confirm-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14rem;
  padding: 36rem 48rem;
  background: var(--paper);
  border-radius: 14rem;
  border: 1rem solid var(--line);
  width: 560rem;
  max-width: 100%;
}
.confirm-avatar {
  width: 96rem;
  height: 96rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 36rem;
  font-weight: 800;
  color: #fff;
}
.confirm-card h2 { font-size: 44rem; font-weight: 800; letter-spacing: -.02em; line-height: 1.1; }
.confirm-role { font-size: 22rem; color: var(--text-secondary); }

.ticket-pill {
  display: inline-flex;
  padding: 8rem 18rem;
  border-radius: 999rem;
  font-size: 14rem;
  font-weight: 800;
  color: #fff;
  text-transform: uppercase;
  letter-spacing: .1em;
}

/* "No" left, "Yes" right — Yes stays first in the markup (tab order). */
.confirm-actions { display: flex; flex-direction: row-reverse; justify-content: center; align-items: center; gap: 14rem; }

/* "Also check me in for Session 4" switch (showAlsoSwitch) */
.also-switch { display: flex; align-items: center; gap: 16rem; font-size: 20rem; color: var(--ink); cursor: pointer; -webkit-tap-highlight-color: transparent; }
.also-switch[hidden] { display: none; }
.also-switch strong { font-weight: 800; }
.also-switch input { position: absolute; opacity: 0; pointer-events: none; }
.switch-track { position: relative; flex: none; width: 64rem; height: 36rem; border-radius: 999rem; background: var(--line); transition: background .15s ease; }
.switch-track::after { content: ""; position: absolute; top: 4rem; left: 4rem; width: 28rem; height: 28rem; border-radius: 50%; background: var(--paper); transition: transform .15s ease; }
.also-switch input:checked + .switch-track { background: var(--blue); }
.also-switch input:checked + .switch-track::after { transform: translateX(28rem); }

/* Which session a name is on (only when several sessions are live) */
.search-result-row .session { font-size: 13rem; font-weight: 800; letter-spacing: .16em; text-transform: uppercase; color: var(--blue); }

.btn {
  border: none;
  font-family: inherit;
  cursor: pointer;
  transition: transform .08s ease, background-color .08s ease, opacity .15s ease;
  -webkit-tap-highlight-color: transparent;
}
.btn:active:not(:disabled) { transform: scale(.97); }
.btn:disabled { opacity: .6; cursor: default; }
.btn--primary, .btn--dark {
  height: 84rem;
  padding: 0 56rem;
  border-radius: 8rem;
  background: var(--ink);
  color: #fff;
  font-size: 18rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12rem;
}
.btn-arrow { color: var(--mint); font-size: 24rem; }
.btn--secondary {
  height: 84rem;
  padding: 0 44rem;
  border-radius: 8rem;
  background: var(--paper);
  border: 1.5rem solid var(--ink);
  color: var(--ink);
  font-size: 18rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.btn--primary:active:not(:disabled), .btn--dark:active:not(:disabled) { background: #2b2b2b; }
.btn--secondary:active:not(:disabled) { background: var(--wash); }
/* "Already have a badge? Check in without printing" — small print under
   Yes/No; the whole line is tappable, only the action is underlined. */
.btn--link {
  height: 56rem;
  padding: 0 12rem;
  background: transparent;
  color: var(--muted);
  font-size: 18rem;
  font-weight: 400;
}
.btn--link u { color: var(--ink); font-weight: 700; text-decoration-thickness: 2rem; text-underline-offset: 5rem; }
.btn--link:active:not(:disabled) { color: var(--ink); }

/* badge / printing */

.screen-badge { gap: 40rem; }

.badge-preview {
  width: 480rem;
  border: 1rem solid var(--line);
  border-radius: 10rem;
  overflow: hidden;
  line-height: 0;
}
.badge-preview:empty { height: 320rem; background: var(--wash); border-color: var(--wash); }
.badge-preview svg, .badge-preview img { display: block; width: 100%; height: auto; }

/* Thin bar under the badge: runs while preparing/printing, full when done. */
.print-progress { width: 420rem; height: 6rem; border-radius: 6rem; background: var(--wash); overflow: hidden; }
.print-progress span {
  display: block;
  width: 40%;
  height: 100%;
  border-radius: 6rem;
  background: var(--blue);
  animation: progressSlide 1.2s ease-in-out infinite;
}
.screen-badge:has(#printCheck:not([hidden])) .print-progress span { width: 100%; animation: none; background: var(--mint-text); }

.print-status {
  display: flex;
  align-items: center;
  gap: 12rem;
  white-space: nowrap;
  color: var(--muted);
}
.print-status span:last-child { font-size: 20rem; font-weight: 600; }

/* success / error */

.screen-success { gap: 40rem; animation: slideUp .45s ease both; }

.result-icon {
  width: 120rem;
  height: 120rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  animation: popIn .4s cubic-bezier(.2, .8, .2, 1) both;
}

.result-copy { display: flex; flex-direction: column; align-items: center; gap: 20rem; max-width: 1100rem; }
.result-copy h1 { font-weight: 800; letter-spacing: -.03em; line-height: 1.05; }
#errorTitle { font-size: 56rem; line-height: 1.1; }
.result-copy p { font-size: 26rem; color: var(--text); line-height: 1.55; }

.reset-note { font-size: 17rem; color: var(--muted); }

.badge-instructions {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12rem;
  max-width: 680rem;
  padding: 28rem 32rem;
  border-radius: 10rem;
  background: var(--paper);
  border: 1rem solid var(--line);
  text-align: left;
}
.badge-instructions h3 { font-size: 24rem; font-weight: 800; letter-spacing: -.01em; }
.badge-instructions ol { margin: 0; padding-left: 24rem; display: flex; flex-direction: column; gap: 8rem; }
.badge-instructions-list--plain { list-style: none; padding-left: 0 !important; }
.badge-instructions li { font-size: 20rem; color: var(--text); line-height: 1.5; }

/* on-screen keyboard — white keys with a hairline, Bump-style labels on the
   controls. Dutch layout is QWERTY (same as US/UK), see #kb in index.html.
   Covers the footer while it's open. */

.kb {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12rem;
  padding: 24rem 24rem calc(32rem + env(safe-area-inset-bottom, 0px));
  background: var(--paper);
  border-top: 1rem solid var(--line);
}
.kb > * { width: min(100%, 1308rem); }

.kb-row { display: flex; gap: 12rem; }
.kb-row--indent { padding: 0 66rem; }

.kb-key {
  flex: 1;
  height: 84rem;
  border: 1rem solid var(--line);
  border-radius: 8rem;
  background: var(--paper);
  color: var(--ink);
  font-family: inherit;
  font-size: 30rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color .05s ease;
  -webkit-tap-highlight-color: transparent;
}
.kb-key:active { background: var(--wash); }

/* Delete / 123-ABC / space / done — labelled controls, Bump button type. */
.kb-key--special, .kb-key--space {
  font-size: 17rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.kb-key--shift { font-size: 30rem; font-weight: 600; letter-spacing: 0; }
.kb-key[data-action="backspace"] { border-color: var(--ink); background: var(--ink); color: #fff; }
.kb-key[data-action="backspace"]:active { background: #2b2b2b; }

/* Shift key's "engaged" state (next letter typed will be uppercase). */
.kb-key--shift.kb-key--active { background: var(--ink); border-color: var(--ink); color: #fff; }
.kb-key--shift.kb-key--active:active { background: #2b2b2b; }

.kb-key--wide { flex: 1.55; }
.kb-key--shift { flex: 1.55; }
.kb-key--space { flex: 7.6; color: var(--muted); }
.kb-toggle { flex: 1.55; }
.kb-key--done { flex: 1.55; border: 2rem solid var(--ink); }
.kb-key--done:active { background: var(--wash); }

/* letters vs numbers/symbols page — toggled via #kbToggle, see script.js. */
.kb-page { display: flex; flex-direction: column; gap: 12rem; }

/* Pull down to reload (see bindPullToReload() in script.js): a circle that
   drops in from under the top bar while pulling, blue once letting go
   will reload. The start screen takes the vertical drag itself
   (touch-action) so the touchscreen doesn't cancel it; the results list
   keeps its own scrolling. */
.screen-search { touch-action: none; }
.search-results { touch-action: pan-y; }
.pull-reload {
  position: fixed;
  top: 0;
  left: 50%;
  z-index: 40;
  width: 64rem;
  height: 64rem;
  margin-top: -72rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1rem solid var(--line);
  background: var(--paper);
  color: var(--muted);
  opacity: 0;
  transform: translate(-50%, 0);
  transition: transform .2s ease, opacity .2s ease, color .15s ease, border-color .15s ease;
  pointer-events: none;
}
.pull-reload svg { width: 30rem; height: 30rem; }
.pull-reload.is-pulling { opacity: 1; transition: color .15s ease, border-color .15s ease; }
.pull-reload.is-ready { color: var(--blue); border-color: var(--blue); }

/* "Need help" confirmation — loud on purpose so it also catches the eye of
   any staff member walking past, in Bump black with a mint check. See
   requestHelp() in script.js for the show/hide timing. */
.help-toast {
  position: absolute;
  inset: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(10, 10, 10, .62);
  animation: helpToastBackdropIn .2s ease both;
}

.help-toast-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20rem;
  width: 100%;
  max-width: 760rem;
  padding: 64rem 60rem;
  border-radius: 14rem;
  background: var(--ink);
  color: #fff;
  text-align: center;
  animation: helpToastCardIn .3s cubic-bezier(.2, .8, .2, 1) both;
}
.help-toast-icon {
  width: 96rem;
  height: 96rem;
  flex: none;
  border-radius: 50%;
  background: var(--mint);
  display: flex;
  align-items: center;
  justify-content: center;
}
.help-toast-card h2 { font-size: 44rem; font-weight: 800; letter-spacing: -.02em; }
.help-toast-card p { font-size: 22rem; line-height: 1.5; color: #cccccc; }

/* Staff peek: long-press "CHECK-IN" next to the logo for 3s (see
   bindStatsPeek() in script.js) — how many are checked in out of the list. */
.stats-peek {
  position: absolute;
  top: 120rem;
  left: 120rem;
  z-index: 25;
  display: flex;
  flex-direction: column;
  gap: 8rem;
  padding: 24rem 28rem;
  border: 1rem solid var(--line);
  border-radius: 10rem;
  background: var(--paper);
  box-shadow: 0 14rem 30rem rgba(0, 0, 0, .07);
  animation: popIn .2s ease both;
  text-align: left;
}
.stats-peek-label { font-size: 13rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: var(--muted); }
.stats-peek-value { font-size: 48rem; font-weight: 800; letter-spacing: -.02em; line-height: 1; }
.stats-peek-value span { font-size: 20rem; font-weight: 600; letter-spacing: 0; color: var(--muted); }

@keyframes helpToastBackdropIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes helpToastCardIn {
  from { opacity: 0; transform: scale(.94); }
  to { opacity: 1; transform: scale(1); }
}

@keyframes spinRing { to { transform: rotate(360deg); } }
@keyframes slideUp { from { opacity: 0; transform: translateY(18rem); } to { opacity: 1; transform: none; } }
@keyframes popIn { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: scale(1); } }
@keyframes progressSlide { from { transform: translateX(-100%); } to { transform: translateX(250%); } }

/* Portrait (1080 wide): a bit smaller so everything still fits the width. */
html:not(.orientation-landscape) .kiosk-header { padding: 0 56rem; }
html:not(.orientation-landscape) .headline { font-size: 92rem; }
html:not(.orientation-landscape) .headline--m { font-size: 68rem; }
html:not(.orientation-landscape) .search-result-row { width: 100%; justify-content: space-between; }
html:not(.orientation-landscape) .stats-peek { left: 56rem; }

/* print-only badge — everything else in the kiosk is suppressed at print
   time. Sized to match the GoogleCafe_Badge.svg artwork's own native size
   (102x68mm) — the new artwork's own viewBox is natively 90x60mm, but
   that's the exact same 3:2 aspect ratio, so it scales up proportionally
   to fill the label with no distortion. Cut lands right at the
   bottom edge of the sticker with no blank label margin below it.
   Deliberately still in real mm/px here, not rem — printing is a fixed
   physical output, completely unrelated to the on-screen fluid scaling
   above. */

/* NOT display:none — badge-render.js measures text width
   (getComputedTextLength()) on the elements inside here to decide whether
   an organization name needs to wrap/shrink, and that always returns 0
   inside a display:none subtree (no layout at all), silently disabling
   the overflow handling on every real print despite it working fine in
   any test that happens to render into an always-visible container.
   visibility:hidden keeps this invisible and non-interactive while still
   being fully laid out; position:fixed keeps it out of normal document
   flow so it can't affect the visible kiosk screens. */
.print-badge { position: fixed; top: 0; left: 0; visibility: hidden; pointer-events: none; z-index: -1; }

/* Explicit size even OUTSIDE @media print — this used to only be set
   inside the print media query, which left this element at a collapsed
   0x0 box the rest of the time. That's a problem beyond just visibility:
   badge-render.js's getComputedTextLength() calls (used to decide
   whether a name needs to shrink/truncate) still return SOME number from
   a 0-sized box, just not an accurate one — confirmed by testing, this
   silently made the real print produce different truncation results than
   an on-screen preview or any test rendering into a normally-sized
   element. Sizing it correctly at all times fixes that at the source. */
.print-badge-side { width: var(--badge-w, 102mm); height: var(--badge-h, 68mm); }
.print-badge-side svg, .print-badge-side img { display: block; width: var(--badge-w, 102mm); height: var(--badge-h, 68mm); }

@media print {
  @page { size: 102mm 68mm; margin: 0; }

  body > *:not(#printBadge) { display: none !important; }

  .print-badge { position: static; visibility: visible; pointer-events: auto; z-index: auto; display: block; }

  /* The kiosk's own dark viewport background (html/body, #161616) otherwise
     shows through wherever the badge artwork itself has transparent areas —
     invisible with the old window.print() path (only the rendered pixels
     ever left the browser) but not with the Electron shell's silent print,
     which captures the real page including that background. Force it
     white for print — same fix as kiosk/style.css and admin/style.css. */
  .print-badge-side { background: #fff; page-break-after: always; }
  .print-badge-side:last-child { page-break-after: auto; }
}
