/* Party Pulse — design tokens
   -----------------------------------------------------------------------
   Palette: a wedding reception after dark — the room the big screen and
   every phone actually get used in. Deep dusk ink, warm rose (alive, the
   "pulse"), champagne gold (reveal, celebration). Committed dark-first
   rather than split light/dark: every surface here is used at an evening
   party, so hedging on a light theme wouldn't serve any real screen.
   Type: Fraunces (self-hosted, one variable-weight file — see vendor
   rationale below) carries the personality at headline size; everything
   read up close (forms, buttons, body copy) stays on system-ui, which
   needs no network trip and is plenty legible on a phone in someone's
   hand. Signature: the pulse-line waveform (see _pulse_line.html) —
   literally the product's name — used as the idle/thinking state on the
   big screen and as the baseline every stat bar grows from on reveal. */

@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 600 700;
  font-display: swap;
  src: url("fonts/fraunces-variable-latin.069cb90e11a3.woff2") format("woff2");
}

:root {
  --ink: #1b1730;
  --surface: #2b2244;
  --surface-2: #382c58;
  --cream: #f6efe4;
  --mist: #a89cc0;
  --rose: #e15a7a;
  --gold: #dba955;

  --font-display: "Fraunces", ui-serif, Georgia, serif;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  --radius: 0.9rem;
  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  min-height: 100%;
}

body {
  font-family: var(--font-body);
  color: var(--cream);
  background: var(--ink);
  line-height: 1.5;
}

a { color: var(--gold); }

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 650;
  line-height: 1.15;
  margin: 0 0 0.6em;
}

.page {
  max-width: 32rem;
  margin: 0 auto;
  padding: 1.75rem 1.25rem 4rem;
}

.wordmark {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  font-family: var(--font-display);
  font-weight: 650;
  font-size: 1.5rem;
  color: var(--cream);
  text-decoration: none;
}
.wordmark span { color: var(--rose); }

.eyebrow {
  display: block;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--mist);
  margin-bottom: 0.4rem;
}

/* Forms & controls -------------------------------------------------- */

label {
  display: block;
  font-size: 0.95rem;
  color: var(--mist);
  margin-bottom: 1rem;
}

input[type="text"], input[type="number"], input[type="password"], select, textarea {
  display: block;
  width: 100%;
  margin-top: 0.35rem;
  padding: 0.65rem 0.8rem;
  font: inherit;
  font-size: 1rem;
  color: var(--cream);
  background: var(--surface);
  border: 1px solid var(--surface-2);
  border-radius: 0.6rem;
}
input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible, a:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}

button, .btn, input[type="submit"] {
  display: inline-block;
  font: inherit;
  font-size: 1rem;
  font-weight: 600;
  padding: 0.7rem 1.1rem;
  border-radius: 0.6rem;
  border: 1px solid var(--rose);
  background: var(--rose);
  color: var(--ink);
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.08s ease, background 0.15s ease;
}
button:hover, .btn:hover { background: #ea7391; }
button:active { transform: translateY(1px); }

button.secondary, .btn.secondary {
  background: transparent;
  color: var(--cream);
  border-color: var(--surface-2);
}
button.secondary:hover { background: var(--surface); border-color: var(--mist); }

fieldset {
  border: 1px solid var(--surface-2);
  border-radius: var(--radius);
  padding: 1.1rem 1.2rem 1.2rem;
  margin: 0 0 1.25rem;
}
legend {
  font-family: var(--font-display);
  font-weight: 650;
  padding: 0 0.4rem;
}

/* Cards --------------------------------------------------------------- */

.card {
  background: var(--surface);
  border: 1px solid var(--surface-2);
  border-radius: var(--radius);
  padding: 1.1rem 1.25rem;
  margin-bottom: 0.9rem;
}

.question-card {
  background: var(--surface);
  border: 1px solid var(--surface-2);
  border-radius: var(--radius);
  padding: 1.2rem 1.3rem;
  margin-bottom: 1rem;
}
.question-card p:first-child {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  margin: 0 0 0.9rem;
}
.question-card--answered {
  opacity: 0.6;
}
.question-card--answered p {
  font-family: var(--font-body);
  font-weight: 500;
  margin: 0;
}

/* Answer choices ---------------------------------------------------------
   Every option in a group is a genuine peer — nothing here is a "primary"
   action the way Kom igång / Skicka förslag are, so choices deliberately
   do NOT use .btn's solid-vs-secondary treatment (that pairing implies a
   featured/default option, which biased Ja over Nej and made multi-choice
   options read as muted/disabled rather than tappable). One equal style
   for every option; the only thing that changes is the option's own label. */
.choices {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
}
.choices--row {
  flex-direction: row;
  gap: 0.7rem;
}

.choice {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  text-align: left;
  padding: 0.85rem 1.05rem;
  background: var(--surface);
  border: 1px solid var(--surface-2);
  border-radius: 0.7rem;
  color: var(--cream);
  font: inherit;
  font-size: 1rem;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.08s ease;
}
.choice:hover { border-color: var(--rose); background: var(--surface-2); }
.choice:active { transform: scale(0.98); }
.choices--row .choice { justify-content: center; text-align: center; flex: 1; }

/* Radio-style dot on multi-choice rows (skipped for the compact Ja/Nej
   row — a centered label is affordance enough at that size) — reinforces
   "pick one from this list" and, while a tap is in flight, fills with the
   same rose→gold gradient the big screen reveals with: the one moment the
   guest's own tap visually becomes "the pulse". */
.choice__dot {
  flex: none;
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 999px;
  border: 2px solid var(--mist);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.choice:hover .choice__dot { border-color: var(--rose); }

/* htmx adds .htmx-request to the form while its POST is in flight (forms
   own the hx-post here, not the individual buttons) -- dim + freeze every
   option in the group so a slow connection reads as "received", not
   "nothing happened, tap again". */
form.htmx-request .choice { opacity: 0.65; pointer-events: none; }
form.htmx-request .choice__dot {
  border-color: transparent;
  background: linear-gradient(135deg, var(--rose), var(--gold));
}

.identity-btn {
  display: block;
  width: 100%;
  text-align: left;
  margin-bottom: 0.55rem;
  background: var(--surface);
  border-color: var(--surface-2);
  color: var(--cream);
  font-weight: 500;
}
.identity-btn:hover { background: var(--surface-2); }

/* Pulse-line signature -------------------------------------------------
   A heartbeat waveform, tripled and looped: the idle/thinking state on
   the big screen, and (statically) the baseline each reveal bar grows
   from. Respects reduced-motion by simply not scrolling. */

.pulse-line {
  width: 100%;
  overflow: hidden;
  line-height: 0;
  color: var(--rose);
}
.pulse-line__svg {
  display: block;
  width: 300%;
  height: 2.5rem;
}
.pulse-line__svg path {
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
@media (prefers-reduced-motion: no-preference) {
  .pulse-line--animated .pulse-line__svg {
    animation: pulse-scroll 6s linear infinite;
  }
}
@keyframes pulse-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-33.333%); }
}

/* Big screen ------------------------------------------------------------
   Viewed from across a room, always dark. Fraunces carries the question
   text and the reveal numbers; everything else stays quiet. */

.screen {
  min-height: 100vh;
  /* Paired with min-height above, this pins .screen's used height at exactly 100vh (when
     min-height and max-height are equal, an auto height has no room left to be anything
     else) instead of letting it grow past the viewport the way it could before this fix --
     see the big vw/vh comment below for how stacked content got tall enough to do that.
     overflow:hidden then clips instead of scrolling the whole page. .results (below) is the
     one part of this layout with genuinely unbounded content -- a variable, host-controlled
     number of breakdown-group x option rows -- so it gets its own overflow-y:auto to absorb
     any excess itself; the title and prompt line never shrink or scroll. 2026-09-07, fixing
     the "used to fit, now scrolls on a normal monitor" regression from d82c8b0 below. */
  max-height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 2.5rem;
  padding: 4vw 6vw;
  background:
    radial-gradient(ellipse at 50% -10%, rgba(225, 90, 122, 0.16), transparent 60%),
    var(--ink);
}

/* Font sizes below were `clamp(min, Nvw, max)` -- a hard rem ceiling that reads fine on a
   laptop preview but plateaus well before it should on an actual big screen: on a 4K
   projector (3840px wide) a `6vw` preferred value is 230px, so the old 5rem/80px max was
   capping it at a third of what the viewport actually had room for -- confirmed too small
   on a real 4K projector, 2026-09-01. Dropped the ceiling (`max(min, Nvw)`, i.e. clamp
   without its top clause) so this keeps growing with viewport width with no upper limit;
   the min clause still protects small/host-preview windows.

   2026-09-07: that fix had a blind spot -- vw alone has no idea how tall the viewport is,
   and the old rem ceiling had *incidentally* also been capping how tall the whole stacked
   column (word/question + pulse line + results panel, all centered by .screen's
   justify-content:center) could get. Drop that ceiling and an entirely ordinary 1920x1080
   monitor -- width generous, nothing special about it -- can end up asking for more total
   height than 100vh once a handful of result rows stack up: reported as "used to fit, now
   scrolls" on a normal display, not just as a projector edge case. Every rule below is now
   `max(rem-floor, min(Nvw, Mvh))` -- bi-axial, bounded by whichever axis is tighter. Mvh is
   picked so it's a no-op at a standard 16:9 aspect (Mvh == Nvw exactly when
   width:height == 16:9, i.e. M = N * 16/9 ~= N * 1.78, then rounded) -- so a plain
   1920x1080 or 1280x720 screen renders identically to before this change; only a viewport
   that's shorter/squarer than 16:9 relative to its width (or just literally short, e.g. a
   browser window with a lot of chrome eating into it) starts pulling sizes down. This is a
   soft/cosmetic safety net, not the actual overflow fix -- for a plain 16:9 screen it does
   nothing at all, by design -- .screen's max-height:100vh + overflow:hidden (above) and
   .results' overflow-y:auto (below) are what actually guarantee the page itself never
   scrolls, on any aspect ratio. */
.screen-word {
  font-family: var(--font-display);
  font-size: max(2.5rem, min(6vw, 10.7vh));
  font-weight: 650;
}
.screen-word span { color: var(--rose); }

.screen-question {
  font-family: var(--font-display);
  font-size: max(1.8rem, min(4vw, 7.1vh));
  font-weight: 650;
  /* Was a flat 46rem -- narrower than .results' own width budget just below (`min(90vw,
     160vh)`), so a long question wrapped to three lines on a wide screen while the
     breakdown data underneath it had plenty of room to spare. Match .results' budget
     exactly instead of an arbitrary rem cap, so the header uses the same real estate the
     panel below it already does and wraps only when the panel itself would too. 2026-09-08. */
  max-width: min(90vw, 160vh);
  margin: 0;
}

.screen .pulse-line { max-width: 48rem; }

/* Results-view pulse-line only (see _pulse_line.html's `compact` param, used solely at
   that include site) -- the idle/thinking and asking states get the full-size waveform
   since it's the only thing on screen there, but once real breakdown data is showing
   underneath it, a 2.5rem-tall decorative flourish is competing with that data for
   vertical space. Shrink it there specifically rather than everywhere. 2026-09-08. */
.pulse-line--compact.pulse-line { max-width: 22rem; }
.pulse-line--compact .pulse-line__svg { height: 1.1rem; }

.results {
  /* was min(56rem, 90vw) -- same ceiling problem as the font sizes above, capping the
     whole results panel at 896px regardless of screen size.

     2026-09-07: given the same bi-axial min(Nvw, Mvh) treatment as the font-size rules
     above, for consistency -- Mvh=160 (90 * 16/9) is a no-op at any screen 16:9 or wider
     and only narrows the panel on a viewport unusually tall/narrow for its width. Width
     isn't actually what caused the overflow bug this session is fixing (a narrower panel
     can *increase* height by wrapping option labels onto more lines, if anything), so this
     line alone doesn't do much; the real fix is the two properties below. */
  width: min(90vw, 160vh);
  display: flex;
  flex-direction: column;
  gap: 1.6rem;
  /* This is the one part of the layout with genuinely unbounded content -- a variable,
     host-controlled number of breakdown-group x option rows -- so it's the one place that
     needs to be able to actually give: min-height:0 overrides a flex item's default
     automatic minimum size (roughly "never shrink below your own content"), which is what
     was stopping this from shrinking at all when .screen's new max-height:100vh (above)
     leaves it less room than it wants; overflow-y:auto then turns "shrunk smaller than its
     content" into "scrolls its own content" instead of clipping it -- or, pre-fix, instead
     of letting .screen grow past 100vh and scroll the whole page, title included.
     Deliberately not flex-grow:1/`flex:1 1 auto` (the obvious "scrollable flex panel"
     idiom): that would make .results always expand to consume whatever vertical space is
     left over, which would pin the title to the top of the screen instead of today's
     vertically-centered look (via .screen's justify-content:center) for the common case of
     a short results list. Leaving flex-grow at its default 0 means .results only ever
     shrinks -- and only once the column is already too tall for 100vh -- so the normal
     case is untouched and only the pathological one (many groups x many options) scrolls. */
  overflow-y: auto;
  min-height: 0;
}

.result-group__label {
  font-size: max(0.85rem, min(1.3vw, 2.3vh));
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--mist);
  margin-bottom: 0.5rem;
  text-align: left;
}

.result-bar-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-bottom: 0.6rem;
}
.result-bar-row:last-child { margin-bottom: 0; }

.result-bar-track {
  flex: 1;
  height: max(1.6rem, min(2.2vw, 3.9vh));
  background: var(--surface);
  border-radius: 999px;
  overflow: hidden;
  position: relative;
}
.result-bar-fill {
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--rose), var(--gold));
  transform-origin: left;
  animation: grow-bar 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
@media (prefers-reduced-motion: reduce) {
  .result-bar-fill { animation: none; }
}
@keyframes grow-bar {
  from { transform: scaleX(0); }
}

.result-value {
  font-family: var(--font-display);
  font-weight: 650;
  font-size: max(1.1rem, min(2vw, 3.6vh));
  color: var(--gold);
  min-width: 4.5ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.result-number {
  font-family: var(--font-display);
  font-weight: 650;
  font-size: max(2.2rem, min(6vw, 10.7vh));
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

.result-count {
  font-size: 0.85rem;
  color: var(--mist);
}

/* Host console ---------------------------------------------------------
   Same palette, quieter: the host is operating this live, not admiring
   it — clarity and speed over display-face flourish. */

.host-page { max-width: 40rem; }
.host-page h1 { font-size: 1.6rem; margin-bottom: 0.3rem; }
.host-header { display: flex; justify-content: space-between; align-items: baseline; }
.host-header .wordmark { margin: 0; }
.host-logout { font-size: 0.85rem; color: var(--mist); }
.host-logout:hover { color: var(--cream); }

/* Nav cards: deliberately NOT a row of adjacent buttons -- an earlier version used one
   solid .btn next to two .btn.secondary in a row, which visually read as a tab bar with a
   selected/disabled state that didn't actually exist (every link here is always equally
   clickable). Cards in a grid carry no such implication. */
.nav-grid { display: grid; gap: 0.7rem; margin: 1.2rem 0 1.6rem; }
.nav-card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--surface-2);
  border-left: 3px solid var(--rose);
  border-radius: var(--radius);
  padding: 1rem 1.2rem;
  text-decoration: none;
  color: var(--cream);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.nav-card:hover { border-color: var(--rose); background: var(--surface-2); }
.nav-card__title {
  font-family: var(--font-display);
  font-weight: 650;
  font-size: 1.1rem;
  color: var(--cream);
}
.nav-card__desc { font-size: 0.88rem; color: var(--mist); margin-top: 0.2rem; }
.host-status {
  display: inline-block;
  font-size: 0.85rem;
  padding: 0.3rem 0.7rem;
  border-radius: 999px;
  background: var(--surface);
  color: var(--mist);
}
.host-status--live { background: rgba(225, 90, 122, 0.2); color: var(--rose); }

/* Suggestions panel ("Intressanta fynd") -----------------------------------
   Decision aid only, deliberately styled as a list of quiet, clickable cards
   rather than anything resembling the reveal UI above -- clicking loads the
   form's fields, it never reveals anything itself. See pulse/suggestions.py
   and PLAN.md "Interesting stats suggestions". */
.suggestions { margin-top: 1.6rem; }
.suggestions h2 { font-size: 1.15rem; margin-bottom: 0.3rem; }
.suggestions__intro, .suggestions__empty {
  font-size: 0.88rem;
  color: var(--mist);
  margin: 0 0 1rem;
}
/* One of the three ranked lists ("Mest olika" / "Mest lika" / "Störst
   procentskillnad", see pulse/suggestions.py) -- same card style throughout,
   just under its own heading and separated with a bit of extra top margin so
   the three panels read as distinct sections rather than one long list. */
.suggestions__group { margin-top: 1.4rem; }
.suggestions__group h3 { font-size: 1rem; margin-bottom: 0.2rem; }
.suggestion-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}
.suggestion-card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--surface-2);
  border-left: 3px solid var(--gold);
  border-radius: var(--radius);
  padding: 0.85rem 1.05rem;
  text-decoration: none;
  color: var(--cream);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.suggestion-card:hover { border-color: var(--gold); background: var(--surface-2); }
.suggestion-card__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.6rem;
}
.suggestion-card__question {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
}
.suggestion-card__meta {
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mist);
  margin: 0.2rem 0 0.4rem;
}
.suggestion-card__blurb { margin: 0; font-size: 0.92rem; }
.suggestion-card__score {
  margin-top: 0.4rem;
  font-size: 0.78rem;
  color: var(--mist);
  font-variant-numeric: tabular-nums;
}
.badge {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 600;
  padding: 0.2rem 0.55rem;
  border-radius: 999px;
  white-space: nowrap;
}
.badge--small-sample {
  background: rgba(219, 169, 85, 0.2);
  color: var(--gold);
}

/* Combined "Topp 10, alla kategorier" panel (pulse/suggestions.py's `Highlight` /
   `compute_suggestions().highlights`) -- sits above the six original per-category lists,
   reusing .suggestion-card/.suggestion-list as-is (same card, same click-through) plus one
   small addition, the category tag, so a merged card still says which of the six lists it
   came from. */
.suggestions__group--highlights h3 { font-size: 1.15rem; }
.badge--category {
  background: var(--surface-2);
  color: var(--mist);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-right: 0.4rem;
}
/* One of the (at most two) reserved "Mest lika" picks (Johan's decision 2: a
   magnitude-sorted round-robin never naturally surfaces similarity, so up to one highlight
   from "Mest lika" and one from "Mest lika, grupp mot grupp" are reserved separately) --
   the category tag above already names it, this is just a quiet visual "pinned" cue so it
   doesn't read as randomly out of place among the higher-magnitude round-robin picks. */
.suggestion-card--similarity { border-left-color: var(--rose); }

/* The six original per-category lists (Mest olika/lika, Störst procentskillnad, and their
   grupp-mot-grupp counterparts) are unchanged in content but now collapsed by default below
   the combined panel above, via a plain <details>/<summary> disclosure -- no JS framework,
   matching this app's htmx-only/no-build-step approach (PLAN.md "Tech stack decision"). */
.suggestions__all summary {
  cursor: pointer;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  color: var(--cream);
  margin-top: 1.6rem;
  padding: 0.6rem 0;
}
.suggestions__all summary:hover { color: var(--gold); }

/* QR sign ("/qr/") -------------------------------------------------------
   Standalone poster, not extending base.html/.page -- pulled up full-bleed
   on a lobby TV or printed and taped to a wall (pulse/views.py's qr_sign(),
   see PLAN.md "Guest entry QR sign"), so it needs its own centered layout
   rather than the article-width .page column every other route uses.
   On-screen it's the same after-dark party palette as .screen; @media
   print swaps to plain white/ink so a printed copy doesn't try to lay down
   a full-bleed dark background in a home printer (wasted toner, and most
   browsers don't even print background colors by default) -- everything
   below only re-points colors for that swap, the layout itself is shared. */
.qr-sign {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 1.1rem;
  padding: 6vh 6vw;
  background:
    radial-gradient(ellipse at 50% -10%, rgba(225, 90, 122, 0.16), transparent 60%),
    var(--ink);
}
.qr-sign .wordmark { font-size: 1.3rem; }
.qr-sign__headline {
  font-size: clamp(2rem, 5vw, 3.2rem);
  max-width: 30rem;
}
.qr-sign__sub {
  max-width: 28rem;
  margin: -0.4rem 0 0.6rem;
  color: var(--mist);
  font-size: clamp(1rem, 1.6vw, 1.15rem);
}

/* White card is deliberate even against the dark on-screen background --
   see pulse/qr.py's render_qr_svg(): the code itself is plain black
   modules with no background rect, so it needs real light behind it for a
   phone camera to read it at all, on-screen or on paper. */
.qr-sign__card {
  background: var(--cream);
  border-radius: 1.2rem;
  padding: 1.4rem;
  box-shadow: 0 0.6rem 2.5rem rgba(0, 0, 0, 0.35);
}
.qr-sign__card svg {
  display: block;
  width: min(60vw, 20rem);
  height: min(60vw, 20rem);
}

.qr-sign__url {
  font-family: var(--font-display);
  font-weight: 650;
  font-size: clamp(1.1rem, 2.2vw, 1.4rem);
  color: var(--gold);
  word-break: break-all;
}
.qr-sign__footnote {
  max-width: 26rem;
  margin: -0.6rem 0 0;
  font-size: 0.85rem;
  color: var(--mist);
}
.qr-sign__print-btn { margin-top: 0.8rem; }

/* Poster-style print: keep the actual on-screen look (dark background, gold
   accents, card shadow) instead of flattening to black-on-white, since this
   sign is meant to be printed and put up as-is, not read off paper economically.
   Browsers omit backgrounds by default when printing, so print-color-adjust
   is what makes that dark background actually come out of the printer. */
@media print {
  * {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
    color-adjust: exact !important;
  }
  @page { margin: 0; }
  .qr-sign { min-height: 100vh; }
  .qr-sign__print-btn { display: none; }
}
