/* Kora VPN — "Aurora Glass" design system. Hand-written CSS, no framework.
   Mobile-first, accessible. Dark glass surfaces, mint→blue gradient accents. */

/* Self-hosted variable font (latin subset, 22 KB). No third-party font host:
   visitors never touch fonts.gstatic.com. Display type only — body text stays
   on the system stack. */
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("/static/fonts/space-grotesk-latin.woff2") format("woff2");
}

:root {
  --bg: #070b17;
  --bg-soft: #0d1329;
  --panel: #111936;
  --panel-2: #182247;
  --text: #eef2ff;
  --text-dim: #a6b1d4;
  /* Hint/placeholder ink. Must clear 4.5:1 on every surface it is used on —
     enforced by TestTextTokensMeetWCAGAA against all four backgrounds. */
  --muted: #8d99c0;
  --primary: #5b8cff;
  --primary-600: #4a78f0;
  --primary-700: #3d66e0;
  --accent: #3beab3;
  --warn: #f5b042;
  --danger: #ff6b6b;
  --border: rgba(255, 255, 255, .09);
  --border-strong: rgba(255, 255, 255, .16);
  --glass: rgba(255, 255, 255, .04);
  --glass-2: rgba(255, 255, 255, .07);
  --grad: linear-gradient(135deg, var(--accent), var(--primary));
  --grad-text: linear-gradient(90deg, var(--accent), var(--primary));
  --on-grad: #071022;
  --glow: 0 8px 30px rgba(59, 234, 179, .28);
  --shadow: 0 12px 40px rgba(0, 0, 0, .35);
  --radius: 20px;
  --radius-sm: 14px;
  --radius-pill: 999px;
  --maxw: 1080px;
  --maxw-narrow: 640px;
  --maxw-medium: 880px; /* prose that carries a table: readable lines, room for columns */
  /* Admin is a data surface, not a reading surface: tables were scrolling
     horizontally inside 1080px while ~800px of viewport sat empty on each
     side. Capped rather than full-bleed so rows stay scannable on a very
     wide monitor. */
  --maxw-admin: 1680px;
  --display: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  font-synthesis: none;
}

/* Snappy cross-document navigation where the browser supports it; the
   reduced-motion block below turns it back off. */
@view-transition { navigation: auto; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background:
    radial-gradient(900px 560px at 75% -10%, rgba(59, 234, 179, .10), transparent 70%),
    radial-gradient(820px 540px at -10% 5%, rgba(61, 102, 224, .18), transparent 70%),
    radial-gradient(700px 700px at 110% 45%, rgba(124, 90, 240, .08), transparent 70%),
    var(--bg);
  background-attachment: fixed;
  color: var(--text);
  font: 16px/1.55 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "PingFang SC", "Microsoft YaHei", sans-serif;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, .brand__name { font-family: var(--display); letter-spacing: -.02em; }
a { color: #9db8ff; text-decoration: none; }
a:hover { text-decoration: underline; }
code, pre { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; }
.muted { color: var(--muted); }

/* Text for screen readers only (e.g. bar-chart values). */
.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Skip link: off-screen until focused. */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--panel-2); color: var(--text);
  padding: 10px 16px; border: 1px solid var(--primary); border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; text-decoration: none; }
main:focus { outline: none; }

.container { max-width: var(--maxw); margin: 0 auto; padding: 0 20px; }
.container--narrow { max-width: var(--maxw-narrow); }
.container--medium { max-width: var(--maxw-medium); }

/* ---------- Header ---------- */
.site-header { position: sticky; top: 0; z-index: 20; backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  background: rgba(7, 11, 23, .65); border-bottom: 1px solid var(--border); }
.site-header__inner { display: flex; align-items: center; justify-content: space-between; height: 62px; }
.brand { display: inline-flex; align-items: center; gap: 10px; color: var(--text); }
.brand:hover { text-decoration: none; }
.brand__logo { width: 34px; height: 34px; display: block; }
.brand__name { font-weight: 700; font-size: 18px; }
.site-nav { display: flex; gap: 22px; align-items: center; }
.site-nav a:not(.btn) { color: var(--text-dim); font-weight: 500; }
.site-nav a:not(.btn):hover { color: var(--text); text-decoration: none; }
/* Full glow is too loud at header size. */
.site-nav .btn { margin-left: 4px; box-shadow: 0 4px 14px rgba(59, 234, 179, .22); }

/* ---------- Buttons ---------- */
.btn {
  --b: var(--glass-2);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 18px; border-radius: var(--radius-pill); border: 1px solid transparent;
  background: var(--b); color: var(--text); font-weight: 600; font-size: 15px; cursor: pointer;
  transition: transform .12s ease, background .15s ease, box-shadow .18s ease, filter .15s ease;
  text-decoration: none;
}
.btn:hover { text-decoration: none; transform: translateY(-1px); }
.btn:active { transform: translateY(1px) scale(.99); }
.btn--primary {
  background: var(--grad); color: var(--on-grad); box-shadow: var(--glow);
}
.btn--primary:hover { filter: saturate(1.12) brightness(1.05); box-shadow: 0 10px 36px rgba(59, 234, 179, .38); }
.btn--ghost { background: var(--glass); color: var(--text); border-color: var(--border-strong); }
.btn--ghost:hover { background: var(--glass-2); }
.btn--danger { background: rgba(255, 107, 107, .14); color: #ffc9c9; border-color: rgba(255, 107, 107, .45); }
.btn--danger:hover { background: rgba(255, 107, 107, .22); }
.btn--lg { padding: 14px 26px; font-size: 17px; }
.btn--sm { padding: 6px 13px; font-size: 13px; }
.btn:focus-visible, .input:focus-visible, a:focus-visible, [tabindex]:focus-visible,
summary:focus-visible, button:focus-visible {
  outline: 3px solid rgba(59, 234, 179, .55); outline-offset: 2px; border-radius: 8px;
}

/* A disabled control must look disabled. */
.btn:disabled, .btn[disabled], .btn[aria-busy="true"] { opacity: .55; cursor: progress; transform: none; }
.btn:disabled:hover, .btn[disabled]:hover { background: var(--b); filter: none; }
.btn--ghost:disabled:hover, .btn--ghost[disabled]:hover { background: var(--glass); }

/* ---------- Hero ---------- */
.hero { padding: 78px 0 34px; text-align: center; }
.hero h1 { font-size: clamp(32px, 5.4vw, 56px); font-weight: 700; line-height: 1.06; margin: 0 auto 16px; max-width: 17ch; }
.hero h1 strong { background: var(--grad-text); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero__sub { color: var(--text-dim); font-size: 18px; max-width: 58ch; margin: 0 auto 28px; }
.hero__cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.hero__badges { list-style: none; padding: 0; margin: 28px 0 0; display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; color: var(--text-dim); font-size: 14px; }
.hero__badges li {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--glass); border: 1px solid var(--border); padding: 8px 15px;
  border-radius: var(--radius-pill); backdrop-filter: blur(8px);
}
.hero__badges svg { flex: 0 0 auto; }

/* ---------- Sections ---------- */
.section-title { font-size: 30px; font-weight: 700; margin: 0 0 4px; }
.section-sub { color: var(--text-dim); margin: 0 0 24px; }
.plans { padding: 54px 0; }
.how { padding: 44px 0; }
.faq { padding: 44px 0 64px; }
.faq details { background: var(--glass); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px 18px; margin-bottom: 10px; backdrop-filter: blur(10px); }
.faq details[open] { background: var(--glass-2); }
.faq summary { cursor: pointer; font-weight: 600; }
.faq p { color: var(--text-dim); margin: 8px 0 0; }

/* ---------- Plan grid ---------- */
.plan-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px; }
.plan {
  position: relative; background: var(--glass); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 26px; display: flex; flex-direction: column; gap: 10px;
  backdrop-filter: blur(14px);
  transition: transform .15s ease, border-color .15s ease;
}
.plan:hover { transform: translateY(-2px); border-color: var(--border-strong); }
.plan--featured {
  background: linear-gradient(rgba(14, 20, 42, .92), rgba(14, 20, 42, .92)) padding-box,
              var(--grad) border-box;
  border: 1.5px solid transparent;
  box-shadow: 0 18px 60px rgba(59, 234, 179, .16);
}
.plan__badge {
  position: absolute; top: -13px; left: 50%; transform: translateX(-50%);
  background: var(--grad); color: var(--on-grad); padding: 5px 15px;
  border-radius: var(--radius-pill); font-size: 12px; font-weight: 700; letter-spacing: .3px; white-space: nowrap;
}
.plan__name { margin: 0; font-size: 18px; }
.plan__price { font-family: var(--display); font-size: 40px; font-weight: 700; letter-spacing: -.03em; }
.plan--featured .plan__price { background: var(--grad-text); -webkit-background-clip: text; background-clip: text; color: transparent; }
.plan__duration { color: var(--text-dim); }
.plan__features { list-style: none; padding: 0; margin: 8px 0 0; color: var(--text-dim); display: grid; gap: 7px; }
.plan__features li::before { content: "✓ "; color: var(--accent); font-weight: 700; }
.plan__actions { display: grid; gap: 9px; margin-top: 16px; }
.plan__form { margin: 0; }
.plan__buy { width: 100%; }

/* ---------- Steps ---------- */
.steps { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; counter-reset: step; }
.steps li { background: var(--glass); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; display: flex; gap: 14px; backdrop-filter: blur(10px); }
.step__n {
  width: 34px; height: 34px; flex: 0 0 34px; border-radius: 12px;
  background: var(--grad); color: var(--on-grad); display: grid; place-items: center;
  font-family: var(--display); font-weight: 700;
}
.steps h3 { margin: 0 0 4px; font-size: 16px; }
.steps p { margin: 0; color: var(--text-dim); font-size: 14px; }
.step__time { display: inline-block; margin-left: 6px; color: var(--accent); font-size: 12px; font-weight: 700; letter-spacing: .4px; }

/* ---------- Forms ---------- */
/* The explicit minmax(0,1fr) column matters: with no template, the implicit
   column is auto-sized to the widest item's intrinsic width, so a two-column
   row whose hints didn't fit quietly pushed every input past the card's edge —
   found on the contact form, latent on every other. minmax(0,…) lets items
   shrink below their content size instead of dictating the track. */
.form { display: grid; grid-template-columns: minmax(0, 1fr); gap: 14px; max-width: 420px; }
/* A message needs more room than a login field. */
.form--wide { max-width: 560px; }
/* The admin forms sit in a content column roughly 1200px wide, where the 420px
   default left two thirds of the row empty and squeezed paired fields into
   ~180px each. Wide enough for two comfortable columns, still short enough that
   a line of hint text stays readable. */
.form--admin { max-width: 780px; }
/* align-content:start keeps the rows content-sized. Without it a field's auto
   tracks stretch to whatever height the grid item was given, so in a two-column
   row the field with no hint grew its control to match the sibling's hint —
   the contact form's topic select stood taller than the order id beside it. */
/* The same trap the comment above describes, one level further down: a .field
   with no explicit column gets an auto track sized to its widest item, and an
   <input> is intrinsically ~20 characters wide (about 260px here). In a
   two-column row that pushed a 260px input out of its 177px cell and past the
   card's edge, dragging the hint text across its neighbour's. minmax(0,…) lets
   the input shrink to the cell instead of dictating it. */
.field { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; align-content: start; }
.field__label { color: var(--text-dim); font-size: 14px; }
.input {
  background: var(--bg-soft); color: var(--text); border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm); padding: 12px 14px; font: inherit;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.input:focus { border-color: rgba(59, 234, 179, .5); }
.input::placeholder { color: var(--muted); }
.inline { display: inline; }
.input--sm { width: 160px; padding: 6px 10px; font-size: 13px; border-radius: 10px; }
/* The attach select carries an order id, plan and price per option. */
.input--attach { width: 280px; }
.input--area { resize: vertical; min-height: 120px; line-height: 1.5; }
.form__row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
/* A row holding one field that wants the whole width — a long comma-separated
   list reads badly in half a row with an empty column beside it. */
.form__row--single { grid-template-columns: minmax(0, 1fr); }
/* Says where the pre-filled values came from, so carried-over ones get looked
   at rather than saved unread. */
.form__note {
  margin: 0; padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--glass-2); border: 1px solid var(--border);
  color: var(--text-dim); font-size: 13px; line-height: 1.5;
}
.form__note strong { color: var(--text); font-weight: 600; }
/* Checkbox and label share a line; a hint drops to its own row beneath them.
   As a flex row all three sat side by side, so in a half-width column the hint
   was squeezed into a sliver beside the label and overlapped it. */
.field--check { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; column-gap: 8px; row-gap: 4px; }
.field--check input { width: 18px; height: 18px; accent-color: var(--accent); }
.field--check .field__hint { grid-column: 1 / -1; margin-top: 0; }
.field__error { color: var(--danger); font-size: 12px; margin-top: 2px; }
.field__hint { color: var(--muted); font-size: 12px; margin-top: 2px; }
/* A hint that is reporting something lost — a truncated name — rather than
   merely explaining a field. */
.field__hint--warn { color: var(--warn); }
.field__hint code { font-size: 11px; }
.field__optional { color: var(--muted); font-weight: 400; }
.input[aria-invalid="true"] { border-color: var(--danger); }
.input[aria-invalid="true"]:focus { outline-color: var(--danger); }
@media (max-width: 520px) { .form__row { grid-template-columns: 1fr; } }
.auth { padding: 60px 0; }
.auth h1 { margin: 0 0 18px; }
.alert { border-radius: var(--radius-sm); padding: 11px 15px; margin-bottom: 16px; backdrop-filter: blur(8px); }
.alert--error { background: rgba(255, 107, 107, .12); border: 1px solid rgba(255, 107, 107, .5); color: #ffd5d5; }
.alert--ok { background: rgba(59, 234, 179, .10); border: 1px solid rgba(59, 234, 179, .45); color: #c8f5e2; }
.alert--note { border-left: 3px solid var(--border-strong); }
/* An empty state caused by a failed query must not read like "nothing here". */
.empty--error { color: var(--danger); }

.fieldset {
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 14px 16px 16px; margin: 0; display: grid; gap: 14px;
}
.fieldset__legend { padding: 0 6px; color: var(--text-dim); font-size: 14px; }
.fieldset > .field__hint { margin: 0; }
.fieldset[hidden] { display: none; }

/* ---------- Server reachability check ---------- */
.server-check { font-size: 12px; margin-left: 6px; }
.server-check__ok { color: var(--accent); }
.server-check__fail { color: var(--danger); word-break: break-word; }
.server-check.htmx-request::after { content: "testing…"; color: var(--muted); }
.server-check.htmx-request > * { display: none; }

/* ---------- Pay (crypto) ---------- */
.pay { padding: 50px 0; }
.pay__order { color: var(--text-dim); }
.pay__coins { display: grid; gap: 14px; margin: 18px 0; }
.coin { background: var(--glass); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; backdrop-filter: blur(12px); }
.coin__head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 8px; }
.coin__symbol { font-family: var(--display); font-weight: 700; }
.coin__network { color: var(--text-dim); font-size: 13px; }
.coin__addr code { display: block; word-break: break-all; background: var(--bg-soft); padding: 9px; border-radius: 8px; }
.coin__actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 10px 0 0; }
.coin__actions-hint { flex-basis: 100%; }
.coin__qr { margin-top: 10px; max-width: 180px; height: auto; background: #fff; padding: 8px; border-radius: 12px; image-rendering: pixelated; }
.pay__hint { color: var(--text-dim); background: var(--glass); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 14px; }
.pay__form { margin-top: 16px; }
.pay__exact { font-size: 17px; margin: 14px 0 10px; }
.pay__amount { font-size: 20px; font-weight: 700; background: var(--bg-soft); padding: 4px 10px; border-radius: 8px; letter-spacing: .5px; }
.pay__fallback { margin-top: 18px; color: var(--text-dim); }
.pay__fallback summary { cursor: pointer; }

/* ---------- Pay screen: network tiles ----------
   Which chains we accept, at a glance, above any amount or address. One glowing
   tile per configured chain plus a dashed invitation to ask for another — new
   networks get added when customers request them. Deliberately selector-shaped:
   when a second chain lands in CRYPTO_CHAINS, this grid is the picker. */
.tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(128px, 1fr)); gap: 12px; margin: 18px 0; }
.tile {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--glass); padding: 16px 12px;
  display: grid; justify-items: center; gap: 8px; text-align: center;
}
.tile__name { font-family: var(--display); font-weight: 600; }
.tile__sub { color: var(--muted); font-size: 12.5px; line-height: 1.35; }
.tile--live {
  border-color: rgba(59, 234, 179, .45);
  background: linear-gradient(180deg, rgba(59, 234, 179, .08), rgba(91, 140, 255, .05));
  box-shadow: 0 0 0 1px rgba(59, 234, 179, .12), 0 8px 24px rgba(0, 0, 0, .25);
}
.tile__badge {
  font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); font-weight: 700;
}
.tile--ask { border-style: dashed; background: transparent; text-decoration: none; color: var(--text-dim); }
.tile--ask:hover, .tile--ask:focus-visible { color: var(--text); border-color: var(--border-strong, var(--border)); }
.tile__plus {
  width: 34px; height: 34px; border-radius: 50%; border: 1px dashed var(--border);
  display: grid; place-items: center; color: var(--muted); font-size: 19px;
}

/* Chain marks (inline SVG) align with the text they always accompany. */
.chain-mark { vertical-align: -3px; }
.coin__symbol .chain-mark { margin-right: 2px; }
.btn .chain-mark { vertical-align: -3px; margin-right: 7px; }

/* Storefront: names the coin and network under the crypto button. */
.plan__paynote { margin: -2px 0 0; text-align: center; color: var(--muted); font-size: 12.5px; }

/* ---------- Pay screen: live status ----------
   The page a customer stares at after sending USDC. Everything here exists to
   answer one question — "is this thing still working?" — so the card is never
   still while it is waiting: a sweep across the top edge and a pulse under the
   heading, plus the timestamp pay.js stamps on every completed poll. */
.paystatus {
  position: relative; overflow: hidden;
  margin: 18px 0; padding: 20px 22px;
  background: var(--glass); border: 1px solid var(--border);
  border-radius: var(--radius); backdrop-filter: blur(12px);
}
.paystatus__title { margin: 0; font-weight: 600; }
.paystatus__sub { margin: 6px 0 0; color: var(--text-dim); font-size: 14px; }
.paystatus__meta { margin: 10px 0 0; color: var(--muted); font-size: 12.5px; min-height: 1em; }
.paystatus__actions { margin: 16px 0 0; }

/* Indeterminate sweep along the top edge — the "we are still looking" signal
   that survives being glanced at from across a room. */
.paystatus--waiting::before,
.paystatus--submitted::before,
.paystatus--confirming::before {
  content: ""; position: absolute; inset: 0 auto auto 0; height: 2px; width: 40%;
  background: var(--grad); border-radius: 2px;
  animation: paystatus-sweep 2.1s ease-in-out infinite;
}
@keyframes paystatus-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}

/* Three dots breathing in sequence. Sized and spaced to read as one object at
   a glance rather than three separate marks. */
.paystatus__pulse { display: flex; gap: 6px; margin-bottom: 12px; }
.paystatus__pulse span {
  width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  animation: paystatus-pulse 1.4s ease-in-out infinite;
}
.paystatus__pulse span:nth-child(2) { animation-delay: .18s; }
.paystatus__pulse span:nth-child(3) { animation-delay: .36s; }
@keyframes paystatus-pulse {
  0%, 100% { opacity: .28; transform: scale(.72); }
  40%      { opacity: 1;   transform: scale(1); }
}

/* Confirmed: the one moment on this page worth a bit of colour. */
.paystatus--active, .paystatus--extended { border-color: rgba(59, 234, 179, .38); box-shadow: var(--glow); }
.paystatus--active .paystatus__title, .paystatus--extended .paystatus__title { color: var(--accent); }
.paystatus__mark {
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; margin-bottom: 12px;
  background: var(--grad); color: var(--on-grad); font-weight: 700; font-size: 18px;
  animation: paystatus-land .3s ease-out;
}
@keyframes paystatus-land { from { opacity: 0; transform: scale(.6); } }

.paystatus--closed, .paystatus--unpayable { border-color: var(--border-strong); }

/* ---------- Redeem / pass card ---------- */
.redeem { padding: 50px 0; }
.redeem__order { color: var(--text-dim); margin-top: 0; }
.card {
  background: var(--glass); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 26px; margin-top: 18px; box-shadow: var(--shadow); backdrop-filter: blur(14px);
}
.card__head { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }

/* Countdown ring: SVG progress circle around the remaining time. The <text>
   fallback inside stays "—" until redeem.js fills it; the ring track is always
   visible so the component reads as a timer even before the first tick. */
.ring { position: relative; width: 132px; height: 132px; flex: 0 0 132px; }
.ring svg { width: 132px; height: 132px; display: block; }
.ring__track { fill: none; stroke: rgba(255, 255, 255, .08); stroke-width: 9; }
.ring__bar {
  fill: none; stroke: url(#ring-grad); stroke-width: 9; stroke-linecap: round;
  transform: rotate(-90deg); transform-origin: 50% 50%;
  transition: stroke-dashoffset 1s linear;
}
.ring--soon .ring__bar { stroke: var(--warn); }
.ring__label {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1px; text-align: center;
}
.countdown {
  font-family: var(--display); font-variant-numeric: tabular-nums; font-weight: 700;
  font-size: 19px; letter-spacing: -.02em; max-width: 92px;
}
.countdown--soon { color: var(--warn); }
.ring__sub { color: var(--muted); font-size: 10px; font-weight: 600; letter-spacing: .8px; }

.active-grid { display: grid; grid-template-columns: 1fr; gap: 18px; margin-top: 18px; }
@media (min-width: 760px) { .active-grid { grid-template-columns: 260px 1fr; align-items: start; } }
/* image-rendering: a QR is not a photograph. Smoothing blurs adjacent modules
   into each other at any non-integer scale, which is what stops phones reading
   it; nearest-neighbour keeps the edges hard. 260px (up from 220) also gives
   the camera a larger target to focus on at arm's length. */
.active-grid__qr img {
  width: 100%; max-width: 260px; height: auto;
  background: #fff; padding: 10px; border-radius: 14px;
  image-rendering: pixelated;
}
.conf { background: var(--bg-soft); border: 1px solid var(--border-strong); border-radius: 12px; padding: 12px; overflow: auto; max-height: 240px; margin: 0; }
.conf code { font-size: 13px; color: var(--text); white-space: pre; }
.active-grid__actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }

.conf-panel { margin-top: 12px; }
.conf-panel > summary { cursor: pointer; color: var(--text-dim); font-size: 14px; }
.conf-panel > summary:hover { color: var(--text); }
.conf-panel > .conf { margin-top: 10px; }

.applinks { margin: 8px 0 4px; padding-left: 18px; }
.applinks li { margin: 2px 0; }

.howto { margin-top: 16px; }
.howto summary { cursor: pointer; color: var(--text-dim); font-weight: 600; }

/* ---------- Location cards (storefront) ---------- */
.locations { padding: 10px 0 54px; }
.loc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; margin-top: 20px; }
.loc-card {
  background: var(--glass); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 20px; text-align: center; backdrop-filter: blur(10px);
  transition: transform .15s ease, border-color .15s ease;
}
.loc-card:hover { transform: translateY(-2px); border-color: var(--border-strong); }
.loc-card__flag { font-size: 32px; display: block; margin-bottom: 6px; }
.loc-card__name { font-weight: 600; }
.loc-card__city { color: var(--text-dim); font-size: 13px; }
.locations__more { color: var(--text-dim); margin: 16px 0 0; text-align: center; }
.locations__soon { color: var(--text-dim); margin: 10px 0 0; text-align: center; }
.locations__soon strong { color: var(--text); }

/* Region heading over a run of location panels. A label, not a title: the
   panels are the content, this just says where a run of them lives. */
.loc-region {
  display: flex; align-items: center; gap: 8px;
  margin: 20px 0 10px; font-size: 13px; font-weight: 600;
  letter-spacing: .8px; text-transform: uppercase; color: var(--text-dim);
}
.loc-region:first-of-type { margin-top: 12px; }
.loc-region__count {
  font-family: inherit; font-size: 11px; font-weight: 700; letter-spacing: 0;
  padding: 1px 8px; border-radius: var(--radius-pill);
  background: var(--glass-2); border: 1px solid var(--border); color: var(--muted);
}

/* ---------- Redeem: one collapsible panel per location ---------- */
.loc-panel {
  background: var(--bg-soft); border: 1px solid var(--border);
  border-radius: var(--radius-sm); margin-bottom: 10px; overflow: hidden;
}
.loc-panel > summary {
  cursor: pointer; padding: 13px 15px; font-weight: 600;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.loc-panel > summary::-webkit-details-marker { display: none; }
.loc-panel > summary::after { content: "▾"; color: var(--muted); transition: transform .15s ease; }
.loc-panel[open] > summary::after { transform: rotate(180deg); }
.loc-panel[open] > summary { border-bottom: 1px solid var(--border); }
.loc-panel__title { display: inline-flex; align-items: center; gap: 8px; }
.loc-panel__badge {
  background: rgba(59, 234, 179, .14); color: var(--accent); border: 1px solid rgba(59, 234, 179, .35);
  font-size: 12px; font-weight: 700; padding: 3px 10px; border-radius: var(--radius-pill); white-space: nowrap;
}
.loc-panel .active-grid { padding: 14px; }

.loc-accordion { margin-top: 18px; }
.loc-accordion > details { background: var(--glass); border: 1px solid var(--border); border-radius: var(--radius-sm); margin-bottom: 10px; }
.loc-accordion > details > summary { cursor: pointer; padding: 14px 18px; font-weight: 600; display: flex; align-items: center; gap: 8px; }
.loc-accordion > details > summary:hover { background: var(--glass-2); border-radius: var(--radius-sm); }
.loc-accordion__badge { background: var(--accent); color: var(--on-grad); font-size: 12px; font-weight: 700; padding: 2px 9px; border-radius: var(--radius-pill); }

/* spinner for pending */
.card--pending { text-align: center; }
.card__spinner {
  width: 30px; height: 30px; border-radius: 50%;
  border: 3px solid var(--border-strong); border-top-color: var(--accent);
  margin: 0 auto 10px; animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.card__actions { margin: 18px 0 10px; }
.card--expired { text-align: center; }
.htmx-request .card__spinner { display: block; }

/* ---------- Error ---------- */
.error { padding: 80px 0; text-align: center; }
.error h1 { font-size: 60px; margin: 0; background: var(--grad-text); -webkit-background-clip: text; background-clip: text; color: transparent; }
.error p { color: var(--text-dim); }

/* ---------- Admin ---------- */
.admin { padding: 30px 0 60px; }

/* Sidebar layout without touching the page templates: admin__head's children
   (the h1 and the nav) are promoted to grid items via display:contents, the
   nav takes the left column across every row, and everything else stacks in
   the right column. Row gap stays 0 — vertical rhythm comes from the blocks'
   own margins, as before. */
.admin .container {
  max-width: var(--maxw-admin);
  display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 0 24px; align-items: start;
}
.admin .container > * { grid-column: 2; min-width: 0; }
.admin__head { display: contents; }
.admin__head > h1 { grid-column: 2; margin: 0 0 20px; }
.admin__nav {
  grid-column: 1; grid-row: 1 / span 30;
  position: sticky; top: 82px;
  display: flex; flex-direction: column; gap: 4px;
  padding: 12px; border-radius: var(--radius);
  background: var(--glass); border: 1px solid var(--border); backdrop-filter: blur(14px);
}
.admin__nav .btn {
  justify-content: flex-start; border-radius: 10px; background: transparent;
  border-color: transparent; color: var(--text-dim); font-size: 13.5px; padding: 8px 11px;
}
.admin__nav .btn:hover { background: var(--glass-2); color: var(--text); transform: none; }
.admin__nav .btn[aria-current="page"] {
  background: linear-gradient(90deg, rgba(59, 234, 179, .14), rgba(91, 140, 255, .10));
  border-color: rgba(59, 234, 179, .28); color: var(--text);
}
.admin__nav .btn--primary {
  background: var(--grad); color: var(--on-grad); justify-content: center; margin-top: 8px;
  border-radius: var(--radius-pill); box-shadow: var(--glow);
}
.admin__nav form { display: contents; }
.admin__nav form .btn { width: 100%; }

/* Notification count. It rides the nav link so the work queue is legible from
   every admin page, and sits at the link's trailing edge so the numbers line up
   down the sidebar instead of wandering with each label's length. */
.nav-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px; margin-left: auto;
  border-radius: var(--radius-pill); background: var(--grad); color: var(--on-grad);
  font-family: var(--display); font-size: 11.5px; font-weight: 700; line-height: 1;
}
.nav-badge--unknown {
  background: rgba(245, 176, 66, .16); color: var(--warn);
  border: 1px solid rgba(245, 176, 66, .45);
}

/* A closed notification stays listed as history — dimmed, so the eye lands on
   the rows that are still work. */
.row-closed td { opacity: .62; }
.notif__detail { margin-top: 3px; font-size: 12.5px; white-space: normal; max-width: 68ch; }

@media (max-width: 1000px) {
  .admin .container { display: block; }
  .admin__head { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 18px; }
  .admin__head > h1 { margin: 0; }
  .admin__nav { position: static; flex-direction: row; flex-wrap: wrap; padding: 8px; }
  .admin__nav .btn--primary { margin-top: 0; }
}

.admin__periods { display: flex; gap: 6px; flex-wrap: wrap; margin: 18px 0 8px; }
.admin__actions { display: flex; gap: 5px; align-items: center; flex-wrap: nowrap; }
.admin__actions form { margin: 0; }
.table td.admin__actions { min-width: 0; white-space: nowrap; }
.admin__actions .btn { white-space: nowrap; }
.admin__actions .server-check { flex-basis: 100%; margin-left: 0; }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 14px; margin-bottom: 30px; }
.stat { background: var(--glass); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px 20px; backdrop-filter: blur(12px); }
.stat__n, .stat__value { font-family: var(--display); font-size: 30px; font-weight: 700; letter-spacing: -.02em; }
.stat__l, .stat__label { color: var(--text-dim); font-size: 13px; margin-top: 2px; }
.stat-row { display: flex; gap: 14px; flex-wrap: wrap; }
.stat-row .stat { flex: 1 1 180px; }

.table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--glass); backdrop-filter: blur(12px); }
.table { width: 100%; border-collapse: collapse; min-width: 520px; }
/* Narrow summary tables (e.g. revenue by plan/method) live in half-width
   columns; the 640px floor is for wide row-action tables only. */
.form__row .table { min-width: 0; }
.table th, .table td { padding: 9px 12px; text-align: left; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.table thead th { background: rgba(13, 19, 41, .6); color: var(--text-dim); font-weight: 600; font-size: 11px; letter-spacing: .4px; text-transform: uppercase; white-space: nowrap; }
.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr:hover { background: var(--glass-2); }
.empty { color: var(--muted); padding: 24px; text-align: center; }
.table td.nowrap { white-space: nowrap; font-variant-numeric: tabular-nums; }
.badge { display: inline-block; padding: 3px 10px; border-radius: var(--radius-pill); font-size: 12px; font-weight: 600; text-transform: capitalize; }
.badge--ok { background: rgba(59, 234, 179, .13); color: var(--accent); border: 1px solid rgba(59, 234, 179, .3); }
.badge--warn { background: rgba(245, 176, 66, .13); color: var(--warn); border: 1px solid rgba(245, 176, 66, .35); }
.badge--muted { background: var(--panel-2); color: var(--text-dim); }
.row-ok td { background: rgba(59, 234, 179, .05); }

.stat__row { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; }
.stat__spark { width: 72px; height: 28px; flex: 0 0 auto; margin-bottom: 4px; }
.stat__spark polyline { stroke: var(--primary); }

/* Revenue area chart: SVG rendered from handler-computed geometry. The line
   color (#14a577) is deliberately darker than --accent — it sits in the
   validated lightness band for chart marks on this surface. */
.revchart {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--glass); padding: 10px; margin: 12px 0 4px;
}
.revchart svg { display: block; width: 100%; height: 160px; }
.revchart__grid line { stroke: rgba(255, 255, 255, .06); stroke-width: 1; }
.revchart__area { fill: url(#rev-fill); }
.revchart__line { fill: none; stroke: #14a577; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.revchart__today { fill: var(--accent); stroke: var(--bg-soft); stroke-width: 2; }

/* "Need more time?" nudge at the bottom of the active pass card. */
.nudge {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 18px; padding: 14px 16px;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-sm);
}
.nudge__text { display: flex; flex-direction: column; gap: 2px; font-size: 14px; }
.nudge .btn { white-space: nowrap; }

/* A renewal listed under the pass it paid for: quieter than the pass, and
   visibly hanging off it rather than standing beside it. */
.history__renewal td { color: var(--text-dim); font-size: 13px; }
.history__branch { color: var(--muted); padding-left: 6px; }

/* Extend this pass. The same dashed frame as the nudge it grew out of — it sits
   at the foot of a live pass and must read as an offer, not as another step of
   the setup above it. */
.renew {
  display: grid; gap: 12px;
  margin-top: 18px; padding: 16px;
  border: 1px dashed var(--border-strong); border-radius: var(--radius-sm);
}
.renew__head { display: flex; flex-direction: column; gap: 2px; font-size: 14px; }
.renew__plans {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px; margin: 0; padding: 0; border: 0;
}
/* The whole tile is the target, not just the 18px dot. */
.renew__plan {
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center;
  column-gap: 8px; row-gap: 2px;
  padding: 10px 12px; cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--glass);
}
.renew__plan input { width: 18px; height: 18px; accent-color: var(--accent); }
.renew__plan:has(input:checked) { border-color: var(--accent); }
/* :has is recent enough to need a fallback that still shows the choice. */
.renew__plan input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.renew__planname { font-size: 14px; }
.renew__planprice { grid-column: 2; color: var(--text-dim); font-size: 13px; }
.renew__code { max-width: 260px; }
.renew__actions { display: flex; flex-wrap: wrap; gap: 9px; }
.renew__note { font-size: 12px; margin: 0; }

/* Identity columns read as labels, not paragraphs: "Kora VPN - Beta" was
   wrapping onto four lines inside a narrow auto-sized column. */
.table td:first-child, .table th:first-child { white-space: nowrap; }
.table .wrap, .table td.wrap { white-space: normal; }

/* Server-rendered revenue bars: no chart library, legible with CSS off. */
.barchart {
  display: flex; align-items: flex-end; gap: 3px; height: 140px;
  list-style: none; margin: 12px 0 4px; padding: 10px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--glass);
}
.barchart__col { flex: 1; height: 100%; display: flex; align-items: flex-end; }
.barchart__bar {
  width: 100%; min-height: 2px; border-radius: 3px 3px 0 0;
  background: var(--primary); display: block;
}
.barchart__col--today .barchart__bar { background: var(--accent); }
.barchart__axis {
  display: flex; justify-content: space-between;
  color: var(--text-dim); font-size: 12px; margin: 0 0 8px;
}

/* ---------- Admin ⌘K palette (markup injected by admin.js) ---------- */
.palette {
  width: min(480px, calc(100vw - 32px));
  padding: 10px; margin: 12vh auto 0; border: 1px solid var(--border-strong);
  border-radius: var(--radius); background: rgba(13, 19, 41, .96); color: var(--text);
  box-shadow: var(--shadow); backdrop-filter: blur(16px);
}
.palette::backdrop { background: rgba(4, 7, 16, .6); backdrop-filter: blur(3px); }
.palette__input { width: 100%; }
.palette__list { list-style: none; margin: 8px 0 0; padding: 0; max-height: 300px; overflow-y: auto; }
.palette__item { padding: 10px 12px; border-radius: 10px; cursor: pointer; color: var(--text-dim); font-size: 14px; font-weight: 500; }
.palette__item:hover { background: var(--glass-2); color: var(--text); }
.palette__item[aria-selected="true"] {
  background: linear-gradient(90deg, rgba(59, 234, 179, .14), rgba(91, 140, 255, .10));
  color: var(--text);
}

/* ---------- Confirmation dialog (markup injected by admin.js) ---------- */
/* Top-centred rather than viewport-centred: the action that opened it lives in
   the upper half of the page, so the eyes are already there. */
.confirm {
  width: min(430px, calc(100vw - 32px));
  margin: 9vh auto auto;
  padding: 20px;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: rgba(13, 19, 41, .96); color: var(--text);
  box-shadow: var(--shadow); backdrop-filter: blur(16px);
}
.confirm::backdrop { background: rgba(4, 7, 16, .6); backdrop-filter: blur(3px); }
.confirm[open] { animation: confirm-in .14s ease-out; }
.confirm[open]::backdrop { animation: fade-in .14s ease-out; }
@keyframes confirm-in { from { opacity: 0; transform: translateY(-8px) scale(.97); } }
@keyframes fade-in { from { opacity: 0; } }
.confirm__msg { margin: 0 0 18px; line-height: 1.5; overflow-wrap: break-word; }
.confirm__btns { display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; }

/* ---------- Peers: locations expander ---------- */
/* The collapsed summary is the whole cell's content, so it must not look like
   a link but must still read as pressable. */
.peer-locs > summary {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px; border-radius: var(--radius-pill);
  background: var(--glass); border: 1px solid var(--border);
  font-size: 13px; font-weight: 600;
}
.peer-locs > summary::-webkit-details-marker { display: none; }
.peer-locs > summary::after { content: "▾"; color: var(--muted); font-size: 10px; }
.peer-locs[open] > summary::after { transform: rotate(180deg); }
.peer-locs > summary:hover { background: var(--glass-2); border-color: var(--border-strong); }
.peer-locs__list { display: grid; gap: 4px; margin-top: 8px; }
.peer-locs__row {
  display: grid; grid-template-columns: minmax(140px, 1fr) auto minmax(120px, auto) auto;
  gap: 12px; align-items: center;
  padding: 6px 10px; border-radius: 10px; background: var(--bg-soft);
  font-size: 12.5px; white-space: nowrap;
}
.peer-locs__key { color: var(--text-dim); font-size: 11.5px; }
.peer-locs__data { font-variant-numeric: tabular-nums; }

/* ---------- Pass page: setup block ---------- */
/* The one thing a customer should do, and the one code worth scanning. Sits
   above the per-location panels because those invite scanning five codes in a
   row, which is the job this replaces.

   Stacked, not side-by-side: the pass page uses the 640px narrow container, so
   the card is ~600px inside and two columns never actually fit — an earlier
   side-by-side version simply wrapped, leaving its divider as a vertical rule
   with nothing beside it. */
.setup {
  margin: 18px 0 8px; padding: 22px;
  border-radius: var(--radius); background: var(--glass-2);
  border: 1px solid var(--border-strong);
}
.setup__main { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.setup__title { margin: 0; font-size: 19px; }
.setup__how { margin: 0; color: var(--text-dim); font-size: 14px; line-height: 1.55; }
.setup__how strong { color: var(--text); font-weight: 600; }
/* The hand-off to a phone: a rule above it, because it is the second way to
   do the same job rather than a separate feature. */
.setup__phone {
  display: flex; align-items: center; gap: 16px;
  margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border);
}
.setup__qr {
  width: 104px; height: 104px; flex: 0 0 auto;
  background: #fff; padding: 7px; border-radius: 12px;
  image-rendering: pixelated;
}
.setup__phonetext { margin: 0; color: var(--text-dim); font-size: 13.5px; line-height: 1.55; }
.setup__phonetext strong { color: var(--text); }
/* Its counterpart under the per-location codes, which ARE WireGuard configs. */
.qr-caption { margin: 8px 0 0; color: var(--text-dim); font-size: 12.5px; text-align: center; max-width: 220px; }
.qr-caption strong { color: var(--text); }
.setup__alt { margin: 22px 0 10px; font-size: 14px; }

@media (max-width: 420px) {
  .setup { padding: 18px; }
  .setup__qr { width: 92px; height: 92px; }
}

/* ---------- Footer ---------- */
.site-footer { border-top: 1px solid var(--border); padding: 26px 0; color: var(--muted); font-size: 14px; }
.site-footer p { margin: 0; }
.site-footer__links { display: flex; gap: 16px; margin-top: 10px; flex-wrap: wrap; }
.site-footer__links a { color: var(--text-dim); }
.site-footer__links a:hover { color: var(--text); }

/* ---------- Cookie consent ---------- */
.consent { position: fixed; left: 0; right: 0; bottom: 0; z-index: 50; padding: 0 16px 16px; pointer-events: none; }
.consent[hidden] { display: none; }
.consent__inner {
  max-width: var(--maxw); margin: 0 auto; padding: 16px 20px; pointer-events: auto;
  display: flex; gap: 16px; align-items: center; justify-content: space-between; flex-wrap: wrap;
  background: rgba(13, 19, 41, .92); border: 1px solid var(--border-strong);
  border-radius: var(--radius); box-shadow: var(--shadow); backdrop-filter: blur(16px);
}
.consent p { margin: 0; color: var(--text-dim); font-size: 14px; }
.consent a { color: var(--accent); }
.consent__btns { display: flex; gap: 8px; flex-shrink: 0; }
@media (max-width: 600px) {
  .consent__inner { flex-direction: column; align-items: stretch; }
  .consent__btns { justify-content: flex-end; }
}

/* ---------- Legal pages ---------- */
.legal { padding: 28px 0 60px; }
.legal .card { margin-top: 0; line-height: 1.6; }
.legal h1 { font-size: 26px; margin: 0 0 4px; }
.legal h2 { font-size: 17px; margin: 28px 0 10px; color: var(--text); }
.legal p { margin: 0 0 12px; color: var(--text-dim); }
.legal ul { margin: 0 0 12px; padding-left: 20px; color: var(--text-dim); }
.legal li { margin-bottom: 6px; }
.legal strong { color: var(--text); }
.legal code { background: var(--bg-soft); padding: 1px 5px; border-radius: 4px; font-size: 13px; }
.legal__meta { color: var(--muted); font-size: 13px; margin: 0 0 20px !important; }
.legal .table-wrap { margin: 0 0 14px; }
.legal .table td { color: var(--text-dim); white-space: normal; vertical-align: top; }
.legal .table td:first-child { color: var(--text); font-weight: 600; white-space: nowrap; }

/* A table of prose (the partner tiers) stacks into labelled blocks on a phone
   instead of scrolling sideways: each cell prints its column name from
   data-label. The header row stays in the DOM for screen readers. */
@media (max-width: 640px) {
  .table--stack { min-width: 0; }
  .table--stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .table--stack tbody tr { display: block; padding: 8px 0; border-bottom: 1px solid var(--border); }
  .table--stack tbody tr:last-child { border-bottom: none; }
  .table--stack td, .legal .table--stack td:first-child {
    display: grid; grid-template-columns: 6.5em minmax(0, 1fr); gap: 10px;
    padding: 4px 12px; border-bottom: none; white-space: normal;
  }
  .table--stack td::before {
    content: attr(data-label); color: var(--text-dim);
    font-size: 11px; font-weight: 600; letter-spacing: .4px; text-transform: uppercase; padding-top: 3px;
  }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  .card__spinner { animation: none; }
  /* The dots stay visible at full strength rather than frozen mid-fade, and the
     sweep goes away entirely — the timestamp under the copy is what tells a
     reduced-motion visitor the page is still polling. */
  .paystatus--waiting::before, .paystatus--submitted::before,
  .paystatus--confirming::before { animation: none; display: none; }
  .paystatus__pulse span { animation: none; opacity: 1; transform: none; }
  .paystatus__mark { animation: none; }
  .confirm[open], .confirm[open]::backdrop { animation: none; }
  * { transition: none !important; }
  .btn:hover, .plan:hover, .loc-card:hover { transform: none; }
}
