/* ============================================================================
 * portal.css — the ONE stylesheet for the public portal.
 *
 * VISUAL LANGUAGE: a sibling of ecosystem.ventor.tech (the VentorTech shop), not a
 * reinterpretation of it. The two anchors below are SOURCED from the shop's own
 * stylesheets, not eyeballed:
 *   * Manrope, weights 400 + 700 — literally what the shop requests from Google Fonts.
 *   * #4A90E2 — the accent/link blue in the shop's child theme.
 *
 * ONE DELIBERATE DEPARTURE, and it is an accessibility one: white text on #4A90E2
 * measures 3.29:1, which fails WCAG AA for normal text. So the brand blue stays the
 * ACCENT (links on white, focus rings, icons) and the primary BUTTON is a darkened
 * sibling (#2F76C4, 4.66:1 with white). A button nobody with average eyesight can
 * read in sunlight is not on brand either.
 *
 * Everything is airy, rounded, restrained: white cards on a neutral-grey page, one
 * blue, generous whitespace. Serious B2B, no gimmicks.
 * ==========================================================================*/

/* --- Design tokens ------------------------------------------------------- */
:root {
    /* Surfaces. The page is neutral grey, NOT white — that is what makes the white
     cards read as cards. */
    --vt-bg: #F4F5F7;
    --vt-surface: #FFFFFF;
    --vt-border: #E3E6EA;
    --vt-border-input: #D6DAE0;
    /* one step darker, so a field looks like a field */

    /* Text. Near-black grey, never pure black. Both pass AA on white AND on --vt-bg. */
    --vt-text: #1D2127;
    /* 16.2:1 on white */
    --vt-text-muted: #5F6771;
    /* 5.7:1 on white, 5.3:1 on the grey page */

    /* Blue. See the header for why there are four of them. */
    --vt-blue: #4A90E2;
    /* the shop's brand accent: icons, focus ring */
    --vt-blue-600: #2F76C4;
    /* primary button fill        — 4.66:1 with white */
    --vt-blue-700: #2B6CB8;
    /* button hover, link colour  — 5.33:1 on white */
    --vt-blue-800: #1F63B0;
    /* button active              — 6.05:1 on white */
    --vt-blue-tint: #EAF2FB;
    /* the faintest wash, for the ready badge */

    --vt-danger: #DC3545;
    /* the shop's own red: borders, accents */
    --vt-danger-text: #96222C;
    /* AA on white — the red we are allowed to SET TEXT in */
    --vt-success: #1E7A50;

    --r-card: 16px;
    --r-field: 10px;
    --r-pill: 999px;

    --shadow-card: 0 1px 2px rgba(16, 24, 40, .04), 0 8px 24px rgba(16, 24, 40, .06);
    --shadow-lift: 0 2px 4px rgba(16, 24, 40, .05), 0 14px 32px rgba(16, 24, 40, .10);

    /* 4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 — one scale, no magic numbers below. */
    --s-1: 4px;
    --s-2: 8px;
    --s-3: 12px;
    --s-4: 16px;
    --s-5: 24px;
    --s-6: 32px;
    --s-7: 48px;
    --s-8: 64px;

    --container: 1120px;
    /* The width of the card in ALL FOUR states, not just the form — the waiting and
     credentials cards use it too. One token on purpose: this is ONE page that swaps its
     contents, so if the card changed width on each state flip it would visibly jump under
     the user's cursor.
     720, not 560: at 560 the two-column rows left each field around 250px, which is a
     cramped box for a company name. This gives each column ~330px and still keeps a
     comfortable measure for the prose on the waiting screen. */
    --form-width: 720px;
    /* The credentials card is WIDER than the form. A one-time width change on the final state
       flip is fine — what would be wrong is the card jittering between the form and the waiting
       screen, which is why THOSE two share --form-width. This screen has to fit
       "https://<id>.demo.vntr.dev" + a Copy button on one line without wrapping. */
    --creds-width: 800px;
}

/* --- Reset (the minimum, not a framework) -------------------------------- */
*,
*::before,
*::after {
    box-sizing: border-box;
}

body,
h1,
h2,
h3,
p,
ul,
ol,
dl,
dd,
figure {
    margin: 0;
    padding: 0;
}

ul,
ol {
    list-style: none;
}

img {
    max-width: 100%;
    display: block;
}

body {
    /* Manrope, with a system stack behind it: if Google Fonts is blocked or slow the
     page renders in the fallback rather than showing nothing (font-display: swap). */
    font-family: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    font-size: 16px;
    line-height: 1.6;
    color: var(--vt-text);
    background: var(--vt-bg);
    -webkit-font-smoothing: antialiased;
}

.container {
    width: 100%;
    max-width: var(--container);
    margin: 0 auto;
    padding: 0 var(--s-5);
}

/* --- Focus: VISIBLE, everywhere, no exceptions ---------------------------
 * :focus-visible only, so a mouse user does not get a ring on every click — but a
 * keyboard user gets one on every single interactive element. Removing it "because
 * it is ugly" makes the portal unusable without a mouse; it stays. */
:focus-visible {
    outline: 3px solid var(--vt-blue);
    outline-offset: 2px;
    border-radius: var(--s-1);
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: var(--s-2);
    background: var(--vt-surface);
    color: var(--vt-blue-700);
    padding: var(--s-3) var(--s-4);
    border-radius: var(--r-field);
    box-shadow: var(--shadow-card);
    font-weight: 700;
    z-index: 10;
}

.skip-link:focus {
    left: var(--s-4);
}

/* --- Header: two tiers, like the shop ------------------------------------ */
.site-header {
    background: var(--vt-surface);
    border-bottom: 1px solid var(--vt-border);
}

.utility-row {
    /* SAME surface as the nav below it (the header is ONE white slab). It sat on --vt-bg
       before, which made the header look like two unrelated strips of markup that had come
       apart. The tiers are distinguished by type size and a hairline, not by colour. */
    background: var(--vt-surface);
    border-bottom: 1px solid var(--vt-border);
    font-size: .8125rem;
}

.utility-row__inner {
    display: flex;
    justify-content: flex-end;
}

.utility-links {
    display: flex;
    gap: var(--s-5);
    padding: var(--s-2) 0;
}

.utility-links a {
    color: var(--vt-text-muted);
    text-decoration: none;
}

.utility-links a:hover {
    color: var(--vt-blue-700);
}

.main-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-4);
    padding-top: var(--s-4);
    padding-bottom: var(--s-4);
}

.logo {
    display: inline-flex;
}

.logo img {
    height: 42px;
    width: auto;
}

.nav-links {
    display: flex;
    gap: var(--s-6);
}

.nav-links a {
    color: var(--vt-text);
    text-decoration: none;
    font-weight: 700;
}

.nav-links a:hover {
    color: var(--vt-blue-700);
}

/* --- Type scale ----------------------------------------------------------- */
.hero__headline,
.screen__title {
    /* Capped at 2.25rem, not 2.75: the form is the hero here, not the sentence above it. */
    font-size: clamp(1.75rem, 1.1rem + 1.8vw, 2.25rem);
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -.02em;
}

.hero__subhead,
.screen__subhead {
    font-size: 1rem;
    color: var(--vt-text-muted);
    max-width: 40em;
    margin: var(--s-2) auto 0;
}

/* --- Hero + form (the form IS the hero) ----------------------------------- */
.hero {
    /* 24 above, not 64: the headline does not need a stadium, and this is the single biggest
       contributor to the form clearing the fold on a 900px-tall laptop (measured). */
    padding: var(--s-5) 0 var(--s-7);
}

.hero__inner {
    text-align: center;
}

/* --- The product icon: ONE slot, THREE screens ----------------------------
   The hero (a product is chosen), the waiting screen, and the credentials card all show the
   same artwork — the PO's product SVGs, the ones the tiles already use. The markup is a fixed
   macro (see portal.html) and portal.js only sets a src and toggles [hidden]; the size is
   decided here, per context, so the JS never carries a pixel.

   THE [hidden] RULES BELOW ARE LOAD-BEARING, not tidiness. `hidden` only works because the UA
   stylesheet says `[hidden] { display: none }` — and ANY author `display` beats it. The moment
   we give .product-icon `display: grid`, a hidden icon becomes a VISIBLE empty box on the bare
   "/". Every element in this file that we hide from JS and also give a `display` to needs its
   own [hidden] rule. */
.product-icon[hidden],
.product-icon__img[hidden],
.product-icon__monogram[hidden],
.creds__product[hidden],
.creds__requested[hidden] {
    display: none;
}

.product-icon {
    display: grid;
    place-items: center;
    width: 64px;
    height: 64px;
    margin: 0 auto var(--s-3);
}

.product-icon__img {
    width: 100%;
    height: 100%;
    /* the artwork comes in mixed aspect ratios — contain, never stretch (the tiles' lesson) */
    object-fit: contain;
}

/* The fallback for a product with no artwork yet: the same lettered monogram the tiles use. */
.product-icon__monogram {
    width: 100%;
    height: 100%;
    display: grid;
    place-items: center;
    border-radius: var(--r-card);
    background: var(--vt-blue-tint);
    color: var(--vt-blue-700);
    font-size: 1.25rem;
    font-weight: 700;
}

/* Smaller on the waiting screen: the spinner and the progress list are the subject there; the
   icon is context, and it sits between them. */
.waiting-card .product-icon {
    width: 48px;
    height: 48px;
    margin: var(--s-3) auto var(--s-2);
}

.card {
    background: var(--vt-surface);
    border: 1px solid var(--vt-border);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-card);
    padding: var(--s-6);
}

.hero-grid {
    display: grid;
    grid-template-columns: minmax(0, var(--form-width)) minmax(0, 320px);
    gap: var(--s-5);
    justify-content: center;
    align-items: start;
    margin-top: var(--s-5);
}

.form-card {
    text-align: left;
    padding: var(--s-4) var(--s-5) var(--s-5);
}

/* The alternative route, deliberately QUIET: a secondary button, no shadow-lift, no accent
   fill. It must be findable by the person who wants it and ignorable by the person who does
   not — a card that shouted would compete with the form, and the form is the point. */
.walkthrough-card {
    text-align: left;
    background: var(--vt-bg);
    box-shadow: none;
}

.walkthrough-card__title {
    font-size: 1.0625rem;
    font-weight: 700;
}

.walkthrough-card__text {
    font-size: .9375rem;
    color: var(--vt-text-muted);
    margin: var(--s-2) 0 var(--s-4);
}

.field {
    margin-bottom: var(--s-4);
}

/* Two fields side by side. `1fr 1fr` and not `auto` — the two columns must be EQUAL
   whatever is in them, or a long product label would starve the version select next to
   it. `align-items: start` keeps both fields top-aligned when one of them grows (an
   inline error appears under `name` but not under `company`, and without this the
   shorter field would centre itself against the taller one and the labels would stop
   lining up).

   It collapses to one column under 640px — see the phone block at the bottom. Two
   half-width fields on a 375px screen are two fields nobody can type in. */
.field-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s-4);
    align-items: start;
}

/* A note that belongs to the ROW, not to one field in it: it sits under both columns and
   pulls up tight against them (the fields' own bottom margin already spaced them). */
.field__note--row {
    margin-top: calc(var(--s-4) * -1 + var(--s-1));
    margin-bottom: var(--s-5);
}

.field__label {
    display: block;
    font-size: .875rem;
    font-weight: 700;
    margin-bottom: var(--s-2);
}

.field__input {
    width: 100%;
    min-height: 48px;
    /* comfortable, and a real touch target on a phone */
    padding: var(--s-3) var(--s-4);
    font: inherit;
    color: var(--vt-text);
    background: var(--vt-surface);
    border: 1px solid var(--vt-border-input);
    border-radius: var(--r-field);
    transition: border-color .15s ease, box-shadow .15s ease;
}

.field__input:hover {
    border-color: #C3C9D2;
}

.field__input:focus {
    border-color: var(--vt-blue);
    box-shadow: 0 0 0 3px rgba(74, 144, 226, .18);
    outline: none;
    /* replaced by the ring above, not removed */
}

select.field__input {
    appearance: none;
    /* The chevron is an inline data-URI: one fewer request, and it cannot 404. */
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%235F6771' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--s-4) center;
    padding-right: var(--s-7);
}

.field__note {
    font-size: .875rem;
    color: var(--vt-text-muted);
    margin-top: var(--s-1);
}

.field__error,
.form-error {
    font-size: .875rem;
    color: var(--vt-danger-text);
    font-weight: 700;
    margin-bottom: var(--s-4);
}

.form-error {
    background: #FDF2F3;
    border: 1px solid var(--vt-danger);
    border-radius: var(--r-field);
    padding: var(--s-3) var(--s-4);
}

.field--check {
    display: flex;
    align-items: flex-start;
    gap: var(--s-3);
    margin-bottom: var(--s-2);
}

.field--check input {
    width: 20px;
    height: 20px;
    margin-top: 2px;
    flex-shrink: 0;
    accent-color: var(--vt-blue-600);
}

.field--check label {
    font-size: .9375rem;
    color: var(--vt-text-muted);
}

.field--check a {
    color: var(--vt-blue-700);
}

/* --- Buttons -------------------------------------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    min-height: 48px;
    padding: var(--s-3) var(--s-5);
    font: inherit;
    font-weight: 700;
    border-radius: var(--r-field);
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
    transition: background-color .15s ease, border-color .15s ease, transform .05s ease;
}

.btn:active {
    transform: translateY(1px);
}

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

.btn--primary {
    background: var(--vt-blue-600);
    color: #FFF;
}

/* 4.66:1 — AA */
.btn--primary:hover {
    background: var(--vt-blue-700);
}

.btn--primary:active {
    background: var(--vt-blue-800);
}

.btn--primary[disabled] {
    background: #A8C4E4;
    cursor: not-allowed;
}

.btn--secondary {
    background: var(--vt-surface);
    color: var(--vt-blue-700);
    border-color: var(--vt-border-input);
}

.btn--secondary:hover {
    border-color: var(--vt-blue);
    background: var(--vt-blue-tint);
}

.btn--copy {
    min-height: 32px;
    padding: var(--s-1) var(--s-3);
    font-size: .8125rem;
    background: var(--vt-bg);
    color: var(--vt-text-muted);
    border-color: var(--vt-border-input);
}

.btn--copy:hover {
    color: var(--vt-blue-700);
    border-color: var(--vt-blue);
}

.slug-note {
    max-width: var(--form-width);
    margin: var(--s-4) auto 0;
    font-size: .9375rem;
    color: var(--vt-text-muted);
}

/* --- "What you get" ------------------------------------------------------- */
.what-you-get {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: var(--s-5);
    margin-top: var(--s-7);
    text-align: left;
}

.what-you-get__title {
    font-size: 1rem;
    font-weight: 700;
    margin-bottom: var(--s-1);
}

.what-you-get__text {
    font-size: .9375rem;
    color: var(--vt-text-muted);
}

/* --- Waiting screen ------------------------------------------------------- */
.screen {
    padding: var(--s-8) 0;
}

.screen__inner {
    text-align: center;
}

.waiting-card,
.failed-card {
    max-width: var(--form-width);
    margin: 0 auto;
    text-align: center;
}

.spinner {
    width: 40px;
    height: 40px;
    margin: 0 auto var(--s-5);
    border: 3px solid var(--vt-border);
    border-top-color: var(--vt-blue);
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}

/* The progress list: the step we are ACTUALLY in is bold, the ones behind it are
   ticked, the ones ahead are muted. No percentage — we do not know one, so we do
   not draw one. */
.steps {
    margin-top: var(--s-6);
    text-align: left;
    display: inline-block;
}

.steps li {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: var(--s-2) 0;
    color: var(--vt-text-muted);
    font-size: .9375rem;
}

.steps li::before {
    content: "";
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    border: 2px solid var(--vt-border-input);
    border-radius: 50%;
}

.steps li[data-state="done"] {
    color: var(--vt-text);
}

.steps li[data-state="done"]::before {
    border-color: var(--vt-success);
    background: var(--vt-success) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='8' fill='none'%3E%3Cpath d='M1 4l2.5 2.5L9 1' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center no-repeat;
}

.steps li[data-state="current"] {
    color: var(--vt-text);
    font-weight: 700;
}

.steps li[data-state="current"]::before {
    border-color: var(--vt-blue);
    box-shadow: inset 0 0 0 3px var(--vt-blue-tint);
}

.slow-note {
    margin-top: var(--s-5);
    font-size: .875rem;
    color: var(--vt-text-muted);
}

/* --- Rotating cards ------------------------------------------------------- */
.rotator {
    max-width: var(--form-width);
    margin: var(--s-6) auto 0;
}

.rotator__card {
    text-align: left;
    min-height: 132px;
    transition: opacity .4s ease;
}

.rotator__card[data-fading="true"] {
    opacity: 0;
}

.rotator__title {
    font-size: 1.0625rem;
    font-weight: 700;
    margin-bottom: var(--s-2);
}

.rotator__text {
    color: var(--vt-text-muted);
    font-size: .9375rem;
}

.rotator__dots {
    display: flex;
    justify-content: center;
    gap: var(--s-2);
    margin-top: var(--s-4);
}

.rotator__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    border: 0;
    padding: 0;
    background: var(--vt-border-input);
    cursor: pointer;
}

.rotator__dot[aria-selected="true"] {
    background: var(--vt-blue);
}

/* --- Credentials ---------------------------------------------------------- */
.creds-card {
    max-width: var(--creds-width);
    margin: 0 auto;
    text-align: center;
}

/* WHAT THIS DEMO IS, directly under the title: the icon, the product's real name and the Odoo
   version, on one line. Then the day they asked for it. Both exist for the reader who is NOT the
   person who clicked the button 40 seconds ago — the one arriving on a resume link days later,
   cold, who has to be able to place this page in two seconds. */
.creds__product {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    margin-top: var(--s-3);
    font-weight: 700;
}

.creds__product .product-icon {
    /* inline with the text here, not a block above it — so it sizes down and loses its margin */
    width: 32px;
    height: 32px;
    margin: 0;
}

.creds__requested {
    margin-top: var(--s-1);
    font-size: .9375rem;
    color: var(--vt-text-muted);
}

.creds-card__badge {
    width: 48px;
    height: 48px;
    margin: 0 auto var(--s-5);
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--vt-blue-tint);
    color: var(--vt-success);
    font-size: 1.5rem;
    font-weight: 700;
}

.creds-card .btn--primary {
    margin: var(--s-6) 0 var(--s-5);
}

.creds {
    text-align: left;
}

.creds__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-3);
    padding: var(--s-3) 0;
    border-top: 1px solid var(--vt-border);
    flex-wrap: wrap;
}

.creds__row dt {
    font-size: .875rem;
    font-weight: 700;
    color: var(--vt-text-muted);
    flex-shrink: 0;
}

.creds__row dd {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    min-width: 0;
    /* lets a long password ellipsise instead of overflowing */
}

.creds__row code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .9375rem;
    background: var(--vt-bg);
    border: 1px solid var(--vt-border);
    border-radius: var(--s-1);
    padding: var(--s-1) var(--s-2);
    overflow-wrap: anywhere;
}

.creds__value--truncate {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    min-width: 0;
    /* the flex child must be allowed to shrink, or it never ellipsises */
}

.creds__note {
    margin-top: var(--s-5);
    font-size: .875rem;
    color: var(--vt-text-muted);
}

/* The e-mail line (6b-2). Two very different messages share one element, because they answer
   the same question — "is this also in my inbox?" — and only one of them can ever be true.

   `sent` is a quiet aside: muted, inherits .creds__note. `failed` is NOT an aside. That e-mail
   carries the resume link, which is the only way back to this password once the tab is closed,
   so the person has to notice this one and act on it (save the details) before they navigate
   away. Hence the warning colour, the tint and the left border — the same visual grammar the
   failed screen uses, in miniature, and never a red wall for what is still a working demo. */
.creds__note--email[data-status="failed"] {
    margin-top: var(--s-4);
    padding: var(--s-3) var(--s-4);
    border-left: 3px solid var(--vt-danger);
    border-radius: var(--r-field);
    background: #FDF2F3;
    color: var(--vt-danger-text);
    font-weight: 700;
    text-align: left;
}

.creds__try {
    margin-top: var(--s-4);
    padding: var(--s-4);
    background: var(--vt-blue-tint);
    border-radius: var(--r-field);
    font-size: .9375rem;
    text-align: left;
}

/* --- Failed --------------------------------------------------------------- */
.failed-card {
    border-top: 3px solid var(--vt-danger);
}

.failed-detail {
    margin-top: var(--s-4);
    padding: var(--s-3) var(--s-4);
    background: var(--vt-bg);
    border-radius: var(--r-field);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .8125rem;
    color: var(--vt-text-muted);
    text-align: left;
    overflow-wrap: anywhere;
}

.failed-actions {
    display: flex;
    gap: var(--s-3);
    justify-content: center;
    margin-top: var(--s-6);
    flex-wrap: wrap;
}

/* --- Product panel -------------------------------------------------------- */
.products {
    padding: var(--s-8) 0;
    border-top: 1px solid var(--vt-border);
}

.products__title {
    font-size: 1.5rem;
    font-weight: 700;
    text-align: center;
}

.products__subtitle {
    text-align: center;
    color: var(--vt-text-muted);
    margin-top: var(--s-2);
    margin-bottom: var(--s-6);
}

.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: var(--s-5);
}

.tile__link {
    display: flex;
    flex-direction: column;
    align-items: center;
    height: 100%;
    padding: var(--s-5);
    background: var(--vt-surface);
    border: 1px solid var(--vt-border);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-card);
    text-decoration: none;
    color: inherit;
    text-align: center;
    transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}

.tile__link:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-lift);
    border-color: var(--vt-blue);
}

/* Name at the TOP, per the brief. min-height reserves TWO lines, so a one-line name
   ("Direct Print PRO") and a two-line one ("QuickBooks Online Connector") start their icons at
   the same y — the row reads as a row instead of a stagger. */
.tile__name {
    font-weight: 700;
    min-height: 2.8em;
}

.tile__icon {
    /* flex: 1 — the icon block eats whatever vertical slack the card has, and centres the
       artwork inside it. This is what keeps the icons aligned across tiles of unequal text. */
    flex: 1;
    display: grid;
    place-items: center;
    width: 88px;
    min-height: 88px;
    margin: var(--s-4) 0;
}

.tile__icon img {
    width: 88px;
    height: 88px;
    object-fit: contain;
    /* artwork comes in mixed aspect ratios — never stretch it to fill the box */
}

.tile__monogram {
    width: 88px;
    height: 88px;
    display: grid;
    place-items: center;
    border-radius: var(--r-card);
    background: var(--vt-blue-tint);
    color: var(--vt-blue-700);
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: .02em;
}

.tile__cta {
    /* PINNED to the bottom of the card, whatever is above it. `margin-top: auto` in a flex
       column consumes the remaining space, so the links line up across the row even when the
       names above them wrap to different heights. It used to depend on the blurb's flex-grow —
       and then the blurb was deleted. A layout must not rely on an element that may be removed. */
    margin-top: auto;
    padding-top: var(--s-4);
    font-size: .875rem;
    font-weight: 700;
    color: var(--vt-blue-700);
}

/* --- Footer --------------------------------------------------------------- */
.site-footer {
    background: var(--vt-surface);
    border-top: 1px solid var(--vt-border);
    padding: var(--s-7) 0 var(--s-5);
}

.footer-bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s-4);
    flex-wrap: wrap;
    margin-top: var(--s-6);
    padding-top: var(--s-5);
    border-top: 1px solid var(--vt-border);
}

.footer-links {
    display: flex;
    gap: var(--s-5);
    flex-wrap: wrap;
}

.footer-links a {
    color: var(--vt-text-muted);
    text-decoration: none;
    font-size: .875rem;
}

.footer-links a:hover {
    color: var(--vt-blue-700);
}

.footer-legal {
    font-size: .875rem;
    color: var(--vt-text-muted);
}

/* --- Phone ---------------------------------------------------------------- */
@media (max-width: 640px) {
    .container {
        padding: 0 var(--s-4);
    }

    .hero {
        padding: var(--s-6) 0 var(--s-5);
    }

    .card {
        padding: var(--s-5);
    }

    /* One column. Two half-width fields on a 375px screen are two fields nobody can type
     in — the vertical space we saved on a laptop is not worth an unusable form on a
     phone, which is where a good share of these prospects will be. */
    .field-row {
        grid-template-columns: 1fr;
        gap: 0;
    }

    .field__note--row {
        margin-top: calc(var(--s-5) * -1 + var(--s-1));
    }

    /* The walkthrough card stacks BELOW the form — the self-serve path is the default and it
       stays first. Source order already puts the form first, so this is just the grid
       collapsing; nobody has to scroll past an advert to reach the thing they came for. */
    .hero-grid {
        grid-template-columns: 1fr;
    }

    /* The utility row is the first thing to go: three secondary links are not worth a
     row of a phone's viewport when the form is the point of the page. */
    .utility-row {
        display: none;
    }

    .main-row {
        padding-top: var(--s-3);
        padding-bottom: var(--s-3);
    }

    .nav-links {
        gap: var(--s-4);
        font-size: .9375rem;
    }

    .creds__row {
        align-items: flex-start;
        flex-direction: column;
        gap: var(--s-2);
    }

    .footer-bottom {
        flex-direction: column;
        align-items: flex-start;
    }
}

/* --- Reduced motion -------------------------------------------------------
 * Someone who asked the OS for less motion means it. The spinner stops spinning
 * (it becomes a static ring), the card cross-fade becomes an instant swap, and the
 * tile lift and button nudge stop.
 *
 * What does NOT stop: the cards still ROTATE. Rotation is a content change, not an
 * animation, and freezing it would leave a reduced-motion user staring at one card
 * for 90 seconds — that is a worse experience, not an accessible one. It is the
 * MOVEMENT we remove, not the information. */
@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }

    .spinner {
        border-top-color: var(--vt-blue);
        animation: none;
    }

    .btn:active {
        transform: none;
    }

    .tile__link:hover {
        transform: none;
    }
}