@import url("https://fonts.googleapis.com/css2?family=Bangers&family=Lato:wght@400;700;900&display=swap");

/* ─── QuizRace Marketing Palette Tokens ─────────────────────────────────── */
/* Attensi "Default Colors" — marketing surface only. Do not import in app.  */
:root {
  /* Brand — Epic Purple */
  --qr-brand: #6f23a8;
  --qr-brand-strong: #481370;
  --qr-brand-tint: #f4d8fd;
  --qr-brand-accent: #c469e3;

  /* Accent */
  --qr-coin: #ffc700;

  /* Neutrals, set for the dark purple canvas: carbon is the ink and canvas and
     paper are the surfaces under it, not the other way round. */
  --qr-carbon: #f5f5f5;
  --qr-coal: #c6c6c6;
  --qr-steel: #4d4d4d;
  --qr-paper: #1c1c1c;
  --qr-canvas: #121212;

  /* Semantic */
  --qr-link: #0076ff;
  --qr-success: #00ae40;

  /* Derived convenience values */
  --qr-border: #2c2c2c;
  --qr-muted: #8f8f8f;
  /* Secondary text where the surface is purple rather than dark neutral: the
     grey goes dirty against it and only reaches 4.7:1 on the footer's fill.
     Pulled most of the way to the brand tint, this lands at 8.4:1 — still a
     step under the coal links at 8.9:1, so labels do not outshine the things
     they label. */
  --qr-muted-tint: color-mix(in srgb, var(--qr-brand-tint) 60%, var(--qr-muted));
  --qr-radius-sm: 11px;
  --qr-radius-md: 16px;
  --qr-radius-lg: 22px;

  /* The canvas as a gradient, so the sticky header can repaint a copy of it
     behind itself: a slow purple wave down the length of the page, so the dark
     cards and slabs read as floating on brand colour. */
  --qr-page-wash: linear-gradient(
    180deg,
    var(--qr-brand) 0%,
    var(--qr-brand-strong) 25%,
    var(--qr-brand) 50%,
    var(--qr-brand-strong) 75%,
    var(--qr-brand) 100%
  );
  /* What the bar shows before JS has measured the washes for it. */
  --qr-header-fallback: var(--qr-brand-strong);
  --qr-hero-glow: radial-gradient(
    1200px 720px at var(--qr-hero-drift) 6%,
    color-mix(in srgb, var(--qr-brand) 30%, transparent),
    transparent 68%
  );
  --qr-hero-wash: linear-gradient(180deg, #390e59 0%, #390e59 38%, rgba(57, 14, 89, 0) 100%);

  animation: qr-hero-drift 26s ease-in-out infinite alternate;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    animation: none;
  }
}

/* ─── Base resets (marketing pages only) ────────────────────────────────── */
body {
  margin: 0;
}

/* The UA's [hidden] rule loses to any class that sets display, so hiding a
   .qr-tiers or .qr-billing with the attribute alone does nothing. */
[hidden] {
  display: none !important;
}

.qr-page {
  font-family: "Lato", system-ui, sans-serif;
  color: var(--qr-carbon);
  background: var(--qr-page-wash);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.qr-page h1,
.qr-page h2,
.qr-page h3 {
  font-weight: 900;
  letter-spacing: -0.5px;
  line-height: 1.05;
}

/* ─── Layout ─────────────────────────────────────────────────────────────── */
.qr-wrap {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 24px;
}

/* ─── Buttons ────────────────────────────────────────────────────────────── */
.qr-btn {
  display: inline-block;
  font-family: inherit;
  font-weight: 900;
  font-size: 16px;
  border-radius: var(--qr-radius-sm);
  padding: 11px 18px;
  cursor: pointer;
  border: 2px solid transparent;
  text-decoration: none;
  transition:
    transform 0.12s ease,
    box-shadow 0.12s ease;
  line-height: 1;
}

.qr-btn:hover {
  transform: translateY(-1px);
}

.qr-btn--primary {
  background: var(--qr-brand);
  color: #fff;
  border-color: var(--qr-brand);
}

.qr-btn--primary:hover {
  background: var(--qr-brand-strong);
  border-color: var(--qr-brand-strong);
}

/* A translucent white plate instead of an outline. It separates the secondary
   action from the purple without adding another hairline next to the header's
   other buttons, and being translucent it tracks the gradient behind it.
   Transparent border rather than none, so the box model still matches. */
.qr-btn--ghost {
  background: rgba(255, 255, 255, 0.14);
  color: var(--qr-carbon);
  border-color: transparent;
}

.qr-btn--ghost:hover {
  background: rgba(255, 255, 255, 0.26);
  color: var(--qr-carbon);
}

.qr-btn--lg {
  font-size: 16px;
  padding: 14px 28px;
  border-radius: var(--qr-radius-md);
}

/* Flex rather than the base inline-block so the glyph centres against the label
   instead of sitting on its baseline. Centred explicitly because the mobile nav
   stretches these to full width, where text-align can't reach a flex child. */
.qr-btn--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

/* Sized off the 14px label rather than the icon's own 24px box, which would
   tower over it. currentColor carries it. The glyph still stands 2px above that
   line box and would make the button taller than the ghost one beside it, so
   the negative block margin folds the overhang back. */
.qr-btn__icon {
  width: 16px;
  height: 16px;
  margin-block: -1px;
  flex-shrink: 0;
}

/* Label is the darkest purple on the page — the hero and footer wash — rather
   than a neutral black, so the white buttons read as part of the brand. The
   shadow replaces the hairline this used to carry, which was a dark ring on the
   dark header. */
.qr-btn--light {
  background: #fff;
  color: #390e59;
  box-shadow:
    0 1px 2px rgba(23, 23, 23, 0.06),
    0 4px 12px -4px rgba(23, 23, 23, 0.18);
}

/* Dimming to --qr-paper would land on near-black under dark text; a dimmed
   white keeps the press feedback readable. */
.qr-btn--light:hover {
  background: #e6e6e6;
  box-shadow:
    0 2px 4px rgba(23, 23, 23, 0.08),
    0 8px 20px -6px rgba(23, 23, 23, 0.22);
}

/* ─── Header / Nav ───────────────────────────────────────────────────────── */
.qr-header {
  position: sticky;
  top: 0;
  z-index: 50;
}

/* Fully opaque — that's what keeps the nav text legible over whatever scrolls
   under it — but painted with the page's own washes offset to the header's
   position, so the bar is the exact colour of the backdrop it covers instead of
   a band of its own. The mobile sheet shares the recipe, off by
   --qr-wash-shift. enhance.js feeds the geometry; with the sizes left at 0 the
   layers don't paint and both fall back to the flat fill. */
.qr-header,
html.nav-open .qr-nav__panel {
  background-color: var(--qr-header-fallback);
  background-image: var(--qr-hero-glow), var(--qr-hero-wash), var(--qr-page-wash);
  background-repeat: no-repeat;
  background-size:
    var(--qr-hero-w, 0px) var(--qr-hero-h, 0px),
    var(--qr-hero-w, 0px) var(--qr-hero-h, 0px),
    var(--qr-page-w, 0px) var(--qr-page-h, 0px);
  background-position:
    var(--qr-hero-x, 0px) calc(var(--qr-hero-y, 0px) - var(--qr-wash-shift, 0px)),
    var(--qr-hero-x, 0px) calc(var(--qr-hero-y, 0px) - var(--qr-wash-shift, 0px)),
    var(--qr-page-x, 0px) calc(var(--qr-page-y, 0px) - var(--qr-wash-shift, 0px));
}

/* The panel is the header's surface dropping down, so it takes the same washes
   — shifted by its own offset inside the header, since it inherits the bar's
   geometry vars but sits 64px lower. */
html.nav-open .qr-nav__panel {
  --qr-wash-shift: 64px;
}

/* At the very top there is nothing to match: the hero is directly behind, so
   the bar just gets out of its way. */
.qr-header--top {
  background: transparent;
}

.qr-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64px;
}

.qr-logo img {
  display: block;
}

/* No gap for the same reason .qr-nav__right has none: each link carries its own
   padding now, which does the separating. The row is pulled back by one pad and
   border so the first label still starts where it did beside the logo. */
.qr-nav__links {
  display: flex;
  gap: 0;
  list-style: none;
  font-weight: 700;
  font-size: 16px;
  /* Coal only reached 4.07:1 on the purple; carbon takes the links to 6.4:1. */
  color: var(--qr-carbon);
  padding: 0;
  margin: 0 0 0 -16px;
}

/* Boxed to .qr-nav__login's measurements, down to the transparent border, so
   the hover fill lands as a pill of the same height and radius as the controls
   on the other side of the bar. */
.qr-nav__links a {
  display: inline-flex;
  align-items: center;
  color: inherit;
  text-decoration: none;
  padding: 11px 14px;
  border: 2px solid transparent;
  border-radius: var(--qr-radius-sm);
  line-height: 1;
}

/* Brand purple was all but invisible against a purple header; this is the fill
   the login link and the ghost button already use. */
.qr-nav__links a:hover {
  background: rgba(255, 255, 255, 0.14);
}

.qr-nav__right {
  display: flex;
  align-items: center;
  /* No gap, so the two tertiary controls sit flush and read as one group; the
     solid CTA buys its own separation back below. */
  gap: 0;
  font-weight: 700;
  font-size: 16px;
}

.qr-nav__login + .qr-btn {
  margin-left: 16px;
}

/* Boxed like a button rather than sized to its own label, so the hover fill
   lands as a 40px pill in line with the buttons beside it instead of a tight
   rectangle round the text. The transparent border and line-height 1 are how
   .qr-btn reaches that height, so the two stay level. */
.qr-nav__login {
  color: var(--qr-carbon);
  text-decoration: none;
  padding: 11px 14px;
  border: 2px solid transparent;
  border-radius: var(--qr-radius-sm);
  line-height: 1;
}

/* Colour stays put on hover: the old rule went brand purple, which is all but
   invisible on a purple header. */
.qr-nav__login:hover {
  background: rgba(255, 255, 255, 0.14);
}

/* In the bar the ghost button sits between this plain link and a solid white
   one, where a resting fill read as a third weight of button. Stripped to text
   and icon so the two tertiary controls match, and given the same hover fill so
   they behave alike too. Pricing keeps the filled ghost. */
.qr-nav__right .qr-btn--ghost {
  background: transparent;
}

.qr-nav__right .qr-btn--ghost:hover {
  background: rgba(255, 255, 255, 0.14);
}

.qr-nav__toggle {
  display: none;
  background: none;
  border: none;
  font-size: 22px;
  cursor: pointer;
  padding: 6px;
  color: var(--qr-carbon);
}

/* One-shot notice band above the header, hidden until enhance.js unhides it. */
.qr-notice {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 16px;
  padding: 12px 16px;
  background: var(--qr-paper);
  color: var(--qr-carbon);
  text-align: center;
}

.qr-notice[hidden] {
  display: none;
}

.qr-notice__text {
  margin: 0;
  font-size: 15px;
}

.qr-notice__link {
  color: var(--qr-coin);
  font-weight: 800;
  text-decoration: underline;
  white-space: nowrap;
}

/* Below the breakpoint the header's "Join a game" goes behind the hamburger —
   and joining is the one thing a player, as opposed to a host, arrives here to
   do. It comes back out as a bar of its own above the header, and the copy in
   the sheet stands down so there is only ever one of them. Coin rather than
   brand purple because the bar has to read as a different thing from the
   header it sits on, not a taller version of it. */
.qr-joinbar {
  display: none;
  align-items: center;
  justify-content: center;
  gap: 10px;
  height: 40px;
  padding: 0 16px;
  background: var(--qr-coin);
  color: var(--qr-brand-strong);
  font-weight: 900;
  font-size: 16px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  text-decoration: none;
  /* The band is a fixed 40px, so a second line would spill straight out of it. */
  white-space: nowrap;
}

.qr-joinbar__icon {
  width: 14px;
  height: 14px;
}

/* Uneven vertical padding: the label is all caps on line-height 1, so the glyphs
   sit high in their box and need the extra pixel on top to look centred. */
.qr-joinbar__pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--qr-brand-strong);
  border-radius: 999px;
  padding: 5px 12px 4px;
  color: #fff;
  font-size: 13px;
  letter-spacing: 1px;
  line-height: 1;
}

/* Below ~360px the sentence and the pill stop clearing the edges. The decorative
   icon goes and the tracking tightens, so the label itself keeps its size. */
@media (max-width: 360px) {
  .qr-joinbar {
    letter-spacing: 0.8px;
  }

  .qr-joinbar__icon {
    display: none;
  }

  .qr-joinbar__pill {
    letter-spacing: 0.6px;
  }
}

/* ─── Hero ───────────────────────────────────────────────────────────────── */
.qr-hero {
  padding: 64px 0 40px;
}

.qr-hero__grid {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: 44px;
  align-items: center;
}

.qr-eyebrow {
  color: var(--qr-brand);
  font-weight: 900;
  font-size: 16px;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  margin-bottom: 14px;
}

.qr-hero__text h1 {
  font-size: 52px;
  margin-bottom: 18px;
}

.qr-lead {
  font-size: 19px;
  color: var(--qr-coal);
  max-width: 460px;
  margin-bottom: 26px;
}

.qr-cta-row {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
}

.qr-cta-row--center {
  justify-content: center;
}

.qr-microcopy {
  margin-top: 14px;
  font-size: 16px;
  color: var(--qr-muted);
}

.qr-video {
  position: relative;
  background: #171717;
  border: none;
  border-radius: var(--qr-radius-lg);
  aspect-ratio: 16 / 10;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  overflow: hidden;
  box-shadow: 0 24px 50px -16px rgba(0, 0, 0, 0.35);
}

.qr-video:hover .qr-video__play {
  transform: scale(1.08);
}

.qr-video__play {
  width: 74px;
  height: 74px;
  border-radius: 50%;
  background: var(--qr-brand);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 26px;
  transition: transform 0.15s;
  box-shadow: 0 8px 24px color-mix(in srgb, var(--qr-brand) 50%, transparent);
  pointer-events: none;
}

.qr-video__star {
  position: absolute;
  top: 16px;
  right: 18px;
  color: var(--qr-coin);
  font-size: 26px;
  pointer-events: none;
}

.qr-video__label {
  position: absolute;
  bottom: 14px;
  left: 18px;
  color: #fff;
  font-weight: 700;
  font-size: 16px;
  opacity: 0.9;
  pointer-events: none;
}

/* ─── Trust Logos ────────────────────────────────────────────────────────── */
/* Sits in the hero's lower band, over the gradient. `qr-wrap` keeps it on the
   page grid; `margin: 0 auto` from it centres the box between left/right 0. */
.qr-hero__trust {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 44px;
}

.qr-trust__label {
  text-align: center;
  color: rgba(255, 255, 255, 0.5);
  font-weight: 900;
  font-size: 16px;
  letter-spacing: 1px;
  margin-bottom: 18px;
}

/* Seamless loop: two identical tracks scroll as one: when the first has fully
   left, the second sits exactly where it started. The copy is aria-hidden so
   the logos are announced once. */
.qr-logowheel {
  --qr-logowheel-gap: 72px;
  --qr-logowheel-fade: 40px;
  display: flex;
  gap: var(--qr-logowheel-gap);
  overflow: hidden;
  /* Fixed fade, so the logos stay close to the grid edge at any width. */
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent,
    #000 var(--qr-logowheel-fade),
    #000 calc(100% - var(--qr-logowheel-fade)),
    transparent
  );
  mask-image: linear-gradient(
    90deg,
    transparent,
    #000 var(--qr-logowheel-fade),
    #000 calc(100% - var(--qr-logowheel-fade)),
    transparent
  );
}

.qr-logowheel__track {
  display: flex;
  flex: 0 0 auto;
  justify-content: space-around;
  gap: var(--qr-logowheel-gap);
  min-width: 100%;
  list-style: none;
  padding: 0;
  margin: 0;
  animation: qr-logowheel 38s linear infinite;
}

.qr-logowheel:hover .qr-logowheel__track {
  animation-play-state: paused;
}

.qr-logowheel__logo {
  white-space: nowrap;
  font-weight: 900;
  font-size: 18px;
  color: #fff;
  opacity: 0.7;
}

@keyframes qr-logowheel {
  to {
    transform: translateX(calc(-100% - var(--qr-logowheel-gap)));
  }
}

@media (prefers-reduced-motion: reduce) {
  .qr-logowheel__track {
    animation: none;
  }
  .qr-logowheel__track[aria-hidden="true"] {
    display: none;
  }
}

/* ─── Section blocks ─────────────────────────────────────────────────────── */
/* No band of their own: the sections let the page's wash run behind them
   unbroken, which is what lets the sticky header repaint that wash and match
   the backdrop exactly wherever it stops. The cards inside carry the surface. */
/* Asymmetric on purpose: the space below a section does the separating, so it is
   twice the space above. A heading sits closer to its own content than to the
   section it follows, which is the grouping the eye wants. */
.qr-block {
  padding: 64px 0 128px;
}

.qr-center {
  text-align: center;
  max-width: 660px;
  margin: 0 auto 44px;
}

/* The UA top margin is ~31px at this size and collapses over the kicker's own
   bottom margin, so the kicker can only set the gap once this is zeroed. */
.qr-center h2 {
  font-size: 38px;
  margin: 0 0 14px;
}

.qr-center p {
  color: var(--qr-coal);
  font-size: 17px;
}

.qr-kicker {
  color: var(--qr-brand);
  font-weight: 900;
  font-size: 16px;
  letter-spacing: 1px;
  text-transform: uppercase;
  margin: 0 0 6px;
}

/* ─── How It Works ───────────────────────────────────────────────────────── */
.qr-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  list-style: none;
  padding: 0;
  margin: 0;
}

.qr-step {
  background: var(--qr-paper);
  border: 1px solid var(--qr-border);
  border-radius: 20px;
  padding: 28px;
}

.qr-step__num {
  width: 40px;
  height: 40px;
  border-radius: 12px;
  background: var(--qr-brand);
  color: #fff;
  font-weight: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
  font-size: 18px;
}

.qr-step h3 {
  font-size: 21px;
  margin-bottom: 8px;
}

.qr-step p {
  color: var(--qr-coal);
  font-size: 16px;
}

/* ─── How It Works — expanding flow ──────────────────────────────────────────
   One step open at a time. The open card grows via flex-grow while the others
   shrink; each card clips its own content, so the copy and image are simply
   revealed rather than reflowed. Copy has a fixed width and the image is taken
   out of flow, which keeps every card the same height whichever step is open —
   no jump as the cycle advances. `data-active` is driven by [data-qr-flow];
   without JS the first step stays open. */
.qr-flow {
  --qr-flow-pad: 28px;
  --qr-flow-media: 280px;
  --qr-flow-height: 336px;
  --qr-flow-radius: 20px;
  /* The image sits on its own tighter gutter than the text padding, so it hugs
     the card edge instead of floating in the middle of it. */
  --qr-flow-media-gap: 10px;
  /* Bangers digits stand ~0.73em tall, so this renders one at 110% of the card. */
  --qr-flow-num-size: calc(var(--qr-flow-height) * 1.1 / 0.73);
  counter-reset: qr-flow-step;
  display: flex;
  gap: 20px;
  list-style: none;
  padding: 0;
  margin: 36px 0 0;
}

.qr-flow__step {
  position: relative;
  isolation: isolate;
  counter-increment: qr-flow-step;
  box-sizing: border-box;
  flex: 1 1 0;
  min-width: 0;
  min-height: var(--qr-flow-height);
  overflow: hidden;
  padding: var(--qr-flow-pad);
  /* Closed cards already carry a purple cast; opening one deepens the same mix
     rather than switching hue, so the transition reads as one card lighting up. */
  background: color-mix(in srgb, var(--qr-brand) 14%, #1c1c1c);
  border: 1px solid var(--qr-border);
  border-radius: var(--qr-flow-radius);
  transition:
    flex-grow 0.55s cubic-bezier(0.4, 0, 0.2, 1),
    border-color 0.35s ease,
    background-color 0.35s ease;
}

.qr-flow__step[data-active] {
  flex-grow: 3;
  border-color: color-mix(in srgb, var(--qr-brand) 55%, transparent);
  background: color-mix(in srgb, var(--qr-brand) 34%, #1c1c1c);
}

.qr-flow__step:not([data-active]):hover {
  border-color: var(--qr-steel);
}

.qr-flow__heading {
  font-size: 21px;
  margin: 0;
}

.qr-flow__toggle {
  display: block;
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

/* Whole closed card is the hit area; the open one stays selectable text. */
.qr-flow__step:not([data-active]) .qr-flow__toggle::after {
  content: "";
  position: absolute;
  inset: 0;
}

.qr-flow__toggle:focus-visible {
  outline: 2px solid var(--qr-brand-accent);
  outline-offset: 4px;
}

/* The step number as a watermark, overflowing the card on three sides so the
   card crops it. z-index needs the step's own stacking context to sit above
   the card background but under the copy. */
.qr-flow__step::before {
  content: counter(qr-flow-step);
  position: absolute;
  z-index: -1;
  top: 50%;
  left: -5%;
  transform: translateY(-50%);
  font-family: "Bangers", system-ui, sans-serif;
  font-size: var(--qr-flow-num-size);
  line-height: 1;
  color: rgba(255, 255, 255, 0.06);
  pointer-events: none;
  transition: color 0.35s ease;
}

/* Brand purple would now sit too close to the card it's painted on, so the
   active numeral steps up to the lighter accent to stay readable. */
.qr-flow__step[data-active]::before {
  color: color-mix(in srgb, var(--qr-brand-accent) 26%, transparent);
}

/* Countdown to the next step, along the bottom edge of the open card. The card
   clips it to its own radius. enhance.js owns the duration and drives play/pause
   through the Web Animations API, so the bar and the timer stop together; with
   no script the duration is missing, the shorthand drops out, and the bar stays
   at zero width — which is right, because nothing is advancing either. */
@keyframes qr-flow-countdown {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

/* The unspent part of the dwell. An inset shadow rather than a second box, so
   it picks up the card's radius and costs no layout. */
.qr-flow__step[data-active] {
  box-shadow: inset 0 -3px 0 rgba(255, 255, 255, 0.12);
}

.qr-flow__step[data-active]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: var(--qr-brand-accent);
  animation: qr-flow-countdown var(--qr-flow-step-ms) linear forwards;
}

.qr-flow__copy,
.qr-flow__media {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.3s ease,
    visibility 0s linear 0.3s;
}

.qr-flow__step[data-active] .qr-flow__copy,
.qr-flow__step[data-active] .qr-flow__media {
  opacity: 1;
  visibility: visible;
  transition:
    opacity 0.45s ease 0.2s,
    visibility 0s;
}

/* Coal is a page-wide token tuned for flat cards; here the watermark numeral
   passes behind the text and lifts the backdrop, so this copy takes a brighter
   grey than its siblings. Still a step below the heading's near-white.

   The measure answers to the rake it runs under rather than to the reading
   line — both raked steps put their longest line where the slant is widest. It
   only bites where it crosses a break point: anything from 248 to 258 breaks on
   the same words, and 247 is the first that pulls a word down, which buys the
   text 14px of clearance and costs no line. */
.qr-flow__copy {
  width: 247px;
  margin: 18px 0 0;
  color: #ededed;
  font-size: 16px;
}

.qr-flow__media {
  position: absolute;
  top: var(--qr-flow-media-gap);
  right: var(--qr-flow-media-gap);
  width: var(--qr-flow-media);
  height: calc(100% - var(--qr-flow-media-gap) * 2);
  /* The card's inner radius less the gutter: keeps the image's curve concentric
     with the card's, which is what stops the corners reading as two arcs. */
  border-radius: calc(var(--qr-flow-radius) - 1px - var(--qr-flow-media-gap));
  /* A trapezium leaning into the copy: the right edge stays plumb with the card
     and the left one rakes forward, so the bottom measures two thirds of the
     top. Cover crops from the right so the slant eats margin rather than
     content. The polygon is the fallback shape() rounds off — where shape() is
     understood it wins on order, elsewhere the corners just stay sharp. Both
     slant ends run the same 9px fillet as the box corners, so the tangent
     lengths differ: the blunt bottom needs less run-up than the point at the
     top. Both raked steps take this shape; only the square middle one opts out. */
  clip-path: polygon(0 0, 100% 0, 100% 100%, 33.33% 100%);
  clip-path: shape(
    from calc(33.33% + 6.4px) 100%,
    line to calc(100% - 9px) 100%,
    arc to 100% calc(100% - 9px) of 9px ccw,
    line to 100% 9px,
    arc to calc(100% - 9px) 0 of 9px ccw,
    line to 12.8px 0,
    arc to 1.28% 3.83% of 9px ccw,
    line to 32.69% 98.1%,
    arc to calc(33.33% + 6.4px) 100% of 9px ccw,
    close
  );
  object-fit: cover;
  object-position: right center;
}

/* The desktop geometry of the three media boxes. It sits behind the breakpoint
   rather than on the shared rule above because stacked cards square off and give
   up the rake altogether (see the mobile block). Boxes grow leftward off the
   pinned right gutter, and the rakes are percentages of the box, so a resize
   carries its own slant with it — the extra goes into the margin the rake opens
   beside the copy, not into the copy. */
@media (min-width: 861px) {
  /* One box across all three, which is what makes the three slants come out the
     same length: the rake is a percentage of the width, so equal widths give
     equal edges, and only the direction of the cut is left to differ. */
  .qr-flow__media {
    width: calc(var(--qr-flow-media) * 1.2);
  }

  .qr-flow__step:nth-child(1) .qr-flow__media {
    /* This screenshot is close to twice as wide as it is tall against a box that
       is nearly square, so cover spends 40% of the width on the crop. A length
       rather than the `left` keyword: the window opens 25px past the screenshot's
       own left edge, and everything else still comes off the right. */
    object-position: -25px center;
  }

  /* The middle step takes the same rake mirrored top to bottom, so the row reads
     as a slant that changes its mind once rather than three parallel ones. Same
     edges as its neighbours, swapped: the short one is on top here. Every point
     is the shared shape at 100% less its own y, which is also why the fillets
     turn cw where the others turn ccw — mirroring a path reverses its sweep.

     The cut lands on the top left, which is where this screenshot keeps its join
     code, so the crop gives up the far end of the player grid and holds the left
     of the frame instead: the code and its QR are what the copy beside it
     promises. Even 12% along the crop puts the slant through the code's label. */
  .qr-flow__step:nth-child(2) .qr-flow__media {
    clip-path: polygon(33.33% 0, 100% 0, 100% 100%, 0 100%);
    clip-path: shape(
      from calc(33.33% + 6.4px) 0,
      line to calc(100% - 9px) 0,
      arc to 100% 9px of 9px cw,
      line to 100% calc(100% - 9px),
      arc to calc(100% - 9px) 100% of 9px cw,
      line to 12.8px 100%,
      arc to 1.28% 96.17% of 9px cw,
      line to 32.69% 1.9%,
      arc to calc(33.33% + 6.4px) 0 of 9px cw,
      close
    );
    object-position: left center;
  }
}

@media (prefers-reduced-motion: reduce) {
  .qr-flow__step,
  .qr-flow__step::before,
  .qr-flow__copy,
  .qr-flow__media {
    transition: none;
  }
  /* Nothing advances on its own here, so there is no countdown to show. */
  .qr-flow__step[data-active]::after {
    display: none;
  }
}

/* Stacked, so the cards grow downwards instead of sideways. Nothing is clipped
   here, so the hidden content is taken out of flow rather than just faded. */
@media (max-width: 860px) {
  .qr-flow {
    /* Cards are title-height when closed, so the watermark keys off that. */
    --qr-flow-num-size: 120px;
    flex-direction: column;
    gap: 12px;
  }
  .qr-flow__step,
  .qr-flow__step[data-active] {
    flex: none;
    min-height: 0;
  }
  .qr-flow__copy,
  .qr-flow__media {
    display: none;
  }
  .qr-flow__step[data-active] .qr-flow__copy,
  .qr-flow__step[data-active] .qr-flow__media {
    display: block;
  }
  .qr-flow__copy {
    width: auto;
  }
  /* Stacked under the copy and inside the card's padding, so there is no edge
     to be concentric with — it takes a plain radius of its own. */
  .qr-flow__media {
    position: static;
    width: 100%;
    max-width: 320px;
    height: auto;
    margin-top: 20px;
    border-radius: 14px;
    /* The rake reads as forward motion across a row of cards; stacked, it would
       just look like a corner had been sliced off. */
    clip-path: none;
    object-position: center;
  }
}

/* ─── Features ───────────────────────────────────────────────────────────── */
/* Uneven split rather than half-and-half: the copy only needs a readable
   measure, and the rest buys the screenshot room to be legible. */
.qr-feature {
  display: grid;
  grid-template-columns: 2fr 3fr;
  gap: 44px;
  align-items: start;
  padding: 30px 0;
}

/* Doubles the 60px the two paddings already leave between neighbours, without
   touching the space the first and last keep against the section's own edges. */
.qr-feature + .qr-feature {
  margin-top: 60px;
}

/* Visual on the left every other row. The tracks swap along with it so the wide
   one always follows the image. */
.qr-feature--alt {
  grid-template-columns: 3fr 2fr;
}

.qr-feature--alt .qr-feature__text {
  order: 2;
}

.qr-feature__text h3 {
  font-size: 30px;
  margin-bottom: 14px;
}

.qr-feature__text p {
  color: var(--qr-coal);
  font-size: 17px;
  margin-bottom: 14px;
}

.qr-feature__bullets {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.qr-feature__bullets li {
  font-size: 16px;
  font-weight: 700;
  color: var(--qr-coal);
}

.qr-chk {
  color: var(--qr-carbon);
  font-weight: 900;
  margin-right: 8px;
}

/* Shares the screenshots' proportions so the three stacked blocks stay the same
   shape — full width, a fixed height would read as a much flatter box than the
   real image above it. */
.qr-feature__visual {
  background: linear-gradient(135deg, #212121, #191919);
  border: 1px solid var(--qr-border);
  border-radius: 20px;
  aspect-ratio: 1600 / 1009;
  /* Or the ratio measures the content box and the padding adds on top, leaving
     the placeholders taller than the screenshot. */
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--qr-muted);
  font-weight: 900;
  font-size: 16px;
  text-align: center;
  padding: 24px;
}

/* Same frame, but a real screenshot rather than the placeholder caption: the
   centring and padding exist to hold that text and would letterbox the image,
   and the hairline that gives an empty box its edge just clips the artwork. The
   fill goes too — the artwork is a cut-out on transparency, so anything painted
   behind it shows through instead of the page. The declared ratio goes back to
   the file's own: it is there to give the empty placeholder a shape, and left
   standing it would override the artwork's and crop whatever did not match. */
img.qr-feature__visual {
  display: block;
  width: 100%;
  height: auto;
  padding: 0;
  border: 0;
  background: none;
  aspect-ratio: auto;
  object-fit: cover;
}

/* A narrow measure beside one big panel, as per the reference. The panel takes
   its height from the artwork rather than from the viewport, so the proportions
   hold at every window size — tying it to vh instead would mean the crop
   tightening or slackening as the window changes height. */
@media (min-width: 861px) {
  .qr-feature {
    grid-template-columns: minmax(220px, 260px) 1fr;
  }

  .qr-feature--alt {
    grid-template-columns: 1fr minmax(220px, 260px);
  }
}

/* ─── Audience Split ─────────────────────────────────────────────────────── */
.qr-aud {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  list-style: none;
  padding: 0;
  margin: 0;
}

/* Light cards on the purple canvas — the one row that stops being dark, which is
   what gives this section its own weight in the page. A translucent lift off the
   gradient was the alternative and it read as no card at all. The neutral tokens
   are set for the dark canvas, so every colour inside has to be re-anchored to a
   light-surface value or it lands near-white on white; a whisper of brand in the
   white keeps them from being a foreign rectangle. */
.qr-acard {
  --qr-acard-pad: 28px;
  /* The thumbnail rides a tighter gutter than the text, hugging the card edge
     instead of floating in the middle of it — the same 10px the flow screenshots
     take, so the two rows of cards treat their images alike. Scoped to each
     component rather than shared, as .qr-flow keeps its own metrics. */
  --qr-acard-thumb-gutter: 10px;
  --qr-acard-radius: 20px;
  border: 1px solid color-mix(in srgb, var(--qr-brand) 24%, #fff);
  border-radius: var(--qr-acard-radius);
  padding: var(--qr-acard-pad);
  background: color-mix(in srgb, var(--qr-brand) 13%, #fff);
  box-shadow: 0 14px 30px -18px rgba(24, 5, 40, 0.55);
  /* A column so the link can be sent to the floor of the card. The grid already
     stretches all three to the tallest, so the only thing that moved the links
     out of line was a description running to a third line. */
  display: flex;
  flex-direction: column;
  /* Containing block for the link's click overlay. */
  position: relative;
}

/* The whole card is a click target, so it has to answer the pointer as one —
   without a lift, the only thing that reacted to a hover halfway up the card was
   an arrow down at the floor of it. Focus-within covers the keyboard, where the
   link inside is what actually takes focus. */
.qr-acard:hover,
.qr-acard:focus-within {
  border-color: color-mix(in srgb, var(--qr-brand) 45%, #fff);
  box-shadow: 0 22px 38px -20px rgba(24, 5, 40, 0.7);
  transform: translateY(-2px);
}

/* A banner across the top of the card rather than a tile, so each one is
   identifiable before the heading is read. Decorative — the heading already
   says who it is for — hence the empty alt. The ratio is declared rather than
   left to the file so the space is reserved before it loads, and cover takes
   the crop if a source ever arrives at a different shape. */
.qr-acard__thumb {
  display: block;
  /* Reaches back out through the card's text padding as far as its own gutter,
     which is why the width grows by the overhang on both sides. */
  --qr-acard-thumb-bleed: calc(var(--qr-acard-pad) - var(--qr-acard-thumb-gutter));
  width: calc(100% + var(--qr-acard-thumb-bleed) * 2);
  margin: calc(var(--qr-acard-thumb-bleed) * -1) calc(var(--qr-acard-thumb-bleed) * -1)
    16px;
  /* Both dimensions have to be off the table for the ratio to be consulted, and
     the height attribute reaches the box as a presentational hint. */
  height: auto;
  aspect-ratio: 2 / 1;
  /* The card's inner radius less the border and the gutter, so the image's curve
     stays concentric with the card's — the flow screenshots are cut the same way.
     A radius set independently of the inset reads as two unrelated arcs. */
  border-radius: calc(var(--qr-acard-radius) - 1px - var(--qr-acard-thumb-gutter));
  object-fit: cover;
}

/* Kept at zero even with the thumbnail above: that carries the gap now, and the
   default leading would stack on top of it. */
.qr-acard h3 {
  color: #390e59;
  font-size: 22px;
  margin-top: 0;
  margin-bottom: 10px;
}

/* Flex items do not collapse margins, so the default top margin stacked on the
   heading's bottom one and the gap came out at 26px rather than the 10px the
   rule above reads as. */
.qr-acard p {
  color: #5a5a5a;
  font-size: 16px;
  margin-top: 0;
  margin-bottom: 14px;
}

/* --qr-link is tuned for the dark canvas and only reaches 4.2:1 on a card this
   light, so the blue is taken down a step. */
.qr-acard__link {
  color: #0059c9;
  font-weight: 900;
  font-size: 16px;
  text-decoration: none;
  /* Takes up whatever the description left over, so the three links sit on one
     line across the row and the slack collects above them instead. */
  margin-top: auto;
  /* Or the link box stretches the full width of the card and takes clicks well
     past the end of its own text. */
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Stretched over the whole card so a click anywhere lands on this link. Doing it
   from the anchor rather than wrapping the card in one keeps a single link in the
   accessibility tree, with the visible text as its name. The cost is that card
   text can no longer be selected. */
.qr-acard__link::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--qr-acard-radius);
}

/* The arrow sits here rather than in the markup so that hover can move it on its
   own — and so it stops being read out as part of the link. */
.qr-acard__link::after {
  content: '→';
}

.qr-acard:hover .qr-acard__link::after,
.qr-acard__link:focus-visible::after {
  transform: translateX(4px);
}

@media (prefers-reduced-motion: no-preference) {
  .qr-acard {
    transition:
      transform 160ms ease,
      box-shadow 160ms ease,
      border-color 160ms ease;
  }

  .qr-acard__link::after {
    transition: transform 160ms ease;
  }
}

/* ─── Testimonials ───────────────────────────────────────────────────────── */
.qr-quotes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  list-style: none;
  padding: 0;
  margin: 0;
}

/* A translucent lift off the gradient rather than the light card the audience row
   takes above: it tracks the wash as that shifts down the page, which is also
   what the pricing tiers below do. */
.qr-quote {
  background: rgba(255, 255, 255, 0.09);
  border-radius: 20px;
  padding: 26px;
}

/* Coin is a hair under AA on this surface (4.4:1) — the same hue carried toward
   white clears it while still reading as gold rather than as more white text. */
.qr-quote__stars {
  color: color-mix(in srgb, var(--qr-coin) 75%, #fff);
  font-size: 16px;
  margin-bottom: 12px;
}

.qr-quote__text {
  font-size: 16px;
  margin-bottom: 18px;
}

.qr-quote__who {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* An initial rather than a photo: a lift off the card rather than a colour of
   its own, so it reads at any brand mix. Steel was near-invisible once the card
   lightened. Drop in a headshot by replacing the letter with an <img>. */
.qr-quote__av {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  flex-shrink: 0;
  display: grid;
  place-items: center;
  font-weight: 900;
  font-size: 17px;
  line-height: 1;
  color: #fff;
}

.qr-quote__who strong {
  font-size: 16px;
  display: block;
}

.qr-quote__who span {
  display: block;
  color: #dcdcdc;
  font-size: 16px;
}

/* ─── Pricing ────────────────────────────────────────────────────────────── */
.qr-draftnote {
  text-align: center;
  color: var(--qr-brand);
  font-weight: 900;
  font-size: 16px;
  margin-bottom: 24px;
}

.qr-tiers {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 18px;
  align-items: start;
  list-style: none;
  padding: 0;
  margin: 0;
}

/* The unfilled tiers read as a lightened wash of whatever the page gradient is
   doing behind them — a translucent white tracks the gradient as it shifts,
   where a fixed purple would drift out of step with it. Transparent border
   rather than none, so the box model still matches the pro card's 2px edge. */
.qr-tier {
  background: rgba(255, 255, 255, 0.09);
  border: 1px solid transparent;
  border-radius: 22px;
  padding: 26px;
  position: relative;
}

/* The card itself is painted in the banner colour; the pale surface is a layer
   inset below the banner, so it can carry its own top corners instead of
   starting as a hard square edge under it. */
.qr-tier--pro {
  background: var(--qr-brand);
  /* Without a stacking context of its own, the negative-index layer below
     would sink behind the card's background rather than sitting on it. */
  isolation: isolate;
  border: 2px solid var(--qr-brand);
  box-shadow: 0 20px 40px -18px color-mix(in srgb, var(--qr-brand) 40%, transparent);
}

/* Behind the content but in front of the card's background, which is what
   shows through at the two corner notches. Offset by the banner's fixed 32px. */
.qr-tier--pro::before {
  content: "";
  position: absolute;
  inset: 32px 0 0;
  background: #fff;
  border-radius: 20px;
  z-index: -1;
}

/* A banner across the top of the card rather than a floating pill. The negative
   margins bleed it back out through the card's 26px padding to the inner border
   edge, and line-height 1 plus 8px of padding pins its height to an exact 32px:
   the pale layer below starts at that mark and the sibling cards offset by it,
   so a taller banner reaches down over the layer's top corners and shaves the
   roundest part of the arc off them. */
.qr-tier__badge {
  margin: -26px -26px 26px;
  padding: 8px 12px;
  background: var(--qr-brand);
  color: #fff;
  font-weight: 900;
  font-size: 16px;
  line-height: 1;
  letter-spacing: 0.5px;
  text-align: center;
  /* The card's 22px radius less its 2px border. */
  border-radius: 20px 20px 0 0;
  white-space: nowrap;
}

/* Once the cards share a row the featured one sets the height and the siblings
   sit inset from it — 14px in at each end, which is the break in the row, and
   another 20px off the top so they meet its pale surface rather than its outer
   box (that surface starts below a 2px border and a 32px banner). The 20px goes
   straight back out of their padding, so all four headings still land on one
   line. Written as the siblings pulling in rather than the pro card pushing out
   because negative margins there left it hanging out of the list's own box.

   1001px, not 861px: between the two the list drops to two columns, where the
   siblings have no shared row to be inset from and the offsets just open a gap
   between the rows. */
@media (min-width: 1001px) {
  .qr-tier:not(.qr-tier--pro) {
    margin-block: 34px 14px;
    padding-top: 12px;
  }

  /* The featured card also breaks the row by taking a wider column. Grid sizes
     tracks, not items, so the 1.1fr is positional: it must be moved to match
     whichever column .qr-tier--pro sits in, or the highlight and the wide track
     drift apart. Currently column 2 (Pro). */
  .qr-tiers {
    grid-template-columns: 1fr 1.1fr 1fr 1fr;
  }

  .qr-tier--pro {
    padding-bottom: 40px;
  }

  /* Both offsets absorb the 14px the card gained, so its heading and feature
     list still land on the neighbours' lines despite the taller box. */
  .qr-tier--pro .qr-tier__badge {
    margin-bottom: 13px;
  }
}

/* The UA's 1em top margin on an h3 was adding a hidden 24px above every card
   title; the card's own padding sets the distance instead. */
.qr-tier h3 {
  font-size: 24px;
  margin: 0 0 6px;
}

/* Coal is only 4.95:1 on the raw gradient and muted never clears AA on it at
   all, so the secondary text in every tier takes one lighter value instead. */
.qr-tier__desc {
  color: #dcdcdc;
  font-size: 16px;
  min-height: 54px;
}

.qr-tier__price {
  font-size: 40px;
  font-weight: 900;
  margin: 12px 0 2px;
  /* Baseline, not centre: the sign rides the cap height and the unit sits on
     the number's baseline, so all three read as one price rather than three
     stacked sizes. Tiers with a word instead of a figure just inherit this. */
  display: flex;
  align-items: baseline;
  line-height: 1;
}

/* Roughly cap height on the digits, which is where the sign wants to sit. */
.qr-tier__currency {
  font-size: 0.55em;
  align-self: flex-start;
  padding-top: 0.06em;
}

/* 0.4 of the 40px price is the 16px floor; any smaller and the unit would be
   the one piece of the page under it. */
.qr-tier__unit {
  font-size: 0.4em;
  font-weight: 700;
  color: #dcdcdc;
  margin-left: 2px;
}

.qr-tier__period {
  font-size: 16px;
  color: #dcdcdc;
  font-weight: 700;
}

.qr-tier__rule {
  border: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.16);
  margin: 20px 0 16px;
}

.qr-tier__featlabel {
  color: #fff;
  font-size: 16px;
  font-weight: 800;
  margin: 0 0 12px;
}

.qr-tier__features {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.qr-tier__features li {
  font-size: 16px;
  color: #dcdcdc;
}

.qr-tier__features li::before {
  content: "✓";
  color: var(--qr-success);
  font-weight: 900;
  margin-right: 8px;
}

.qr-tier .qr-btn {
  width: 100%;
  box-sizing: border-box;
  text-align: center;
  margin-top: 18px;
  display: block;
}

/* The one light surface in the row, so every colour inside it has to be
   re-anchored off the values the other tiers use against the gradient. Placed
   after them because .qr-tier--pro h3 and .qr-tier h3 tie on specificity. */
.qr-tier--pro h3,
.qr-tier--pro .qr-tier__featlabel,
.qr-tier--pro .qr-tier__price {
  color: #171717;
}

.qr-tier--pro .qr-tier__desc,
.qr-tier--pro .qr-tier__period,
.qr-tier--pro .qr-tier__unit,
.qr-tier--pro .qr-tier__features li {
  color: #5a5a5a;
}

.qr-tier--pro .qr-tier__rule {
  border-top-color: rgba(0, 0, 0, 0.12);
}

/* --qr-success is 3:1 on white; the ticks need a deeper green to stay legible. */
.qr-tier--pro .qr-tier__features li::before {
  color: #0a7a35;
}

/* ─── FAQ ────────────────────────────────────────────────────────────────── */
/* More than the 128px between two full sections, precisely because this one is
   nested in the pricing block: it has no band of its own to mark the change of
   subject, so the separation has to come from the space alone or it reads as a
   footnote to the plans. */
.qr-faq {
  max-width: 760px;
  margin: 160px auto 0;
}

/* That 160px only exists to clear the tier cards. While pricing is hidden the
   FAQ is this section's only content and the gap reads as a hole — and this
   reverts on its own the moment the tiers lose their hidden attribute. */
.qr-tiers[hidden] ~ .qr-faq {
  margin-top: 0;
}

.qr-faq__heading {
  font-size: 28px;
}

.qr-faq__list {
  list-style: none;
  padding: 0;
  margin: 0;
}

/* --qr-border is a dark neutral meant for the edge of a dark card; ruled across
   the purple canvas it read as a scratch. Translucent white lifts the wash
   instead and tracks it as the gradient shifts down the page, where a fixed
   purple would drift out of step — the same divider the footer's top edge uses. */
.qr-qa {
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
}

/* The row's padding belongs to the button, not the li around it — otherwise the
   strip above and below the question looks part of the control but sits outside
   the hit target. Full width and the plus pushed to the far end, so the whole
   row answers to a click. */
.qr-qa__q {
  font-size: 17px;
  font-weight: 900;
  display: flex;
  width: 100%;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
  background: none;
  border: none;
  padding: 18px 0;
  font-family: inherit;
  text-align: left;
  color: inherit;
}

.qr-qa__a {
  color: var(--qr-coal);
  font-size: 16px;
  padding-bottom: 18px;
  margin: 0;
  display: none;
}

.qr-qa.open .qr-qa__a {
  display: block;
}

/* Two bars rather than a glyph, because a "+" character cannot tween into a
   "−" — the upright bar rotates onto the flat one instead. Brand purple would
   be sitting on a brand-purple canvas. Sized off the question rather than in px
   so the two stay in step, and the box keeps the height the 1.5em glyph had:
   it is the tallest thing in the row, so a shorter one would pull every
   question 5px closer together. */
.qr-qa__pm {
  position: relative;
  flex-shrink: 0;
  width: 0.9em;
  height: 1.5em;
  color: var(--qr-carbon);
}

.qr-qa__pm::before,
.qr-qa__pm::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 0;
  left: 0;
  height: 2px;
  margin-top: -1px;
  border-radius: 1px;
  background: currentColor;
}

/* Upright while closed; the open state lays it flat over its twin. */
.qr-qa__pm::after {
  transform: rotate(90deg);
}

.qr-qa.open .qr-qa__pm::after {
  transform: rotate(0deg);
}

@media (prefers-reduced-motion: no-preference) {
  .qr-qa__pm::after {
    transition: transform 220ms ease;
  }
}

/* ─── Final CTA ──────────────────────────────────────────────────────────── */
.qr-finalcta-wrap {
  padding: 24px 0 0;
}

/* Two corner glows in brand purple over a base deeper than the page behind it,
   so the slab still sits back off the gradient. */
.qr-finalcta {
  background:
    radial-gradient(
      700px 420px at 12% -10%,
      color-mix(in srgb, var(--qr-brand) 62%, transparent),
      transparent 60%
    ),
    radial-gradient(
      900px 520px at 88% 110%,
      color-mix(in srgb, var(--qr-brand) 44%, transparent),
      transparent 65%
    ),
    #290a41;
  color: #fff;
  border-radius: 28px;
  text-align: center;
  padding: 56px 24px;
}

.qr-finalcta h2 {
  font-size: 38px;
  margin-bottom: 14px;
  color: #fff;
}

.qr-finalcta h2 em {
  color: var(--qr-coin);
  font-style: normal;
}

.qr-finalcta p {
  color: #cfcfcf;
  margin-bottom: 24px;
  font-size: 17px;
}

/* ─── Footer ─────────────────────────────────────────────────────────────── */
/* Same wash as the hero, so the page gradient is bookended by it. */
.qr-footer {
  padding: 56px 0 40px;
  background: #390e59;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  margin-top: 64px;
}

.qr-footer__grid {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;
  gap: 24px;
}

.qr-footer__brand {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.qr-footer__tagline {
  color: var(--qr-muted-tint);
  font-size: 16px;
}

.qr-footer__col-title {
  font-size: 16px;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--qr-muted-tint);
  margin-bottom: 14px;
}

.qr-footer__col a {
  display: block;
  color: var(--qr-coal);
  font-size: 16px;
  margin-bottom: 9px;
  font-weight: 700;
  text-decoration: none;
}

/* Brand purple drops to 1.8:1 on this fill — hover has to brighten, not dim. */
.qr-footer__col a:hover {
  color: var(--qr-carbon);
}

.qr-footer__copy {
  margin-top: 36px;
  color: var(--qr-muted-tint);
  font-size: 16px;
}

/* ─── Video Lightbox ─────────────────────────────────────────────────────── */
.qr-lightbox {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
  z-index: 100;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.qr-lightbox.open {
  display: flex;
}

.qr-lightbox__box {
  width: min(900px, 100%);
  aspect-ratio: 16 / 9;
  position: relative;
}

.qr-lightbox__box iframe {
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: 12px;
}

.qr-lightbox__close {
  position: absolute;
  top: -42px;
  right: 0;
  color: #fff;
  font-size: 30px;
  cursor: pointer;
  font-weight: 900;
  background: none;
  border: none;
  line-height: 1;
  font-family: inherit;
}

/* ─── Mobile nav open state ──────────────────────────────────────────────── */
/* The panel drops out of the layout on desktop, so the links and the auth
   buttons stay separate flex items of the bar exactly as before. Only on
   mobile does it become the sheet the hamburger opens, carrying both. */
.qr-nav__panel {
  display: contents;
}

/* Once the panel dissolves, the links are a middle flex item and space-between
   centres them between the wordmark and the auth cluster. An auto margin takes
   all the slack on one side instead, so the links sit with the wordmark and the
   buttons stay pinned right. Guarded, because on mobile the nav is a block in
   the sheet and the indent would just push the links off the sheet's edge. */
@media (min-width: 861px) {
  .qr-nav__panel > nav {
    margin-left: 40px;
    margin-right: auto;
  }
}

@media (max-width: 860px) {
  /* Both bars stay put, stacked: the join bar at the top and the header parked
     directly under it, so the action survives scrolling rather than being a
     thing you only see if you happen to be at the top of the page. */
  .qr-joinbar {
    display: flex;
    position: sticky;
    top: 0;
    z-index: 51;
  }

  .qr-header {
    top: 40px;
  }

  /* The bar above carries it now. */
  .qr-nav__panel .qr-btn--icon {
    display: none;
  }

  .qr-nav__panel {
    display: none;
    position: absolute;
    top: 64px;
    left: 0;
    right: 0;
    padding: 16px 24px 20px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.14);
    z-index: 49;
  }

  html.nav-open .qr-nav__panel {
    display: block;
  }

  /* Stacked, so the row's pull-back becomes a per-item one, keeping the labels
     against the panel's own padding while the hover pill reaches past it. Most
     of the 16px that used to separate them is now inside them, which makes each
     one a 42px tap target. */
  .qr-nav__panel .qr-nav__links {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    font-size: 16px;
  }

  .qr-nav__panel .qr-nav__right {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    margin-top: 18px;
    padding-top: 18px;
    border-top: 1px solid rgba(255, 255, 255, 0.14);
  }

  .qr-nav__panel .qr-nav__login,
  .qr-nav__panel .qr-nav__right .qr-btn {
    text-align: center;
    padding: 12px 20px;
  }

  /* Stacked full-width here, so the bar's horizontal offset would just knock
     the CTA out of line with the controls above it. */
  .qr-nav__panel .qr-nav__login + .qr-btn {
    margin-left: 0;
  }
}

/* ─── Responsive ─────────────────────────────────────────────────────────── */

/* Four across leaves each card ~195px here, which is too narrow for the CTA
   labels — they wrap to two lines and break the button baseline across the row. */
@media (min-width: 861px) and (max-width: 1000px) {
  .qr-tiers {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 860px) {
  .qr-hero__grid,
  .qr-feature,
  .qr-steps,
  .qr-aud,
  .qr-quotes,
  .qr-tiers,
  .qr-footer__grid {
    grid-template-columns: 1fr;
  }

  .qr-nav__toggle {
    display: block;
  }

  .qr-hero__text h1 {
    font-size: 38px;
  }

  .qr-feature--alt .qr-feature__text {
    order: unset;
  }

  /* The player is only ~190px tall here; the desktop pool would be half of it. */
  .qr-demo::before {
    bottom: -26px;
    height: 56px;
  }
}

/* Hero — full-viewport prompt. One job on screen: type a topic. */
.qr-hero--prompt {
  min-height: calc(100svh - 74px);
  display: flex;
  align-items: center;
  padding: 56px 0 88px;
  /* A dark wash that fades out rather than sitting on an opaque base, so the
     page gradient carries the hero into the sections below with no seam. */
  background: var(--qr-hero-glow), var(--qr-hero-wash);
}
.qr-hero__stack {
  width: 100%;
  max-width: 780px;
  margin: 0 auto;
  text-align: center;
}
.qr-eyebrow--hero {
  color: var(--qr-coin);
}
.qr-hero__stack h1 {
  color: #fff;
  font-size: 68px;
  /* Without this the UA's 0.67em top margin swallows the eyebrow's spacing. */
  margin: 0 0 16px;
}
.qr-lead--hero {
  color: rgba(255, 255, 255, 0.78);
  max-width: 520px;
  margin: 0 auto 34px;
}

/* The prompt card */
.qr-hero-form {
  max-width: 719px;
  margin: 0 auto;
}
.qr-hero-form__card {
  display: flex;
  flex-direction: column;
  gap: 32px;
  text-align: left;
  background: #fff;
  border-radius: 24px;
  padding: 32px 32px 16px;
  box-shadow:
    0 2px 5px rgba(15, 23, 42, 0.07),
    0 6px 20px rgba(15, 23, 42, 0.1);
}
.qr-hero-form__input {
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  font: inherit;
  font-size: 18px;
  line-height: 23.4px;
  letter-spacing: -0.15px;
  color: #171717;
  /* An input centres its text in the content box, so the box is one line tall
     and the rest of the design's 71px block hangs below it as padding. */
  height: 23.4px;
  padding: 0 0 47.6px;
}
.qr-hero-form__input::placeholder {
  color: #737373;
}
.qr-hero-form__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.qr-hero-form__hint {
  font-size: 16px;
  letter-spacing: -0.15px;
  color: #737373;
}
.qr-hero-form__actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Icon controls inside the card's bottom bar. */
.qr-hero-form__round {
  display: inline-flex;
  padding: 0;
  border: none;
  background: none;
  color: #b1b1b1;
  cursor: pointer;
  transition: color 0.12s ease;
}
.qr-hero-form__round:hover,
.qr-hero-form__round:focus-visible {
  color: var(--qr-brand);
}
/* A touch above the 24px the icon is drawn at, so the send target reads as the
   primary action rather than another icon in the bar. */
.qr-hero-form__submit svg {
  width: 26.4px;
  height: 26.4px;
}
.qr-microcopy--hero {
  color: rgba(255, 255, 255, 0.55);
}
@media (max-width: 860px) {
  .qr-hero--prompt {
    min-height: calc(100svh - 66px);
    padding: 40px 0 64px;
  }
  .qr-hero__stack h1 {
    font-size: 44px;
  }
  .qr-hero-form__card {
    gap: 20px;
    padding: 20px 20px 12px;
  }
  .qr-hero-form__input {
    font-size: 16px;
    padding-bottom: 24.6px;
  }
  .qr-logowheel {
    --qr-logowheel-gap: 40px;
    --qr-logowheel-fade: 24px;
  }
  .qr-logowheel__logo {
    font-size: 16px;
  }
  .qr-hero__trust {
    bottom: 28px;
  }
}

/* Registered so the browser interpolates the position; an unregistered custom
   property would cut between the two keyframes instead of easing across them. */
@property --qr-hero-drift {
  syntax: "<percentage>";
  inherits: true;
  initial-value: 50%;
}

/* A var() inside a custom property resolves on the element that declares it, so
   the drift has to live on :root alongside --qr-hero-glow. The header inherits
   the finished gradient rather than a second copy of the animation, which is
   what keeps its fill exactly on the backdrop it covers, mid-drift included. */
@keyframes qr-hero-drift {
  from {
    --qr-hero-drift: 44%;
  }
  to {
    --qr-hero-drift: 56%;
  }
}

/* Demo video section */
#demo {
  scroll-margin-top: 80px;
}
/* Full width is the *resting* size: the scroll effect scales down from here so
   the layout box never changes and nothing below reflows as you scroll. */
.qr-demo {
  position: relative;
  max-width: 100%;
  margin: 36px auto 0;
}

/* 0.74 lands the starting width on the 860px this used to be fixed at. */
@keyframes qr-demo-grow {
  from {
    transform: scale(0.74);
  }
  to {
    transform: scale(1);
  }
}

/* Progressive enhancement: without a view timeline the player just sits at its
   full resting width, which is the end state anyway. */
@supports (animation-timeline: view()) {
  @media (min-width: 861px) and (prefers-reduced-motion: no-preference) {
    .qr-demo {
      /* Anchored at the top so the gap under the heading stays put and the
         player only ever grows downward and outward. */
      transform-origin: 50% 0;
      animation: qr-demo-grow linear both;
      animation-timeline: view();
      /* cover 50% is the player centred in the viewport; pulling 15vh off the
         end reaches full width a beat earlier, while it still sits 15% of the
         viewport below centre and is rising. */
      animation-range: entry 25% cover calc(50% - 15vh);
    }
  }
}
/* Ground shadow. ::before rather than ::after so it paints under the player
   without a negative z-index escaping to the page gradient. Tinted deep purple
   because neutral black goes grey and muddy over this canvas. */
.qr-demo::before {
  content: "";
  position: absolute;
  left: 50%;
  /* Centred a hair below the player's bottom edge — any higher and the card
     crops the dense middle of the ellipse, leaving only a faint smudge. */
  bottom: -54px;
  width: 82%;
  height: 112px;
  transform: translateX(-50%);
  background: radial-gradient(
    ellipse at center,
    rgba(24, 5, 40, 0.75) 0%,
    rgba(24, 5, 40, 0.42) 40%,
    rgba(24, 5, 40, 0.14) 62%,
    rgba(24, 5, 40, 0) 78%
  );
  pointer-events: none;
}

/* Hero slides up under the (transparent-at-top) sticky header */
.qr-hero--prompt {
  position: relative;
  box-sizing: border-box;
  margin-top: -65px;
  padding-top: 120px;
  /* Doubled, because flex centring splits the extra padding: lifts the stack 10svh. */
  padding-bottom: calc(88px + 20svh);
  min-height: 100svh;
}
@media (max-width: 860px) {
  .qr-hero--prompt {
    min-height: 100svh;
    padding-top: 104px;
    /* Small screens have no room for the 10svh lift and still fit the wheel. */
    padding-bottom: 104px;
  }
}

/* The lightbox iframe is nested in #qr-lb-frame (created by enhance.js);
   without explicit size the iframe collapses to the 300x150 default. */
#qr-lb-frame {
  width: 100%;
  height: 100%;
}

/* Demo video: poster + inline playback */
.qr-demo .qr-video {
  aspect-ratio: 16 / 9;
}
.qr-video__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}
.qr-video__play {
  position: relative;
  z-index: 1;
}
.qr-video__star,
.qr-video__label {
  z-index: 1;
}
.qr-video--playing {
  cursor: default;
}
.qr-video--playing iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: inherit;
}

/* Sub-page hero (compact, dark, slides under the header like the main hero) */
.qr-subhero {
  margin-top: -65px;
  padding: 150px 0 72px;
  text-align: center;
  background:
    radial-gradient(900px 500px at 15% -10%, rgba(0, 118, 255, 0.5), transparent 60%),
    radial-gradient(
      1000px 550px at 85% -20%,
      color-mix(in srgb, var(--qr-brand) 60%, transparent),
      transparent 60%
    ),
    radial-gradient(1100px 600px at 50% 130%, rgba(255, 55, 120, 0.35), transparent 65%),
    #121212;
}
.qr-subhero h1 {
  color: #fff;
  font-size: 48px;
  max-width: 720px;
  margin: 10px auto 16px;
}
.qr-subhero .qr-lead {
  color: rgba(255, 255, 255, 0.78);
  max-width: 600px;
  margin: 0 auto 30px;
}
@media (max-width: 860px) {
  .qr-subhero {
    padding: 120px 0 56px;
  }
  .qr-subhero h1 {
    font-size: 36px;
  }
}

/* Equal-height pricing cards: features flex, CTA pinned to the bottom */
.qr-tiers {
  align-items: stretch;
}
.qr-tier {
  display: flex;
  flex-direction: column;
}
/* Last child, so it absorbs the slack and cards of unequal feature counts stay
   the same height — which also keeps the CTAs above it on one line. */
.qr-tier__features {
  flex: 1;
}

/* Billing period toggle: a groove cut into the page with a white pill riding in
   it. Translucent black rather than a fixed purple, because the toggle's
   position along the page wash shifts with content and a literal colour would
   end up lighter than its own backdrop half the time — this way it darkens
   whatever it happens to be sitting on. */
.qr-billing {
  position: relative;
  width: max-content;
  margin: 0 auto 28px;
  padding: 4px;
  display: flex;
  background: rgba(23, 5, 38, 0.42);
  border: 1px solid transparent;
  border-radius: 999px;
  box-shadow:
    inset 0 2px 5px rgba(0, 0, 0, 0.45),
    inset 0 -1px 0 rgba(255, 255, 255, 0.07);
}
/* The sliding fill. JS drives x/width off the active button, so it stays put
   until measured — hence the opacity gate on [data-ready]. Shadowed to sit
   proud of the groove. */
.qr-billing__thumb {
  position: absolute;
  top: 4px;
  left: 0;
  height: calc(100% - 8px);
  width: 0;
  border-radius: 999px;
  background: #fff;
  box-shadow:
    0 1px 2px rgba(20, 4, 34, 0.35),
    0 3px 8px -2px rgba(20, 4, 34, 0.28);
  opacity: 0;
  pointer-events: none;
  transition:
    transform 0.38s cubic-bezier(0.32, 0.72, 0, 1),
    width 0.38s cubic-bezier(0.32, 0.72, 0, 1);
}
.qr-billing[data-ready] .qr-billing__thumb {
  opacity: 1;
}
/* First paint must land at the resting position, not travel to it. */
.qr-billing:not([data-ready]) .qr-billing__thumb {
  transition: none;
}
.qr-billing__opt {
  position: relative;
  z-index: 1;
  border: none;
  background: transparent;
  /* Held off full white so the unselected side reads as the quieter of the two
     against the groove. */
  color: rgba(255, 255, 255, 0.72);
  font: inherit;
  font-weight: 700;
  font-size: 16px;
  padding: 8px 18px;
  border-radius: 999px;
  cursor: pointer;
  transition: color 0.38s cubic-bezier(0.32, 0.72, 0, 1);
}
/* Gated on [data-ready] because the active class ships in the markup while the
   thumb is still measuring: unqualified, this would paint dark ink straight
   onto the dark groove for that first frame. Until the pill is under it, the
   label just stays light like its neighbour. */
.qr-billing[data-ready] .qr-billing__opt--active {
  color: #390e59;
}
@media (prefers-reduced-motion: reduce) {
  .qr-billing__thumb,
  .qr-billing__opt {
    transition: none;
  }
}
/* Two backdrops: the dark groove while yearly is unselected, the white thumb
   once it is. Coin only clears AA on the first, so it hands over to amber as
   the pill arrives. */
.qr-billing__save {
  color: var(--qr-coin);
  font-size: 16px;
  font-weight: 900;
  margin-left: 4px;
}
.qr-billing[data-ready] .qr-billing__opt--active .qr-billing__save {
  color: #8a5a00;
}

/* ─── Scroll reveal ──────────────────────────────────────────────────────────
   Each band lifts into place as it enters, so the page arrives a block at a
   time instead of all at once. Scroll-driven, so the rise tracks the scrollbar
   on the way in — but only on the way in: a view timeline is a pure function of
   scroll position and would lower the block again on the way back up, so
   enhance.js drops the animation once a block is fully up. The keyframe ends on
   the element as authored, so there is nothing to hold it in place afterwards.

   Both guards are load-bearing. Without view() the animation would apply with
   no timeline to advance it and leave everything stuck at opacity 0, and under
   reduced motion none of it should move — in each case the correct fallback is
   simply the page as authored, so neither branch needs one. */
@keyframes qr-rise {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* Images drift against their own block at a different rate, which is what reads
   as depth rather than a second reveal. Small on purpose: these sit in an open
   grid with no clipping, so the travel has to stay inside the section's gutter. */
@keyframes qr-drift {
  from {
    transform: translateY(14px);
  }
  to {
    transform: translateY(-14px);
  }
}

@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .qr-center,
    .qr-feature,
    .qr-flow > li,
    .qr-aud > li,
    .qr-quotes > li,
    .qr-billing,
    .qr-tiers > li,
    .qr-faq__list > li,
    .qr-finalcta,
    #demo > .qr-wrap {
      animation: qr-rise linear both;
      /* The 35% end inset pulls the timeline's bottom edge up to the 65% line,
         so cover 0% is the moment the block's top touches it — nothing stirs
         while it is still down in the bottom of the viewport. */
      animation-timeline: view(0% 35%);
      /* Measured against `cover` rather than `entry`, because the entry range is
         exactly the element's own height — a 90px heading would flick through
         the whole fade in a third of the scroll a 550px pricing card takes. The
         cover range is height plus (inset) viewport, so the viewport still
         dominates and every block rises over roughly the same distance. */
      animation-range: cover 0% cover 22%;
    }

    /* Cards in a row all cross the same line at the same instant, so the
       cascade has to come out of the range — an animation-delay means nothing
       on a timeline driven by scroll position. Only while they are a row:
       below 861px every one of these lists is a single column, where the cards
       already stagger by sitting at different heights and a second offset would
       just make the last one drag. Vertical lists (features, FAQ) never get it
       for the same reason. */
    @media (min-width: 861px) {
      .qr-flow > li:nth-child(2),
      .qr-aud > li:nth-child(2),
      .qr-quotes > li:nth-child(2),
      .qr-tiers > li:nth-child(2) {
        animation-range: cover 4% cover 26%;
      }

      .qr-flow > li:nth-child(3),
      .qr-aud > li:nth-child(3),
      .qr-quotes > li:nth-child(3),
      .qr-tiers > li:nth-child(3) {
        animation-range: cover 8% cover 30%;
      }

      .qr-tiers > li:nth-child(4) {
        animation-range: cover 12% cover 34%;
      }
    }

    /* Stacked, the flow cards are title-height and the three of them span barely
       more than a screen, so a timeline each turns the reveal into a stutter —
       the second and third arrive on their own scroll position, well after the
       first. The list rises as one block instead. The other stacked lists keep
       their own timelines: their cards are tall enough that only one is near the
       line at a time, which is a cascade rather than a stutter. */
    @media (max-width: 860px) {
      .qr-flow > li {
        animation: none;
      }

      .qr-flow {
        animation: qr-rise linear both;
        animation-timeline: view(0% 35%);
        animation-range: cover 0% cover 22%;
      }
    }

    /* The demo rises as one block, from the wrapper, because the player inside
       it is already animating a transform of its own on a second timeline — a
       rise on the player would replace that scale rather than compose with it.
       So the heading gives up its own reveal and travels with the wrapper. */
    #demo .qr-center {
      animation: none;
    }

    /* The closing blocks cannot honour the 60% line. Only about 640px of page
       sits beneath the CTA — less than half a screen once the window is tall —
       so at full scroll their tops never climb that high, and they would be
       left part-faded with nothing left to scroll. Requiring nothing more than
       clearing the viewport's own bottom edge is always satisfiable, whatever
       the window height. */
    .qr-faq .qr-center,
    .qr-faq__list > li,
    .qr-finalcta {
      animation-timeline: view();
      animation-range: cover 2% cover 20%;
    }

    .qr-feature__visual {
      animation: qr-drift linear both;
      animation-timeline: view();
      animation-range: cover;
    }
  }
}

/* ─── Legal documents ───────────────────────────────────────────────────── */
.qr-legal-title {
  max-width: 47rem;
  margin: 3.5rem auto 0;
  font-size: clamp(1.75rem, 5vw, 2.25rem);
  line-height: 1.1;
}

.qr-legal {
  max-width: 47rem;
  margin: 0 auto;
  padding: 1.25rem 0 4rem;
  font-size: 1rem;
}

.qr-legal__meta {
  color: var(--qr-coal);
  font-size: 0.875rem;
  margin: 0 0 2rem;
}

.qr-legal h2 {
  font-size: 1.25rem;
  font-weight: 900;
  margin: 2.5rem 0 0.75rem;
}

.qr-legal h3 {
  font-size: 1.0625rem;
  font-weight: 700;
  margin: 1.75rem 0 0.5rem;
}

.qr-legal p,
.qr-legal li {
  color: var(--qr-coal);
  margin: 0 0 0.875rem;
}

.qr-legal ul,
.qr-legal ol {
  padding-left: 1.25rem;
  margin: 0 0 1rem;
}

/* --qr-link fails AA contrast on the purple wash. */
.qr-legal a {
  color: var(--qr-brand-tint);
  text-decoration: underline;
}

.qr-legal a:hover {
  color: var(--qr-coin);
}

.qr-legal__table-wrap {
  overflow-x: auto;
  margin: 0 0 1.5rem;
}

.qr-legal table {
  width: 100%;
  min-width: 34rem;
  border-collapse: collapse;
  font-size: 0.875rem;
}

.qr-legal th,
.qr-legal td {
  border: 1px solid rgba(255, 255, 255, 0.18);
  padding: 0.625rem 0.75rem;
  text-align: left;
  vertical-align: top;
}

.qr-legal th {
  background: var(--qr-paper);
  font-weight: 700;
  color: var(--qr-carbon);
}

.qr-legal__num {
  font-weight: 700;
  margin-right: 0.25rem;
}

/* Text legal has still to complete. */
.qr-placeholder {
  background: var(--qr-coin);
  color: var(--qr-carbon);
  border-radius: 3px;
  padding: 0 0.2em;
}

/* Outspecifies `.qr-legal ul`. */
.qr-legal ul.qr-legal-index {
  list-style: none;
  padding: 0;
}

.qr-legal-index li {
  border-bottom: 1px solid rgba(255, 255, 255, 0.18);
  padding: 0.875rem 0;
}

.qr-legal-index a {
  font-weight: 700;
  text-decoration: none;
}

.qr-legal-index span {
  display: block;
  color: var(--qr-coal);
  font-size: 0.875rem;
}

/* --qr-coal, not the row's --qr-muted, which fails AA for interactive text. */
.qr-footer__utility-btn {
  color: var(--qr-coal);
  font-size: 13px;
  text-decoration: none;
  background: none;
  border: 0;
  padding: 0;
  font-family: inherit;
  cursor: pointer;
}

.qr-footer__utility-btn:hover {
  color: var(--qr-brand);
  text-decoration: underline;
}
