/* base.css — typography, prose, forms. Loaded after critical.css. */

.vx-section { padding-block: var(--vx-section); position: relative; }
.vx-section--tight { padding-block: clamp(3rem, 6vh, 5rem); }
.vx-section--alt { background: var(--vx-bg-secondary); }

.vx-eyebrow {
  display: block; font-size: var(--vx-step--1); font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--vx-primary-dark); margin-bottom: var(--vx-3);
}
.vx-lead { font-size: var(--vx-step-1); color: var(--vx-text-secondary); text-wrap: pretty; }
.vx-head { max-width: 46rem; margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.vx-head--center { margin-inline: auto; text-align: center; }
.vx-head h1, .vx-head h2 { margin-bottom: var(--vx-4); text-wrap: balance; }

.vx-small { font-size: var(--vx-step--1); }
.vx-tiny { font-size: 0.8125rem; line-height: 1.6; color: var(--vx-text-tertiary); }
.vx-center { text-align: center; }
.vx-mt-4 { margin-top: var(--vx-4); }
.vx-mt-6 { margin-top: var(--vx-6); }
.vx-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.vx-link { color: var(--vx-primary-dark); font-weight: 600; text-underline-offset: 3px; }
.vx-link:hover { text-decoration-thickness: 2px; }

/* ------------------------------------------------------------- prose ---- */
/* Legal pages and blog posts: long-form reading, generous measure. */
.vx-prose { max-width: 68ch; }
.vx-prose h2 { font-size: var(--vx-step-2); margin-top: var(--vx-7); margin-bottom: var(--vx-4); scroll-margin-top: calc(var(--vx-header-h) + 1rem); }
.vx-prose h3 { font-size: var(--vx-step-1); margin-top: var(--vx-6); margin-bottom: var(--vx-3); }
.vx-prose p, .vx-prose ul, .vx-prose ol { margin-bottom: var(--vx-4); color: var(--vx-text-secondary); }
.vx-prose ul, .vx-prose ol { padding-inline-start: 1.35rem; }
.vx-prose ul { list-style: disc; }
.vx-prose ol { list-style: decimal; }
.vx-prose li { margin-bottom: var(--vx-2); }
.vx-prose strong { color: var(--vx-text); font-weight: 700; }
.vx-prose a { color: var(--vx-primary-dark); font-weight: 600; }
.vx-prose blockquote {
  margin: var(--vx-5) 0; padding: var(--vx-4) var(--vx-5);
  border-inline-start: 3px solid var(--vx-primary); background: var(--vx-primary-90);
  border-radius: 0 var(--vx-radius) var(--vx-radius) 0; color: var(--vx-text);
}
.vx-prose code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9em;
  background: var(--vx-bg-secondary); padding: 2px 6px; border-radius: 6px;
}
.vx-prose pre { overflow-x: auto; padding: var(--vx-4); background: var(--vx-bg-secondary); border-radius: var(--vx-radius); }
.vx-prose table { width: 100%; border-collapse: collapse; margin-bottom: var(--vx-5); }
.vx-prose th, .vx-prose td { text-align: start; padding: var(--vx-3); border-bottom: 1px solid var(--vx-border); }

/* -------------------------------------------------------------- forms --- */
.vx-field { display: block; margin-bottom: var(--vx-4); }
.vx-field__label { display: block; font-size: var(--vx-step--1); font-weight: 600; margin-bottom: 6px; }
.vx-input, .vx-select, .vx-textarea {
  width: 100%; min-height: 50px; padding: 12px var(--vx-4);
  font: inherit; font-size: var(--vx-step-0); color: var(--vx-text);
  background: var(--vx-surface); border: 1px solid var(--vx-border-strong);
  border-radius: var(--vx-radius); transition: border-color 0.16s ease, box-shadow 0.16s ease;
}
.vx-textarea { min-height: 140px; padding-block: var(--vx-3); resize: vertical; }
.vx-input:focus, .vx-select:focus, .vx-textarea:focus {
  outline: none; border-color: var(--vx-primary);
  box-shadow: 0 0 0 4px var(--vx-primary-90); /* fallback, theme-aware */
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--vx-primary) 18%, transparent);
}
.vx-input::placeholder, .vx-textarea::placeholder { color: var(--vx-text-tertiary); }
.vx-input[aria-invalid="true"] { border-color: var(--vx-danger); }

/* Honeypot: a real field, visually gone, never focusable by a person. */
.vx-hp { position: absolute; left: -9999px; opacity: 0; height: 0; pointer-events: none; }

.vx-form__status { margin-top: var(--vx-4); font-size: var(--vx-step--1); padding: var(--vx-3) var(--vx-4); border-radius: var(--vx-radius); }
.vx-form__status[data-state="ok"] { background: var(--vx-success-90); color: var(--vx-success); font-weight: 600; }
.vx-form__status[data-state="error"] { background: var(--vx-danger-90); color: var(--vx-danger); font-weight: 600; }
.vx-form__privacy { font-size: 0.8125rem; line-height: 1.55; color: var(--vx-text-tertiary); margin-top: var(--vx-3); }

/* Deep-linked sections must not slide under the sticky header. */
[id] { scroll-margin-top: calc(var(--vx-header-h) + 1rem); }

/* layout.css — grids and the structural pieces of the page. */

.vx-grid { display: grid; gap: var(--vx-5); }
@media (min-width: 720px) { .vx-grid--2 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 720px) { .vx-grid--3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .vx-grid--3 { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 640px) { .vx-grid--4 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .vx-grid--4 { grid-template-columns: repeat(4, 1fr); } }

/* --------------------------------------------------------- trust band --- */
.vx-trust { border-block: 1px solid var(--vx-border); background: var(--vx-surface); }
.vx-trust__grid { display: grid; gap: var(--vx-5); padding-block: var(--vx-6); }
@media (min-width: 640px) { .vx-trust__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .vx-trust__grid { grid-template-columns: repeat(4, 1fr); } }
.vx-trust__item { display: flex; gap: var(--vx-3); align-items: flex-start; }
.vx-trust__icon { flex: none; width: 22px; height: 22px; color: var(--vx-success); margin-top: 2px; }
.vx-trust__title { font-weight: 700; font-size: var(--vx-step--1); margin-bottom: 2px; }
.vx-trust__text { font-size: 0.8125rem; line-height: 1.55; color: var(--vx-text-tertiary); }

/* ------------------------------------------------------- feature rows --- */
.vx-feature__kicker {
  /* The feature row is a flex column, so a stretched item would span the full
     width. The pill has to hug its own text. */
  display: inline-block; align-self: flex-start; width: fit-content; font-size: 0.8125rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--vx-primary-dark);
  background: var(--vx-primary-90); padding: 5px 12px; border-radius: 999px; margin-bottom: var(--vx-4);
}
.vx-feature__text { color: var(--vx-text-secondary); font-size: var(--vx-step-1); margin-bottom: var(--vx-5); text-wrap: pretty; }
.vx-feature__points { display: grid; gap: var(--vx-3); }
.vx-feature__points li { display: flex; gap: var(--vx-3); align-items: flex-start; font-size: var(--vx-step-0); }
.vx-feature__check { flex: none; width: 20px; height: 20px; color: var(--vx-success); margin-top: 3px; }

/* ------------------------------------------------------------- footer --- */
.vx-footer { background: var(--vx-surface); border-top: 1px solid var(--vx-border); padding-block: var(--vx-8) var(--vx-6); }
.vx-footer__grid { display: grid; gap: var(--vx-6); }
@media (min-width: 760px) { .vx-footer__grid { grid-template-columns: 1.6fr 1fr 1fr 1fr; } }
.vx-footer__brand p { margin-top: var(--vx-3); color: var(--vx-text-tertiary); font-size: var(--vx-step--1); max-width: 30ch; }
.vx-footer h4 { font-size: var(--vx-step--1); font-weight: 700; margin-bottom: var(--vx-3); }
.vx-footer li { margin-bottom: var(--vx-2); }
.vx-footer a { text-decoration: none; color: var(--vx-text-secondary); font-size: var(--vx-step--1); }
.vx-footer a:hover { color: var(--vx-primary-dark); text-decoration: underline; text-underline-offset: 3px; }
.vx-footer__bottom {
  margin-top: var(--vx-7); padding-top: var(--vx-5); border-top: 1px solid var(--vx-border);
  display: flex; flex-wrap: wrap; gap: var(--vx-4); justify-content: space-between; align-items: center;
  font-size: 0.8125rem; color: var(--vx-text-tertiary);
}

/* -------------------------------------------------- language switcher --- */
.vx-lang { position: relative; }
.vx-lang__btn {
  display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 12px;
  background: transparent; border: 1px solid var(--vx-border); border-radius: var(--vx-radius);
  font: inherit; font-size: var(--vx-step--1); font-weight: 600; color: var(--vx-text-secondary); cursor: pointer;
}
.vx-lang__btn span { text-transform: uppercase; letter-spacing: 0.04em; }
.vx-lang__btn:hover { border-color: var(--vx-border-strong); color: var(--vx-text); }
.vx-lang__menu {
  position: absolute; inset-inline-end: 0; top: calc(100% + 8px); min-width: 168px; z-index: 120;
  background: var(--vx-surface); border: 1px solid var(--vx-border);
  border-radius: var(--vx-radius); box-shadow: var(--vx-shadow-lg); padding: 6px; margin: 0;
}
.vx-lang__menu a {
  display: flex; align-items: center; justify-content: space-between; gap: var(--vx-3);
  padding: 10px 12px; border-radius: 8px; text-decoration: none;
  font-size: var(--vx-step--1); font-weight: 600; color: var(--vx-text-secondary);
}
.vx-lang__menu a:hover { background: var(--vx-bg-secondary); color: var(--vx-text); }
.vx-lang__menu a[aria-current="true"] { color: var(--vx-primary-dark); background: var(--vx-primary-90); }

/* ---------------------------------------------------------- mobile nav -- */
.vx-mobilenav {
  position: fixed; inset: 0; z-index: 150; background: var(--vx-bg);
  padding: var(--vx-6) var(--vx-gutter); display: flex; flex-direction: column; gap: var(--vx-4);
  overflow-y: auto; overscroll-behavior: contain;
  padding-top: max(var(--vx-6), env(safe-area-inset-top));
  padding-bottom: max(var(--vx-6), env(safe-area-inset-bottom));
  padding-inline: max(var(--vx-gutter), env(safe-area-inset-left)) max(var(--vx-gutter), env(safe-area-inset-right));
}
.vx-mobilenav a:not(.vx-btn) { text-decoration: none; font-size: var(--vx-step-2); font-weight: 700; padding-block: var(--vx-3); border-bottom: 1px solid var(--vx-border); }
.vx-mobilenav__close { align-self: flex-end; }
.vx-mobilenav a[aria-current="page"], .vx-mobilenav a[aria-current="true"] {
  color: var(--vx-primary-dark); border-bottom-color: var(--vx-primary);
}
/* Vertical room here, so it always shows. */
.vx-mobilenav .vx-nav__pill {
  display: inline-block; margin-inline-start: 10px; padding: 3px 10px;
  border-radius: 999px; vertical-align: middle;
  background: var(--vx-premium-90); color: var(--vx-premium);
  font-size: 0.75rem; font-weight: 800;
}

/* components.css — cards, the phone mock, the demos, pricing, FAQ. */

/* ---------------------------------------------------------------- card -- */
.vx-card {
  background: var(--vx-surface); border: 1px solid var(--vx-border);
  border-radius: var(--vx-radius-lg); padding: var(--vx-5);
  transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.vx-card h3, .vx-card h4 { margin-bottom: var(--vx-2); }
.vx-card p { color: var(--vx-text-secondary); font-size: var(--vx-step--1); line-height: 1.6; }
/* Icon tile for cards that carry a mark instead of a number. */
.vx-card__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 10px; margin-bottom: var(--vx-4);
  background: var(--vx-primary-90); color: var(--vx-primary-dark);
}
.vx-card__icon--lg { width: 46px; height: 46px; border-radius: 12px; }
.vx-card__glyph { width: 20px; height: 20px; }
.vx-card__icon--lg .vx-card__glyph { width: 24px; height: 24px; }
.vx-founders__glyph { width: 20px; height: 20px; }

.vx-card__num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; border-radius: 10px; margin-bottom: var(--vx-4);
  background: var(--vx-primary-90); color: var(--vx-primary-dark); font-weight: 800; font-size: 0.9rem;
}

/* The "you do not start from scratch" block sits inside the features section
   as an extra, not as a fifth feature - so it is set off by a rule instead of
   being styled like the rows above it. */
.vx-starter { margin-top: var(--vx-7); padding-top: var(--vx-7); border-top: 1px solid var(--vx-border); }

/* Home price teaser. The real card is on /pricing. */
.vx-teaser {
  max-width: 26rem; margin-inline: auto; text-align: center;
  padding: var(--vx-6); border-radius: var(--vx-radius-lg);
  background: var(--vx-surface); border: 1px solid var(--vx-border); box-shadow: var(--vx-shadow);
}
.vx-teaser__amount { font-size: var(--vx-step-4); font-weight: 800; letter-spacing: -0.02em; }
.vx-teaser__per { color: var(--vx-text-secondary); font-size: var(--vx-step-0); margin-inline-start: 6px; }
/* The same green pill as .vx-price__trial on /pricing. The text is mixed toward
   --vx-text because plain --vx-success on --vx-success-90 is 2.7:1 in light;
   the mix gives 4.8:1 light and more in dark. */
.vx-teaser__trial {
  display: inline-flex; align-items: center; margin-top: var(--vx-3); margin-bottom: var(--vx-5);
  padding: 7px 14px; border-radius: 999px; background: var(--vx-success-90);
  color: var(--vx-text); /* fallback: color-mix is Safari 16.2+ / Chrome 111+ */
  color: color-mix(in srgb, var(--vx-success) 65%, var(--vx-text));
  font-size: var(--vx-step--1); font-weight: 700;
}

/* ----------------------------------------------------- problem vignettes -- */
/* Three scenes for the "situation today" cards, built the way the phone demos
   are - markup plus CSS, no raster asset - so they stay crisp at any density,
   follow the theme, and weigh nothing. Purely decorative: the card copy already
   carries the meaning, so each one is aria-hidden. Fixed height keeps the three
   headings on the same line whatever the text length. */
.vx-vig {
  position: relative; height: 104px; margin-bottom: var(--vx-4);
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--vx-radius); border: 1px solid var(--vx-border);
  background: var(--vx-bg-secondary); overflow: hidden;
}

/* 1 - one shared file, two editors, two different numbers. */
.vx-vig__sheet { display: grid; grid-template-columns: repeat(3, 44px); gap: 5px; }
.vx-vig__sheet > span:not(.vx-vig__conflict) {
  height: 18px; border-radius: 4px; background: var(--vx-border-strong); opacity: 0.55;
}
.vx-vig__conflict {
  position: relative; height: 18px; border-radius: 4px; display: grid; place-items: center;
  background: var(--vx-surface); border: 1px solid var(--vx-warning);
}
.vx-vig__conflict i {
  position: absolute; font-style: normal; font-size: 0.6875rem; font-weight: 800;
  color: var(--vx-warning); font-variant-numeric: tabular-nums;
}
.vx-vig__conflict i:nth-child(2) { opacity: 0; }

/* 2 - the shelf empties, the alert arrives after the fact. */
.vx-vig__stock { display: grid; gap: 8px; justify-items: center; }
.vx-vig__boxes { display: flex; gap: 5px; align-items: flex-end; height: 28px; }
.vx-vig__boxes i { width: 16px; height: 26px; border-radius: 3px; background: var(--vx-primary); opacity: 0.8; }
.vx-vig__plank { display: block; width: 122px; height: 5px; border-radius: 3px; background: var(--vx-border-strong); }
.vx-vig__late {
  position: absolute; top: 10px; inset-inline-end: 10px; width: 28px; height: 28px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--vx-danger-90); color: var(--vx-danger);
}
.vx-vig__late svg { width: 15px; height: 15px; }

/* 3 - the same figure carried across three documents, wrong on the third. */
.vx-vig__docs { display: flex; align-items: center; gap: 6px; }
.vx-vig__doc {
  min-width: 52px; padding: 7px 6px; border-radius: 6px; text-align: center;
  background: var(--vx-surface); border: 1px solid var(--vx-border);
  font-size: 0.6875rem; font-weight: 800; color: var(--vx-text-secondary);
  font-variant-numeric: tabular-nums;
}
.vx-vig__doc--bad { background: var(--vx-danger-90); border-color: var(--vx-danger); color: var(--vx-danger); }
.vx-vig__next { width: 12px; height: 12px; flex: none; color: var(--vx-text-tertiary); }

/* --------------------------------------------------------------- bento -- */
/* Spans always add up, so no cell is ever empty: 2 columns (wide / 2+2 / wide)
   up to 999px, 4 columns above (wide+1+1 / 1+1+wide). */
.vx-bento { display: grid; gap: var(--vx-3); grid-template-columns: repeat(2, minmax(0, 1fr)); }
.vx-bento > :nth-child(1), .vx-bento > :nth-child(6) { grid-column: span 2; }
@media (max-width: 359px) {
  .vx-bento { grid-template-columns: minmax(0, 1fr); }
  .vx-bento > :nth-child(n) { grid-column: auto; }
}
@media (min-width: 1000px) { .vx-bento { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--vx-4); } }
.vx-tile {
  position: relative; overflow: hidden; display: flex; flex-direction: column; gap: var(--vx-3);
  background: var(--vx-surface); border: 1px solid var(--vx-border);
  border-radius: var(--vx-radius-lg); padding: var(--vx-4);
  transition: transform 0.2s ease, border-color 0.2s ease;
}
@media (min-width: 1000px) { .vx-tile { padding: var(--vx-5); } }
/* Spotlight follows the pointer. --mx/--my are set by one delegated listener. */
.vx-tile::before {
  content: ""; position: absolute; inset: 0; opacity: 0; pointer-events: none;
  transition: opacity 0.25s ease;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%),
              color-mix(in srgb, var(--vx-primary) 14%, transparent), transparent 62%);
}
.vx-tile:hover { transform: translateY(-2px); border-color: var(--vx-primary-light); }
.vx-tile:hover::before { opacity: 1; }
.vx-tile__title { font-size: var(--vx-step--1); font-weight: 700; line-height: 1.3; text-wrap: balance; }
@media (min-width: 1000px) { .vx-tile__title { font-size: var(--vx-step-0); } }

/* The picture reuses the problem-vignette frame, shorter, and is drawn above the
   title while staying after it in the DOM (title first for screen readers). */
.vx-tile .vx-vig { order: -1; height: 88px; margin-bottom: 0; gap: 8px; padding-inline: 10px; }
@media (min-width: 1000px) { .vx-tile .vx-vig { height: 112px; } }

/* Tile 1 - two warehouses and the transfer between them. */
.vx-wh {
  display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 10px;
  background: var(--vx-surface); border: 1px solid var(--vx-border);
  font-size: 0.8125rem; font-weight: 600; color: var(--vx-text-secondary); white-space: nowrap;
}
.vx-wh svg { flex: none; width: 15px; height: 15px; color: var(--vx-primary); }
.vx-wh b, .vx-ledger b { font-weight: 800; color: var(--vx-text); font-variant-numeric: tabular-nums; }
.vx-vig__swap { flex: none; width: 18px; height: 18px; color: var(--vx-text-tertiary); }

/* Tile 2 - a signed ledger: in, out, transfer. The label may shorten with an
   ellipsis, the number never does: "Transferencia" is the widest label and a
   360px phone gives the frame ~114px. */
.vx-ledger { display: grid; grid-template-columns: minmax(0, 1fr); gap: 4px; width: min(100%, 12rem); min-width: 0; }
.vx-ledger > span {
  display: flex; align-items: center; gap: 6px; padding: 3px 7px; border-radius: 7px;
  background: var(--vx-surface); border: 1px solid var(--vx-border);
  font-size: 0.75rem; font-weight: 600; line-height: 1.4; color: var(--vx-text-secondary);
}
.vx-ledger i { flex: none; width: 7px; height: 7px; border-radius: 50%; }
.vx-ledger__k { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vx-ledger b { flex: none; margin-inline-start: auto; }
/* The 88px frame below 1000px: three rows with air above and below. */
@media (max-width: 999px) {
  .vx-ledger { gap: 3px; }
  .vx-ledger > span { padding: 2px 6px; font-size: 0.6875rem; }
}
@media (max-width: 419px) {
  .vx-tile { padding: var(--vx-3); }
  .vx-tile .vx-vig { padding-inline: 6px; }
  .vx-ledger > span { gap: 5px; }
}
/* One column below 360px: the two warehouse pills and the arrow need ~265px,
   so they wrap onto two lines instead of being clipped by the frame. */
@media (max-width: 359px) {
  .vx-tile .vx-vig { flex-wrap: wrap; align-content: center; row-gap: 6px; height: auto; min-height: 88px; padding-block: 10px; }
  .vx-wh { padding: 5px 8px; gap: 5px; }
}
.vx-ledger [data-dir="in"] i { background: var(--vx-success); }
.vx-ledger [data-dir="out"] i { background: var(--vx-danger); }
.vx-ledger [data-dir="move"] i { background: var(--vx-primary); }

/* Tile 3 - stacked photos with the gallery counter. */
.vx-pics { position: relative; display: grid; grid-template-columns: 56px 26px; grid-template-rows: 26px 26px; gap: 4px; }
.vx-pics i { display: grid; place-items: center; border-radius: 7px; background: var(--vx-primary-90); color: var(--vx-primary); }
.vx-pics i:first-child { grid-row: span 2; border-radius: 10px; }
.vx-pics svg { width: 13px; height: 13px; }
.vx-pics i:first-child svg { width: 20px; height: 20px; }
/* The counter rides on the main photo, as in the app's gallery. */
.vx-pics b {
  position: absolute; left: 28px; bottom: -9px; transform: translateX(-50%); padding: 1px 6px; border-radius: 999px;
  background: var(--vx-surface); border: 1px solid var(--vx-border);
  font-size: 0.6875rem; font-weight: 700; color: var(--vx-text-secondary);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* Tile 4 - the three report cards, as bars. */
.vx-bars { display: grid; gap: 9px; width: min(100%, 11rem); }
.vx-bars i { position: relative; display: block; height: 8px; border-radius: 999px; background: var(--vx-border); overflow: hidden; }
.vx-bars i::before {
  content: ""; position: absolute; inset-block: 0; inset-inline-start: 0;
  width: var(--w); border-radius: inherit; background: var(--c);
}

/* Tile 5 - the supplier record's lead-time row. */
.vx-inforow { display: grid; gap: 2px; padding: 8px 12px; border-radius: 10px; background: var(--vx-surface); border: 1px solid var(--vx-border); }
.vx-inforow small { font-size: 0.6875rem; font-weight: 600; line-height: 1.3; color: var(--vx-text-tertiary); }
.vx-inforow b { display: flex; align-items: center; gap: 6px; font-size: 0.9375rem; font-weight: 800; color: var(--vx-text); }
.vx-inforow svg { flex: none; width: 14px; height: 14px; color: var(--vx-primary); }

/* ------------------------------------------------------------- chips ---- */
/* Short labels in a pill. Shared by the bento (tile 6) and the help card. */
.vx-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.vx-vig.vx-chips { align-content: center; gap: 6px; }
.vx-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 999px;
  background: var(--vx-surface); border: 1px solid var(--vx-border);
  font-size: var(--vx-step--1); font-weight: 600; line-height: 1.3; color: var(--vx-text-secondary);
}
.vx-chip svg { flex: none; width: 15px; height: 15px; color: var(--vx-primary); }
.vx-chip--ok svg { color: var(--vx-success); }
.vx-chip--sm { padding: 4px 10px; font-size: 0.75rem; }

/* The two wide bento tiles (1 and 6) get bigger art from 560px, so they do not
   read as empty panels next to the fuller narrow tiles. The chips wrap. */
@media (min-width: 560px) {
  .vx-bento > :nth-child(1) .vx-wh { padding: 9px 14px; gap: 8px; font-size: 0.9375rem; }
  .vx-bento > :nth-child(1) .vx-wh svg { width: 18px; height: 18px; }
  .vx-bento > :nth-child(1) .vx-vig__swap { width: 22px; height: 22px; }
  .vx-bento > :nth-child(6) .vx-chip--sm { padding: 6px 12px; font-size: 0.875rem; }
  .vx-bento > :nth-child(6) .vx-chip svg { width: 17px; height: 17px; }
}

/* --------------------------------------------------------------- phone -- */
/* The mock shows the app, and the app ships light mode only
   (MOBILE/src/shared/hooks/useThemeColor.ts forces "light"). So the device
   pins the app palette here rather than inheriting the site theme - otherwise
   a visitor in dark mode sees a phone that does not match what they install. */
.vx-phone {
  --app-primary: #6366F1;
  --app-primary-dark: #4F46E5;
  --app-primary-90: #ECF0FC;
  --app-surface: #FFFFFF;
  --app-bg: #F9FAFB;
  /* Verbatim from MOBILE/src/shared/constants/colors.ts - the mock cannot
     drift from the app one hex at a time. */
  --app-border: #E5E7EB;
  --app-bg-secondary: #F3F4F6;
  --app-text: #1A1D29;
  --app-muted: #6B7280;
  --app-success: #12A79A;
  --app-warning: #D97706;
  --app-danger: #EF4444;
  --app-danger-90: #FFEAEA;

  position: relative; width: min(300px, 78vw); margin-inline: auto;
  aspect-ratio: 300 / 620; border-radius: 42px; padding: 11px;
  background: linear-gradient(160deg, #2A3040, #14161F);
  box-shadow: var(--vx-shadow-xl), 0 0 0 1px rgba(255, 255, 255, 0.08) inset;
}
.vx-phone::after {
  content: ""; position: absolute; top: 22px; left: 50%; transform: translateX(-50%);
  width: 86px; height: 22px; border-radius: 999px; background: #14161F; z-index: 3;
}
.vx-phone__screen {
  position: relative; width: 100%; height: 100%; overflow: hidden;
  border-radius: 32px; background: var(--app-bg);
}

.vx-screen { position: absolute; inset: 0; display: flex; flex-direction: column; background: var(--app-bg); }
.vx-screen__bar {
  flex: none; padding: 42px 16px 12px; color: #fff;
  background: linear-gradient(135deg, var(--app-primary-dark), var(--app-primary));
}
.vx-screen__title { font-size: 15px; font-weight: 700; line-height: 1.3; }
.vx-screen__sub { font-size: 11px; opacity: 0.82; margin-top: 2px; }
.vx-screen__body { flex: 1; padding: 12px; display: flex; flex-direction: column; gap: 8px; overflow: hidden; }

/* Rebuilt from the app's InventoryListItem. Every number below is the real one
   scaled by 0.71 - a 254px row in this mock against a 358px card on a 390px
   phone - so the proportions are the product's, not an invention. */
.vx-row {
  display: flex; align-items: stretch; min-height: 72px; overflow: hidden;
  background: var(--app-surface); border: 1px solid var(--app-border); border-radius: 13px;
}
/* Full-bleed on the leading edge, exactly as in the app: the image sets the
   card height rather than the content padding. */
.vx-row__img {
  flex: none; width: 76px; align-self: stretch; display: grid; place-items: center;
  background-color: var(--app-bg-secondary);
  border-start-start-radius: 12px; border-end-start-radius: 12px;
}
.vx-row__img svg { width: 20px; height: 20px; color: #C4C4C6; }
.vx-row__main { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 3px; padding: 9px 10px; }
/* The reference sits ABOVE the designation, in primary - the single biggest
   thing the old mock had backwards. */
.vx-row__ref { font-size: 8.5px; font-weight: 700; letter-spacing: 0.7px; color: var(--app-primary); }
.vx-row__name { font-size: 11.5px; font-weight: 700; color: var(--app-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* In the hero list the designation gets up to two lines, as in the app
   (numberOfLines={2}), instead of being cut after a word or two. `pretty`
   keeps a short unit such as "kg" from ending up alone on the second line. */
.vx-hero .vx-row__name { white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; line-height: 1.25; text-wrap: pretty; }
.vx-row__stock { flex: none; display: flex; flex-direction: column; align-items: flex-end; justify-content: center; gap: 1px; padding: 9px 10px 9px 0; }
.vx-row__qty { font-size: 12px; font-weight: 800; letter-spacing: 1px; color: var(--app-text); }
.vx-row__status { font-size: 8.5px; font-weight: 600; color: var(--app-success); }

/* The app's own rules, not a palette chosen here: in stock leaves the quantity
   in the default text colour because only a state needing attention is worth
   tinting, and a stock-out tints the whole row. */
.vx-row[data-stock="low"] .vx-row__qty,
.vx-row[data-stock="low"] .vx-row__status { color: var(--app-warning); }
.vx-row[data-stock="out"] { background: var(--app-danger-90); border-color: var(--app-danger); }
.vx-row[data-stock="out"] .vx-row__img { background-color: var(--app-danger-90); }
.vx-row[data-stock="out"] .vx-row__img svg { color: var(--app-danger); }
.vx-row[data-stock="out"] .vx-row__ref,
.vx-row[data-stock="out"] .vx-row__qty,
.vx-row[data-stock="out"] .vx-row__status { color: var(--app-danger); }

/* ------------------------------------------------------- demo: scan ----- */
.vx-scan { position: absolute; inset: 0; background: #10121A; display: grid; place-items: center; }
.vx-scan__frame { position: relative; width: 62%; aspect-ratio: 1.5; }
.vx-scan__frame span { position: absolute; width: 26px; height: 26px; border: 3px solid #fff; }
.vx-scan__frame span:nth-child(1) { top: 0; inset-inline-start: 0; border-inline-end: 0; border-bottom: 0; border-start-start-radius: 8px; }
.vx-scan__frame span:nth-child(2) { top: 0; inset-inline-end: 0; border-inline-start: 0; border-bottom: 0; border-start-end-radius: 8px; }
.vx-scan__frame span:nth-child(3) { bottom: 0; inset-inline-start: 0; border-inline-end: 0; border-top: 0; border-end-start-radius: 8px; }
.vx-scan__frame span:nth-child(4) { bottom: 0; inset-inline-end: 0; border-inline-start: 0; border-top: 0; border-end-end-radius: 8px; }
.vx-scan__bars { position: absolute; inset: 22% 14%; display: flex; gap: 3px; align-items: stretch; }
.vx-scan__bars i { display: block; background: #fff; border-radius: 1px; }
.vx-scan__laser {
  position: absolute; inset-inline: 6%; height: 2px; top: 10%;
  background: linear-gradient(90deg, transparent, var(--vx-teal-bright), transparent);
  box-shadow: 0 0 12px var(--vx-teal-bright);
}
.vx-scan__caption {
  position: absolute; inset-inline: 16px; bottom: 22px; text-align: center;
  color: #fff; font-size: 11px; font-weight: 600; opacity: 0.85;
}

/* ------------------------------------------------------- demo: voice ---- */
.vx-voice { padding: 16px; display: flex; flex-direction: column; gap: 14px; }
.vx-voice__wave { display: flex; align-items: center; justify-content: center; gap: 4px; height: 64px; }
.vx-voice__wave i { width: 4px; border-radius: 2px; background: var(--app-primary); height: 12px; }
.vx-voice__field {
  border: 1px solid var(--app-border); border-radius: 10px; padding: 10px 12px;
  background: var(--app-surface); font-size: 12px; color: var(--app-text); min-height: 62px;
}
.vx-voice__typed { display: inline-block; overflow: hidden; white-space: nowrap; vertical-align: bottom; }
.vx-voice__mic {
  align-self: center; width: 52px; height: 52px; border-radius: 50%;
  background: var(--app-primary); display: grid; place-items: center; color: #fff;
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--app-primary) 45%, transparent);
}

/* ------------------------------------------------------- demo: alert ---- */
.vx-alert { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.vx-gauge { position: relative; height: 10px; border-radius: 999px; background: var(--app-border); overflow: hidden; }
.vx-gauge__fill { position: absolute; inset-block: 0; inset-inline-start: 0; width: 78%; border-radius: 999px; background: var(--vx-success); }
.vx-gauge__threshold { position: absolute; inset-block: -4px; inset-inline-start: 28%; width: 2px; background: var(--vx-warning); }
.vx-alert__labels { display: flex; justify-content: space-between; font-size: 10px; color: var(--app-muted); }
.vx-push {
  display: flex; gap: 10px; align-items: flex-start; padding: 10px;
  background: var(--app-surface); border: 1px solid var(--app-border); border-radius: 12px;
  box-shadow: 0 8px 20px -8px rgba(26, 29, 41, 0.25); opacity: 0;
}
.vx-push__icon { flex: none; width: 26px; height: 26px; border-radius: 7px; background: var(--vx-warning-90); color: var(--vx-warning); display: grid; place-items: center; }
.vx-push__title { display: block; font-size: 11px; font-weight: 700; color: var(--app-text); }
.vx-push__text { display: block; font-size: 10px; color: var(--app-muted); line-height: 1.45; margin-top: 1px; }

/* -------------------------------------------------------- demo: flow ---- */
.vx-flow { padding: 16px; display: flex; flex-direction: column; gap: 8px; }
.vx-flow__step {
  display: flex; align-items: center; gap: 9px; padding: 10px;
  background: var(--app-surface); border: 1px solid var(--app-border); border-radius: 10px;
}
.vx-flow__step[data-state="done"] { border-color: color-mix(in srgb, var(--vx-success) 40%, transparent); background: #F4FCFA; }
.vx-flow__dot { flex: none; width: 22px; height: 22px; border-radius: 50%; background: var(--app-primary-90); color: var(--app-primary-dark); display: grid; place-items: center; font-size: 10px; font-weight: 800; }
.vx-flow__step[data-state="done"] .vx-flow__dot { background: var(--vx-success); color: #fff; }
.vx-flow__label { font-size: 11.5px; font-weight: 600; color: var(--app-text); }
.vx-flow__amount { margin-inline-start: auto; font-size: 11px; font-weight: 700; color: var(--app-muted); }

/* ------------------------------------------------ showcase: home screen -- */
/* One real screen, three pins, three captions (MOBILE home-dashboard.tsx).
   DOM order is head, phone, captions: the order a phone reads it. */
.vx-showcase { display: grid; gap: var(--vx-6); grid-template-areas: "head" "media" "list"; }
.vx-showcase__head { grid-area: head; margin-bottom: 0; }
.vx-showcase__media { grid-area: media; position: relative; display: flex; justify-content: center; }
/* The screen goes on below, the pitch does not. #dashboard sits on --vx-bg, so
   the fade follows the theme; an alpha mask would also clip the device shadow.
   It reaches past the media box (a gutter sideways, 48px down) so the device
   shadow ends under it instead of on a hard edge, and it is solid 24px above
   the bottom of the device, so the rounded bezel never shows. The captions
   sit above it. */
.vx-showcase__media::after {
  content: ""; position: absolute; pointer-events: none;
  inset-inline: calc(-1 * var(--vx-gutter)); bottom: -48px; height: 144px;
  background: linear-gradient(to bottom, transparent, var(--vx-bg) 72px);
}
.vx-showcase__list { grid-area: list; position: relative; z-index: 1; width: 100%; max-width: 28rem; margin-inline: auto; }
@media (min-width: 900px) {
  .vx-showcase {
    grid-template-columns: minmax(300px, 0.8fr) 1fr; grid-template-areas: "media head" "media list";
    column-gap: clamp(3rem, 7vw, 6rem); row-gap: var(--vx-6);
  }
  .vx-showcase__head { align-self: end; }
  .vx-showcase__media { align-self: center; }
  /* 32rem keeps each caption on one line in FR and EN. */
  .vx-showcase__list { align-self: start; margin-inline: 0; max-width: 32rem; }
}
/* Stacked on a tablet, the heading shares the centre axis of the phone and
   the captions instead of zig-zagging against them. */
@media (min-width: 560px) and (max-width: 899px) {
  .vx-showcase__head { text-align: center; margin-inline: auto; }
}
.vx-callouts { display: grid; gap: var(--vx-3); counter-reset: callout; }
.vx-callout {
  display: flex; align-items: center; gap: var(--vx-4);
  padding: var(--vx-3) var(--vx-4); border-radius: var(--vx-radius-lg);
  background: var(--vx-surface); border: 1px solid var(--vx-border); box-shadow: var(--vx-shadow-sm);
  font-size: var(--vx-step-0); font-weight: 700; line-height: 1.3; color: var(--vx-text); text-wrap: balance;
}
/* 19px/800 counts as large text: white on primary-dark is 6.3:1 light, 4.5:1 dark. */
.vx-callout::before {
  counter-increment: callout; content: counter(callout); flex: none;
  display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%;
  background: var(--vx-primary-dark); color: #fff; font-size: 1.1875rem; font-weight: 800;
}
@media (min-width: 900px) { .vx-callout { padding: var(--vx-4) var(--vx-5); font-size: var(--vx-step-1); } }

/* A still, not in the sticky sequence: its own width and a height set by its
   content. (0,2,0) beats the 212px narrow-screen rule, which would crush it. */
.vx-phone.vx-phone--home {
  width: min(300px, 100%); aspect-ratio: auto;
  /* A tighter shadow than the sticky phone, so all of it ends under the fade. */
  box-shadow: var(--vx-shadow-lg), 0 0 0 1px rgba(255, 255, 255, 0.08) inset;
}
.vx-phone--home .vx-phone__screen { height: auto; }
.vx-screen.vx-screen--still { position: relative; padding-bottom: 24px; }
.vx-screen__bar--nav { display: flex; align-items: center; gap: 10px; }
.vx-screen__icon { flex: none; width: 16px; height: 16px; }
/* App sizes scaled by 0.72, like .vx-row. App palette only (the app ships light). */
.vx-home { gap: 7px; padding: 12px 11px; overflow: visible; }
.vx-home__card { position: relative; background: var(--app-surface); border: 1px solid var(--app-border); border-radius: 13px; }
.vx-home__shortcuts { display: flex; padding: 10px 3px; }
/* Each column starts from its own label width, so "Configuración" gets the
   room "Pedido" does not need. The ellipsis stays as a safety net. */
.vx-home__shortcut {
  flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 5px;
  font-size: 8.5px; font-weight: 500; line-height: 1.2; color: var(--app-text);
}
.vx-home__shortcut + .vx-home__shortcut { border-inline-start: 1px solid var(--app-border); }
.vx-home__shortcut svg { width: 17px; height: 17px; color: var(--app-primary); }
.vx-home__shortcut span { max-width: 100%; padding-inline: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vx-home__label { margin: 5px 0 -2px; font-size: 8px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--app-muted); }
.vx-home__alert {
  position: relative; display: flex; align-items: center; gap: 7px; padding: 10px 9px 10px 13px;
  font-size: 9.5px; font-weight: 500; line-height: 1.35; color: var(--app-text);
}
.vx-home__alert + .vx-home__alert { border-top: 1px solid var(--app-border); }
.vx-home__alert::before {
  content: ""; position: absolute; inset-inline-start: 0; top: 50%; width: 3px; height: 20px; margin-top: -10px;
  border-radius: 0 3px 3px 0; background: var(--app-danger);
}
.vx-home__alert span { flex: 1; min-width: 0; }
.vx-home__alert b { font-size: 12.5px; font-weight: 700; color: var(--app-danger); }
.vx-home__alert svg { flex: none; width: 11px; height: 11px; color: var(--app-muted); }
.vx-home__total { display: flex; align-items: center; justify-content: space-between; padding: 10px 11px; border-bottom: 1px solid var(--app-border); }
.vx-home__total > b { font-size: 21px; font-weight: 800; line-height: 1.1; color: var(--app-text); }
.vx-home__health { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.25; color: var(--app-success); }
.vx-home__health b { font-size: 11px; font-weight: 700; }
.vx-home__health small { font-size: 8px; font-weight: 600; }
.vx-home__kpi { display: flex; align-items: center; gap: 8px; padding: 7px 11px; }
.vx-home__kpi + .vx-home__kpi { margin-inline: 11px; padding-inline: 0; border-top: 1px solid var(--app-border); }
.vx-home__kpi b { min-width: 28px; font-size: 14px; font-weight: 700; }
.vx-home__kpi[data-tone="ok"] b { color: var(--app-success); }
.vx-home__kpi[data-tone="low"] b { color: var(--app-warning); }
.vx-home__kpi[data-tone="out"] b { color: var(--app-danger); }
.vx-home__kpi span {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px;
  font-size: 10.5px; font-weight: 700; line-height: 1.3; color: var(--app-text);
}
/* The helper line under each state, as stock-row.tsx prints it. */
.vx-home__kpi small {
  font-size: 8.5px; font-weight: 400; color: var(--app-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.vx-home__kpi i { font-style: normal; font-size: 9.5px; font-weight: 600; color: var(--app-primary); }
/* "Last actions": the section title with its "View all" pill and unread
   count, then notification-list-item.tsx cards. It sits under the fade, so the
   screen dissolves into content, not blank space. */
.vx-home__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 3px; }
.vx-home__head .vx-home__label { margin: 0; }
.vx-home__all {
  display: inline-flex; align-items: center; gap: 4px; padding: 3px 7px; border-radius: 9px;
  background: var(--app-primary-90); color: var(--app-primary);
  font-size: 9px; font-weight: 500; text-decoration: underline;
}
.vx-home__all em {
  display: grid; place-items: center; min-width: 13px; height: 13px; padding-inline: 3px; border-radius: 999px;
  background: var(--app-primary); color: #fff; font-size: 8px; font-weight: 700; font-style: normal; text-decoration: none;
}
.vx-home__notes { position: relative; display: grid; gap: 7px; }
.vx-home__note { display: flex; align-items: center; gap: 9px; padding: 10px; border-radius: 12px; }
.vx-home__noteicon {
  flex: none; align-self: flex-start; display: grid; place-items: center; width: 29px; height: 29px; border-radius: 50%;
  background: color-mix(in srgb, var(--app-primary) 10%, transparent); color: var(--app-primary);
}
.vx-home__noteicon svg { width: 13px; height: 13px; }
.vx-home__done { display: none; }
.vx-home__note[data-read="true"] .vx-home__noteicon { background: color-mix(in srgb, var(--app-success) 10%, transparent); color: var(--app-success); }
.vx-home__note[data-read="true"] .vx-home__bell { display: none; }
.vx-home__note[data-read="true"] .vx-home__done { display: block; }
.vx-home__notebody { flex: 1; min-width: 0; display: grid; gap: 3px; font-size: 10px; line-height: 1.4; color: var(--app-muted); }
.vx-home__notebody small { font-size: 8.5px; font-weight: 600; }
.vx-home__notebody b { font-weight: 700; color: var(--app-text); }
.vx-home__dot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--app-primary); }
.vx-home__note[data-read="true"] .vx-home__dot { display: none; }
/* Numbered pins tie each part of the screen to its caption. */
.vx-pin {
  position: absolute; top: -7px; inset-inline-start: -6px; z-index: 1;
  display: grid; place-items: center; width: 18px; height: 18px; border-radius: 50%;
  background: var(--app-primary-dark); color: #fff;
  font-size: 10px; font-weight: 800; font-style: normal; line-height: 1;
  box-shadow: 0 0 0 2px var(--app-surface);
}

/* ---------------------------------------------------------- migration --- */
.vx-map { display: grid; grid-template-columns: 1fr auto 1fr; gap: var(--vx-3); align-items: center; }
.vx-map__head { font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--vx-text-tertiary); }
.vx-map__cell {
  padding: 10px 12px; border-radius: 10px; font-size: var(--vx-step--1); font-weight: 600;
  background: var(--vx-surface); border: 1px solid var(--vx-border);
}
.vx-map__cell--from { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--vx-text-secondary); }
.vx-map__cell--to { border-color: color-mix(in srgb, var(--vx-primary) 40%, transparent); background: var(--vx-primary-90); color: var(--vx-primary-dark); }
.vx-map__arrow { width: 22px; height: 22px; color: var(--vx-primary); }

/* ---------------------------------------------------------- founders ---- */
.vx-founders { border-radius: var(--vx-radius-xl); padding: clamp(2rem, 5vw, 3.5rem); background: var(--vx-surface); border: 1px solid var(--vx-border); box-shadow: var(--vx-shadow); }
.vx-founders__badge {
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 14px; border-radius: 999px;
  background: var(--vx-premium-90); color: var(--vx-premium);
  /* Plain gold on its tint is 3.5:1 in light; mixed toward the ink it is 5.5:1,
     and in dark the mix only lightens it. The line above is the fallback. */
  color: color-mix(in srgb, var(--vx-premium) 70%, var(--vx-text));
  border: 1px solid color-mix(in srgb, var(--vx-premium) 35%, transparent);
  font-size: var(--vx-step--1); font-weight: 700; margin-bottom: var(--vx-4);
}
/* Guarantee, not gift. A seal and a gold rule - no ribbon, no confetti: the
   visitor pays full price on day one, what is promised is that it never moves.
   Discount-shop styling here would contradict the honesty the rest of the page
   is built on, starting with the note right underneath this block. */
.vx-founders--seal {
  background: linear-gradient(160deg, var(--vx-premium-90), var(--vx-surface) 62%);
  border-color: #E2CFA6; /* fallback: color-mix is Safari 16.2+ / Chrome 111+ */
  border-color: color-mix(in srgb, var(--vx-premium) 34%, transparent);
}
.vx-founders__seal {
  display: inline-flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; border-radius: 50%; margin-bottom: var(--vx-4);
  background: var(--vx-surface); color: var(--vx-premium);
  border: 2px solid #E2CFA6;
  border: 2px solid color-mix(in srgb, var(--vx-premium) 42%, transparent);
  box-shadow: var(--vx-shadow-sm);
}
.vx-founders__seal svg { width: 28px; height: 28px; }
/* Only ever rendered when config.json carries a real seatsTaken. */
.vx-founders__seats {
  margin-top: var(--vx-5); padding-top: var(--vx-4);
  border-top: 1px solid #E7D7B5;
  border-top: 1px solid color-mix(in srgb, var(--vx-premium) 26%, transparent);
  font-weight: 700; color: var(--vx-premium);
}

.vx-founders__icon { flex: none; width: 40px; height: 40px; border-radius: 12px; background: var(--vx-primary-90); color: var(--vx-primary-dark); display: grid; place-items: center; }
.vx-honesty {
  margin-top: var(--vx-6); padding: var(--vx-4) var(--vx-5); border-radius: var(--vx-radius);
  background: var(--vx-bg-secondary); border-inline-start: 3px solid var(--vx-primary);
  color: var(--vx-text-secondary); font-size: var(--vx-step--1);
}

/* ------------------------------------------------------------- offer ---- */
/* The lifetime price is read next to the price it locks: the home price
   teaser and the founders card share one block. */
/* Side by side only from 1100px: below that the perks would wrap to three
   lines each. There both cards share their top and bottom edges, and the price
   stays centred inside its card. From 640 to 1099px the price card is a slim
   band (price, pill, button on one line) above the founders card, so it does
   not become a wide, mostly empty slab. */
.vx-offer { display: grid; gap: var(--vx-5); max-width: 68rem; margin-inline: auto; }
@media (min-width: 1100px) { .vx-offer { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: stretch; } }
.vx-offer .vx-teaser { max-width: none; margin: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; padding-block: var(--vx-6); }
@media (min-width: 1100px) { .vx-offer .vx-teaser { padding-block: var(--vx-7); } }
@media (min-width: 640px) and (max-width: 1099px) {
  .vx-offer .vx-teaser { flex-direction: row; flex-wrap: wrap; column-gap: var(--vx-5); row-gap: var(--vx-3); padding: var(--vx-5) var(--vx-6); }
  .vx-offer .vx-teaser__trial { margin: 0; }
}
.vx-offer .vx-founders { padding: clamp(1.5rem, 4vw, 2.5rem); }
.vx-founders__top { display: flex; align-items: center; gap: var(--vx-3); }
.vx-founders__top .vx-founders__seal { width: 48px; height: 48px; margin-bottom: 0; }
.vx-founders__top .vx-founders__seal svg { width: 24px; height: 24px; }
.vx-founders__top .vx-founders__badge { margin-bottom: 0; }
.vx-founders__title { margin-top: var(--vx-4); font-size: var(--vx-step-2); letter-spacing: -0.02em; text-wrap: balance; }
.vx-perks { display: grid; gap: var(--vx-4); margin-top: var(--vx-5); }
@media (min-width: 560px) { .vx-perks { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.vx-perk { display: flex; gap: var(--vx-3); align-items: flex-start; }
.vx-perk h4 { font-size: var(--vx-step-0); line-height: 1.3; text-wrap: balance; }
.vx-perk__sub { margin-top: 2px; font-size: var(--vx-step--1); line-height: 1.45; color: var(--vx-text-secondary); text-wrap: pretty; }
/* The one perk about the price turns gold, to tie it to the seal. */
.vx-perk--lock .vx-founders__icon { background: var(--vx-premium-90); color: var(--vx-premium); }
/* Gold like the rest of the card: an indigo rule here was the one off-palette mark. */
.vx-offer .vx-honesty { margin-top: var(--vx-5); border-inline-start-color: var(--vx-premium); }

/* -------------------------------------------------------------- help ---- */
/* Human support, as the last word of the FAQ: two bubbles, a title, three
   chips and the address. Same width as the FAQ column. From 760px it is two
   columns (bubbles | copy) with the address under the chips, so the copy
   column gets ~520px: the title on one line, the chips on one row. */
.vx-help {
  display: grid; gap: var(--vx-5); justify-items: center; text-align: center;
  max-width: 46rem; margin: var(--vx-7) auto 0; padding: var(--vx-5);
  border-radius: var(--vx-radius-lg); background: var(--vx-surface);
  border: 1px solid var(--vx-border); box-shadow: var(--vx-shadow-sm);
}
.vx-help__title { font-size: var(--vx-step-1); margin-bottom: var(--vx-3); text-wrap: balance; }
.vx-help .vx-chips { justify-content: center; }
.vx-help .vx-chip { background: var(--vx-surface-secondary); }
.vx-help__cta { display: grid; gap: var(--vx-2); justify-items: center; width: 100%; }
.vx-help__cta .vx-btn { width: 100%; }
.vx-help__art { display: grid; gap: 6px; width: 92px; }
.vx-help__art i { display: grid; gap: 5px; padding: 9px 10px; border-radius: 12px; border: 1px solid var(--vx-border); }
.vx-help__art i:first-child { background: var(--vx-bg-secondary); border-end-start-radius: 4px; margin-inline-end: 14px; }
.vx-help__art i:last-child { background: var(--vx-primary-90); border-end-end-radius: 4px; margin-inline-start: 14px; }
.vx-help__art b { display: block; height: 5px; border-radius: 3px; background: var(--vx-border-strong); }
.vx-help__art i:first-child b:last-child { width: 65%; }
.vx-help__art i:last-child b { background: var(--vx-primary-light); }
@media (min-width: 760px) {
  .vx-help {
    grid-template-columns: auto minmax(0, 1fr); column-gap: var(--vx-6); row-gap: var(--vx-4);
    justify-items: start; align-items: center; text-align: start; padding: var(--vx-5) var(--vx-6);
  }
  .vx-help__art { grid-row: span 2; }
  .vx-help .vx-chips { justify-content: flex-start; }
  .vx-help__cta { grid-column: 2; display: flex; flex-wrap: wrap; align-items: center; gap: var(--vx-4); width: auto; }
  .vx-help__cta .vx-btn { width: auto; }
}
/* Dark: the first bubble (--vx-bg-secondary) is 1.07:1 on the card, and the
   link (--vx-primary-dark) is 3.3:1 on --vx-surface. Lift both. The Apple tile
   (--vx-ink) is 1.4:1 on the card, so it gets the same hairline as .vx-store.
   Scoped to the help card and the iOS bar so nothing above #dashboard changes. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .vx-help__art i:first-child { background: var(--vx-border); }
  :root:not([data-theme="light"]) .vx-help .vx-link { color: var(--vx-primary); }
  :root:not([data-theme="light"]) .vx-ios__icon { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.22); }
}

/* ----------------------------------------------------------- pricing ---- */
.vx-toggle { display: inline-flex; padding: 4px; border-radius: 999px; background: var(--vx-bg-secondary); border: 1px solid var(--vx-border); }
.vx-toggle button {
  border: 0; background: transparent; font: inherit; font-size: var(--vx-step--1); font-weight: 600;
  padding: 9px 18px; border-radius: 999px; cursor: pointer; color: var(--vx-text-secondary);
  display: inline-flex; align-items: center; gap: 8px; transition: background 0.2s ease, color 0.2s ease;
}
.vx-toggle button[aria-pressed="true"] { background: var(--vx-surface); color: var(--vx-text); box-shadow: var(--vx-shadow-sm); }
.vx-toggle__save { font-size: 0.7rem; font-weight: 800; padding: 2px 7px; border-radius: 999px; background: var(--vx-success-90); color: var(--vx-success); }

.vx-price-card {
  position: relative; max-width: 560px; margin-inline: auto;
  border-radius: var(--vx-radius-xl); padding: clamp(1.75rem, 4vw, 2.75rem);
  background: var(--vx-surface); border: 1px solid var(--vx-border); box-shadow: var(--vx-shadow-lg);
}
.vx-price-card::before {
  content: ""; position: absolute; inset: 0; border-radius: var(--vx-radius-xl); padding: 1.5px;
  background: linear-gradient(135deg, var(--vx-violet), var(--vx-primary) 50%, var(--vx-teal-bright));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none;
}
/* The guarantee rides INSIDE the offer: it is the reason the price is worth
   locking in, so it belongs in the same frame as the price rather than in a
   second card above it. */
.vx-price__guarantee {
  display: flex; gap: var(--vx-4); align-items: flex-start;
  margin-bottom: var(--vx-5); padding-bottom: var(--vx-5);
  border-bottom: 1px solid var(--vx-border);
}
.vx-price__seal {
  flex: none; display: grid; place-items: center; width: 42px; height: 42px;
  border-radius: 50%; background: var(--vx-premium-90); color: var(--vx-premium);
  border: 1px solid #E2CFA6;
  border: 1px solid color-mix(in srgb, var(--vx-premium) 42%, transparent);
}
.vx-price__seal svg { width: 21px; height: 21px; }
.vx-price__guarantee-title { font-weight: 800; color: var(--vx-premium); font-size: var(--vx-step-0); }
.vx-price__guarantee-text { color: var(--vx-text-secondary); font-size: var(--vx-step--1); line-height: 1.55; margin-top: 3px; }
/* One line, one baseline. The switch wraps under the name on a narrow card and
   keeps the same left edge - never centred against left-aligned neighbours. */
.vx-price__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--vx-3) var(--vx-4); flex-wrap: wrap; margin-bottom: var(--vx-5);
}
.vx-price__included { margin-top: var(--vx-6); font-size: var(--vx-step-0); }

.vx-assure { display: grid; gap: var(--vx-4); max-width: 560px; margin: var(--vx-6) auto 0; }
@media (min-width: 560px) { .vx-assure { grid-template-columns: repeat(2, 1fr); } }
.vx-assure__item { display: flex; gap: 10px; align-items: flex-start; }
.vx-assure__item svg { flex: none; width: 16px; height: 16px; color: var(--vx-success); margin-top: 3px; }
.vx-assure__title { font-weight: 700; font-size: var(--vx-step--1); }
.vx-assure__text { font-size: 0.8125rem; line-height: 1.5; color: var(--vx-text-tertiary); margin-top: 1px; }

/* Required, but from inside the card they competed with the offer. */
.vx-price__terms { max-width: 560px; margin: var(--vx-6) auto 0; }
.vx-price__terms p { font-size: 0.75rem; line-height: 1.6; color: var(--vx-text-tertiary); margin-bottom: 8px; }

.vx-price__name { font-size: var(--vx-step-1); font-weight: 700; }
.vx-price__amount { display: flex; align-items: baseline; flex-wrap: wrap; gap: 8px; margin-block: 0 6px; }
.vx-price__number { font-size: clamp(2.75rem, 7vw, 4rem); font-weight: 800; letter-spacing: -0.04em; line-height: 1; }
.vx-price__per { color: var(--vx-text-tertiary); font-size: var(--vx-step-0); }
.vx-price__equiv { color: var(--vx-text-secondary); font-size: var(--vx-step-0); font-weight: 600; min-height: 1.5em; }
.vx-price__trial {
  display: inline-flex; align-items: center; gap: 8px; margin-block: var(--vx-5) var(--vx-3);
  padding: 7px 14px; border-radius: 999px; background: var(--vx-success-90); color: var(--vx-success);
  font-weight: 700; font-size: var(--vx-step--1);
}
.vx-price__list { display: grid; gap: 10px; margin-top: var(--vx-5); }
@media (min-width: 600px) { .vx-price__list { grid-template-columns: repeat(2, 1fr); } }
.vx-price__list li { display: flex; gap: 10px; align-items: flex-start; font-size: var(--vx-step--1); color: var(--vx-text-secondary); }
.vx-price__list svg { flex: none; width: 18px; height: 18px; color: var(--vx-success); margin-top: 2px; }

/* --------------------------------------------------------------- faq ---- */
.vx-faq { max-width: 46rem; margin-inline: auto; }
.vx-faq details { border-bottom: 1px solid var(--vx-border); }
.vx-faq summary {
  display: flex; align-items: center; justify-content: space-between; gap: var(--vx-4);
  padding-block: var(--vx-5); cursor: pointer; list-style: none;
  font-size: var(--vx-step-1); font-weight: 700; color: var(--vx-text);
}
.vx-faq summary::-webkit-details-marker { display: none; }
.vx-faq summary::after {
  content: ""; flex: none; width: 12px; height: 12px; border-inline-end: 2.5px solid var(--vx-text-tertiary);
  border-bottom: 2.5px solid var(--vx-text-tertiary); transform: rotate(45deg) translateY(-3px);
  transition: transform 0.22s ease;
}
.vx-faq details[open] summary::after { transform: rotate(225deg) translateY(-3px); }
.vx-faq details[open] summary { color: var(--vx-primary-dark); }
.vx-faq__answer { padding-bottom: var(--vx-5); color: var(--vx-text-secondary); max-width: 62ch; }
/* Home only: one icon per question. /pricing and /support keep the plain list. */
.vx-faq--icons summary { justify-content: flex-start; }
/* The turned chevron pokes ~5px past the box; pull it back over the rule. */
.vx-faq--icons summary::after { margin-inline-end: 5px; }
.vx-faq__icon {
  flex: none; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 12px;
  background: var(--vx-primary-90); color: var(--vx-primary-dark);
}
.vx-faq__icon svg { width: 20px; height: 20px; }
.vx-faq__q { flex: 1; min-width: 0; text-wrap: balance; }
.vx-faq--icons .vx-faq__answer { padding-inline-start: calc(40px + var(--vx-4)); }

/* -------------------------------------------------------------- store --- */
/* Two shapes, one rule: the live store is SOLID, a store with no listing yet is
   OUTLINED. That is the whole visual grammar - so the iOS badge can carry the
   real Apple mark without ever reading as a second live download button. */
.vx-store {
  display: inline-flex; align-items: center; gap: 10px; min-height: 52px;
  padding: 8px 18px; border-radius: var(--vx-radius);
  background: var(--vx-ink); color: #fff; text-decoration: none;
  border: 1px solid transparent;
}
/* The two marks have different natural proportions - the Play arrow is
   30.684 x 34.293, the Apple mark 814 x 1000 - so they are sized separately
   rather than both crammed into one square. */
.vx-store__glyph { flex: none; }
.vx-store__glyph--play { width: 23px; height: 26px; }
.vx-store__glyph--apple { width: 21px; height: 26px; margin-block-start: -2px; }
.vx-store__lines { display: flex; flex-direction: column; line-height: 1.15; text-align: start; }
.vx-store__small { font-size: 0.6875rem; opacity: 0.72; }
.vx-store__big { font-size: 0.95rem; font-weight: 700; }

/* Outlined variant. Colour comes from --vx-text and --vx-border-strong, which
   DO follow the theme, so the Apple mark (currentColor) is black on a light
   page and white on a dark one - the two renderings Apple allows. */
.vx-store--soon { background: transparent; color: var(--vx-text); border: 1px dashed var(--vx-border-strong); cursor: default; }
.vx-store--soon .vx-store__small { opacity: 1; color: var(--vx-text-tertiary); }
/* It is a link to the notify form, not a dead chip, so it gets link affordances. */
a.vx-store--soon { cursor: pointer; transition: border-color 0.16s ease, background 0.16s ease; }
a.vx-store--soon:hover, a.vx-store--soon:focus-visible { border-color: var(--vx-text); background: var(--vx-bg-secondary); }

/* On the dark CTA band the solid badge would be black on black, so the pair
   inverts: Play becomes the white badge, iOS stays outlined in white. */
.vx-band .vx-store { background: #fff; color: var(--vx-ink); }
.vx-band .vx-store--soon { background: transparent; color: #fff; border-color: rgba(255, 255, 255, 0.4); }
.vx-band .vx-store--soon .vx-store__small { color: rgba(255, 255, 255, 0.72); }
.vx-band a.vx-store--soon:hover, .vx-band a.vx-store--soon:focus-visible {
  background: rgba(255, 255, 255, 0.12); border-color: #fff;
}
/* In dark mode the page ground is #111827 and the badge is #100F0D - readable,
   but the badge edge disappears into the page. A hairline gives it back. The
   guard mirrors critical.css so an explicit light theme still opts out. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .vx-store { border-color: rgba(255, 255, 255, 0.22); }
  :root:not([data-theme="light"]) .vx-band .vx-store { border-color: transparent; }
}

/* ------------------------------------------------------- bottom cta bar --- */
/* Mobile-only. The header CTA cannot fit next to the logo, language switcher
   and burger at 360px, and hiding it outright left the whole page without a
   call to action. env(safe-area-inset-bottom) keeps it clear of the iPhone
   home indicator. */
.vx-bottom-cta { display: none; }
@media (max-width: 899px) {
  .vx-bottom-cta {
    display: block; position: fixed; z-index: 90;
    inset-inline: 0; bottom: 0;
    padding: var(--vx-3) var(--vx-gutter);
    padding-bottom: calc(var(--vx-3) + env(safe-area-inset-bottom));
    background: var(--vx-surface);
    border-top: 1px solid var(--vx-border);
    box-shadow: var(--vx-shadow-lg);
  }
  .vx-bottom-cta .vx-btn { width: 100%; }
  /* Reserve the strip so it never covers the last of the footer. */
  body { padding-bottom: 84px; }
}
/* The nav is a full-screen overlay; the bar must not float above it. */
.vx-mobilenav:not([hidden]) ~ .vx-bottom-cta { display: none; }

/* ---------------------------------------------------------- ios notify --- */
/* A slim bar. A store with no listing yet is OUTLINED (same grammar as
   .vx-store--soon): the dashed edge says "not yet" before any word does. */
.vx-ios {
  max-width: 56rem; margin-inline: auto; display: grid; gap: var(--vx-4);
  justify-items: center; text-align: center; padding: var(--vx-5);
  border-radius: var(--vx-radius-lg); background: var(--vx-surface);
  border: 1px dashed var(--vx-border-strong);
}
.vx-ios__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 54px; height: 54px;
  border-radius: var(--vx-radius); background: var(--vx-ink); color: #fff;
}
.vx-ios__title { font-size: var(--vx-step-2); text-wrap: balance; }
.vx-ios__copy .vx-form__privacy { margin-top: 4px; text-wrap: balance; }
.vx-ios__actions { display: grid; gap: var(--vx-2); justify-items: center; }
/* The bare address is a fallback, not a second call to action: secondary text
   (4.8:1 light, 5.6:1 dark), still underlined as a link. */
.vx-ios__actions .vx-link { color: var(--vx-text-tertiary); font-weight: 500; }
@media (min-width: 760px) {
  .vx-ios {
    grid-template-columns: auto minmax(0, 1fr) auto; column-gap: var(--vx-5); align-items: center;
    justify-items: start; text-align: start; padding: var(--vx-5) var(--vx-6);
  }
}
.vx-ios__icon svg.vx-store__glyph { width: 24px; height: 29px; margin-block-start: -2px; }
.vx-ios-line { display: inline-flex; align-items: center; gap: 6px; color: var(--vx-text-secondary); font-weight: 600; }
.vx-ios-line svg.vx-store__glyph { width: 12px; height: 15px; margin-block-start: -2px; }
.vx-ios-line:hover { color: var(--vx-text); }

/* -------------------------------------------------------------- misc ---- */
.vx-band { border-radius: var(--vx-radius-xl); padding: clamp(2.5rem, 6vw, 4.5rem); text-align: center; position: relative; overflow: hidden; background: var(--vx-ink-band); color: #fff; }
.vx-band__glow {
  position: absolute; inset: -50% -20% auto -20%; height: 160%; pointer-events: none;
  background: radial-gradient(40% 50% at 30% 30%, color-mix(in srgb, var(--vx-violet) 42%, transparent), transparent 70%),
              radial-gradient(40% 50% at 72% 20%, color-mix(in srgb, var(--vx-teal-bright) 34%, transparent), transparent 70%);
}
.vx-band > * { position: relative; z-index: 1; }
.vx-band h2 { margin-bottom: var(--vx-4); text-wrap: balance; }
.vx-band p { color: rgba(255, 255, 255, 0.8); max-width: 46ch; margin-inline: auto; }
.vx-band .vx-form__privacy { color: rgba(255, 255, 255, 0.6); }
.vx-band .vx-input { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.25); color: #fff; }
.vx-band .vx-input::placeholder { color: rgba(255, 255, 255, 0.5); }
.vx-band .vx-field__label { color: rgba(255, 255, 255, 0.85); }

.vx-inline-form { display: grid; gap: var(--vx-3); max-width: 30rem; margin-inline: auto; margin-top: var(--vx-6); text-align: start; }
@media (min-width: 560px) { .vx-inline-form__row { display: grid; grid-template-columns: 1fr auto; gap: var(--vx-3); } }

.vx-pending {
  display: flex; gap: var(--vx-4); align-items: flex-start;
  padding: var(--vx-5); border-radius: var(--vx-radius-lg);
  background: var(--vx-warning-90); border: 1px solid color-mix(in srgb, var(--vx-warning) 32%, transparent);
  margin-bottom: var(--vx-7);
}
.vx-pending__label { font-weight: 800; color: var(--vx-warning); font-size: var(--vx-step--1); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 4px; }
.vx-pending p { color: var(--vx-text-secondary); font-size: var(--vx-step--1); }

.vx-toc { position: sticky; top: calc(var(--vx-header-h) + var(--vx-5)); align-self: start; }
.vx-toc h4 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--vx-text-tertiary); margin-bottom: var(--vx-3); }
.vx-toc a { display: block; padding: 6px 0; font-size: var(--vx-step--1); color: var(--vx-text-secondary); text-decoration: none; }
.vx-toc a:hover { color: var(--vx-primary-dark); }
.vx-legal-layout { display: grid; gap: var(--vx-7); }
@media (min-width: 960px) { .vx-legal-layout { grid-template-columns: 220px 1fr; gap: var(--vx-8); } }

/* Legal documents render from Markdown, so the prose rules in base.css do most
   of the work. These cover what a policy needs and a blog post does not. */
.vx-prose > h2:first-child { margin-top: 0; }
.vx-prose th { font-weight: 700; color: var(--vx-text); }
/* A grid track is auto-sized to its MIN-CONTENT by default, so the table's
   min-width below would push the whole column - and the page - wider than a
   phone. min-width:0 lets the column shrink and hands the overflow to the
   scroller, which is the only element that should ever scroll sideways. */
.vx-legal-layout > * { min-width: 0; }
/* A legal-basis or retention table has columns of real sentences and cannot
   reflow at 360px. Scrolling the table keeps the PAGE free of a horizontal
   scrollbar, which is what actually breaks a mobile read. */
.vx-table-scroll { overflow-x: auto; margin-bottom: var(--vx-5); overscroll-behavior-x: contain; }
.vx-table-scroll table { min-width: 32rem; margin-bottom: 0; }

.vx-steps { counter-reset: step; display: grid; gap: var(--vx-4); }
.vx-steps li { counter-increment: step; display: flex; gap: var(--vx-4); align-items: flex-start; }
.vx-steps li::before {
  content: counter(step); flex: none; width: 30px; height: 30px; border-radius: 50%;
  background: var(--vx-primary); color: #fff; display: grid; place-items: center;
  font-weight: 800; font-size: 0.85rem;
}

.vx-postcard { display: block; text-decoration: none; }
.vx-postcard time { font-size: 0.8125rem; color: var(--vx-text-tertiary); }
.vx-postcard h3 { margin-block: 8px 6px; font-size: var(--vx-step-1); }
.vx-postcard:hover h3 { color: var(--vx-primary-dark); }
.vx-empty { padding: var(--vx-8) var(--vx-5); text-align: center; color: var(--vx-text-tertiary); border: 1px dashed var(--vx-border-strong); border-radius: var(--vx-radius-lg); }

/* ------------------------------------------------- sticky feature list -- */
/* The media comes FIRST in the DOM so it can stick on narrow screens too,
   where the phone would otherwise end up below every row it illustrates.
   On wide screens `order` moves it back to the right-hand column. */
.vx-feature-layout { display: grid; gap: clamp(1.5rem, 4vw, 3.5rem); }
@media (min-width: 900px) {
  .vx-feature-layout { grid-template-columns: 1fr minmax(300px, 0.85fr); align-items: start; }
  .vx-feature-layout__media { order: 2; }
  .vx-feature-layout__rows { order: 1; }
}
.vx-feature-row { padding-block: clamp(2rem, 6vh, 3.5rem); }
.vx-feature-row + .vx-feature-row { border-top: 1px solid var(--vx-border); }
@media (min-width: 900px) {
  .vx-feature-row { min-height: 62vh; display: flex; flex-direction: column; justify-content: center; }
}
.vx-feature-row h3 { margin-bottom: var(--vx-4); font-size: var(--vx-step-3); letter-spacing: -0.026em; text-wrap: balance; }

/* A smaller device on narrow screens, so the sticky frame plus the header
   still leaves room to read the row beside it. */
@media (max-width: 899px) {
  .vx-phone { width: min(212px, 56vw); }
  /* The hero list shrinks with its device (212/300) instead of truncating every
     name to one letter: a miniature of the real screen, photo included. */
  .vx-hero .vx-screen__body { zoom: 0.71; }
  .vx-hero .vx-screen__sub { zoom: 0.85; }
  .vx-sticky__media { position: sticky; top: calc(var(--vx-header-h) + 8px); z-index: 2; }
}
/* Below 379px the device itself narrows (56vw, 179px at 320), so the list
   keeps shrinking with it rather than squeezing every name to a letter. */
@media (max-width: 378px) {
  .vx-hero .vx-screen__body { zoom: 0.66; }
  .vx-hero .vx-screen__sub { zoom: 0.78; }
}
@media (max-width: 339px) {
  .vx-hero .vx-screen__body { zoom: 0.6; }
  .vx-hero .vx-screen__sub { zoom: 0.7; }
}

/* Numbered cards count themselves, so the template never hard-codes an index. */
.vx-grid { counter-reset: card; }
.vx-card__num::before { counter-increment: card; content: counter(card); }

/* ---------------------------------------------------------------------------
   animations.css

   No effect here is decorative. Each one demonstrates a benefit the copy
   claims: the scanner shows speed, the wave shows hands-free entry, the gauge
   shows the alert arriving before the stock-out, the chain shows no retyping.

   Everything is neutralised under prefers-reduced-motion at the bottom of the
   file, and every animated element is fully legible in its resting state.
--------------------------------------------------------------------------- */

/* ------------------------------------------------------- hero aurora ---- */
@keyframes vx-drift {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  33% { transform: translate3d(2%, 1.5%, 0) scale(1.06); }
  66% { transform: translate3d(-1.5%, 2%, 0) scale(1.03); }
}
.vx-hero__aurora { animation: vx-drift 26s ease-in-out infinite; will-change: transform; }

/* ------------------------------------------------- sticky phone demo ---- */
.vx-sticky { position: relative; }
@media (min-width: 900px) {
  .vx-sticky__media { position: sticky; top: calc(var(--vx-header-h) + 3vh); }
}
/* Screens are stacked; the active one is brought forward. JS sets data-active
   from an IntersectionObserver on the matching feature row. */
.vx-screen { opacity: 0; transform: scale(0.98); transition: opacity 0.4s ease, transform 0.4s ease; pointer-events: none; }
.vx-screen[data-active="true"] { opacity: 1; transform: none; }
html:not(.js) .vx-screen { position: relative; opacity: 1; transform: none; }
html:not(.js) .vx-phone { display: none; }
/* The home-screen phone is a still with its own static screen, so unlike the
   sequence it stays visible without JS. (0,3,1) beats the rule above. */
html:not(.js) .vx-phone.vx-phone--home { display: block; }

/* --------------------------------------------------------- demo: scan --- */
@keyframes vx-laser { 0% { top: 12%; } 50% { top: 84%; } 100% { top: 12%; } }
@keyframes vx-lock {
  0%, 55% { transform: scale(1); border-color: #fff; }
  70% { transform: scale(0.94); border-color: var(--vx-teal-bright); }
  100% { transform: scale(1); border-color: var(--vx-teal-bright); }
}
[data-active="true"] .vx-scan__laser { animation: vx-laser 2.6s ease-in-out infinite; }
[data-active="true"] .vx-scan__frame span { animation: vx-lock 2.6s ease-in-out infinite; }

@keyframes vx-slide-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
[data-active="true"] .vx-scan__result { animation: vx-slide-in 0.5s 1.5s both ease-out; }

/* -------------------------------------------------------- demo: voice --- */
@keyframes vx-wave {
  0%, 100% { height: 10px; }
  50% { height: 44px; }
}
[data-active="true"] .vx-voice__wave i { animation: vx-wave 0.9s ease-in-out infinite; }
.vx-voice__wave i:nth-child(1) { animation-delay: 0s; }
.vx-voice__wave i:nth-child(2) { animation-delay: 0.12s; }
.vx-voice__wave i:nth-child(3) { animation-delay: 0.24s; }
.vx-voice__wave i:nth-child(4) { animation-delay: 0.06s; }
.vx-voice__wave i:nth-child(5) { animation-delay: 0.3s; }
.vx-voice__wave i:nth-child(6) { animation-delay: 0.18s; }
.vx-voice__wave i:nth-child(7) { animation-delay: 0.36s; }
.vx-voice__wave i:nth-child(8) { animation-delay: 0.1s; }
.vx-voice__wave i:nth-child(9) { animation-delay: 0.28s; }

/* The dictated text writes itself, character by character. */
@keyframes vx-type { from { width: 0; } to { width: 100%; } }
[data-active="true"] .vx-voice__typed { animation: vx-type 2.2s steps(34, end) 0.4s both; }

@keyframes vx-pulse {
  0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--vx-primary) 45%, transparent); }
  70% { box-shadow: 0 0 0 16px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
[data-active="true"] .vx-voice__mic { animation: vx-pulse 1.8s ease-out infinite; }

/* -------------------------------------------------------- demo: alert --- */
@keyframes vx-drain {
  0% { width: 78%; background: var(--vx-success); }
  45% { width: 30%; background: var(--vx-success); }
  60% { width: 24%; background: var(--vx-warning); }
  100% { width: 24%; background: var(--vx-warning); }
}
[data-active="true"] .vx-gauge__fill { animation: vx-drain 3.4s ease-in-out infinite; }

@keyframes vx-push-in {
  0%, 55% { opacity: 0; transform: translateY(-10px); }
  70%, 92% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(-6px); }
}
[data-active="true"] .vx-push { animation: vx-push-in 3.4s ease-out infinite; }

/* --------------------------------------------------------- demo: flow --- */
@keyframes vx-step-done {
  from { opacity: 0.35; transform: translateX(-6px); }
  to { opacity: 1; transform: none; }
}
[data-active="true"] .vx-flow__step { animation: vx-step-done 0.5s both ease-out; }
.vx-flow__step:nth-of-type(1) { animation-delay: 0.2s; }
.vx-flow__step:nth-of-type(2) { animation-delay: 0.75s; }
.vx-flow__step:nth-of-type(3) { animation-delay: 1.3s; }
.vx-flow__step:nth-of-type(4) { animation-delay: 1.85s; }

/* ----------------------------------------------------------- mapping ---- */
@keyframes vx-draw { from { stroke-dashoffset: var(--len, 60); } to { stroke-dashoffset: 0; } }
.vx-map__arrow path { stroke-dasharray: var(--len, 60); stroke-dashoffset: var(--len, 60); }
.is-in .vx-map__arrow path { animation: vx-draw 0.6s ease-out forwards; }
.vx-map__row:nth-child(2) .vx-map__arrow path { animation-delay: 0.12s; }
.vx-map__row:nth-child(3) .vx-map__arrow path { animation-delay: 0.24s; }
.vx-map__row:nth-child(4) .vx-map__arrow path { animation-delay: 0.36s; }

/* ------------------------------------------------------- price morph ---- */
.vx-price__number, .vx-price__equiv, .vx-price__per {
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.vx-price-card[data-swapping="true"] .vx-price__number,
.vx-price-card[data-swapping="true"] .vx-price__equiv,
.vx-price-card[data-swapping="true"] .vx-price__per {
  opacity: 0; transform: translateY(-6px);
}

/* --------------------------------------------------------- home pins ---- */
/* Rest state is the finished picture: no .is-in (no JS) means no animation. */
@keyframes vx-pin-pop { from { transform: scale(0); } }
.is-in .vx-pin {
  animation: vx-pin-pop 0.4s both cubic-bezier(0.34, 1.56, 0.64, 1);
  animation-delay: calc(0.35s + var(--i, 0) * 0.15s);
}

/* ------------------------------------------------- bento: report bars --- */
@keyframes vx-grow { from { transform: scaleX(0); } }
.vx-bars i::before { transform-origin: 0 50%; }
.is-in .vx-bars i::before { animation: vx-grow 0.8s both cubic-bezier(0.22, 1, 0.36, 1); }
.vx-bars i:nth-child(2)::before { animation-delay: 0.1s; }
.vx-bars i:nth-child(3)::before { animation-delay: 0.2s; }

/* ------------------------------------------------ problem vignettes ----- */
/* Each one rests on the finished picture, so a card that never receives .is-in
   (no JS, no IntersectionObserver) is still a complete illustration. */
@keyframes vx-vig-flip {
  0%, 40% { opacity: 0; }
  50%, 90% { opacity: 1; }
  100% { opacity: 0; }
}
.is-in .vx-vig__conflict i:nth-child(2) { animation: vx-vig-flip 4.4s ease-in-out infinite; }

@keyframes vx-vig-empty {
  0%, 12% { opacity: 0.8; transform: none; }
  52%, 100% { opacity: 0; transform: translateY(6px); }
}
.is-in .vx-vig__boxes i { animation: vx-vig-empty 5s ease-in-out infinite; }
.vx-vig__boxes i:nth-child(1) { animation-delay: 0s; }
.vx-vig__boxes i:nth-child(2) { animation-delay: 0.18s; }
.vx-vig__boxes i:nth-child(3) { animation-delay: 0.36s; }
.vx-vig__boxes i:nth-child(4) { animation-delay: 0.54s; }
.vx-vig__boxes i:nth-child(5) { animation-delay: 0.72s; }

/* Deliberately late: it only shows up once the boxes are already gone. */
@keyframes vx-vig-late {
  0%, 60% { opacity: 0; transform: scale(0.6); }
  72%, 90% { opacity: 1; transform: none; }
  100% { opacity: 0; transform: scale(0.9); }
}
.is-in .vx-vig__late { animation: vx-vig-late 5s ease-out infinite; }

@keyframes vx-vig-copied { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
.is-in .vx-vig__doc { animation: vx-vig-copied 0.45s both ease-out; }
.vx-vig__doc:nth-of-type(1) { animation-delay: 0.15s; }
.vx-vig__doc:nth-of-type(2) { animation-delay: 0.65s; }
.vx-vig__doc:nth-of-type(3) { animation-delay: 1.15s; }

/* ---------------------------------------------------------------------------
   One switch turns all of it off. Under reduced motion every element must
   still be complete and readable - which is why each animation above ends on
   the state the element already has at rest.
--------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .vx-reveal { opacity: 1; transform: none; }
  .vx-hero__aurora { animation: none; }
  .vx-map__arrow path { stroke-dashoffset: 0; }
  .vx-push { opacity: 1; }
  .vx-voice__typed { width: 100%; }
  .vx-gauge__fill { width: 24%; background: var(--vx-warning); }
  .vx-vig__conflict i:nth-child(2) { opacity: 0; }
  .vx-vig__boxes i { opacity: 0.8; transform: none; }
  .vx-vig__late { opacity: 1; transform: none; }
  .vx-vig__doc { opacity: 1; transform: none; }
  /* Same specificity as the rules that start them, or they would still run
     (fill "both" plus the delays keeps pins at scale(0) for up to 0.65s). */
  .is-in .vx-pin, .is-in .vx-bars i::before { animation: none; }
}
