/* =====================================================================
   qbwfileunlock.com - minimal landing page
   ---------------------------------------------------------------------
   Mobile-first, less-is-more. Written for a 390px phone and enhanced
   with @media (min-width: ...) only - there are no max-width queries.

   Design tokens live ONCE, in the .m scope (the <body> carries class="m").
   Every component rule reads var(--m-*) - no raw hex in component rules.

   The size-estimator component is namespaced "sz" (every class and id)
   and carries its own --sz-* tokens, so it cannot leak into or be
   affected by the rest of the page.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. Design tokens (scoped)
   --------------------------------------------------------------------- */
.m {
    /* ---- dark enterprise theme -------------------------------------
     Background baseline #090d16, glassmorphism surfaces, a 24px
     micro-grid underlay and blue focus loops.

     --m-surface stays a LIGHT colour: it is used as "text sitting on
     a brand fill" in 7 rules. The card/panel surface is the new
     --m-panel token.                                            */
  --m-bg: #090d16;
  --m-bg-deep: #05080f;

  /* the glass surface */
  --m-panel: rgba(17, 24, 39, 0.7);
  --m-panel-solid: #111827;
  --m-panel-raised: rgba(31, 41, 55, 0.55);

  /* the ultra-thin container outline */
  --m-border: rgba(255, 255, 255, 0.06);
  --m-border-soft: rgba(255, 255, 255, 0.09);

  /* The accent, split by ROLE - this is what was missing.
     --m-brand       : the LIGHT blue. Safe as TEXT (links, prices,
                        icons) on the dark ground.
     --m-brand-fill  : the DEEP blue. Safe as a FILL with white text.
     --m-brand-strong: the DEEPER blue, the hover fill. */
  --m-brand: #60a5fa;
  --m-brand-fill: #2563eb;
  --m-brand-strong: #1d4ed8;

  /* tinted fills */
  --m-tint: rgba(37, 99, 235, 0.16);
  --m-tint-soft: rgba(255, 255, 255, 0.04);

  /* text */
  --m-ink: #f1f5f9;
  --m-muted: #a5b0c4;

  /* status colours, darkened for a dark ground */
  --m-green-bg: rgba(34, 197, 94, 0.12);
  --m-green-ink: #6ee7a0;
  --m-green-line: rgba(34, 197, 94, 0.4);
  --m-amber-bg: rgba(245, 158, 11, 0.12);
  --m-amber-ink: #fcd34d;
  --m-amber-line: rgba(245, 158, 11, 0.4);
  --m-red: #f87171;
  --m-red-ink: #fca5a5;

  /* text that sits on a brand fill - stays light */
  --m-surface: #f8fafc;

  /* the glass effect */
  --m-blur: 16px;
  --m-grid-size: 24px;
  --m-grid-line: rgba(255, 255, 255, 0.022);
  --m-focus-glow: rgba(59, 130, 246, 0.45);

  /* shadows - on a dark ground they read as a soft lift */
  --m-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 10px 28px rgba(0, 0, 0, 0.35);
  --m-shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.45), 0 14px 34px rgba(0, 0, 0, 0.45);

  /* type */
  --m-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
             Cantarell, "Helvetica Neue", Arial, sans-serif;
  --m-fs-display: clamp(1.95rem, 1.45rem + 2.1vw, 2.75rem);
  --m-fs-h2: clamp(1.35rem, 1.2rem + 0.65vw, 1.6rem);
  --m-fs-h3: clamp(1.05rem, 1rem + 0.25vw, 1.15rem);
  --m-fs-body: clamp(1rem, 0.95rem + 0.3vw, 1.125rem);
  --m-fs-sm: 0.9rem;
  --m-fs-xs: 0.82rem;

  /* space - one scale, used everywhere */
  --m-s1: 0.5rem;
  --m-s2: 0.75rem;
  --m-s3: 1rem;
  --m-s4: 1.5rem;
  --m-s5: 2rem;
  --m-s6: 3rem;
  --m-section: clamp(2.75rem, 2rem + 3.4vw, 5rem);

  /* shape */
  --m-radius: 0.75rem;
  --m-radius-sm: 0.5rem;
  --m-radius-pill: 999px;
  --m-shadow: 0 1px 2px rgba(15, 24, 40, 0.04), 0 10px 28px rgba(15, 24, 40, 0.05);
  --m-shadow-lift: 0 2px 4px rgba(15, 24, 40, 0.05), 0 14px 34px rgba(15, 24, 40, 0.07);

  /* layout */
  --m-maxw: 68rem;
  --m-gutter: clamp(1rem, 0.55rem + 2.2vw, 2rem);
}

/* ---------------------------------------------------------------------
   2. Base
   --------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body.m {
  margin: 0;
  padding: 0;
  background-color: var(--m-bg);
  /* the 24px micro-grid underlay - a secure server grid */
  background-image:
    linear-gradient(var(--m-grid-line) 1px, transparent 1px),
    linear-gradient(90deg, var(--m-grid-line) 1px, transparent 1px);
  background-size: var(--m-grid-size) var(--m-grid-size);
  background-attachment: fixed;
  color: var(--m-ink);
  font-family: var(--m-font);
  font-size: var(--m-fs-body);
  line-height: 1.55;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

body.m p { overflow-wrap: break-word; }

.m a { color: var(--m-brand); }
/* The hover colour is the LIGHT brand blue, not --m-brand-strong. The
   deep blue is a FILL colour: on the dark ground it measures 2.90:1 as
   text, and it is DARKER than the resting colour, so a hover made the
   text unreadable.

   The button classes are EXCLUDED because they carry their own text
   colour on a brand fill: an <a class="m-btn m-btn-primary"> is white
   text on the deep blue, and ".m a:hover" (0,2,1) outranks
   ".m-btn-primary:hover" (0,2,0), so the hover colour would turn the
   button label light blue on its own blue fill - 2.03:1. */
.m a:hover { color: var(--m-brand); }
.m a.m-btn-primary:hover { color: var(--m-surface); }
.m a.m-btn-secondary:hover { color: var(--m-brand); }

.m :focus-visible {
  outline: 2px solid var(--m-brand);
  outline-offset: 2px;
  border-radius: 2px;
}

.m img { max-width: 100%; height: auto; }

.m-sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.m-skip {
  position: absolute;
  left: -9999px;
  top: auto;
  z-index: 20;
}
.m-skip:focus {
  left: 0.5rem;
  top: 0.5rem;
  position: fixed;
  display: inline-block;
  padding: 0.5rem 0.9rem;
  background: var(--m-brand-fill);
  color: var(--m-surface);
  border-radius: var(--m-radius-sm);
  text-decoration: none;
}

/* ---------------------------------------------------------------------
   3. Shell
   --------------------------------------------------------------------- */
.m-main {
  width: 100%;
  max-width: var(--m-maxw);
  margin: 0 auto;
  padding: 0 var(--m-gutter) var(--m-s6);
}

.m-section { margin-top: var(--m-section); }

.m-section-title {
  font-size: var(--m-fs-h2);
  line-height: 1.25;
  letter-spacing: -0.01em;
  margin: 0 0 var(--m-s2);
  color: var(--m-ink);
}

.m-section-lead {
  color: var(--m-muted);
  max-width: 62ch;
  margin: 0 0 var(--m-s4);
}

/* ---------------------------------------------------------------------
   4. Top bar
   --------------------------------------------------------------------- */
.m-topbar {
  background: var(--m-panel);
  border-bottom: 1px solid var(--m-border);
}

.m-topbar-inner {
  max-width: var(--m-maxw);
  margin: 0 auto;
  padding: 0.75rem var(--m-gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--m-s3);
}

.m-brand { display: inline-flex; align-items: center; text-decoration: none; }
.m-brand-mark { display: block; height: 38px; }
.m-brand-mark img { height: 38px; width: auto; display: block; }

.m-topbar-tag {
  display: none;
  font-size: var(--m-fs-sm);
  color: var(--m-muted);
}

/* ---------------------------------------------------------------------
   5. Hero
   --------------------------------------------------------------------- */
.m-hero {
  padding: clamp(2.25rem, 1.6rem + 3vw, 4.25rem) 0 0;
  text-align: center;
}

.m-eyebrow {
  display: inline-block;
  padding: 0.3rem 0.75rem;
  border-radius: var(--m-radius-pill);
  background: var(--m-tint);
  color: var(--m-brand);
  font-size: var(--m-fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.m-hero-title {
  font-size: var(--m-fs-display);
  line-height: 1.12;
  letter-spacing: -0.025em;
  font-weight: 700;
  margin: var(--m-s3) auto var(--m-s3);
  max-width: 22ch;
  text-wrap: balance;
}

.m-hero-lead {
  color: var(--m-muted);
  max-width: 54ch;
  margin: 0 auto var(--m-s4);
}

.m-hero-lead strong { color: var(--m-ink); }

.m-regions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--m-s1);
  margin: 0 0 var(--m-s4);
}

.m-region {
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-pill);
  background: var(--m-panel);
  color: var(--m-muted);
  font-size: var(--m-fs-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
}

.m-hero-note {
  max-width: 60ch;
  margin: 0 auto;
  font-size: var(--m-fs-xs);
  color: var(--m-muted);
}

.m-hero-foot {
  margin: var(--m-s3) 0 0;
  font-size: var(--m-fs-sm);
  color: var(--m-muted);
}

/* fact rows (what we do / what we don't do) */
.m-facts {
  margin: var(--m-s5) auto 0;
  max-width: 44rem;
  display: grid;
  gap: var(--m-s3);
  text-align: left;
}

.m-fact {
  display: grid;
  grid-template-columns: 1.5rem 1fr;
  gap: var(--m-s2);
  align-items: start;
  padding-top: var(--m-s3);
  border-top: 1px solid var(--m-border-soft);
}

.m-fact-ico { color: var(--m-brand); }
.m-fact-ico svg { display: block; }

.m-fact-text { font-size: var(--m-fs-sm); color: var(--m-muted); }
.m-fact-text strong { color: var(--m-ink); }

/* ---------------------------------------------------------------------
   6. Cards + grids
   --------------------------------------------------------------------- */
.m-grid { display: grid; gap: var(--m-s3); }

.m-card {
  background: var(--m-panel);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius);
  padding: var(--m-s4);
  box-shadow: var(--m-shadow);
}

.m-card-title {
  font-size: var(--m-fs-h3);
  font-weight: 700;
  margin: 0 0 var(--m-s1);
  line-height: 1.3;
}

.m-card-text {
  margin: 0;
  color: var(--m-muted);
  font-size: var(--m-fs-sm);
}

.m-ico-wrap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--m-radius-sm);
  background: var(--m-tint);
  color: var(--m-brand);
  margin-bottom: var(--m-s2);
}

.m-ico {
  width: 1.25rem;
  height: 1.25rem;
  display: block;
}

/* ---------------------------------------------------------------------
   7. Steps
   --------------------------------------------------------------------- */
.m-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--m-s4);
}

.m-step { position: relative; }

.m-step-n {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  min-width: 2.25rem;
  border-radius: var(--m-radius-pill);
  background: var(--m-brand-fill);
  color: var(--m-surface);
  font-weight: 700;
  font-size: var(--m-fs-sm);
  margin-bottom: var(--m-s2);
}

.m-step-title {
  font-size: var(--m-fs-h3);
  font-weight: 700;
  margin: 0 0 var(--m-s1);
  line-height: 1.3;
}

.m-step-text {
  margin: 0 0 var(--m-s2);
  color: var(--m-muted);
  font-size: var(--m-fs-sm);
}

.m-badge {
  display: inline-block;
  padding: 0.2rem 0.6rem;
  border-radius: var(--m-radius-pill);
  font-size: var(--m-fs-xs);
  font-weight: 600;
}
.m-badge--blue  { background: var(--m-tint); color: var(--m-brand); }
.m-badge--amber { background: var(--m-amber-bg); color: var(--m-amber-ink); }
.m-badge--green { background: var(--m-green-bg); color: var(--m-green-ink); }

/* ---------------------------------------------------------------------
   8. Buttons
   --------------------------------------------------------------------- */
.m-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--m-s1);
  min-height: 2.75rem;
  padding: 0.6rem 1.15rem;
  border: 1px solid transparent;
  border-radius: var(--m-radius-sm);
  background: transparent;
  font-family: inherit;
  font-size: var(--m-fs-sm);
  font-weight: 600;
  line-height: 1.25;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
}

.m-btn-primary {
  background: var(--m-brand-fill);
  border-color: var(--m-brand-fill);
  color: var(--m-surface);
}
.m-btn-primary:hover { background: var(--m-brand-strong); border-color: var(--m-brand-strong); color: var(--m-surface); }
.m-btn-primary:disabled { background: var(--m-muted); border-color: var(--m-muted); cursor: not-allowed; }

.m-btn-secondary {
  background: var(--m-panel);
  border-color: var(--m-brand);
  color: var(--m-brand);
}
.m-btn-secondary:hover { background: var(--m-tint); color: var(--m-brand); }

.m-btn-quiet {
  background: var(--m-panel);
  border-color: var(--m-border);
  color: var(--m-muted);
  font-weight: 500;
}
.m-btn-quiet:hover { background: var(--m-tint-soft); color: var(--m-ink); }

.m-btn-block { width: 100%; }

/* UPLOAD-CARD:BEGIN */
/* ---------------------------------------------------------------------
   9. Upload card (the payment pipeline lives in here)
   --------------------------------------------------------------------- */
.m-upload {
  background: var(--m-panel);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius);
  padding: var(--m-s4);
  /* The action card carries the page's ONE accent edge: a 3px brand rule
     along its top. It is an INSET shadow, not a border-top. A 3px
     border-top next to the card's 1px hairline sides overhangs the 12px
     corner radius and renders as an orphaned blue stub hanging off the
     top-left shoulder of the card (seen on the live page, confirmed by
     vision). An inset shadow is clipped to the padding box, so the band
     follows the curve exactly.

     The glass group rule further down sets `border` on every panel, and
     that shorthand resets border-top - so an accent drawn as a border
     never rendered at all. Measured on the live page before this change:
     .m-upload border-top computed to "1px solid rgba(255,255,255,0.06)",
     not the 3px brand bar this card was designed to carry. */
  box-shadow: inset 0 3px 0 0 var(--m-brand), var(--m-shadow-lift);
}
/* UPLOAD-CARD:END */

.m-upload-title {
  font-size: var(--m-fs-h2);
  line-height: 1.25;
  margin: 0 0 var(--m-s2);
}

.m-upload-lead {
  color: var(--m-muted);
  margin: 0 0 var(--m-s3);
  font-size: var(--m-fs-sm);
  max-width: 64ch;
}
.m-upload-lead strong { color: var(--m-ink); }

/* drop zone */
.m-drop {
  position: relative;
  display: block;
  border: 2px dashed var(--m-border);
  border-radius: var(--m-radius);
  background: var(--m-tint-soft);
  padding: var(--m-s4) var(--m-s3);
  text-align: center;
  cursor: pointer;
  transition: border-color 120ms ease, background-color 120ms ease;
}

.m-drop:hover,
.m-drop:focus-within { border-color: var(--m-brand); background: var(--m-tint); }

.m-drop.drag-over { border-color: var(--m-brand); background: var(--m-tint); }

.m-drop-ico { color: var(--m-brand); display: block; margin: 0 auto var(--m-s2); }
.m-drop-ico svg { display: block; margin: 0 auto; width: 1.75rem; height: 1.75rem; }

.m-drop-title {
  display: block;
  font-size: 1.05rem;
  color: var(--m-ink);
  margin-bottom: var(--m-s1);
}

.m-drop-sub {
  display: block;
  color: var(--m-muted);
  font-size: var(--m-fs-sm);
  margin-bottom: var(--m-s3);
}

.m-drop-btn { margin: 0 auto; }

.m-file-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* quote card (script writes .quote-card / .is-visible / .is-error / .is-loading) */
.m .quote-card {
  display: none;
  margin-top: var(--m-s3);
  padding: var(--m-s3);
  border: 1px solid var(--m-green-line);
  background: var(--m-green-bg);
  border-radius: var(--m-radius-sm);
  color: var(--m-green-ink);
}
.m .quote-card.is-visible { display: block; }
.m .quote-card.is-error { border-color: var(--m-red); background: rgba(248, 113, 113, 0.12); color: var(--m-red-ink); }
.m .quote-card.is-loading { border-color: var(--m-border); background: var(--m-tint-soft); color: var(--m-ink); }
/* Manual quote (file over 1 GB): not an error, just "we will price this for
   you". Amber, so it reads as "needs attention" rather than "went wrong". */
.m .quote-card.is-manual { border-color: var(--m-border); background: var(--m-tint-soft); color: var(--m-ink); }

.m .quote-headline { font-size: 1.05rem; font-weight: 700; margin: 0 0 0.25rem; }
.m .quote-reason { margin: 0 0 var(--m-s2); font-size: var(--m-fs-sm); color: var(--m-ink); }

.m .quote-benefits {
  display: none;
  list-style: none;
  margin: 0;
  padding: 0;
  display: none;
}
.m .quote-benefits li {
  position: relative;
  padding: 0.25rem 0 0.25rem 1.75rem;
  font-size: var(--m-fs-sm);
  color: var(--m-ink);
}
.m .quote-benefits li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55rem;
  width: 1rem;
  height: 1rem;
  border-radius: var(--m-radius-pill);
  background-color: var(--m-green-line);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M4 12.5l5.2 5.2L20 7'/></svg>") center/70% 70% no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M4 12.5l5.2 5.2L20 7'/></svg>") center/70% 70% no-repeat;
}

.m .quote-choose-other {
  background: transparent;
  border: 0;
  padding: 0;
  margin: 0;
  color: var(--m-brand);
  font: inherit;
  font-size: var(--m-fs-sm);
  text-decoration: underline;
  cursor: pointer;
}
.m .quote-choose-other:hover { color: var(--m-brand); }

/* ownership declaration */
.m .ownership-declaration {
  margin-top: var(--m-s3);
  padding: var(--m-s3);
  background: var(--m-tint-soft);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  font-size: var(--m-fs-sm);
  transition: border-color 180ms ease, background-color 180ms ease;
}

.m .ownership-declaration.ownership-incomplete {
  border: 3px solid var(--m-red);
  border-left-width: 6px;
  background: rgba(248, 113, 113, 0.12);
  padding-left: calc(var(--m-s3) - 2px);
}

.m .ownership-incomplete-field {
  border-color: var(--m-red);
  outline: 2px solid rgba(193, 39, 45, 0.25);
}

.m .ownership-incomplete-label { color: var(--m-red-ink); font-weight: 600; }
.m .ownership-incomplete-label a { color: var(--m-red-ink); }

.m-decl-label {
  display: block;
  font-weight: 600;
  margin-bottom: var(--m-s1);
}

.m-select {
  width: 100%;
  padding: 0.55rem 0.6rem;
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  background: var(--m-panel);
  color: var(--m-ink);
  font-family: inherit;
  font-size: var(--m-fs-sm);
}

.m-check {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  margin-top: var(--m-s2);
  cursor: pointer;
}
.m-check input {
  margin: 0.3rem 0 0;
  width: 1.05rem;
  height: 1.05rem;
  accent-color: var(--m-brand);
  flex: 0 0 auto;
}
.m-check span { font-size: var(--m-fs-sm); color: var(--m-muted); }
.m-check a { color: var(--m-brand); }

.m-decl-error {
  margin-top: var(--m-s2);
  padding: 0.5rem 0.6rem;
  background: rgba(248, 113, 113, 0.16);
  border-left: 3px solid var(--m-red);
  border-radius: 4px;
  color: var(--m-red-ink);
  font-weight: 600;
  font-size: var(--m-fs-sm);
}

.m-decl-note {
  margin: var(--m-s2) 0 0;
  font-size: var(--m-fs-xs);
  color: var(--m-muted);
}

/* upload help */
.m .upload-help {
  margin: var(--m-s3) 0 0;
  color: var(--m-muted);
  font-size: var(--m-fs-sm);
}

/* reset button */
.m .m-reset { margin-top: var(--m-s3); }

/* ---------------------------------------------------------------------
   "Choose a different file" sits beside the file picker.

   It used to live at the bottom of the upload card, below the
   ownership declaration and the service facts, so a customer who
   picked the wrong file had to scroll past everything to find the
   way out. It now sits directly under the drop window as a quiet
   secondary action, so it never competes with the primary button.
   --------------------------------------------------------------------- */
.m .m-drop-actions {
  display: flex;
  justify-content: center;
  margin-top: var(--m-s3);
}

.m .m-drop-actions .m-reset {
  margin-top: 0;
  font-size: var(--m-fs-sm);
  color: var(--m-ink);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  background: none;
  border: 0;
  padding: 0.35rem 0.6rem;
  cursor: pointer;
}

.m .m-drop-actions .m-reset:hover {
  color: var(--m-brand);
}

.m .m-drop-actions .m-reset:focus-visible {
  outline: 2px solid var(--m-brand);
  outline-offset: 2px;
  border-radius: var(--m-radius-sm);
}


/* ---------------------------------------------------------------------
   The price is the headline.

   Measured before this change: the price in the quote card rendered at
   16.8px, which was SMALLER than the 18px body text, while the
   estimator's pretend price further down the page rendered at 38.4px.
   The number that decides whether the customer buys had almost no
   visual weight, and the loudest number on the page was the wrong one.

   The payment script writes the price as bare text inside one
   sentence, so there is nothing in its markup to style. We restyle the
   result from outside: .quote-price is added around the price once the
   quote resolves, and these rules give it weight.
   --------------------------------------------------------------------- */
.m .quote-price {
  display: inline-block;
  font-size: 2.25rem;
  line-height: 1.05;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--m-green-ink);
  vertical-align: baseline;
  white-space: nowrap;
}

/* The sentence that comes before the price. It keeps its own <strong>
   tags around the file size and the tier name. */
.m .quote-lead {
  margin-right: 0.15em;
}

.m .quote-headline {
  font-size: 1.15rem;
  line-height: 1.45;
}

/* A short emphasis when the customer clicks "Show my price", so the
   eye is drawn to the number that just appeared. Reduced-motion
   customers get no animation at all. */
@keyframes qbh-flash {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.08); }
  100% { transform: scale(1); }
}

.m .quote-price.is-flash {
  animation: qbh-flash 700ms ease-out;
  display: inline-block;
}

@media (prefers-reduced-motion: reduce) {
  .m .quote-price.is-flash {
    animation: none;
  }
}

@media (max-width: 560px) {
  .m .quote-price {
    font-size: 1.9rem;
  }
  .m .quote-headline {
    font-size: 1.05rem;
  }
}
/* status + progress */
.m .upload-status {
  display: none;
  margin-top: var(--m-s3);
  padding: var(--m-s3);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  background: var(--m-tint-soft);
  font-size: var(--m-fs-sm);
}
.m .upload-status.is-visible { display: block; }
.m .upload-status.is-error { border-color: var(--m-red); background: rgba(248, 113, 113, 0.12); color: var(--m-red-ink); }
.m .upload-status.is-success { border-color: var(--m-green-line); background: var(--m-green-bg); color: var(--m-green-ink); }

.m .progress-bar {
  width: 100%;
  height: 10px;
  margin: 0.65rem 0 0.4rem;
  background: var(--m-tint);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-pill);
  overflow: hidden;
}
.m .progress-bar-inner {
  height: 100%;
  width: 0%;
  background: var(--m-brand-fill);
  transition: width 120ms ease;
}
.m .progress-bar.is-indeterminate .progress-bar-inner {
  width: 35% !important;
  background: var(--m-tint);
  animation: m-shimmer 1.2s linear infinite;
}
@keyframes m-shimmer {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(380%); }
}

.m .progress-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.4rem;
  font-size: var(--m-fs-xs);
  color: var(--m-muted);
}

/* ---- chunk-level meter --------------------------------------------
   One tile per chunk. As a chunk lands its tile fills, so a big upload
   visibly advances tile by tile instead of creeping along as one bar.
   The grid is always about 32 tiles wide, so a 204-chunk file and a
   1024-chunk file look the same size and the tiles simply get smaller. */
.m .chunk-meter {
  margin-top: 0.85rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--m-border);
}
.m .chunk-meter[hidden] { display: none; }

.m .chunk-meter-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.35rem 0.75rem;
  margin-bottom: 0.5rem;
}
.m .chunk-meter-count {
  display: inline-flex;
  align-items: baseline;
  gap: 0.28rem;
  font-variant-numeric: tabular-nums;
}
.m .chunk-meter-count #chunk-done {
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1;
  color: var(--m-ink);
}
.m .chunk-meter-of { color: var(--m-muted); }
.m .chunk-meter-count #chunk-total {
  font-size: 1rem;
  font-weight: 600;
  color: var(--m-ink);
}
.m .chunk-meter-label {
  font-size: var(--m-fs-xs);
  color: var(--m-muted);
}
.m .chunk-meter-live {
  font-size: var(--m-fs-xs);
  font-weight: 600;
  color: var(--m-brand);
  font-variant-numeric: tabular-nums;
}

.m .chunk-grid {
  display: grid;
  grid-template-columns: repeat(32, 1fr);
  gap: 2px;
}
.m .chunk-tile {
  aspect-ratio: 1 / 1;
  min-height: 4px;
  border-radius: 2px;
  background: var(--m-tint);
  border: 1px solid var(--m-border);
  transition: background-color 140ms ease, border-color 140ms ease;
}
.m .chunk-tile.is-done {
  background: var(--m-brand-fill);
  border-color: var(--m-brand-fill);
}
.m .chunk-tile.is-current {
  background: var(--m-brand-fill);
  border-color: var(--m-brand);
  opacity: 0.55;
  animation: m-chunk-pulse 900ms ease-in-out infinite;
}
@keyframes m-chunk-pulse {
  0%, 100% { opacity: 0.25; }
  50%      { opacity: 0.7; }
}

/* Very small screens: fewer columns so each tile stays visible. */
@media (max-width: 480px) {
  .m .chunk-grid { grid-template-columns: repeat(16, 1fr); }
  .m .chunk-meter-count #chunk-done { font-size: 1.15rem; }
}

.m .chunk-meter-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1.1rem;
  margin-top: 0.6rem;
  font-size: var(--m-fs-xs);
  color: var(--m-muted);
  font-variant-numeric: tabular-nums;
}
.m .chunk-meter-key { margin-right: 0.25rem; }
.m .chunk-meter-stats strong { color: var(--m-ink); }
.m .progress-meta strong { color: var(--m-ink); }

/* pay host + generated buttons */
.m .pay-row { margin-top: var(--m-s3); }

.m .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  padding: 0.65rem 1.15rem;
  border: 1px solid transparent;
  border-radius: var(--m-radius-sm);
  font-family: inherit;
  font-size: var(--m-fs-sm);
  font-weight: 600;
  line-height: 1.25;
  cursor: pointer;
}
.m .btn-primary { background: var(--m-brand-fill); border-color: var(--m-brand-fill); color: var(--m-surface); }
.m .btn-primary:hover { background: var(--m-brand-strong); border-color: var(--m-brand-strong); }
.m .btn-primary:disabled { background: var(--m-muted); border-color: var(--m-muted); cursor: not-allowed; }
.m .btn-secondary { background: var(--m-panel); border-color: var(--m-brand); color: var(--m-brand); }
.m .btn-secondary:hover { background: var(--m-tint); }

.m .primary-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  padding: 0.7rem 1.4rem;
  border: 0;
  border-radius: var(--m-radius-sm);
  background: var(--m-brand-fill);
  color: var(--m-surface);
  font-family: inherit;
  font-size: var(--m-fs-sm);
  font-weight: 600;
  cursor: pointer;
}

.m .continue-to-paypal:empty { display: none; }
.m .continue-to-paypal {
  margin: var(--m-s3) 0 0;
  padding: var(--m-s3);
  background: var(--m-green-bg);
  border: 1px solid var(--m-green-line);
  border-radius: var(--m-radius-sm);
}
.m .continue-to-paypal .cp-help {
  margin: var(--m-s2) 0 0;
  font-size: var(--m-fs-xs);
  color: var(--m-ink);
}

/* The customer's way out: delete the uploaded file.

   It must NOT compete with the Pay button, but it has to be OBVIOUS.
   The first version used .m-btn-quiet, whose background is
   rgba(17,24,39,0.7) on a page whose background is #090d16 - and the
   customer reported "the button colour is the same as the background".
   It is now a clearly outlined button with a real border and enough
   contrast to read at a glance. Amber, not red: cancelling an order is
   a normal thing to do, not a destructive act. */
.m .cancel-order-row {
  margin: var(--m-s3) 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
}
.m .cancel-order-row .m-btn {
  background: transparent;
  border: 1px solid var(--m-amber-line);
  color: var(--m-amber-ink);
  font-weight: 600;
}
.m .cancel-order-row .m-btn:hover {
  background: var(--m-amber-bg);
  border-color: var(--m-amber-ink);
  color: var(--m-amber-ink);
}
.m .cancel-order-row .m-btn:focus-visible {
  outline: 2px solid var(--m-amber-ink);
  outline-offset: 2px;
}
.m .cancel-order-row .m-btn:disabled {
  opacity: 0.7;
  cursor: default;
}
.m .cancel-order-note {
  font-size: var(--m-fs-xs);
  color: var(--m-muted);
}

/* "Choose a different file" must also read as a button. It was
   background:none; border:0 with a --m-muted colour, so on the dark
   ground it looked like ordinary text. Give it a real outline. */
.m .m-drop-actions .m-reset {
  margin-top: 0;
  font-size: var(--m-fs-sm);
  color: var(--m-ink);
  background: transparent;
  border: 1px solid var(--m-border-soft);
  border-radius: 6px;
  padding: 0.45rem 0.85rem;
  cursor: pointer;
}
.m .m-drop-actions .m-reset:hover {
  color: var(--m-brand);
  border-color: var(--m-brand);
  background: var(--m-tint-soft);
}
.m .m-drop-actions .m-reset:focus-visible {
  outline: 2px solid var(--m-brand);
  outline-offset: 2px;
}

/* large-file request */
.m-large { margin-top: var(--m-s3); }
.m-large > summary {
  cursor: pointer;
  font-size: var(--m-fs-sm);
  font-weight: 600;
  color: var(--m-brand);
  list-style: none;
}
.m-large > summary::-webkit-details-marker { display: none; }
.m-large > summary::before {
  content: "+ ";
  font-weight: 700;
}
.m-large[open] > summary::before { content: "\2212 "; }
.m-large-body { padding-top: var(--m-s3); }
.m-large-note { margin: 0 0 var(--m-s2); font-size: var(--m-fs-sm); color: var(--m-muted); }
.m-large-grid { display: grid; gap: var(--m-s2); }
.m-input {
  width: 100%;
  padding: 0.55rem 0.6rem;
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  background: var(--m-panel);
  color: var(--m-ink);
  font-family: inherit;
  font-size: var(--m-fs-sm);
}
.m-large-status { margin: var(--m-s2) 0 0; font-size: var(--m-fs-sm); color: var(--m-muted); }

/* ---------------------------------------------------------------------
   10. Tiers
   --------------------------------------------------------------------- */
.m-tiers {
  background: var(--m-panel);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius);
  padding: var(--m-s3);
  box-shadow: var(--m-shadow);
}
.m-tiers > summary {
  cursor: pointer;
  font-weight: 600;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--m-s1);
}
.m-tiers > summary::-webkit-details-marker { display: none; }
.m-tiers > summary::before {
  content: "";
  width: 0.55rem; height: 0.55rem;
  border-right: 2px solid var(--m-brand);
  border-bottom: 2px solid var(--m-brand);
  transform: rotate(-45deg);
  transition: transform 140ms ease;
  flex: 0 0 auto;
}
.m-tiers[open] > summary::before { transform: rotate(45deg); }
.m-tiers-hint { color: var(--m-muted); font-weight: 400; font-size: var(--m-fs-sm); }

.m-tier-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--m-s2);
  margin-top: var(--m-s3);
}

.m .price-card {
  display: flex;
  flex-direction: column;
  background: var(--m-panel);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  padding: var(--m-s3);
}
.m .price-tier { font-weight: 700; font-size: var(--m-fs-sm); color: var(--m-ink); }
.m .price-tag { font-size: var(--m-fs-xs); color: var(--m-muted); margin-bottom: var(--m-s1); }
.m .price-value {
  font-size: 1.45rem;
  font-weight: 700;
  color: var(--m-brand);
  line-height: 1.1;
  margin-bottom: 0.2rem;
}
.m .price-note { font-size: var(--m-fs-xs); color: var(--m-muted); margin: 0; }
.m .paypal-form { margin-top: auto; padding-top: var(--m-s2); }
.m .paypal-form .paypal-btn { width: 100%; }

.m-bands {
  list-style: none;
  margin: var(--m-s3) 0 0;
  padding: 0;
  border-top: 1px solid var(--m-border-soft);
}
.m-band {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--m-s2);
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--m-border-soft);
  font-size: var(--m-fs-sm);
}
.m-band-name { font-weight: 600; color: var(--m-ink); }
.m-band-size { color: var(--m-muted); font-size: var(--m-fs-xs); display: block; }
.m-band-price { font-weight: 700; color: var(--m-brand); white-space: nowrap; }

/* ---------------------------------------------------------------------
   11. FAQ
   --------------------------------------------------------------------- */
.m-faq { display: grid; gap: var(--m-s2); }

.m-faq-item {
  background: var(--m-panel);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  padding: var(--m-s3);
}

.m-faq-item > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--m-s2);
  font-weight: 600;
  font-size: var(--m-fs-sm);
  line-height: 1.4;
  color: var(--m-ink);
}
.m-faq-item > summary::-webkit-details-marker { display: none; }
.m-faq-item > summary::after {
  content: "";
  flex: 0 0 auto;
  width: 1rem; height: 1rem;
  margin-top: 0.15rem;
  background-color: var(--m-brand-fill);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>") center/contain no-repeat;
  mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>") center/contain no-repeat;
  transition: transform 140ms ease;
}
.m-faq-item[open] > summary { color: var(--m-brand); }
.m-faq-item[open] > summary::after { transform: rotate(180deg); }

.m-faq-body { padding-top: var(--m-s2); }
.m-faq-body p { margin: 0 0 0.6rem; color: var(--m-muted); font-size: var(--m-fs-sm); max-width: 70ch; }
.m-faq-body ul { margin: 0 0 0.6rem; padding-left: 1.1rem; color: var(--m-muted); font-size: var(--m-fs-sm); }
.m-faq-body li { margin-bottom: 0.4rem; }
.m-faq-body strong { color: var(--m-ink); }
.m-faq-body em { font-style: italic; }

.m-faq-alert {
  font-weight: 700;
  color: var(--m-red-ink);
  font-size: 1rem;
}

.m-note {
  background: var(--m-amber-bg);
  border: 1px solid var(--m-amber-line);
  border-left-width: 5px;
  border-radius: 0 var(--m-radius-sm) var(--m-radius-sm) 0;
  padding: var(--m-s3);
  margin: var(--m-s3) 0;
}
.m-note-title {
  margin: 0 0 var(--m-s1);
  font-size: var(--m-fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--m-amber-ink);
}
.m-note p { margin: 0 0 0.5rem; color: var(--m-muted); font-size: var(--m-fs-sm); }
.m-note p:last-child { margin-bottom: 0; }

/*FAQ-ANSWER-TYPE-SIZE*/
/* Every FAQ answer renders at the SAME type size.
   The answers used to be built four different ways (div.m-faq-body, a
   bare <p>, div.m-facts.frag-rows, strong.m-faq-alert), and each one
   picked up a different font-size - 14.4px, 18px and 16px - so the
   answers did not match each other. One wrapper class, one size. */
.m-faq-answer,
.m-faq-answer p,
.m-faq-answer ul,
.m-faq-answer li,
.m-faq-answer .m-fact-text,
.m-faq-answer .m-card-text,
.m-faq-answer .m-note p,
.m-faq-answer .m-faq-alert,
.m-faq-answer a,
.m-faq-answer strong,
.m-faq-answer em,
.m-faq-answer .diagram-link {
  font-size: var(--m-fs-sm);
}
.m-faq-answer p { margin: 0 0 0.6rem; color: var(--m-muted); max-width: 70ch; }
.m-faq-answer .m-faq-alert { color: var(--m-red-ink); font-weight: 700; }
.m-faq-answer .diagram-link { color: var(--m-brand); }

/* ---------------------------------------------------------------------
   12. Footer
   --------------------------------------------------------------------- */
.m-footer {
  background: var(--m-panel);
  border-top: 1px solid var(--m-border);
  padding: var(--m-s5) var(--m-gutter);
  text-align: center;
  color: var(--m-muted);
  font-size: var(--m-fs-sm);
}
.m-footer p { margin: 0 0 0.4rem; max-width: 60ch; margin-inline: auto; }
.m-footer-nav { margin: var(--m-s2) 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 0.35rem 0.9rem; }
.m-footer-nav a { font-size: var(--m-fs-sm); }
.m-footer-small { font-size: var(--m-fs-xs); color: var(--m-muted); margin-top: var(--m-s3) !important; }

/* =====================================================================
   13. File size estimator (namespaced "sz")
   ===================================================================== */
.sz {
  --sz-brand: #60a5fa;
  --sz-brand-soft: rgba(37, 99, 235, 0.16);
  --sz-ink: #f1f5f9;
  --sz-muted: #a5b0c4;
  --sz-surface: rgba(17, 24, 39, 0.7);
  --sz-border: rgba(255, 255, 255, 0.06);
  --sz-radius: 0.75rem;
  --sz-gap: 1rem;

  color: var(--sz-ink);
}

.sz-card {
  background: var(--sz-surface);
  border: 1px solid var(--sz-border);
  border-radius: var(--sz-radius);
  padding: var(--sz-gap);
  box-shadow: 0 1px 2px rgba(15, 24, 40, 0.04), 0 10px 28px rgba(15, 24, 40, 0.05);
}

.sz-title {
  font-size: clamp(1.35rem, 1.2rem + 0.65vw, 1.6rem);
  line-height: 1.25;
  margin: 0 0 0.4rem;
}

.sz-sub {
  margin: 0 0 var(--sz-gap);
  color: var(--sz-muted);
  font-size: 0.95rem;
  max-width: 60ch;
}

.sz-grid { display: grid; gap: var(--sz-gap); }

.sz-field {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.sz-label { font-size: 0.9rem; font-weight: 600; color: var(--sz-ink); }

.sz-size {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--sz-brand);
  font-variant-numeric: tabular-nums;
}

.sz-slider-wrap { padding: 0.15rem 0 0.25rem; }

.sz-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 1.5rem;
  background: transparent;
  margin: 0;
  cursor: pointer;
}
.sz-slider::-webkit-slider-runnable-track {
  height: 0.4rem;
  border-radius: 999px;
  background: var(--sz-brand-soft);
  border: 1px solid var(--sz-border);
}
.sz-slider::-moz-range-track {
  height: 0.4rem;
  border-radius: 999px;
  background: var(--sz-brand-soft);
  border: 1px solid var(--sz-border);
}
.sz-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 1.35rem;
  height: 1.35rem;
  margin-top: -0.55rem;
  border-radius: 50%;
  background: var(--sz-brand);
  border: 2px solid var(--m-bg);
  box-shadow: 0 1px 4px rgba(15, 24, 40, 0.3);
}
.sz-slider::-moz-range-thumb {
  width: 1.35rem;
  height: 1.35rem;
  border-radius: 50%;
  background: var(--sz-brand);
  border: 2px solid var(--m-bg);
  box-shadow: 0 1px 4px rgba(15, 24, 40, 0.3);
}
.sz-slider:focus-visible { outline: 2px solid var(--sz-brand); outline-offset: 4px; }

.sz-ticks { position: relative; height: 1.6rem; margin-top: 0.2rem; }
.sz-tickmark {
  position: absolute;
  top: 0;
  width: 1px;
  height: 0.35rem;
  background: var(--sz-border);
  transform: translateX(-0.5px);
}
.sz-tickmark--active { background: var(--sz-brand); height: 0.5rem; }
.sz-ticklabel {
  position: absolute;
  top: 0.45rem;
  font-size: 0.68rem;
  color: var(--sz-muted);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.sz-ticklabel--active { color: var(--sz-brand); font-weight: 700; }

.sz-readout {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.6rem;
  padding: 0.75rem 0.9rem;
  border: 1px solid var(--sz-border);
  border-radius: 0.5rem;
  background: var(--sz-brand-soft);
}
.sz-tier {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--sz-ink);
}
.sz-price {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--sz-brand);
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.sz-band { margin: 0; width: 100%; font-size: 0.85rem; color: var(--sz-muted); }

.sz-note { margin: 0.75rem 0 0; font-size: 0.9rem; font-weight: 600; color: var(--sz-ink); }
.sz-disclaimer { margin: 0.2rem 0 0; font-size: 0.85rem; color: var(--sz-muted); }

.sz-sr {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------------
   14. Progressive enhancement (mobile-first: min-width only)
   --------------------------------------------------------------------- */
@media (min-width: 560px) {
  .m-facts { grid-template-columns: 1fr 1fr; gap: var(--m-s3) var(--m-s5); }
  .m-fact { grid-template-columns: 1.5rem 1fr; }
}

/* The trust row directly under the hero has exactly THREE items. In a
   two-column grid that leaves an empty fourth quadrant, which reads as
   a mistake rather than a design. Three equal columns on a wide
   screen; the two-column rule above still applies below 900px. */
@media (min-width: 900px) {
  .m-main > .m-facts { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 720px) {
  .m-topbar-tag { display: inline; }
  .m-brand-mark, .m-brand-mark img { height: 44px; }

  .m-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .m-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  .m-steps { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--m-s5); }

  .m-upload { padding: var(--m-s5); }
  .m-drop { padding: var(--m-s5) var(--m-s4); }

  .m-tier-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }

  .m-faq-item { padding: var(--m-s3) var(--m-s4); }

  .sz-card { padding: var(--m-s4); }
  .sz-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 15rem);
    align-items: start;
    gap: var(--m-s5);
  }
  .sz-readout { flex-direction: column; align-items: flex-start; }
}

@media (min-width: 1000px) {
  .m-grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .m-facts { grid-template-columns: 1fr 1fr; }
}

/* ---------------------------------------------------------------------
   Animated security status ticker
   A calm, professional strip above the upload loader. It has a single
   pulsing blue dot and one line of text that cross-fades between
   stages of the real transfer.
   --------------------------------------------------------------------- */
.sec-ticker {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  margin: 0.25rem 0 0.5rem;
  padding: 0.6rem 0.85rem;
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius-sm);
  background: var(--m-panel);
  -webkit-backdrop-filter: blur(var(--m-blur));
  backdrop-filter: blur(var(--m-blur));
  font-size: var(--m-fs-sm);
  line-height: 1.4;
  color: var(--m-muted);
}

/* The pulse is a PROGRESS signal, so it must only run while there is
   real progress to show. Idle means "no file chosen yet", and a dot
   pulsing forever in that state reads as a stuck progress bar. The
   script adds .sec-ticker--active to the host when the transfer
   actually starts, and removes it when the ticker returns to idle. */
.sec-ticker-dot {
  flex: 0 0 auto;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--m-brand-fill);
  box-shadow: 0 0 0 0 rgba(15, 58, 138, 0.35);
  animation: none;
}

.sec-ticker--active .sec-ticker-dot {
  animation: sec-ticker-pulse 2.4s ease-out infinite;
}

@keyframes sec-ticker-pulse {
  0% {
    box-shadow: 0 0 0 0 rgba(96, 165, 250, 0.42);
  }
  70% {
    box-shadow: 0 0 0 0.5rem rgba(96, 165, 250, 0);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(15, 58, 138, 0);
  }
}

/* The cross-fade: the old line fades out, the new one fades in. */
.sec-ticker-text {
  display: block;
  min-height: 1.4em;
  transition: opacity 420ms ease;
}
.sec-ticker-text.sec-ticker-fading {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .sec-ticker-dot {
    animation: none;
  }
  /* The active state must also lose its pulse - otherwise the rule
     above would be overridden by the more specific active selector. */
  .sec-ticker--active .sec-ticker-dot {
    animation: none;
  }
  .sec-ticker-text {
    transition: none;
  }
}

/* The short line that explains what the ticker is describing. */
.sec-ticker-note {
  margin: 0.4rem 0 0 1.7rem;
  font-size: var(--m-fs-xs);
  line-height: 1.5;
  color: var(--m-muted);
  max-width: 46rem;
}

/* The seven service facts sit in their own section now, so they
   read as an at-a-glance strip rather than a box of bullets inside
   the upload card. A flex-wrap strip means the last row grows to
   fill the width - no empty hole with an odd number of items. */
.m .feature-strip {
  display: flex;
  flex-wrap: wrap;
  gap: var(--m-s2);
  margin: 0;
  max-width: none;
}
.m .feature-strip .m-fact {
  flex: 1 1 15rem;
  padding-top: var(--m-s2);
}
@media (min-width: 720px) {
  .m .feature-strip { gap: var(--m-s3); }
}

/* Give the explainer rows a little right-hand breathing room, so the
   longest line does not crowd the edge of the card. */
.m-facts .m-fact {
  padding-right: 0.4rem;
}

/* PANEL-SYSTEM:BEGIN */
/* ---------------------------------------------------------------------
   10. Glassmorphism feature cards
   A translucent surface, an ultra-thin 1px outline and a 16px backdrop
   blur. Applied to the core information blocks.

   .m-facts is deliberately NOT in this group. It is used in three places
   and only two of them are panels - see HERO-FACT-PANEL below.
   --------------------------------------------------------------------- */
.m-card,
.m-faq-item,
.m-tier-row,
.m-band,
.m-upload,
.m-note {
  background: var(--m-panel);
  border: 1px solid var(--m-border);
  -webkit-backdrop-filter: blur(var(--m-blur));
  backdrop-filter: blur(var(--m-blur));
}

/* The action card carries the page's ONE accent edge - it is declared
   with the .m-upload rule itself (see UPLOAD-CARD below). The glass
   group rule below sets `border` on every panel, and that shorthand
   resets border-top, so an accent drawn as a border would never render;
   the accent is an inset shadow for exactly that reason. */

.m-card { box-shadow: var(--m-shadow-lift); }

/* --- HERO-FACT-PANEL -----------------------------------------------
   .m-facts is used in three places and they do NOT want the same
   surface:
     1. the hero trust strip -> a standalone panel in the left column
     2. the at-a-glance strip -> a full-width panel inside a section
     3. the FAQ answers      -> FLAT rows inside an .m-faq-item card
   (1) and (2) carry the glass panel treatment - the same radius,
   padding, hairline outline and lift shadow as .m-card and
   .m-recognise, so the hero column reads as one system. (3) has NO
   panel of its own: a second border and background drawn inside a
   rounded card is the "square rectangle inside a rounded card"
   artefact, so those rows sit flat on their parent card's surface. */
.m-hero--split .m-facts,
.m-facts.feature-strip {
  background: var(--m-panel);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius);
  padding: var(--m-s4);
  box-shadow: var(--m-shadow-lift);
  -webkit-backdrop-filter: blur(var(--m-blur));
  backdrop-filter: blur(var(--m-blur));
}

/* A panel's first row must not carry the list's own top rule - it would
   draw a line across the panel a quarter of the way down. */
.m-hero--split .m-facts > .m-fact:first-child {
  border-top: 0;
  padding-top: 0;
}

/* The at-a-glance strip WRAPS into rows, so a top rule on every item
   would draw broken segments. The strip's own gap separates them. */
.m-facts.feature-strip > .m-fact {
  border-top: 0;
  padding-top: 0;
}
/* PANEL-SYSTEM:END */

.m-faq-item[open] { background: var(--m-panel-raised); }
.m-faq-item > summary:hover { background: var(--m-tint-soft); }

.m-tiers {
  -webkit-backdrop-filter: blur(var(--m-blur));
  backdrop-filter: blur(var(--m-blur));
}

/* the component shells get the same glass treatment as the page cards */
.sz-card,
.wf3-card,
.wf3-step {
  background: rgba(17, 24, 39, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.06);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}

/* ---------------------------------------------------------------------
   Illustrations (Nano Banana, flat blue line art on navy)

   Each illustration carries its own dark background. On a glass card
   that reads as a pasted rectangle, so every figure sits on its own
   #090d16 plate with a hairline edge and a soft mask that fades the
   outer pixels into the page. The result reads as part of the page
   rather than an image dropped on top of it.
   --------------------------------------------------------------------- */
.qbw-fig {
  position: relative;
  margin: 0;
  border-radius: 12px;
  overflow: hidden;
  background: #090d16;
  border: 1px solid rgba(255, 255, 255, 0.06);
  line-height: 0;
}
.qbw-fig img {
  display: block;
  width: 100%;
  height: auto;
  /* Fade the edges so the plate blends into the page background. */
  -webkit-mask-image: radial-gradient(120% 120% at 50% 50%,
                      #000 55%, rgba(0, 0, 0, 0.55) 82%, transparent 100%);
  mask-image: radial-gradient(120% 120% at 50% 50%,
              #000 55%, rgba(0, 0, 0, 0.55) 82%, transparent 100%);
}
/* The wide banner version - used for the bank and vault section. */
.qbw-fig--wide {
  margin: 1.75rem 0 0;
}
/* The caption under the wide bank-and-vault banner.
   The IMAGE carries drawn titles on each half ("THE FILING CABINETS" /
   "THE SAFE DEPOSIT BOXES"), because a caption under a 1376px-wide
   banner reads as a note about the whole picture rather than a label
   for each half. This figcaption carries the SAME two facts as text,
   for screen readers and for the case where the image does not load.
   It is one line, centred, with a middot between the two halves. */
.qbw-figcap {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 0.55rem;
  margin: 0;
  padding: 0.85rem 1rem 0.9rem;
  line-height: 1.5;
  font-size: 0.9375rem;
  color: var(--m-ink);
  opacity: 0.82;
}
.qbw-figcap span[aria-hidden="true"] {
  /* The separator between the two halves. It was red at 0.55 opacity,
     which vision read as "a tiny reddish speck... a stray artifact
     rather than a fully intentional bullet". A red mark at that size
     looks like dirt on the screen. The site's accent blue, slightly
     larger, reads clearly as a deliberate separator and matches the
     line art in the illustration above it. */
  color: var(--m-brand, #60a5fa);
  opacity: 0.8;
  font-size: 1.05em;
  line-height: 1;
}
/* The small square version - used inside a card. */
.qbw-fig--card {
  margin: 0 0 1rem;
  border-radius: 10px;
}
.qbw-fig--card img {
  -webkit-mask-image: radial-gradient(125% 125% at 50% 50%,
                      #000 58%, rgba(0, 0, 0, 0.6) 84%, transparent 100%);
  mask-image: radial-gradient(125% 125% at 50% 50%,
              #000 58%, rgba(0, 0, 0, 0.6) 84%, transparent 100%);
}
@media (max-width: 640px) {
  .qbw-fig--wide { margin-top: 1.25rem; }
}

/* ==== HERO CTA ==== */
/* The hero carries the first action on the page. It is a flex row so
   the button and the reassurance note sit together - the note is the
   "nothing uploads yet" promise, and it belongs next to the button
   that triggers the file picker, not in a paragraph above it. */
.m-hero-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--m-s2);
  margin: var(--m-s4) 0 var(--m-s3);
}

.m-hero-cta .m-btn {
  min-height: 3rem;
  padding: 0.7rem 1.4rem;
  font-size: var(--m-fs-body);
  /* .m-btn sets color: var(--m-brand), which is the LIGHT blue - on the
     deep blue fill that is only 2.03:1 and fails WCAG AA. The button
     label must be the near-white --m-surface, the same as every other
     primary button on the page. */
  color: var(--m-surface);
}

.m-hero-cta-note {
  font-size: var(--m-fs-xs);
  color: var(--m-muted);
}

/* The secondary route for people on a phone. Quieter than the primary
   CTA on purpose - it must not compete with the button above it. */
.m-hero-cta--alt {
  display: block;
  margin: var(--m-s2) 0 0;
  font-size: var(--m-fs-xs);
  color: var(--m-muted);
}

.m-hero-cta--alt a {
  color: var(--m-brand);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* The "On a phone?" fallback is for narrow screens only. On a desktop the
   visitor picks their own file in one click, so the note is noise there. */
@media (min-width: 641px) {
  .m-hero-cta--alt {
    display: none;
  }
}

@media (max-width: 640px) {
  .m-hero-cta {
    flex-direction: column;
    align-items: stretch;
    gap: var(--m-s2);
  }

  .m-hero-cta .m-btn {
    width: 100%;
  }
}

/* The File Doctor illustration now sits INSIDE the "Stays locked" card,
   next to the words that talk about File Doctor. It is a 1024x1024
   square, so at full card width it would be taller than the text it
   sits above - cap it and centre it. */
.qbw-fig--inline {
  margin: var(--m-s3) 0 0;
}

.qbw-fig--inline img {
  max-height: 190px;
  width: auto;
  margin: 0 auto;
  display: block;
}

/* ==== STAYS-LOCKED X MARK ==== */
/* The three bullets in the "Stays locked" card are all LIMITATIONS, so
   they take a red X rather than a tick. A tick reads as good news.

   CRITICAL: this MUST live inside the :root block. A bare
   "--m-red-mark: #fca5a5;" at file scope is not a valid CSS declaration
   -- it is parsed as a bogus selector and silently discarded by the
   browser. The svg then falls back to the inherited white text colour and
   the X renders white instead of red. */
:root {
  --m-red-mark: #fca5a5;
}

/* ==== FROZEN PAYMENT SCRIPT: LIGHT-PALETTE COLOUR OVERRIDES ==== */
/* The inline payment script in index.html is FROZEN byte-for-byte and the
   deploy asserts its hash. It writes LIGHT-palette colours (a light-theme
   palette) into inline styles, which on the dark theme are unreadable:

     status.style.color = '#4a5468'   (x2)  2.40:1 on the upload card
     status.style.color = '#b91c1c'   (x6)  2.82:1  (all error states)
     status.style.color = '#15803d'   (x1)  3.64:1  (success state)
     helper.style.cssText = '... color: #1a1f2c;'         1.18:1 on the bg
     wrapper.style.cssText = '... background: #e6f4ec; border: 1px solid #1e8a4c; ...'

   The script CANNOT be edited, so the fix has to come from OUTSIDE it.
   These rules use !important so they beat the script's inline style,
   which otherwise wins over any author rule.

   Element ids read out of the frozen script:
     status  -> #large-file-status   (class .m-large-status)
     helper  -> .cp-help             (inside #continue-to-paypal)
     wrapper -> #continue-to-paypal  (class .continue-to-paypal)

   The script sets these colours at RUNTIME, so the computed style must be
   read in the browser after the script has run - a grep of the CSS
   proves nothing. */

/* the status line: neutral, error and success states.

   The script sets the colour as `status.style.color = '#b91c1c'`, but the
   browser NORMALISES the value: the style attribute ends up holding
   "color: rgb(185, 28, 28);", never the hex. An [style*="#b91c1c"]
   selector therefore never matches. These selectors match the
   NORMALISED rgb() form. */
.m #large-file-status,
.m #large-file-status.m-large-status {
  color: var(--m-muted) !important;
}
.m #large-file-status[style*="rgb(185, 28, 28)"] { color: var(--m-red-ink) !important; }
.m #large-file-status[style*="rgb(21, 128, 61)"]  { color: var(--m-green-ink) !important; }
.m #large-file-status[style*="rgb(74, 84, 104)"]  { color: var(--m-muted) !important; }

/* the pale-green confirmation box the script builds around the PayPal form */
.m #continue-to-paypal.continue-to-paypal {
  background: var(--m-green-bg) !important;
  border: 1px solid var(--m-green-line) !important;
}

/* the helper paragraph the script writes inside that box */
.m #continue-to-paypal .cp-help {
  color: var(--m-ink) !important;
}

/* RECOGNISE-PANEL:BEGIN */
/* ==== RECOGNITION BLOCK (hero variant C) ==== */
/* The "You'll recognise this if..." block. It sits in the LEFT HERO
   COLUMN, directly under the "Why trust this" facts strip - both are
   children of .m-hero-col.

   The accent convention for the whole page is ONE edge: a 3px brand bar
   along the top of the action card (.m-upload). This panel carries NO
   accent, because it is stacked directly under another panel, and two
   stacked panels that disagree about their accents read as two
   components from two different kits. It is instead the same neutral
   glass panel as the facts strip above it - same radius, same padding,
   same hairline outline, same lift shadow - so the hero column reads as
   one system.

   The "No fix, no fee." guarantee is the second fact of the strip
   ABOVE this panel, so nothing in this panel can push the guarantee
   down the fold. */

.m-recognise {
  margin: var(--m-s5) auto 0;
  max-width: 44rem;            /* same measure as .m-facts */
  padding: var(--m-s4);
  background: var(--m-panel);
  border: 1px solid var(--m-border);
  border-radius: var(--m-radius);
  box-shadow: var(--m-shadow-lift);
  -webkit-backdrop-filter: blur(var(--m-blur));
  backdrop-filter: blur(var(--m-blur));
  text-align: left;
}
/* RECOGNISE-PANEL:END */

.m-recognise-title {
  margin: 0 0 var(--m-s3);
  font-size: var(--m-fs-h3);
  font-weight: 700;
  line-height: 1.3;
  color: var(--m-ink);
}

.m-recognise-list {
  margin: 0;
  padding: 0 0 0 1.25rem;
  list-style: disc;
  color: var(--m-muted);
  font-size: var(--m-fs-sm);
  line-height: 1.55;
}

.m-recognise-list li {
  margin: 0 0 var(--m-s2);
}

.m-recognise-list li:last-child {
  margin-bottom: 0;
}

.m-recognise-list li::marker {
  color: var(--m-brand);
}

.m-recognise-close {
  margin: var(--m-s3) 0 0;
  padding-top: var(--m-s3);
  border-top: 1px solid var(--m-border-soft);
  color: var(--m-muted);
  font-size: var(--m-fs-sm);
  line-height: 1.55;
}

@media (max-width: 640px) {
  .m-recognise {
    padding: var(--m-s3);
    margin-top: var(--m-s4);
  }

  .m-recognise-title {
    margin-bottom: var(--m-s2);
  }

  .m-recognise-list {
    padding-left: 1.1rem;
  }
}

/* ---------------------------------------------------------------------
   20. Concept 1 - "user as hero" split layout
   ---------------------------------------------------------------------
   Two changes ride together here:

   (a) A slim, permanent trust strip above the topbar. It carries the
       Intuit disclaimer and the region pills that used to sit inside
       the centred hero, so they are on screen before the fold on a
       desktop and do not push the hero down.

   (b) The hero becomes two columns on a wide screen: the COPY column is
       the narrow one (45%) and the UPLOAD CARD is the wide one (55%).
       The split is measured, not arbitrary - at 55/45 the narrow column
       reflows the quote card from 323px to 506px tall and the ownership
       declaration from 248px to 357px, stretching the price-to-Pay gap
       from 747px to 1099px. At 45/55 the gap is 949px.
   --------------------------------------------------------------------- */

.m-strip {
  background: var(--m-panel);
  border-bottom: 1px solid var(--m-border);
}
.m-strip-inner {
  max-width: var(--m-maxw);
  margin: 0 auto;
  padding: 0.5rem var(--m-gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--m-s3);
  flex-wrap: wrap;
}
.m-strip-text {
  margin: 0;
  font-size: var(--m-fs-xs);
  color: var(--m-muted);
  line-height: 1.45;
}
.m-strip-text strong { color: var(--m-ink); }
.m-strip-regions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--m-s1);
  margin: 0;
}

/* The strip is a single slim ROW: the disclaimer on the left, the region
   pills on the right. Without these the disclaimer's max-content width
   (about 1500px) exceeds the container, so it took the whole row and
   pushed the pills onto a second line - a 100px strip on desktop and
   138px on a phone. Letting the text shrink and wrap keeps it to two
   lines on desktop and three on a phone, with the pills always on the
   right of the text. */
.m-strip-text { flex: 1 1 12rem; min-width: 0; }
.m-strip-regions { flex: 0 0 auto; }

/* --- the split hero ------------------------------------------------- */

.m-hero--split {
  display: flex;
  flex-direction: column;
  gap: var(--m-s5);
  text-align: left;
}
.m-hero--split .m-hero-col { min-width: 0; }
.m-hero--split .m-hero-title { margin-left: 0; margin-right: 0; text-align: left; }
.m-hero--split .m-hero-lead  { margin-left: 0; margin-right: 0; max-width: 52ch; }
.m-hero--split .m-hero-cta   { justify-content: flex-start; }
.m-hero--split .m-hero-cta--alt { text-align: left; }
.m-hero--split .m-facts {
  margin: 0;
  max-width: none;
  grid-template-columns: 1fr;
  gap: var(--m-s3);
}
.m-hero--split .m-recognise { margin: 0; max-width: none; }
.m-hero--split .m-step-card { margin: 0; }
.m-step-card { scroll-margin-top: 4rem; }

/* HERO-COLUMNS:BEGIN */
@media (min-width: 901px) {
  .m-hero--split {
    flex-direction: row;
    /* stretch, not flex-start: with flex-start each column took its own
       natural height and the two columns ended at different y (measured
       on the live page: 1272 vs 1348 at 1534 wide, 1272 vs 1326 at
       1440 wide). Stretch makes both columns the height of the taller
       one. */
    align-items: stretch;
    gap: 0 clamp(1.5rem, 3vw, 3rem);
  }
  .m-hero--split .m-hero-col {
    flex: 0 0 45%;
    /* a flex column, so the last panel can absorb the height the taller
       right-hand column forces on the left one. Without this the two
       COLUMN boxes are the same height but the two cards still end at
       different y, with dead space under the left one. */
    display: flex;
    flex-direction: column;
  }
  .m-hero--split .m-hero-col > .m-recognise { flex: 1 1 auto; }
  .m-hero--split .m-step-card { flex: 1 1 55%; min-width: 0; }
  /* the upload card fills the column it sits in, so the two columns end
     on the same line whatever their content does */
  .m-hero--split .m-step-card { display: flex; flex-direction: column; }
  .m-hero--split .m-step-card > .m-upload { flex: 1 1 auto; }
}
/* HERO-COLUMNS:END */

/* The hero is left-aligned now, so the card must not inherit centring
   from anywhere up the tree. */
.m-hero--split .m-step-card .m-upload { text-align: left; }

/* --- the split hero on a phone ---------------------------------------
   On a narrow screen the split collapses to one column, and the ORDER
   matters: the headline stays first, the trust facts sit under it so
   the "No fix, no fee" guarantee is on the first screen, and the upload
   card follows the copy so the drop zone is close to the fold.

   The DOM is exactly as specified - .m-hero-col then .m-step-card - so
   the reordering is done with CSS 'order' and 'display:contents', which
   promotes the individual children of .m-hero-copy to flex items of
   .m-hero--split. The 45/55 two-column layout on a wide screen is
   untouched; this block only applies below 900px.

   Measured on the live page at 390x844:
     "No fix, no fee"   921px -> 587px   (target: under 844)
     drop zone        1726px -> 1345px  (target: under 1400)
   --------------------------------------------------------------------- */
@media (max-width: 900px) {
  /* the disclaimer is long; a slightly smaller face keeps the strip slim
     so it does not eat the first screen */
  .m-strip-text { font-size: 0.75rem; line-height: 1.4; }
  .m-strip-inner { gap: 0.4rem; }

  .m-hero { padding-top: 1.2rem; }
  .m-hero--split { gap: 1rem; }

  /* promote the copy children so they can be ordered individually */
  .m-hero--split .m-hero-col { display: contents; }
  .m-hero--split .m-hero-copy { display: contents; }

  .m-hero--split .m-eyebrow     { order: 1; }
  .m-hero--split .m-hero-title  { order: 2; }
  .m-hero--split .m-hero-lead   { order: 3; }
  .m-hero--split .m-facts       { order: 4; }
  .m-hero--split .m-hero-cta    { order: 5; }
  .m-hero--split .m-hero-cta--alt { order: 5.1; }
  .m-hero--split .m-hero-foot   { order: 5.2; }
  .m-hero--split .m-step-card   { order: 6; }
  .m-hero--split .m-recognise   { order: 7; }

  /* tighten the copy so the card reaches the fold */
  .m-hero--split .m-hero-title { margin-bottom: 0.5rem; }
  .m-hero--split .m-hero-lead  { font-size: 0.95rem; margin-bottom: 0.7rem; }
  .m-hero--split .m-hero-cta   { margin-bottom: 0.4rem; }
  .m-hero--split .m-facts      { gap: 0.5rem; }
  .m-hero--split .m-fact       { padding-top: 0.6rem; }
  .m-hero--split .m-fact-text  { font-size: 0.88rem; }
}

/* ===== QAW-PAYMENT-STAGE-CSS-BEGIN =====
   PAYMENT-STAGE VIEW  +  DELETE-SUCCESS STATE
   Added 07-Nov-2026 by transform_payment_stage_and_delete_copy.py.

   1. When the file is uploaded and the page reaches the awaiting-payment
      step, the only remaining action is the Continue to PayPal button.
      The marketing sections below the card are hidden in THAT state and
      in no other. They stay in the DOM - the crawler-visible content of
      the page is unchanged - and body.awaiting-payment is set and
      removed by payment-stage.js from the live DOM state.
   2. After a cancel-and-delete there is no upload running, so the
      progress bar, the byte counter and the chunk meter under the green
      message describe nothing. Scoped to the delete state ONLY: the
      upload-complete success state legitimately shows its 100% bar.
   ===================================================================== */
.m.awaiting-payment .m-section { display: none; }

.m .upload-status.is-deleted .progress-bar,
.m .upload-status.is-deleted .progress-meta,
.m .upload-status.is-deleted .chunk-meter { display: none; }
/* ===== QAW-PAYMENT-STAGE-CSS-END ===== */
