/* ==========================================================================
   Convene — application theme

   1. Tokens (light + dark)
   2. Base
   3. App shell
   4. Components
   5. Public check-in
   6. Auth screens
   7. Landing
   8. Third-party overrides
   9. Responsive / print / motion

   Theming: every colour is a custom property declared once for light and
   re-declared under [data-theme="dark"]. Nothing hard-codes a hex outside
   section 1, so dark mode needs no per-component work and cannot drift.

   White-label: --brand and --brand-2 are overridden inline per organization,
   so a tenant's colours flow through every accent without a second stylesheet.
   ========================================================================== */

/* ─────────────────────────── 1. Tokens — light ────────────────────────── */

:root,
[data-theme="light"] {
  --brand:        #4f46e5;
  --brand-2:      #7c3aed;
  --brand-soft:   #eef2ff;

  --bg:           #f7f8fc;
  --surface:      #ffffff;
  --surface-2:    #f9fafb;
  --sidebar:      #0f1729;
  --sidebar-hover:#1c2740;
  --sidebar-text: #94a3b8;
  --sidebar-label:#8ea0ba;
  --sidebar-dim:  #7d8ea8;

  --ink:          #0f172a;
  --ink-2:        #334155;
  --muted:        #64748b;

  --line:         #e5e7eb;
  --line-2:       #d1d5db;

  --success:      #059669;
  --warning:      #d97706;
  --danger:       #dc2626;
  --info:         #0284c7;

  /* The status colours above are FILLS — correct behind white text, and 3.5:1
     as ink on their own pale tint. These are the same hues moved far enough to
     read, for the (many) places a status is shown as coloured type. */
  --success-ink:  #047857;
  --warning-ink:  #b45309;
  --danger-ink:   #b91c1c;
  --info-ink:     #0369a1;

  --shadow-xs:    0 1px 2px rgba(15,23,42,.06);
  --shadow-sm:    0 1px 3px rgba(15,23,42,.09), 0 1px 2px rgba(15,23,42,.06);
  --shadow:       0 4px 12px -2px rgba(15,23,42,.10), 0 2px 6px -2px rgba(15,23,42,.06);
  --shadow-lg:    0 20px 40px -12px rgba(15,23,42,.18);

  --radius:       14px;
  --radius-sm:    10px;
  --sidebar-w:    252px;
  --topbar-h:     62px;

  /* Bootstrap utilities read the *-rgb forms, not the plain ones: .text-body
     is `rgba(var(--bs-body-color-rgb), var(--bs-text-opacity))`. Setting only
     --bs-body-color leaves those utilities on Bootstrap's default near-black,
     which is invisible on our dark chrome — so both forms are declared here. */
  --bs-primary:            #4f46e5;
  --bs-primary-rgb:        79,70,229;
  --bs-body-color:         #0f172a;
  --bs-body-color-rgb:     15,23,42;
  --bs-body-bg:            #f7f8fc;
  --bs-body-bg-rgb:        247,248,252;
  --bs-border-color:       #e5e7eb;
  --bs-emphasis-color:     #0f172a;
  --bs-emphasis-color-rgb: 15,23,42;
  --bs-secondary-color:    #6b7280;
  --bs-secondary-color-rgb:107,114,128;

  color-scheme: light;
}

/* ─────────────────────────── 1b. Tokens — dark ────────────────────────────
   Not an inversion: surfaces are desaturated navy so the brand still reads,
   and text sits near #e2e8f0 rather than pure white to cut halation. */

[data-theme="dark"] {
  --brand:        #6366f1;
  --brand-2:      #8b5cf6;
  --brand-soft:   #1e1b4b;

  --bg:           #0b1120;
  --surface:      #131c31;
  --surface-2:    #1a2540;
  --sidebar:      #080d19;
  --sidebar-hover:#161f36;
  --sidebar-text: #94a3b8;
  --sidebar-label:#8ea0ba;
  --sidebar-dim:  #7d8ea8;

  --ink:          #e2e8f0;
  --ink-2:        #cbd5e1;
  --muted:        #94a3b8;

  --line:         #243049;
  --line-2:       #2f3d5c;

  --success:      #34d399;
  --warning:      #fbbf24;
  --danger:       #f87171;
  --info:         #38bdf8;

  /* On the dark surface the fills are already light enough to read. */
  --success-ink:  #34d399;
  --warning-ink:  #fbbf24;
  --danger-ink:   #fca5a5;
  --info-ink:     #38bdf8;

  --shadow-xs:    0 1px 2px rgba(0,0,0,.30);
  --shadow-sm:    0 1px 3px rgba(0,0,0,.36);
  --shadow:       0 4px 14px rgba(0,0,0,.42);
  --shadow-lg:    0 20px 44px rgba(0,0,0,.55);

  --bs-body-color:         #e2e8f0;
  --bs-body-color-rgb:     226,232,240;
  --bs-body-bg:            #0b1120;
  --bs-body-bg-rgb:        11,17,32;
  --bs-border-color:       #243049;
  --bs-emphasis-color:     #f8fafc;
  --bs-emphasis-color-rgb: 248,250,252;
  --bs-secondary-color:    #94a3b8;
  --bs-secondary-color-rgb:148,163,184;

  color-scheme: dark;
}

/* ─────────────────────────────── 2. Base ──────────────────────────────── */

* { box-sizing: border-box; }

/* Fluid root.
   Every font-size in this file is expressed in rem, so this single rule sets
   the scale of the whole interface. Previously everything was fixed px, which
   is why the UI looked correct on a laptop and shrank into the corner of a
   large or high-resolution display — the pixels got smaller, the type did not.
   Floor 16px keeps phones exactly where they were; the ceiling stops a very
   wide monitor turning the app cartoonishly large. */
html {
  font-size: clamp(16px, 0.34vw + 14.4px, 20px);
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-size: 0.9375rem;          /* 15px at the 16px floor, 18.75px at the cap */
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  /* Only colours transition — animating "all" makes the sidebar and every
     chart jank on theme switch. */
  transition: background-color .2s ease, color .2s ease;
}

a { text-decoration: none; color: var(--brand); }
a:hover { color: var(--brand-2); }

h1,h2,h3,h4,h5,h6 { font-weight: 700; letter-spacing: -.02em; color: var(--ink); }

::selection { background: var(--brand); color: #fff; }

.text-muted { color: var(--muted) !important; }

/* Scrollbars, so dark mode has no bright gutter. */
* { scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 6px; }
*::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* ──────────────────────────── 3. App shell ────────────────────────────── */

.af-topbar {
  height: var(--topbar-h);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 1040;
  display: flex; align-items: center;
  padding: 0 18px;
}

.af-brand { display: flex; align-items: center; gap: 10px; }
.af-brand-mark {
  width: 2.125rem; height: 2.125rem; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  color: #fff; font-size: 1.0625rem; flex-shrink: 0;
  box-shadow: 0 4px 10px rgba(79,70,229,.32);
}
.af-brand-name { font-weight: 800; font-size: 1.0313rem; color: var(--ink); letter-spacing: -.03em; }

.af-shell { display: flex; min-height: calc(100vh - var(--topbar-h)); }

.af-sidebar {
  width: var(--sidebar-w); flex: 0 0 var(--sidebar-w);
  background: var(--sidebar);
  position: sticky; top: var(--topbar-h);
  height: calc(100vh - var(--topbar-h));
  overflow-y: auto;
  display: flex; flex-direction: column;
  padding: 14px 0;
  z-index: 1030;
}
.af-nav-label {
  /* Was #64748b, which is 3.76:1 on the sidebar — under AA, and this is the
     smallest, most letter-spaced text in the app. Tokenised so the sidebar
     footer reads from the same value instead of drifting from it. */
  color: var(--sidebar-label); font-size: 0.6563rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  padding: 14px 20px 6px;
}
.af-nav { list-style: none; margin: 0; padding: 0 10px; }
.af-nav a {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 12px; margin-bottom: 2px;
  border-radius: var(--radius-sm);
  color: var(--sidebar-text); font-size: 0.875rem;
  transition: background .15s, color .15s;
}
.af-nav a i { font-size: 1rem; width: 20px; text-align: center; }
.af-nav a:hover { background: var(--sidebar-hover); color: #fff; }
.af-nav a.active {
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  color: #fff; font-weight: 600;
  box-shadow: 0 4px 12px rgba(79,70,229,.36);
}
.af-sidebar-foot { margin-top: auto; padding: 14px 20px 4px; border-top: 1px solid #1e293b; }

.af-main { flex: 1 1 auto; min-width: 0; padding: 22px 24px 44px; }

.af-page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; margin-bottom: 20px;
}
.af-page-title { font-size: 1.4375rem; font-weight: 800; margin: 0 0 2px; }
.af-page-sub { color: var(--muted); font-size: 0.875rem; margin: 0; }

.af-backdrop { display: none; position: fixed; inset: 0; background: rgba(15,23,42,.5); z-index: 1025; }
.af-backdrop.show { display: block; }

/* ───────────────────────────── 4. Components ──────────────────────────── */

.af-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-xs);
  margin-bottom: 20px;
}
.af-card-head {
  padding: 15px 19px; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
}
.af-card-title { font-size: 0.9688rem; font-weight: 700; margin: 0; }
.af-card-body { padding: 19px; }
.af-card-body.tight { padding: 0; }

/* Stat tiles */
.af-stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-xs);
  padding: 17px 19px; height: 100%;
  display: flex; align-items: center; gap: 14px;
  transition: transform .18s, box-shadow .18s, border-color .18s;
  position: relative; overflow: hidden;
}
.af-stat:hover { transform: translateY(-2px); box-shadow: var(--shadow); border-color: var(--brand); }
.af-stat-icon {
  width: 2.875rem; height: 2.875rem; border-radius: 12px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 1.25rem; color: #fff;
}
.af-stat-value { font-size: 1.5rem; font-weight: 800; line-height: 1.1; letter-spacing: -.03em; }
.af-stat-label { font-size: 0.7188rem; color: var(--muted); font-weight: 700;
                 text-transform: uppercase; letter-spacing: .05em; }
.af-stat-sub { font-size: 0.75rem; color: var(--muted); }

.grad-indigo  { background: linear-gradient(135deg,#4f46e5,#7c3aed); }
.grad-emerald { background: linear-gradient(135deg,#059669,#10b981); }
.grad-amber   { background: linear-gradient(135deg,#d97706,#f59e0b); }
.grad-rose    { background: linear-gradient(135deg,#e11d48,#f43f5e); }
.grad-sky     { background: linear-gradient(135deg,#0284c7,#0ea5e9); }
.grad-violet  { background: linear-gradient(135deg,#7c3aed,#a855f7); }
.grad-slate   { background: linear-gradient(135deg,#334155,#64748b); }
.grad-teal    { background: linear-gradient(135deg,#0d9488,#14b8a6); }

/* Forms */
.form-label { font-weight: 600; font-size: 0.8125rem; color: var(--ink-2); margin-bottom: 5px; }
.form-control, .form-select {
  border-radius: var(--radius-sm);
  border-color: var(--line-2);
  background: var(--surface);
  color: var(--ink);
  font-size: 0.875rem; padding: .55rem .8rem;
}
.form-control:focus, .form-select:focus {
  background: var(--surface); color: var(--ink);
  border-color: var(--brand);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 15%, transparent);
}
.form-control::placeholder { color: var(--muted); opacity: .75; }
.input-group-text {
  background: var(--surface-2); border-color: var(--line-2);
  color: var(--muted); border-radius: var(--radius-sm);
}
.form-text { font-size: 0.7813rem; color: var(--muted); }
.required::after { content: ' *'; color: var(--danger); }
.form-check-input:checked { background-color: var(--brand); border-color: var(--brand); }

.btn { border-radius: var(--radius-sm); font-weight: 600; font-size: 0.875rem; padding: .55rem 1.05rem; }
.btn-primary { background: var(--brand); border-color: var(--brand); }
.btn-primary:hover, .btn-primary:focus {
  background: color-mix(in srgb, var(--brand) 85%, #000);
  border-color: color-mix(in srgb, var(--brand) 85%, #000);
}
.btn-brand {
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  color: #fff; border: none;
  box-shadow: 0 6px 16px rgba(79,70,229,.3);
}
.btn-brand:hover { color: #fff; filter: brightness(1.08); transform: translateY(-1px); }
.btn-lg { padding: .72rem 1.4rem; font-size: 0.9688rem; }
.btn-sm { padding: .34rem .72rem; font-size: 0.8rem; }

.badge { font-weight: 600; letter-spacing: .01em; padding: .38em .65em; }

/* Status pills — semantic, so a status is recognisable at a glance anywhere. */
.pill { display: inline-flex; align-items: center; gap: 5px;
        font-size: 0.7188rem; font-weight: 700; padding: 3px 9px; border-radius: 20px;
        text-transform: uppercase; letter-spacing: .03em; }
.pill-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.pill-active,   .pill-completed { background: color-mix(in srgb, var(--success) 14%, transparent); color: var(--success); }
.pill-trial,    .pill-draft     { background: color-mix(in srgb, var(--warning) 16%, transparent); color: var(--warning); }
.pill-expired,  .pill-cancelled { background: color-mix(in srgb, var(--danger) 14%, transparent);  color: var(--danger); }
.pill-suspended                 { background: color-mix(in srgb, var(--danger) 14%, transparent);  color: var(--danger); }
.pill-neutral                   { background: color-mix(in srgb, var(--muted) 15%, transparent);   color: var(--muted); }

/* Avatars */
.af-avatar {
  border-radius: 50%; object-fit: cover;
  background: var(--brand-soft); color: var(--brand);
  display: inline-flex; align-items: center; justify-content: center;
  font-weight: 700; flex-shrink: 0;
}
.af-avatar-sm { width: 2.125rem; height: 2.125rem; font-size: 0.8125rem; }
.af-avatar-md { width: 3.25rem; height: 3.25rem; font-size: 1.1875rem; }
.af-avatar-lg { width: 5.5rem; height: 5.5rem; font-size: 2rem; }

/* Tables */
.table { --bs-table-color: var(--ink); --bs-table-bg: transparent; margin-bottom: 0; }
.table > thead th {
  background: var(--surface-2);
  font-size: 0.7rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted);
  border-bottom: 1px solid var(--line); white-space: nowrap;
  padding: 11px 14px;
}
.table > tbody td { padding: 12px 14px; vertical-align: middle; border-color: var(--line); }
.table > tbody tr:hover > * { background: var(--surface-2); }

/* Empty states */
.af-empty { text-align: center; padding: 52px 20px; color: var(--muted); }
.af-empty i { font-size: 2.75rem; opacity: .28; display: block; margin-bottom: 14px; }
.af-empty h3 { font-size: 1rem; color: var(--ink); margin-bottom: 6px; }
.af-empty p { font-size: 0.8438rem; max-width: 380px; margin: 0 auto 16px; }

/* Skeleton loader */
.af-skeleton {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--line) 37%, var(--surface-2) 63%);
  background-size: 400% 100%;
  animation: af-shimmer 1.4s ease-in-out infinite;
  border-radius: 6px;
}
@keyframes af-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

/* QR panel */
.af-qr-frame {
  background: #fff; border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px;
  display: inline-block; box-shadow: var(--shadow-xs);
}
.af-qr-frame img { display: block; width: 200px; height: 200px; image-rendering: pixelated; }

.af-copy {
  display: flex; align-items: center; gap: 8px;
  background: var(--surface-2); border: 1px dashed var(--line-2);
  border-radius: var(--radius-sm); padding: 9px 12px;
  font-size: 0.8rem; word-break: break-all;
}

.chart-box { position: relative; height: 280px; }
.chart-box.short { height: 210px; }

/* Live indicator */
.af-live {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.7188rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--success);
}
.af-live-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--success);
  animation: af-pulse 1.8s ease-in-out infinite;
}
@keyframes af-pulse {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 color-mix(in srgb, var(--success) 60%, transparent); }
  50%      { opacity: .7; box-shadow: 0 0 0 6px transparent; }
}

/* A row that just arrived via polling. */
.af-row-new { animation: af-flash 2s ease-out; }
@keyframes af-flash {
  0%   { background: color-mix(in srgb, var(--success) 22%, transparent); }
  100% { background: transparent; }
}

/* Timeline */
.af-timeline { position: relative; padding-left: 28px; }
.af-timeline::before { content:''; position:absolute; left:8px; top:4px; bottom:4px; width:2px; background: var(--line); }
.af-timeline-item { position: relative; padding-bottom: 16px; }
.af-timeline-item::before {
  content:''; position:absolute; left:-24px; top:5px; width:11px; height:11px; border-radius:50%;
  background: var(--brand); border: 2px solid var(--surface);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--brand) 26%, transparent);
}

/* Surfaces Bootstrap paints itself */
.modal-content, .dropdown-menu, .offcanvas, .accordion-item, .list-group-item, .card {
  background: var(--surface); color: var(--ink); border-color: var(--line);
}
.modal-header, .modal-footer { border-color: var(--line); }
.dropdown-item { color: var(--ink-2); }
.dropdown-item:hover, .dropdown-item:focus { background: var(--surface-2); color: var(--ink); }
.dropdown-divider { border-color: var(--line); }
.bg-light { background: var(--surface-2) !important; color: var(--ink); }
.border, .border-top, .border-bottom, .border-start, .border-end { border-color: var(--line) !important; }
hr { border-color: var(--line); opacity: 1; }
.progress { background: var(--line); border-radius: 5px; height: 8px; }
.progress-bar { background: var(--brand); border-radius: 5px; }
.nav-tabs { border-color: var(--line); }
.nav-tabs .nav-link { color: var(--muted); border: none; border-bottom: 2px solid transparent; font-weight: 600; }
.nav-tabs .nav-link:hover { color: var(--ink); border-bottom-color: var(--line-2); }
.nav-tabs .nav-link.active { color: var(--brand); background: transparent; border-bottom-color: var(--brand); }

/* ──────────────────────── 5. Public check-in ──────────────────────────── */

.af-public {
  min-height: 100vh;
  display: flex; align-items: center; justify-content: center;
  padding: 22px 14px;
  background:
    radial-gradient(ellipse 70% 50% at 50% 0%, color-mix(in srgb, var(--brand) 16%, transparent), transparent 70%),
    var(--bg);
}
.af-checkin {
  width: 100%; max-width: 480px;
  background: var(--surface);
  border-radius: 20px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.af-checkin-banner {
  height: 132px; position: relative;
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  background-size: cover; background-position: center;
}
.af-checkin-banner::after {
  content:''; position:absolute; inset:0;
  background: radial-gradient(circle at 20% 120%, rgba(255,255,255,.22), transparent 60%);
}
.af-checkin-logo {
  width: 68px; height: 68px; border-radius: 16px; background: #fff;
  border: 3px solid #fff; object-fit: contain; padding: 5px;
  margin: -40px auto 0; position: relative; z-index: 2; display: block;
  box-shadow: var(--shadow);
}
.af-checkin-body { padding: 16px 24px 26px; }
.af-checkin-meta { display: flex; align-items: center; gap: 9px; font-size: 0.8438rem;
                   color: var(--ink-2); padding: 7px 0; }
.af-checkin-meta i { color: var(--brand); width: 18px; text-align: center; flex-shrink: 0; }

.af-success-check {
  width: 84px; height: 84px; border-radius: 50%; margin: 0 auto 18px;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--success) 14%, transparent);
  color: var(--success); font-size: 2.625rem;
  animation: af-pop .45s cubic-bezier(.16,1,.3,1);
}
@keyframes af-pop { 0% { transform: scale(.4); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }

/* ── Certificate ────────────────────────────────────────────────────────────
   A double-border frame rather than an image is what reads as "certificate"
   here — no asset to ship, and it scales to any paper size when printed. */
.af-cert-page { min-height: 100vh; padding: 28px 14px; background: var(--bg); }
.af-cert {
  width: 100%; max-width: 900px; margin: 0 auto;
  background: var(--surface);
  border-radius: 4px;
  box-shadow: var(--shadow-lg);
  padding: 10px;
}
.af-cert-frame {
  border: 2px solid var(--cert-brand, var(--brand));
  border-radius: 2px;
  padding: 3px;
}
.af-cert-inner {
  border: 1px solid color-mix(in srgb, var(--cert-brand, var(--brand)) 45%, transparent);
  border-radius: 2px;
  padding: clamp(28px, 5vw, 56px) clamp(24px, 6vw, 64px);
  text-align: center;
}
.af-cert-logo {
  width: 64px; height: 64px; border-radius: 12px; object-fit: contain;
  margin: 0 auto 14px; display: block; padding: 6px; background: var(--surface-2);
}
.af-cert-eyebrow {
  color: var(--cert-brand, var(--brand)); font-weight: 700; font-size: 0.75rem;
  letter-spacing: .18em; text-transform: uppercase; margin-bottom: 22px;
}
.af-cert-lede { color: var(--muted); font-size: 0.9375rem; margin-bottom: 6px; }
.af-cert-name {
  font-size: clamp(1.7rem, 4.4vw, 2.7rem); font-weight: 800; letter-spacing: -.02em;
  color: var(--ink); margin: 4px 0 18px;
  font-family: Georgia, 'Times New Roman', serif;
}
.af-cert-event { font-size: 1.15rem; font-weight: 700; color: var(--ink); margin-bottom: 4px; }
.af-cert-meta { color: var(--muted); font-size: 0.875rem; margin-bottom: 30px; }
.af-cert-org { color: var(--ink-2); font-size: 0.9375rem; font-weight: 600; margin-bottom: 36px; }

.af-cert-sign-row {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 24px; margin-top: 30px; text-align: left; flex-wrap: wrap;
}
.af-cert-sign-line {
  border-top: 1px solid var(--line-2); padding-top: 8px; min-width: 220px;
}
.af-cert-sign-name  { font-weight: 700; font-size: 0.9375rem; }
.af-cert-sign-title { color: var(--muted); font-size: 0.8125rem; }
.af-cert-verify {
  text-align: right; color: var(--muted); font-size: 0.75rem; line-height: 1.5;
}
.af-cert-verify code {
  color: var(--ink-2); background: var(--surface-2); padding: 1px 6px;
  border-radius: 4px; font-size: 0.75rem;
}
.af-cert-actions { max-width: 900px; margin: 20px auto 0; display: flex; gap: 10px; justify-content: center; }

@media (max-width: 575.98px) {
  .af-cert-sign-row { flex-direction: column; align-items: stretch; text-align: center; }
  .af-cert-verify { text-align: center; }
  .af-cert-sign-line { text-align: center; }
}

@media print {
  .af-cert-page { padding: 0; background: #fff; }
  .af-cert { box-shadow: none; max-width: none; }
}

/* ────────────────────────── 6. Auth screens ───────────────────────────── */

/* Column, not a centring box: the navbar and masi trim sit at their natural
   height at the top and the card centres in whatever space is left. Centring
   every child would lay the navbar out beside the form. */
.af-auth {
  min-height: 100vh; display: flex; flex-direction: column;
  background: linear-gradient(150deg, #1e1b4b 0%, #312e81 45%, #4c1d95 100%);
  position: relative;
}
.af-auth > .lp-navbar, .af-auth > .af-masi { flex: 0 0 auto; }
/* Tapa runner down the left edge, matching the hero. */
.af-auth::before {
  content:''; position:absolute; top:0; bottom:0; left:0; width: min(300px, 32%);
  background-image: var(--tapa); background-size: 120px 120px; opacity: .42;
  pointer-events: none; z-index: 0;
  -webkit-mask-image: linear-gradient(to right, #000 55%, transparent);
          mask-image: linear-gradient(to right, #000 55%, transparent);
}
.af-auth-wrap {
  width: 100%; max-width: 430px; margin: auto;
  padding: 34px 16px; position: relative; z-index: 1;
}
.af-auth-md    { max-width: 33rem; }
.af-auth-split { max-width: 68rem; }
/* The signup form has enough fields to look like a ribbon in a single column
   on a large display, so it gets a pitch panel beside it instead. */
.af-auth-split .af-auth-pitch { color: #fff; }
.af-auth-split .af-auth-pitch h2 {
  color: #fff; font-size: clamp(1.5rem, 2.2vw, 2.1rem);
  font-weight: 800; letter-spacing: -.03em; line-height: 1.15;
}
.af-auth-split .af-auth-pitch p { color: rgba(255,255,255,.74); }
.af-auth-point { display: flex; gap: .8rem; align-items: flex-start; margin-bottom: 1.1rem; }
.af-auth-point i {
  flex: 0 0 1.75rem; width: 1.75rem; height: 1.75rem; border-radius: 8px;
  display: grid; place-items: center; font-size: .9rem; color: #fff;
  background: rgba(255,255,255,.16);
}
.af-auth-point strong { color: #fff; display: block; font-size: .95rem; }
.af-auth-point span   { color: rgba(255,255,255,.68); font-size: .875rem; }
.af-auth-brand { text-align: center; margin-bottom: 22px; color: #fff; }
.af-auth-brand .af-brand-mark { width: 3.25rem; height: 3.25rem; font-size: 1.5625rem; margin: 0 auto 12px; }
.af-auth-brand h1 { color: #fff; font-size: 1.3125rem; margin: 0 0 4px; }
.af-auth-brand p { color: rgba(255,255,255,.66); font-size: 0.8438rem; margin: 0; }
.af-auth-card {
  background: var(--surface); border-radius: 18px; padding: 30px 28px;
  box-shadow: 0 24px 60px rgba(0,0,0,.34);
}
.af-auth-card h2 { font-size: 1.1875rem; margin: 0 0 4px; }
.af-auth-card .sub { color: var(--muted); font-size: 0.8438rem; margin: 0 0 20px; }
.af-auth-foot { text-align: center; margin-top: 18px; font-size: 0.8438rem; color: rgba(255,255,255,.72); }
.af-auth-foot a { color: #c7d2fe; font-weight: 600; }

.af-meter { height: 5px; border-radius: 3px; background: var(--line); overflow: hidden; margin-top: 7px; }
.af-meter span { display: block; height: 100%; width: 0; transition: width .25s, background .25s; }

/* ───────────────────────────── 7. Landing ─────────────────────────────── */

/* ── Pacific motif ──────────────────────────────────────────────────────────
   A masi (tapa cloth) geometry — concentric diamonds with corner triangles.
   Used at low opacity over the brand gradient so it reads as cloth texture
   rather than wallpaper. White strokes, so one tile works on any brand colour
   and survives a white-label palette change. */
:root {
  --tapa: url("data:image/svg+xml,%3Csvg width='120' height='120' viewBox='0 0 120 120' xmlns='http://www.w3.org/2000/svg'%3E%3Cpolygon points='60,4 116,60 60,116 4,60' fill='none' stroke='white' stroke-opacity='.20' stroke-width='2'/%3E%3Cpolygon points='60,22 98,60 60,98 22,60' fill='none' stroke='white' stroke-opacity='.13' stroke-width='1.4'/%3E%3Cpolygon points='60,40 80,60 60,80 40,60' fill='white' fill-opacity='.10'/%3E%3Cpolygon points='0,0 22,0 0,22' fill='white' fill-opacity='.16'/%3E%3Cpolygon points='120,0 98,0 120,22' fill='white' fill-opacity='.16'/%3E%3Cpolygon points='0,120 22,120 0,98' fill='white' fill-opacity='.16'/%3E%3Cpolygon points='120,120 98,120 120,98' fill='white' fill-opacity='.16'/%3E%3C/svg%3E");
}

/* Masi trim — a woven band that separates sections, in the brand palette. */
.af-masi {
  height: 22px; width: 100%;
  background-color: #eef0fb;
  background-image: url("data:image/svg+xml,%3Csvg width='76' height='26' viewBox='0 0 76 26' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='76' height='26' fill='%23eef0fb'/%3E%3Cpath d='M0 26 L19 5 L38 26 Z' fill='%23c7cbf0'/%3E%3Cpath d='M38 0 L57 21 L76 0 Z' fill='%23dcc9f5'/%3E%3Cpolygon points='19,12 22.5,15.5 19,19 15.5,15.5' fill='%23a5abe8'/%3E%3Cpolygon points='57,7 60.5,10.5 57,14 53.5,10.5' fill='%23c3a8ef'/%3E%3C/svg%3E");
  background-size: 76px 26px; background-repeat: repeat-x;
  border-top: 1px solid #e0e4f6; border-bottom: 1px solid #e0e4f6;
}
.af-masi.thin { height: 14px; background-size: 46px 16px; }
[data-theme="dark"] .af-masi {
  background-color: #1a1f3a;
  background-image: url("data:image/svg+xml,%3Csvg width='76' height='26' viewBox='0 0 76 26' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='76' height='26' fill='%231a1f3a'/%3E%3Cpath d='M0 26 L19 5 L38 26 Z' fill='%233b3f70'/%3E%3Cpath d='M38 0 L57 21 L76 0 Z' fill='%234a3a72'/%3E%3Cpolygon points='19,12 22.5,15.5 19,19 15.5,15.5' fill='%23575ca0'/%3E%3Cpolygon points='57,7 60.5,10.5 57,14 53.5,10.5' fill='%236b4fa3'/%3E%3C/svg%3E");
  border-color: #262c4e;
}

/* ── Navbar ─────────────────────────────────────────────────────────────────
   Transparent while it sits over the coloured hero, then settling onto the
   brand gradient once the page scrolls under it. Both states are light-on-dark,
   so the link colour never has to jump. */
.lp-navbar {
  /* Always on brand colour. `sticky-top` keeps the bar in normal flow, so a
     transparent bar at scroll-0 would put white text on the light page
     background and disappear — the colour is not decoration, it is what makes
     the links legible. Scrolling deepens it rather than introducing it. */
  background: linear-gradient(135deg, var(--brand) 0%, var(--brand-2) 100%);
  border-bottom: 1px solid transparent;
  padding: .62rem 0;
  transition: background .3s ease, box-shadow .3s ease, padding .3s ease;
}
.lp-navbar.scrolled,
.lp-navbar.solid {
  background: linear-gradient(135deg, #3d33b8 0%, #4c1d95 100%);
  box-shadow: 0 8px 26px -10px rgba(15,23,42,.62);
  padding: .42rem 0;
}
/* Masi motif woven through the bar. */
.lp-navbar::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background-image: var(--tapa); background-size: 120px 120px;
  opacity: .26; transition: opacity .3s ease;
}
.lp-navbar.scrolled::after, .lp-navbar.solid::after { opacity: .34; }
.lp-navbar > .container { position: relative; z-index: 1; }

.lp-brand { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; }
.lp-brand-mark {
  width: 2.125rem; height: 2.125rem; border-radius: 10px; flex: 0 0 2.125rem;
  display: grid; place-items: center; color: #fff; font-size: 1.0625rem;
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  box-shadow: 0 3px 10px -2px rgba(79,70,229,.55);
}
.lp-brand-name { font-weight: 800; letter-spacing: -.02em; color: #fff; font-size: 1.12rem; }
.lp-navbar .lp-brand-mark { background: rgba(255,255,255,.16); box-shadow: none; }
.lp-navbar.scrolled .lp-brand-mark,
.lp-navbar.solid   .lp-brand-mark { background: rgba(255,255,255,.2); }

.lp-nav-link {
  color: rgba(255,255,255,.84); font-weight: 500; font-size: .945rem;
  padding: .5rem .85rem; border-radius: 8px; transition: color .15s, background .15s;
}
.lp-nav-link:hover, .lp-nav-link:focus { color: #fff; background: rgba(255,255,255,.14); }
.lp-nav-link.active { color: #fff; font-weight: 600; background: rgba(255,255,255,.16); }

.lp-toggler {
  border: 1px solid rgba(255,255,255,.35); border-radius: 9px; padding: .3rem .6rem;
  font-size: 1.35rem; color: #fff; line-height: 1;
}
.lp-toggler:focus { box-shadow: 0 0 0 .2rem rgba(255,255,255,.3); }

.lp-btn-ghost {
  border: 1px solid rgba(255,255,255,.5); color: #fff; font-weight: 600;
  padding: .44rem 1.05rem; border-radius: 9px; background: transparent;
}
.lp-btn-ghost:hover { border-color: #fff; color: var(--brand); background: #fff; }

/* Used both in the navbar (on colour) and in page content (on surface), so it
   carries its own gradient rather than depending on the backdrop. */
.lp-btn-solid {
  background: #fff; color: var(--brand); font-weight: 700;
  padding: .44rem 1.05rem; border-radius: 9px;
  border: 1px solid transparent; box-shadow: 0 3px 10px -3px rgba(15,23,42,.35);
}
.lp-btn-solid:hover { color: var(--brand-2); background: #fff; filter: brightness(1.02); }
/* Outside the bar it needs to be the brand colour, not white on white. */
.lp-footer .lp-btn-solid,
section .lp-btn-solid,
.af-card .lp-btn-solid {
  background: linear-gradient(135deg, var(--brand), var(--brand-2));
  color: #fff; box-shadow: 0 3px 10px -3px rgba(79,70,229,.6);
}
.lp-footer .lp-btn-solid:hover,
section .lp-btn-solid:hover,
.af-card .lp-btn-solid:hover { color: #fff; filter: brightness(1.07); }

.lp-theme-btn {
  border: 1px solid rgba(255,255,255,.35); color: #fff; border-radius: 9px;
  padding: .44rem .7rem; background: transparent;
}
.lp-theme-btn:hover { color: var(--brand); border-color: #fff; background: #fff; }

/* The collapsed menu is a panel, not a squashed row. It also forces the solid
   bar, because an open menu floating over page content is unreadable. */
@media (max-width: 991.98px) {
  #lpNavMenu { padding: .85rem .25rem .55rem; }
  .lp-nav-link { display: block; padding: .62rem .7rem; }
  .lp-btn-ghost, .lp-btn-solid, .lp-theme-btn { width: 100%; text-align: center; }
}

/* ── Hero ───────────────────────────────────────────────────────────────────
   A photograph of the thing the product does, behind a brand overlay.

   The overlay is asymmetric on purpose. The photo is busiest on the left —
   faces, signage, a tablet — which is exactly where the headline sits, so the
   scrim is near-opaque there and thins out to the right where the check-in
   card floats and the picture should still read. Legibility first; the image
   is atmosphere, not content. */
.lp-hero {
  /* The gradient is the fallback: if the image 404s or has not arrived yet,
     the hero is still the branded panel it was before, never bare white. */
  background:
    linear-gradient(155deg,#1e1b4b 0%,#312e81 45%,#4c1d95 100%);
  color: #fff; padding: 84px 0 92px; position: relative; overflow: hidden;
  isolation: isolate;
}
/* Photo layer. image-set lets the browser take WebP when it can (124 KB) and
   fall back to JPEG (196 KB) when it cannot. */
.lp-hero::after {
  content:''; position:absolute; inset:0; z-index:-2;
  background-image: url('../img/hero.jpg');
  background-image: image-set(url('../img/hero.webp') type('image/webp'),
                              url('../img/hero.jpg')  type('image/jpeg'));
  background-size: cover;
  background-position: 68% center;   /* keeps the QR signage and desk in frame */
  background-repeat: no-repeat;
}
/* Overlay layer — brand colour, weighted to the left. */
.lp-hero::before {
  content:''; position:absolute; inset:0; z-index:-1;
  background:
    linear-gradient(100deg,
      rgba(23,20,64,.96) 0%,
      rgba(38,33,110,.92) 30%,
      rgba(60,30,140,.78) 56%,
      rgba(76,29,149,.55) 78%,
      rgba(88,35,160,.46) 100%),
    radial-gradient(circle at 84% 6%, rgba(139,92,246,.42), transparent 58%);
}
/* Phones get the 760px variant — a 1376px photo on a 390px screen is waste. */
@media (max-width: 767.98px) {
  .lp-hero::after {
    background-image: url('../img/hero-sm.jpg');
    background-image: image-set(url('../img/hero-sm.webp') type('image/webp'),
                                url('../img/hero-sm.jpg')  type('image/jpeg'));
    background-position: 62% center;
  }
  /* Text runs full width on a phone, so the scrim must be even across it. */
  .lp-hero::before {
    background:
      linear-gradient(180deg, rgba(23,20,64,.94) 0%, rgba(60,30,140,.9) 60%, rgba(76,29,149,.88) 100%);
  }
}
/* Tapa cloth across the hero — a wide band down the left, the way a masi
   runner is laid, rather than a flat tile over the whole panel. Its own
   element rather than a pseudo-element: ::before and ::after are both spoken
   for by the overlay and the photo, and a third layer needs a third box.
   Lower opacity than elsewhere so it textures the photo instead of fighting
   it for attention. */
.lp-hero-tapa {
  position: absolute; top: 0; bottom: 0; left: 0; width: min(20.5rem, 34%);
  background-image: var(--tapa); background-size: 120px 120px;
  opacity: .34; z-index: 0; pointer-events: none;
  -webkit-mask-image: linear-gradient(to right, #000 50%, transparent);
          mask-image: linear-gradient(to right, #000 50%, transparent);
}

/* Any brand-gradient band (page headers, closing CTA) gets the same cloth. */
.af-band { position: relative; overflow: hidden; }
.af-band::before {
  content:''; position:absolute; inset:0; pointer-events:none;
  background-image: var(--tapa); background-size: 120px 120px; opacity: .3;
}
.af-band > .container { position: relative; z-index: 1; }
.lp-hero > .container { position: relative; z-index: 1; }

/* Typewriter.
   Every phrase is rendered invisibly into the same grid cell as the animated
   one, so the box is always sized to the *tallest and widest* phrase and the
   hero never changes height while typing. A min-height reserving a single line
   is not enough: the moment a longer phrase wraps to two lines the whole
   banner grows, then shrinks again as it is erased. Letting the browser
   measure the real phrases means it stays correct at every viewport width and
   font size, with no magic numbers. */
.af-type-wrap { display: inline-grid; vertical-align: top; text-align: left; }
.af-type-wrap > * { grid-area: 1 / 1; }
.af-type-ghost { visibility: hidden; pointer-events: none; user-select: none; }
.af-type {
  color: #c4b5fd;
  text-shadow: 0 2px 18px rgba(23,20,64,.55);
}
.af-type-caret {
  display: inline-block; width: .06em; min-width: 2px; height: .95em;
  background: #a78bfa; margin-left: .06em; vertical-align: -0.1em;
  animation: afCaret 1.05s steps(1) infinite;
}
@keyframes afCaret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* Exposed to screen readers, invisible on screen. */
.af-sr-only {
  position: absolute !important; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
@media (prefers-reduced-motion: reduce) { .af-type-caret { animation: none; } }

.lp-hero h1 {
  color:#fff; font-size: clamp(2.05rem, 5vw, 3.35rem);
  letter-spacing:-.038em; line-height: 1.1; font-weight: 800;
  text-shadow: 0 2px 24px rgba(23,20,64,.5);
}
.lp-hero .lead { color: rgba(255,255,255,.8); font-size: 1.08rem; }
.lp-eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.2);
  color: #fff; font-size: .8rem; font-weight: 600; letter-spacing: .01em;
  padding: .34rem .85rem; border-radius: 30px; margin-bottom: 1.1rem;
}
.lp-trust { color: rgba(255,255,255,.62); font-size: .85rem; }
.lp-stat-n { font-size: 1.75rem; font-weight: 800; color: #fff; letter-spacing: -.03em; line-height: 1; }
.lp-stat-l { color: rgba(255,255,255,.6); font-size: .78rem; text-transform: uppercase; letter-spacing: .07em; }

/* Section furniture */
.lp-section-tag {
  color: var(--brand); font-weight: 700; font-size: .78rem;
  text-transform: uppercase; letter-spacing: .1em;
}
.lp-h2 { font-weight: 800; letter-spacing: -.03em; font-size: clamp(1.5rem, 3vw, 2.05rem); }

/* ── Scroll reveal ──────────────────────────────────────────────────────────
   Elements start slightly offset and fade in when they enter the viewport.
   The .js-reveal class is only added by script, so with JavaScript disabled
   nothing is ever hidden — content must never depend on an animation running. */
.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .55s cubic-bezier(.22,.61,.36,1),
              transform .55s cubic-bezier(.22,.61,.36,1);
  will-change: opacity, transform;
}
.js-reveal [data-reveal].is-visible {
  opacity: 1; transform: none; will-change: auto;
}

/* ── Section illustrations ──────────────────────────────────────────────── */
.af-ill { width: 100%; height: auto; max-width: 20rem; display: block; margin: 0 auto; overflow: visible; }
.af-ill-shadow { fill: rgba(15,23,42,.10); }
[data-theme="dark"] .af-ill-shadow { fill: rgba(0,0,0,.4); }

/* The whole illustration drifts, so it does not sit dead on the page. */
.af-ill-sheet, .af-ill-scan, .af-ill-chart { animation: afFloat 6s ease-in-out infinite; }
.af-ill-scan  { animation-duration: 7s; animation-delay: -1.5s; }
.af-ill-chart { animation-duration: 8s; animation-delay: -3s; }
@keyframes afFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-9px); }
}

/* Ticks draw themselves onto the sheet, one after another. */
.af-ill-tick polyline {
  stroke-dasharray: 26; stroke-dashoffset: 26;
  animation: afDraw 4.5s ease-in-out infinite;
}
@keyframes afDraw {
  0%, 8%    { stroke-dashoffset: 26; }
  22%, 78%  { stroke-dashoffset: 0; }
  92%, 100% { stroke-dashoffset: 26; }
}

/* Pencil writes along the page. */
.af-ill-pencil { animation: afWrite 4.5s ease-in-out infinite; transform-origin: 60% 60%; }
@keyframes afWrite {
  0%, 100% { transform: translate(0,0) rotate(0deg); }
  25%      { transform: translate(-6px,-16px) rotate(-4deg); }
  50%      { transform: translate(-11px,-32px) rotate(2deg); }
  75%      { transform: translate(-5px,-14px) rotate(-2deg); }
}

/* Scanner sweep + confirmation pulse. */
.af-ill-scanline { animation: afSweep 2.8s cubic-bezier(.45,0,.55,1) infinite; opacity: .9; }
@keyframes afSweep {
  0%, 100% { transform: translateY(0);   opacity: 0; }
  10%      { opacity: .95; }
  50%      { transform: translateY(58px); opacity: .95; }
  90%      { opacity: 0; }
}
.af-ill-pulse { animation: afPop 2.8s ease-in-out infinite; transform-origin: 130px 150px; }
@keyframes afPop {
  0%, 45%   { opacity: 0; transform: scale(.7); }
  60%, 88%  { opacity: 1; transform: scale(1); }
  100%      { opacity: 0; transform: scale(.7); }
}

/* Bars grow from the baseline. */
.af-ill-bars rect {
  y: 150px; height: 0;
  animation: afGrow 3.6s cubic-bezier(.22,.61,.36,1) infinite;
}
@keyframes afGrow {
  0%, 6%    { height: 0;             y: 150px; }
  35%, 70%  { height: var(--h, 40px); y: calc(150px - var(--h, 40px)); }
  96%, 100% { height: 0;             y: 150px; }
}
.af-ill-dot { animation: afBlink 1.6s ease-in-out infinite; }
@keyframes afBlink { 0%,100% { opacity: 1; } 50% { opacity: .25; } }

/* Floating tapa diamonds behind a section — depth without another image. */
.af-floaters { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 0; }
.af-floater {
  position: absolute; border: 2px solid var(--brand);
  opacity: .09; border-radius: 6px; transform: rotate(45deg);
  animation: afDrift 18s ease-in-out infinite;
}
.af-floater:nth-child(1) { width: 4.5rem; height: 4.5rem; top: 12%;  left: 6%;  }
.af-floater:nth-child(2) { width: 2.75rem; height: 2.75rem; top: 62%; left: 13%; animation-delay: -4s; border-color: var(--brand-2); }
.af-floater:nth-child(3) { width: 6rem;   height: 6rem;   top: 22%;  right: 7%; animation-delay: -9s; }
.af-floater:nth-child(4) { width: 2rem;   height: 2rem;   top: 74%;  right: 16%; animation-delay: -13s; border-color: var(--brand-2); }
@keyframes afDrift {
  0%, 100% { transform: rotate(45deg) translate(0,0); }
  33%      { transform: rotate(52deg) translate(10px,-16px); }
  66%      { transform: rotate(38deg) translate(-8px,12px); }
}
.af-has-floaters { position: relative; }
.af-has-floaters > .container { position: relative; z-index: 1; }

/* One switch turns all of it off for anyone who asked the OS to stop motion. */
@media (prefers-reduced-motion: reduce) {
  .af-ill-sheet, .af-ill-scan, .af-ill-chart, .af-ill-pencil, .af-ill-scanline,
  .af-ill-pulse, .af-ill-tick polyline, .af-ill-bars rect, .af-ill-dot, .af-floater {
    animation: none !important;
  }
  .af-ill-bars rect { height: var(--h, 40px); y: calc(150px - var(--h, 40px)); }
  .af-ill-tick polyline { stroke-dashoffset: 0; }
  .js-reveal [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* Footer */
.lp-footer { background: var(--surface-2); border-top: 1px solid var(--line); }
.lp-footer-head {
  font-size: .78rem; text-transform: uppercase; letter-spacing: .09em;
  color: var(--ink); font-weight: 700; margin-bottom: .9rem;
}
.lp-footer a { color: var(--muted); text-decoration: none; }
.lp-footer a:hover { color: var(--brand); }
.lp-footer-base {
  border-top: 1px solid var(--line); padding: 1.1rem 0;
  display: flex; flex-wrap: wrap; gap: .5rem; justify-content: space-between;
  font-size: .83rem; color: var(--muted);
}
.lp-feature {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 25px; height: 100%;
  box-shadow: var(--shadow-xs); transition: transform .2s, box-shadow .2s;
}
.lp-feature:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.lp-step {
  width: 3.125rem; height: 3.125rem; border-radius: 14px; display: flex;
  align-items: center; justify-content: center; font-size: 1.3125rem; color:#fff; margin-bottom: 14px;
}
.lp-price {
  background: var(--surface); border: 2px solid var(--line);
  border-radius: 18px; padding: 28px; height: 100%; position: relative;
  transition: transform .2s, border-color .2s, box-shadow .2s;
}
.lp-price:hover { transform: translateY(-4px); box-shadow: var(--shadow-lg); }
.lp-price.featured { border-color: var(--brand); box-shadow: var(--shadow); }
.lp-price-badge {
  position: absolute; top: -12px; left: 50%; transform: translateX(-50%);
  background: linear-gradient(135deg, var(--brand), var(--brand-2)); color: #fff;
  font-size: 0.6875rem; font-weight: 700; padding: 4px 14px; border-radius: 20px;
  text-transform: uppercase; letter-spacing: .06em;
}
.lp-price-amount { font-size: 2.4rem; font-weight: 800; letter-spacing: -.03em; }
/* Marks a check-in that staff entered by hand rather than the attendee
   scanning. Deliberately quiet — informative, not an accusation. */
.af-badge-manual {
  background: var(--brand-soft); color: var(--brand);
  font-size: 0.625rem; font-weight: 700; padding: 2px 7px;
  text-transform: uppercase; letter-spacing: .04em; vertical-align: 1px;
}
[data-theme="dark"] .af-badge-manual { background: #262c4e; color: #a5b4fc; }

.lp-save-badge {
  background: var(--success); color: #fff; font-size: 0.6875rem; font-weight: 700;
  padding: 2px 8px; vertical-align: 1px;
}

/* ────────────────────── 8. Third-party overrides ──────────────────────── */

/* DataTables 2.x uses dt-* class names, not the legacy dataTables_* ones. */
.dt-container { color: var(--ink-2); }
.dt-container .dt-search input,
.dt-container .dt-length select {
  border: 1px solid var(--line-2); border-radius: 8px; padding: 5px 10px;
  background: var(--surface); color: var(--ink); font-size: 0.8125rem;
}
.dt-container .dt-search input:focus, .dt-container .dt-length select:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 16%, transparent);
}
.dt-container .dt-info, .dt-container .dt-length label, .dt-container .dt-search label {
  color: var(--muted); font-size: 0.7813rem;
}
.page-link { background: var(--surface); border-color: var(--line); color: var(--ink-2); }
.page-link:hover { background: var(--surface-2); color: var(--brand); border-color: var(--line); }
.page-item.active .page-link { background: var(--brand); border-color: var(--brand); color:#fff; }
.page-item.disabled .page-link { background: var(--surface); color: var(--muted); border-color: var(--line); }

/* Tom Select */
.ts-control {
  background: var(--surface) !important; border-color: var(--line-2) !important;
  border-radius: var(--radius-sm) !important; color: var(--ink);
}
.ts-dropdown { background: var(--surface); border-color: var(--line); color: var(--ink); }
.ts-dropdown .active { background: var(--brand-soft); color: var(--brand); }

/* Flatpickr */
.flatpickr-calendar { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-lg); }
.flatpickr-months .flatpickr-month, .flatpickr-weekdays, span.flatpickr-weekday {
  background: var(--surface); color: var(--ink); fill: var(--ink);
}
.flatpickr-day { color: var(--ink-2); }
.flatpickr-day:hover { background: var(--surface-2); border-color: var(--surface-2); }
.flatpickr-day.selected { background: var(--brand); border-color: var(--brand); color:#fff; }
.flatpickr-day.prevMonthDay, .flatpickr-day.nextMonthDay, .flatpickr-day.flatpickr-disabled { color: var(--muted); }

/* SweetAlert2 */
.swal2-popup { background: var(--surface) !important; color: var(--ink) !important; border-radius: 16px !important; }
.swal2-title, .swal2-html-container { color: var(--ink) !important; }

/* ─────────────────── 9. Responsive / print / motion ───────────────────── */

@media (max-width: 991.98px) {
  .af-sidebar {
    position: fixed; left: 0; top: var(--topbar-h);
    transform: translateX(-100%); transition: transform .25s ease;
    box-shadow: var(--shadow-lg);
  }
  .af-sidebar.open { transform: translateX(0); }
  .af-main { padding: 18px 15px 40px; }
  .af-page-title { font-size: 1.25rem; }
}

@media (max-width: 575.98px) {
  body { font-size: 0.875rem; }
  .af-card-body { padding: 15px; }
  .af-card-head { padding: 13px 15px; }
  .af-stat { padding: 14px 15px; gap: 12px; }
  .af-stat-icon { width: 2.5rem; height: 2.5rem; font-size: 1.0625rem; }
  .af-stat-value { font-size: 1.25rem; }
  .af-qr-frame img { width: 168px; height: 168px; }
  .af-auth-card { padding: 24px 20px; }
  .chart-box { height: 240px; }
}

@media print {
  .af-topbar, .af-sidebar, .af-backdrop, .no-print, .btn { display: none !important; }
  .af-main { padding: 0; }
  .af-card { box-shadow: none; border: 1px solid #ccc; break-inside: avoid; }
  body { background: #fff; color: #000; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; transition-duration: .001ms !important;
  }
}


/* ── Sidebar footer ───────────────────────────────────────────────────────
   These were inline hex values in the layout — #64748b, #94a3b8 and #475569
   on a near-black sidebar, measuring 3.8:1, 5.9:1 and 2.4:1. Tokenised so
   they follow the theme instead of being three unrelated greys, and lifted
   to pass AA. */
.af-side-note { color: var(--sidebar-text); }
.af-side-dim  { color: var(--sidebar-dim); }

/* Appearance menu — the tick marks which of light/dark/system is chosen. */
.af-theme-menu .dropdown-item { display: flex; align-items: center; }
.af-theme-tick { opacity: 0; }
.af-theme-menu .dropdown-item.is-on { color: var(--brand); font-weight: 600; }
.af-theme-menu .dropdown-item.is-on .af-theme-tick { opacity: 1; }

/* ── Brand as ink ─────────────────────────────────────────────────────────
   --brand is a FILL colour: correct behind white text, and frequently
   unreadable AS text, because a tenant may pick any colour they like. Every
   place the brand is used for type reads --brand-ink instead, which is the
   same hue moved just far enough to pass AA against the current theme. */
.af-avatar { color: var(--brand-ink-soft); }
.af-table a.fw-semibold,
.af-card a.fw-semibold { color: var(--brand-ink); }

/* Bootstrap's own secondary (#6c757d) is 4.0:1 on white and 3.2:1 on our dark
   surface — an outline button's label is the thing you read to know what it
   does. */
.btn-outline-secondary { --bs-btn-color: var(--ink-2); --bs-btn-border-color: var(--line-2); }
.btn-outline-secondary:hover { --bs-btn-hover-color: #fff; }

/* --danger is tuned as a FILL; as an outline button label on the dark surface
   it reads 3.75:1. */
[data-theme="dark"] .btn-outline-danger { --bs-btn-color: #fca5a5; --bs-btn-border-color: #b91c1c; }


/* ── Guided steps rail ────────────────────────────────────────────────────
   Lives in app.css rather than survey-dash.css because steps_rail() renders on
   several screens, and one of them (the builder) does not load that file — the
   rail then fell back to Bootstrap link colours and became unreadable. */
.sd-steps {
  display: flex; align-items: center; gap: .2rem;
  overflow-x: auto;
  padding: .55rem .7rem;
  margin-bottom: 1rem;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.sd-step {
  display: flex; align-items: center; gap: .45rem;
  padding: .35rem .7rem;
  border-radius: 8px;
  font-size: .82rem;
  font-weight: 500;
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
  transition: background .12s, color .12s;
}
.sd-step:hover { background: color-mix(in srgb, var(--ink) 5%, transparent); color: var(--ink); }
.sd-step-mark {
  width: 18px; height: 18px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  border: 1.5px solid currentColor;
  font-size: .62rem;
}
.sd-step.is-done { color: var(--success-ink); }
.sd-step.is-done .sd-step-mark { background: var(--success); border-color: var(--success); color: #fff; }
.sd-step.is-now  {
  color: var(--brand-ink, var(--brand)); font-weight: 700;
  background: color-mix(in srgb, var(--brand) 10%, transparent);
}
.sd-step-sep { color: var(--line-2); flex: 0 0 auto; }

/* Anything that paints white on the brand fill reads --on-brand instead: a
   tenant with a light brand colour otherwise gets 2.8:1 badges. */
.ef-q-badge, .ef-q-num, .sd-step.is-done .sd-step-mark { color: var(--on-brand, #fff); }

/* Bootstrap's --warning is a fill; as small type it is 1.6:1 on white. */
.text-warning { color: var(--warning-ink) !important; }

/* ===========================================================================
   Delete confirmation — what goes with it.
   =========================================================================== */
.ef-dd-list {
  margin: 0 0 .9rem;
  padding-left: 1.15rem;
  color: var(--muted);
  font-size: .9rem;
  line-height: 1.7;
}
.ef-dd-list strong { color: var(--ink); }

.ef-dd-warn {
  margin: 0 0 .9rem;
  padding: .6rem .75rem;
  border-radius: .5rem;
  font-size: .85rem;
  color: var(--danger-ink, #b91c1c);
  background: rgba(220, 38, 38, .1);
  border: 1px solid rgba(220, 38, 38, .3);
}
:root[data-theme="dark"] .ef-dd-warn,
:root:not([data-theme="light"]) .ef-dd-warn { color: #fca5a5; }

/* A dropdown inside a .btn-group must not be clipped by the table's overflow
   container, or the menu opens and is immediately cut off at the row edge. */
.af-table .dropdown-menu { z-index: 1056; }

/* ===========================================================================
   Collapsible sidebar.

   Collapsed is an icon rail, not a hidden sidebar: navigation stays one click
   away and the page it is on stays visible. The width lives in a custom
   property so the sidebar and the main column are driven by ONE number — the
   two cannot drift apart mid-transition and overlap.
   =========================================================================== */
:root { --sidebar-w-collapsed: 4.25rem; }

/* Both sides animate on the same curve and duration, so the main column looks
   like it is following the sidebar rather than racing it. */
.af-sidebar {
  transition: width .24s cubic-bezier(.4, 0, .2, 1),
              flex-basis .24s cubic-bezier(.4, 0, .2, 1);
  will-change: width;
}

.af-nav a > span,
.af-nav-label,
.af-sidebar-foot {
  /* Fades out over the first half of the collapse, so the labels are gone
     before the rail is narrow enough to clip them mid-word. */
  transition: opacity .12s ease, transform .16s ease;
}

/* ── the toggle ───────────────────────────────────────────────────────────
   A small round button riding on the seam between the sidebar and the page —
   the spot VS Code, Notion and Linear all put this, because it reads as
   belonging to the BOUNDARY rather than buried in the nav list below it. That
   is also why it sits at eye level near the top rather than after the last
   link: it has to be found once and never hunted for again.

   Fixed to the viewport, not the sidebar, so it stays put while the page under
   it scrolls — matching the sidebar's own sticky behaviour — and positioned
   from the same --sidebar-w custom property the sidebar's width comes from,
   so the two can never drift out of sync mid-animation. */
.af-collapse {
  position: fixed;
  top: calc(var(--topbar-h) + 14px);
  left: calc(var(--sidebar-w) - 15px);
  z-index: 1035;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--line, rgba(148, 163, 184, .35));
  border-radius: 50%;
  background: var(--surface, #fff);
  color: var(--muted, #64748b);
  box-shadow: 0 2px 6px rgba(15, 23, 42, .16);
  cursor: pointer;
  transition: left .24s cubic-bezier(.4, 0, .2, 1),
              background .15s, color .15s, box-shadow .15s, transform .15s;
}
.af-collapse:hover {
  background: var(--brand); border-color: var(--brand); color: #fff;
  box-shadow: 0 3px 10px rgba(79, 70, 229, .35);
}
.af-collapse:active { transform: scale(.92); }
.af-collapse:focus-visible { outline: 2px solid var(--brand-2); outline-offset: 2px; }
.af-collapse i { font-size: .8rem; transition: transform .24s ease; }

/* The text label from the old full-width button is gone — a circle this small
   has no room for one — so the same information now lives only in the title
   attribute and this visually-hidden span, for anyone using a screen reader. */
.af-collapse-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap;
}

/* ── collapsed state ──────────────────────────────────────────────────── */
@media (min-width: 992px) {
  :root.sidebar-collapsed .af-sidebar {
    width: var(--sidebar-w-collapsed);
    flex-basis: var(--sidebar-w-collapsed);
    overflow-x: hidden;          /* nothing pokes out of the rail mid-animation */
  }

  :root.sidebar-collapsed .af-collapse {
    left: calc(var(--sidebar-w-collapsed) - 15px);
  }

  :root.sidebar-collapsed .af-nav a > span,
  :root.sidebar-collapsed .af-nav-label,
  :root.sidebar-collapsed .af-sidebar-foot {
    opacity: 0;
    /* visibility rather than display:none — display cannot transition, and the
       label would vanish in one frame while the rail was still wide. */
    visibility: hidden;
    transform: translateX(-6px);
  }

  :root.sidebar-collapsed .af-sidebar-foot {
    height: 0; padding: 0; margin: 0; border: 0; overflow: hidden;
  }

  /* Icons centre themselves in the rail instead of staying left-aligned
     against an edge that is no longer there. */
  :root.sidebar-collapsed .af-nav { padding: 0 .5rem; }
  :root.sidebar-collapsed .af-nav a { justify-content: center; padding-inline: 0; }
  :root.sidebar-collapsed .af-nav a > span { position: absolute; }

  :root.sidebar-collapsed .af-nav-label { height: 0; padding: 0; }
  :root.sidebar-collapsed .af-collapse i { transform: rotate(180deg); }
}

/* Below the breakpoint the sidebar is already an off-canvas drawer that slides
   in over the page. Collapsing a drawer to a rail means nothing there, so the
   toggle is hidden and the collapsed class is ignored. */
@media (max-width: 991.98px) {
  .af-collapse { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .af-sidebar,
  .af-nav a > span,
  .af-nav-label,
  .af-sidebar-foot,
  .af-collapse,
  .af-collapse i { transition: none; }
}

/* ===========================================================================
   Empty-state illustrations.

   Here rather than in a page stylesheet because inline_illustration() is a
   global helper: the surveys dashboard, the import picker, the published
   screen and the live screen all call it, and they do not share a single
   page CSS file between them. An <svg> whose fill never resolves paints
   solid black, so the miss is not subtle — it is a black rectangle in the
   middle of an empty state, which is the first screen a new account sees.
   =========================================================================== */
/* ── Illustrations ────────────────────────────────────────────────────── */
.ef-il { display: block; margin: 0 auto .4rem; max-width: 100%; height: auto; }
.ef-il .il-card  { fill: none; stroke: var(--line-2); stroke-width: 2; }
.ef-il .il-ring  { fill: none; stroke: var(--line-2); stroke-width: 2.5; }
.ef-il .il-line  { stroke: var(--line-2); stroke-width: 2.5; fill: none; }
.ef-il .il-dim   { opacity: .55; }
.ef-il .il-fill  { fill: color-mix(in srgb, var(--brand) 22%, transparent); }
.ef-il .il-solid { fill: var(--muted); }
.ef-il .il-ink   { stroke: var(--brand); stroke-width: 2.5; fill: none; }
.ef-il--error .il-ink, .ef-il--error .il-ring { stroke: var(--warning); }
.ef-il--done .il-fill { fill: color-mix(in srgb, var(--success) 20%, transparent); }
.ef-il--done .il-ink  { stroke: var(--success); }

/* The only illustration that moves is the one that means "still waiting". */
.ef-il .il-pulse { fill: none; stroke: var(--brand); stroke-width: 2; opacity: 0; transform-origin: 60px 46px; }
.ef-il--waiting .il-pulse-1 { animation: efRipple 2.4s ease-out infinite; }
.ef-il--waiting .il-pulse-2 { animation: efRipple 2.4s ease-out infinite 1.2s; }
@keyframes efRipple {
  0%   { opacity: .55; transform: scale(.55); }
  100% { opacity: 0;   transform: scale(1.35); }
}
