/*
 * Business card designs. The markup (.vc …) is produced in two places that
 * must stay in sync with this file: app/Views/cards/_card.php (server, for
 * the public page) and public/js/card-render.js (wallet and editor preview):
 *
 *   article.vc.vc-theme-<key>
 *     div.vc-face
 *       span.vc-mark            initials; only some designs show it
 *       div.vc-head
 *         div.vc-id             name + role
 *         p.vc-company
 *       ul.vc-contacts
 *
 * A card is a miniature of a real European business card, 85 × 55 mm. Its
 * proportions are fixed (aspect-ratio) and everything inside is sized in cqw,
 * 1% of the card's own width, so a card looks the same at every size: in the
 * wallet list, the pile, the editor preview and the public page. Note that cqw
 * only works on descendants of .vc (the container), which is why the padding
 * and the decorations live on .vc-face and not on .vc itself.
 *
 * A design is a .vc-theme-<key> block: it sets the colour properties and may
 * rearrange the parts above. The key must be listed in config/plans.php →
 * themes.allowed and have a name in app/lang (card_theme_<key>).
 *
 * The colour properties, all of which a card can set itself in its style
 * attribute (cards.colors, Service\CardColors), so a design uses them and does
 * not hard-code a colour where a person's choice could land:
 *   --vc-bg       the background; a gradient design builds it from --vc-a and --vc-b
 *   --vc-fg       text; --vc-muted the quieter text (the role)
 *   --vc-accent   the stripe, the panel, the band, the frame: a surface
 *   --vc-ink      the accent where it is the colour of text (the company); the accent
 *                 itself when that reads on the background, else the text colour
 *   --vc-on       text laid on the accent (the monogram on the panel, the name on the band)
 * A design that has colours to offer lists them in config/palettes.php.
 */

[x-cloak] { display: none !important; }

/*
 * Safe areas. In an installed app the page can run under the status bar, the
 * camera cut-out and the gesture bar; everything fixed to an edge pads itself
 * with these. They are variables, not bare env() calls, so a test can set them
 * by hand (the layout asks for them with viewport-fit=cover).
 * --kb is the height of the on-screen keyboard (wallet.js; 0 where the browser
 * already shrinks the page for it).
 */
:root {
    --sat: env(safe-area-inset-top, 0px);
    --sar: env(safe-area-inset-right, 0px);
    --sab: env(safe-area-inset-bottom, 0px);
    --sal: env(safe-area-inset-left, 0px);
    --bar-h: 3.5rem;
    --kb: 0px;
}

/*
 * The page's scrollbar keeps its place when the page is locked. An opened card, a card held up and the
 * menu all set overflow: hidden on <body>; on a desktop that takes the scrollbar away, the viewport
 * becomes wider and everything on the page shifts sideways. With the gutter kept, nothing moves.
 * (Phones have no such bar.)
 */
html { scrollbar-gutter: stable; }

/* ── App shell ───────────────────────────────────────────────────────── */

/*
 * Light or dark with the theme. An installed iOS app draws white status icons on
 * top of the bar, so a dark strip the height of the inset sits behind them
 * whatever the theme (where --sat is 0 the strip has no height).
 */
.ev-topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 40;
    box-sizing: border-box;
    height: calc(var(--bar-h) + var(--sat));
    padding: var(--sat) max(.75rem, var(--sar)) 0 max(1rem, var(--sal));
    display: flex;
    align-items: center;
    gap: .75rem;
    background:
        linear-gradient(#111827, #111827) top / 100% var(--sat) no-repeat,
        rgba(255, 255, 255, .92);
    color: #111827;
    border-bottom: 1px solid rgba(0, 0, 0, .1);
    box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
    -webkit-backdrop-filter: blur(5px);
    backdrop-filter: blur(5px);
}
.dark .ev-topbar {
    background: rgba(17, 24, 39, .92);
    color: #ffffff;
    border-bottom-color: rgba(255, 255, 255, .14);
    box-shadow: 0 4px 14px rgba(0, 0, 0, .35);
}

.ev-topbar-home {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: .65rem;
    color: inherit;
    text-decoration: none;
}

.ev-topbar-icon {
    flex: none;
    width: 1.9rem;
    height: auto;
    color: #2563eb;
}
.dark .ev-topbar-icon { color: #93c5fd; }
/* The front card of the icon hides the ones behind it, so it is filled with the bar's colour. */
.ev-icon-front { fill: #ffffff; }
.dark .ev-icon-front { fill: #111827; }

.ev-topbar-name {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: 1.05rem;
    font-weight: 600;
}

/* Reserves the room of the menu button, with a hairline before it. */
.ev-topbar-menu {
    align-self: stretch;
    width: calc(2.75rem + .75rem);
    margin: .6rem 0;
    border-left: 1px solid rgba(0, 0, 0, .15);
}
.dark .ev-topbar-menu { border-left-color: rgba(255, 255, 255, .2); }

/*
 * The menu button is not inside the top bar: the bar is a stacking context
 * and could not rise above the open menu, which covers the screen's full height.
 * The button turns from three lines into a cross as the menu opens.
 */
.ev-menu-btn {
    position: fixed;
    z-index: 55;
    top: calc(var(--sat) + (var(--bar-h) - 2.75rem) / 2);
    right: max(.75rem, var(--sar));
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: .6rem;
    background: #2563eb;
    color: #ffffff;
}
.ev-menu-btn:hover { background: #1d4ed8; }
.ev-menu-btn .ev-ico-close,
.ev-menu-btn[aria-expanded="true"] .ev-ico-open { display: none; }
.ev-menu-btn[aria-expanded="true"] .ev-ico-close { display: block; }

/* What lies behind an open menu or card is blurred, so the eye stays on the front. */
.ev-overlay {
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    animation: ev-fade-in .2s ease-out;
}
@keyframes ev-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.ev-menu-backdrop {
    position: fixed;
    inset: 0;
    z-index: 51;
    background: rgba(0, 0, 0, .45);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
    animation: ev-fade-in .18s ease-out;
}

/* A two-way switch (the wallet view, the theme) in the menu. */
.ev-seg {
    display: flex;
    border: 1px solid #d1d5db;
    border-radius: .5rem;
    overflow: hidden;
}
.ev-seg button {
    flex: 1;
    padding: .6rem .5rem;
    font-size: .875rem;
    line-height: 1.2;
}
.ev-seg button + button { border-left: 1px solid #d1d5db; }
.ev-seg button[aria-pressed="true"] { background: #2563eb; color: #ffffff; }
.dark .ev-seg,
.dark .ev-seg button + button { border-color: #4b5563; }

/* Full height, flush with the right edge; 20% of the screen stays free on the left (to a limit on wide screens). */
.ev-menu {
    position: fixed;
    z-index: 52;
    top: 0;
    bottom: 0;
    right: 0;
    box-sizing: border-box;
    width: min(80vw, 26rem);
    padding: 0 var(--sar) max(1rem, var(--sab)) 0;
    overflow-y: auto;
    box-shadow: -8px 0 28px rgba(0, 0, 0, .45);
    animation: ev-menu-in .18s ease-out;
}
@keyframes ev-menu-in {
    from { transform: translateX(100%); }
    to   { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .ev-menu, .ev-menu-backdrop, .ev-overlay, .ev-sheet { animation: none; }
}
/* The same band as the top bar (and the same dark strip behind the status icons), so the button sits on it as before. */
.ev-menu-head {
    box-sizing: border-box;
    height: calc(var(--bar-h) + var(--sat));
    margin: 0 0 .25rem;
    padding: var(--sat) 4.5rem 0 1.25rem;
    display: flex;
    align-items: center;
    background:
        linear-gradient(#111827, #111827) top / 100% var(--sat) no-repeat,
        #ffffff;
    color: #111827;
    border-bottom: 1px solid #e5e7eb;
    font-size: 1.05rem;
    font-weight: 600;
}
.dark .ev-menu-head {
    background: #111827;
    color: #ffffff;
    border-bottom-color: rgba(255, 255, 255, .14);
}
html.ev-menu-open body { overflow: hidden; }

.ev-main {
    padding-top: calc(var(--bar-h) + var(--sat) + 1.25rem);
    padding-bottom: 1.5rem;
    padding-left: max(1rem, var(--sal));
    padding-right: max(1rem, var(--sar));
}

/* The wallet's background: a fixed layer, because background-attachment: fixed is broken on iOS. */
/* Dark whatever the theme (and with "no texture" chosen), so the light text on it always reads. */
body.ev-textured { background-color: #111827; color: #f3f4f6; }
body.ev-textured::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background: var(--ev-texture, none) center / cover no-repeat;
}

/* ── Wallet page ─────────────────────────────────────────────────────── */

/* Room for the search bar (and the keyboard, and the install banner) so the last card scrolls clear of it. */
.ev-wallet { padding-bottom: calc(5.5rem + var(--sab) + var(--kb) + var(--install-h, 0px)); }

.ev-searchbar {
    position: fixed;
    left: 0;
    right: 0;
    /* The install banner (below) sits under it, at the bottom edge; public/js/install-banner.js sets --install-h. */
    bottom: calc(var(--kb) + var(--install-h, 0px));
    z-index: 40;
    box-sizing: border-box;
    padding: .5rem max(.75rem, var(--sar)) max(.5rem, var(--sab)) max(.75rem, var(--sal));
    background: rgba(255, 255, 255, .92);
    border-top: 1px solid rgba(0, 0, 0, .1);
    -webkit-backdrop-filter: blur(5px);
    backdrop-filter: blur(5px);
}
.dark .ev-searchbar {
    background: rgba(17, 24, 39, .9);
    border-top-color: rgba(255, 255, 255, .14);
}
/* With the keyboard up the gesture bar is hidden under it. */
html.kb-open .ev-searchbar { padding-bottom: .5rem; }
/* With the install banner under it the banner has taken the gesture bar's room. */
html.ev-has-install .ev-searchbar { padding-bottom: .5rem; }

/*
 * "Install the app" (public/js/install-banner.js): a bar at the bottom edge, over the wallet's search bar's
 * place, which rides above it. Under the cookie notice (50) and the menu (51 and up), over the search bar (40).
 * The keyboard hides it: it would sit on top of the keyboard otherwise.
 */
.ev-install {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 45;
    box-sizing: border-box;
    display: flex;
    align-items: flex-start;
    gap: .75rem;
    padding: .75rem max(.75rem, var(--sar)) max(.75rem, var(--sab)) max(.75rem, var(--sal));
    background: #ffffff;
    color: #111827;
    border-top: 1px solid #e5e7eb;
    box-shadow: 0 -4px 16px rgba(0, 0, 0, .12);
}
.dark .ev-install { background: #1f2937; color: #f3f4f6; border-top-color: #374151; }
.ev-install[hidden], .ev-install [hidden] { display: none !important; }
html.kb-open .ev-install { display: none; }
html.kb-open { --install-h: 0px !important; }
.ev-install-icon { flex: none; width: 2.75rem; height: 2.75rem; border-radius: .6rem; }
.ev-install-main { flex: 1; min-width: 0; }
.ev-install-title { margin: 0; font-size: .95rem; font-weight: 600; }
.ev-install-text, .ev-install-steps { margin: .2rem 0 0; font-size: .85rem; color: #4b5563; }
.dark .ev-install-text, .dark .ev-install-steps { color: #d1d5db; }
.ev-install-steps { padding-left: 1.2rem; list-style: decimal; } /* the page's reset takes list markers away */
.ev-install-steps li { margin: .15rem 0; }
.ev-install-share { display: inline-block; vertical-align: -.2em; margin: 0 .1em; color: #2563eb; }
.ev-install-btn {
    display: inline-block;
    margin-top: .6rem;
    padding: .45rem 1.1rem;
    border: 0;
    border-radius: .5rem;
    background: #2563eb;
    color: #ffffff;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.ev-install-close {
    flex: none;
    width: 2rem;
    height: 2rem;
    border: 0;
    background: transparent;
    color: inherit;
    font-size: 1.5rem;
    line-height: 1;
    opacity: .6;
    cursor: pointer;
}

/* Rides on the search bar, so it follows the keyboard. */
.ev-fab {
    position: absolute;
    right: max(1rem, var(--sar));
    bottom: calc(100% + 1rem);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.5rem;
    height: 3.5rem;
    border-radius: 50%;
    background: #2563eb;
    color: #ffffff;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
}
.ev-fab:hover { background: #1d4ed8; }

/* Text sitting straight on a texture needs something to read against; the panel follows the theme. */
.ev-panel {
    background: rgba(255, 255, 255, .88);
    color: #111827;
    border: 1px solid rgba(0, 0, 0, .1);
    border-radius: .75rem;
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
}
.dark .ev-panel {
    background: rgba(17, 24, 39, .78);
    color: #f3f4f6;
    border-color: rgba(255, 255, 255, .14);
}

/*
 * Under a card in the list: when and where they met, on a see-through dark plate.
 * The texture behind can be any colour, light or dark; the plate keeps the text
 * readable on all of them (and in both themes, which do not change the texture).
 */
.ev-caption {
    margin: .4rem .5rem 0;
    padding: .35rem .75rem;
    border-radius: .6rem;
    background: rgba(17, 24, 39, .62);
    border: 1px solid rgba(255, 255, 255, .12);
    color: #f3f4f6;
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
}

/* The opened card: a sheet that slides up from the bottom edge of the screen. */
.ev-sheet {
    padding-bottom: max(1rem, var(--sab));
    animation: ev-sheet-in .3s cubic-bezier(.2, .8, .2, 1);
}
@keyframes ev-sheet-in {
    from { transform: translateY(100%); }
    to   { transform: none; }
}
/* Centred on a wide screen, so it rises a little instead. */
@media (min-width: 640px) {
    .ev-sheet { animation-name: ev-sheet-rise; }
}
@keyframes ev-sheet-rise {
    from { transform: translateY(2rem); opacity: 0; }
    to   { transform: none; opacity: 1; }
}

/* Closing: the sheet slides back down, fast but visibly, while the screen clears (wallet.js waits CLOSE_MS for it). */
.ev-overlay.ev-closing {
    pointer-events: none;
    animation: ev-fade-out .2s ease-in forwards;
}
.ev-overlay.ev-closing .ev-sheet { animation: ev-sheet-out .2s ease-in forwards; }
@keyframes ev-fade-out {
    from { opacity: 1; }
    to   { opacity: 0; }
}
@keyframes ev-sheet-out {
    from { transform: translateY(var(--drag-y, 0px)); }
    to   { transform: translateY(100%); }
}
@media (min-width: 640px) {
    .ev-overlay.ev-closing .ev-sheet { animation-name: ev-sheet-sink; }
}
@keyframes ev-sheet-sink {
    from { transform: translateY(var(--drag-y, 0px)); opacity: 1; }
    to   { transform: translateY(2rem); opacity: 0; }
}

/* The knob at the top of the sheet, and what can be pulled down by it (wallet.js, dragStart). */
.ev-grab {
    position: absolute;
    top: .4rem;
    left: 50%;
    width: 2.5rem;
    height: .3rem;
    margin-left: -1.25rem;
    border-radius: 99px;
    background: #d1d5db;
    pointer-events: none;
}
.dark .ev-grab { background: #4b5563; }
.ev-sheet [data-drag] {
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
}

/*
 * Hold the opened card and all of its text is selected, to be copied (wallet.js, selectAll). Until then it
 * is not selectable: pulling the card is a drag, and a drag with the mouse would select text instead.
 */
.ev-sheet.ev-selecting .vc,
.ev-sheet.ev-selecting .vc * {
    -webkit-user-select: text;
    user-select: text;
}
.ev-sheet.ev-selecting .vc ::selection { background: rgba(37, 99, 235, .55); color: #ffffff; }

/*
 * "More" / "Less". A section that slides open and shut: its grid row grows from 0 to
 * the height of its content, which needs no measuring. While shut it is hidden from
 * focus and screen readers once the slide has finished. The gap above it lives in
 * the content (pt-4), so a shut section takes no room at all.
 */
.ev-collapse {
    display: grid;
    grid-template-rows: 0fr;
    margin-top: 0 !important;
    transition: grid-template-rows .25s ease;
}
.ev-collapse.ev-open { grid-template-rows: 1fr; }
.ev-collapse-inner {
    min-height: 0;
    overflow: hidden;
    visibility: hidden;
    transition: visibility 0s linear .25s;
}
.ev-collapse.ev-open > .ev-collapse-inner {
    visibility: visible;
    transition-delay: 0s;
}

/* The note of an opened card: three lines tall at rest; wallet.js sets the height it grows to. */
.ev-note {
    max-height: 3.75rem;
    overflow: hidden;
    transition: max-height .25s ease;
}

@media (prefers-reduced-motion: reduce) {
    .ev-overlay.ev-closing, .ev-overlay.ev-closing .ev-sheet { animation: none; }
    .ev-collapse, .ev-collapse-inner, .ev-note { transition: none; }
}

/* In the preview the card has to stand clear of the sheet, which is white in the light theme. */
.ev-sheet .vc {
    box-shadow:
        0 20px 36px -10px rgba(0, 0, 0, .55),
        0 10px 20px -6px rgba(0, 0, 0, .35),
        0 0 0 1px rgba(0, 0, 0, .12);
}
.dark .ev-sheet .vc {
    box-shadow:
        0 20px 36px -10px rgba(0, 0, 0, .9),
        0 10px 20px -6px rgba(0, 0, 0, .7),
        0 0 0 1px rgba(255, 255, 255, .16);
}

/* The card that is open leaves a grey, see-through ghost of itself in its place in the list. */
.vc-item .vc {
    filter: grayscale(0) opacity(1);
    transition: filter .25s ease;
}
.vc-item.vc-ghost .vc { filter: grayscale(1) opacity(.5); }

.vc {
    --vc-bg: #ffffff;
    --vc-fg: #111827;
    --vc-muted: #6b7280;
    --vc-accent: #2563eb;

    position: relative;
    box-sizing: border-box;
    container-type: inline-size;
    width: 100%;
    max-width: 26rem;
    aspect-ratio: 85 / 55;
    overflow: hidden;
    border-radius: 1rem;
    background: var(--vc-bg);
    color: var(--vc-fg);
    text-align: left;
    box-shadow: 0 10px 25px -8px rgba(0, 0, 0, .25), 0 2px 6px rgba(0, 0, 0, .08);
}

.vc-face {
    position: absolute;
    inset: 0;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 3cqw;
    padding: 6.5cqw 6.5cqw 5.5cqw 8.8cqw;
    overflow: hidden;
}

/*
 * Accent stripe down the left edge (the default arrangement). It is decoration and never takes a
 * touch: in the Elegant design this same pseudo-element is a frame laid over the whole card, and
 * without pointer-events: none it sat on top of the phone, the email and the links.
 */
.vc-face::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 2.2cqw;
    background: var(--vc-accent);
    pointer-events: none;
}

.vc-mark { display: none; }

.vc-head,
.vc-id { min-width: 0; }

.vc-name {
    margin: 0;
    font-size: 6.6cqw;
    line-height: 1.15;
    font-weight: 700;
    overflow-wrap: anywhere;
    /* At most two lines: the card has a fixed size. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
}

.vc-role,
.vc-company {
    margin: .8cqw 0 0;
    font-size: 3.7cqw;
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vc-role { color: var(--vc-muted); }

.vc-company {
    margin-top: .4cqw;
    font-size: 3.9cqw;
    font-weight: 600;
    color: var(--vc-ink, var(--vc-accent));
}

.vc-contacts {
    display: grid;
    gap: .9cqw;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 3.5cqw;
    line-height: 1.3;
}

.vc-contacts li {
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vc-contacts a {
    color: inherit;
    text-decoration: none;
}

.vc-contacts a:hover,
.vc-contacts a:focus-visible {
    text-decoration: underline;
}

/* ── Designs ─────────────────────────────────────────────────────────── */

/* Classic, Dark and Colorful share the default arrangement: a stripe on the left. */

.vc-theme-classic {
    --vc-bg: #ffffff;
    --vc-fg: #111827;
    --vc-muted: #6b7280;
    --vc-accent: #2563eb;
}

.vc-theme-dark {
    --vc-bg: #111827;
    --vc-fg: #f9fafb;
    --vc-muted: #9ca3af;
    --vc-accent: #60a5fa;
}

.vc-theme-accent {
    --vc-bg: linear-gradient(135deg, var(--vc-a, #2563eb) 0%, var(--vc-b, #7c3aed) 100%);
    --vc-fg: #ffffff;
    --vc-muted: rgba(255, 255, 255, .8);
    --vc-accent: #fde68a;
}

/* Split: a coloured panel with the initials, the details beside it. */
.vc-theme-split {
    --vc-bg: #ffffff;
    --vc-fg: #0f172a;
    --vc-muted: #64748b;
    --vc-accent: #0f2b4a;
}
.vc-theme-split .vc-face {
    display: grid;
    grid-template-columns: 31% 1fr;
    grid-template-rows: 1fr auto;
    grid-template-areas: "mark head" "mark contacts";
    gap: 0;
    padding: 0;
}
.vc-theme-split .vc-face::before { display: none; }
.vc-theme-split .vc-mark {
    grid-area: mark;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--vc-accent);
    color: var(--vc-on, #ffffff);
    font-size: 11cqw;
    font-weight: 700;
    letter-spacing: .04em;
}
.vc-theme-split .vc-head { grid-area: head; padding: 7cqw 6cqw 0 6.5cqw; }
.vc-theme-split .vc-contacts { grid-area: contacts; padding: 0 6cqw 6cqw 6.5cqw; }

/* Elegant: centred, serif name, a thin frame. */
.vc-theme-centered {
    --vc-bg: #fbf7ef;
    --vc-fg: #2b2118;
    --vc-muted: #8a7a66;
    --vc-accent: #a8782a;
}
.vc-theme-centered .vc-face {
    align-items: center;
    justify-content: flex-start;
    gap: 3cqw;
    padding: 8.5cqw 9cqw 7cqw;
    text-align: center;
}
.vc-theme-centered .vc-face::before {
    inset: 3cqw;
    width: auto;
    background: none;
    border: .3cqw solid var(--vc-accent);
    border-radius: 1.2cqw;
    opacity: .55;
}
.vc-theme-centered .vc-head { width: 100%; }
.vc-theme-centered .vc-name {
    font-family: Georgia, "Times New Roman", serif;
    font-size: 6.4cqw;
    font-weight: 600;
}
.vc-theme-centered .vc-contacts {
    width: 100%;
    margin-top: auto;
    justify-items: center;
    font-size: 3.3cqw;
    line-height: 1.25;
}

/* Banner: name and role on a coloured band across the top. */
.vc-theme-band {
    --vc-bg: #ffffff;
    --vc-fg: #0f172a;
    --vc-muted: #64748b;
    --vc-accent: #0e7490;
}
.vc-theme-band .vc-face {
    display: grid;
    grid-template-rows: 27cqw auto 1fr;
    gap: 0;
    padding: 0;
}
.vc-theme-band .vc-face::before { inset: 0 0 auto 0; width: auto; height: 27cqw; }
.vc-theme-band .vc-head { display: contents; }
.vc-theme-band .vc-id { position: relative; padding: 5.5cqw 7cqw 0; color: var(--vc-on, #ffffff); }
.vc-theme-band .vc-name { font-size: 6cqw; }
.vc-theme-band .vc-role { color: var(--vc-on, #ffffff); opacity: .85; }
.vc-theme-band .vc-company { margin: 0; padding: 3.2cqw 7cqw 0; }
.vc-theme-band .vc-contacts { align-self: end; padding: 0 7cqw 5cqw; }

/* Monogram: initials in a circle next to the name, contacts in two columns. */
.vc-theme-badge {
    --vc-bg: #f1f5f9;
    --vc-fg: #0f172a;
    --vc-muted: #64748b;
    --vc-accent: #7c3aed;
}
.vc-theme-badge .vc-face {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-rows: 1fr auto;
    grid-template-areas: "mark head" "contacts contacts";
    column-gap: 4.5cqw;
    padding: 6.5cqw 7cqw 6cqw;
}
.vc-theme-badge .vc-face::before { display: none; }
.vc-theme-badge .vc-mark {
    grid-area: mark;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 17cqw;
    height: 17cqw;
    border-radius: 50%;
    background: var(--vc-accent);
    color: var(--vc-on, #ffffff);
    font-size: 6.4cqw;
    font-weight: 700;
}
.vc-theme-badge .vc-head { grid-area: head; }
.vc-theme-badge .vc-contacts {
    grid-area: contacts;
    grid-template-columns: 1fr 1fr;
    column-gap: 3cqw;
    padding-top: 3cqw;
    border-top: .3cqw solid rgba(15, 23, 42, .12);
    font-size: 3.3cqw;
}

/* Minimal: big type, spaced capitals, contacts in monospace on the right. */
.vc-theme-mono {
    --vc-bg: #f5f5f4;
    --vc-fg: #1c1917;
    --vc-muted: #78716c;
    --vc-accent: #1c1917;
}
/* The rule sits close to the top edge so that the name still starts at 6.5cqw like the others (see "The pile" below). */
.vc-theme-mono .vc-face { padding: 3.5cqw 7cqw 6cqw; }
.vc-theme-mono .vc-face::before { display: none; }
.vc-theme-mono .vc-head::before {
    content: "";
    display: block;
    width: 9cqw;
    height: 1cqw;
    margin-bottom: 2cqw;
    background: var(--vc-accent);
}
.vc-theme-mono .vc-name { font-size: 7.4cqw; font-weight: 800; letter-spacing: -.02em; }
.vc-theme-mono .vc-role,
.vc-theme-mono .vc-company {
    font-size: 3cqw;
    font-weight: 500;
    letter-spacing: .14em;
    text-transform: uppercase;
}
.vc-theme-mono .vc-company { font-weight: 700; }
.vc-theme-mono .vc-contacts {
    justify-items: end;
    text-align: right;
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 3.2cqw;
}

/* Bold: vivid background, capitals, the initials as a large watermark. */
.vc-theme-bold {
    --vc-bg: linear-gradient(155deg, var(--vc-a, #f97316) 0%, var(--vc-b, #db2777) 100%);
    --vc-fg: #ffffff;
    --vc-muted: rgba(255, 255, 255, .85);
    --vc-accent: #ffffff;
}
.vc-theme-bold .vc-face::before { display: none; }
.vc-theme-bold .vc-mark {
    display: block;
    position: absolute;
    right: -3cqw;
    bottom: -12cqw;
    font-size: 50cqw;
    font-weight: 900;
    line-height: 1;
    letter-spacing: -.05em;
    color: var(--vc-fg);
    opacity: .16;
    pointer-events: none;
}
.vc-theme-bold .vc-head,
.vc-theme-bold .vc-contacts { position: relative; }
.vc-theme-bold .vc-name {
    font-size: 7cqw;
    font-weight: 900;
    text-transform: uppercase;
}
.vc-theme-bold .vc-company {
    display: block;
    width: fit-content;
    max-width: 100%;
    margin-top: 2cqw;
    padding: .7cqw 2.6cqw;
    border-radius: 99px;
    background: rgba(255, 255, 255, .22);
    color: var(--vc-fg);
    font-size: 3.4cqw;
}

/*
 * Herbalife, for an independent member. The mark is the company's: three leaves in its green
 * (rgb 38 100 49), cut off by the right and bottom edges so that only a hint of it shows. The
 * stripe has the colours of the level tabs on the company's own site. The leaves are a picture
 * inlined here, so the card needs no second request and looks the same offline.
 * The mark is decoration and never takes a touch (pointer-events: none), and the text is laid
 * over it, not under it. It is the company's trademark: docs/PRODUCT.md ("herbalife") says to
 * decide who may use the design before the public launch.
 */
.vc-theme-herbalife {
    --vc-bg: #ffffff;
    --vc-fg: #121921;
    --vc-muted: #5d6b65;
    --vc-accent: #266431;
}
.vc-theme-herbalife .vc-face::before {
    background: linear-gradient(180deg, #3c9e43 0%, #1a7142 38%, #1b3e35 70%, #121921 100%);
}
.vc-theme-herbalife .vc-face::after {
    content: "";
    position: absolute;
    /* About three quarters of the mark shows: enough to read as the three leaves, no more. */
    right: -7cqw;
    bottom: -8cqw;
    width: 32cqw;
    height: 38.2cqw; /* the mark is 22.5 : 26.85 */
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-19020.05 -25.4 22.5 26.85'%3E%3Cpath fill='%23266431' d='m -19006.157,0.98830167 c -0.799,-1.4364 -1.161,-2.69401507 -1.159,-4.02168407 0,-2.398055 1.274,-4.549355 4.208,-7.1227246 0.273,-0.23939 0.976,-0.835862 1.562,-1.325494 1.304,-1.089964 1.809,-1.544227 2.305,-2.075341 0.105,-0.111917 0.194,-0.19954 0.198,-0.194719 0.05,0.05859 0.354,0.612476 0.479,0.860872 1.449,2.88545 1.226,5.4685636 -0.693,8.0395316 -0.401,0.536148 -0.799,0.988557 -1.39,1.578907 -0.663,0.662575 -1.153,1.097996 -2.504,2.22455 -1.45,1.20840107 -1.688,1.41806007 -2.259,1.98894707 -0.269,0.26895893 -0.492,0.48901593 -0.495,0.48901593 0,0 -0.117,-0.198837 -0.252,-0.44186093 z m -4.311,-9.19448007 c -0.588,-0.04989 -1.106,-0.154014 -1.635,-0.328739 -1.749,-0.578238 -3.214,-1.9062276 -4.54,-4.1145586 -0.527,-0.878294 -0.93,-1.667958 -1.742,-3.415061 -0.722,-1.555953 -1.115,-2.321213 -1.471,-2.867524 l -0.161,-0.246314 0.111,-0.02967 c 1.775,-0.473348 3.131,-0.541158 4.442,-0.222152 1.893,0.460264 3.495,1.760999 4.87,3.952462 0.549,0.876923 1.077,1.892806 1.896,3.652074 0.753,1.619675 1.144,2.3821716 1.526,2.9768066 l 0.133,0.208074 -0.23,0.06178 c -0.734,0.196868 -1.34,0.308092 -1.97,0.361783 -0.336,0.02867 -0.956,0.03424 -1.229,0.01105 z m 4.329,-2.4289366 c -0.621,-1.13221 -0.969,-2.143072 -1.104,-3.212628 -0.04,-0.326997 -0.04,-1.198962 0,-1.527791 0.13,-1.041989 0.555,-2.150404 1.199,-3.130473 0.916,-1.392039 2.005,-2.487625 4.66,-4.686506 0.931,-0.771771 1.514,-1.292046 1.969,-1.76064 l 0.408,-0.419257 0.258,0.467491 c 0.596,1.078441 0.927,2.0082 1.106,3.103678 0.06,0.364218 0.05,1.510475 -0.01,1.885984 -0.308,1.76309 -1.216,3.309624 -2.974,5.064184 -0.655,0.654237 -1.143,1.087611 -2.566,2.277083 -1.335,1.11605 -1.98,1.690195 -2.372,2.111888 -0.153,0.164467 -0.285,0.298929 -0.294,0.298804 0,-1.24e-4 -0.133,-0.212442 -0.275,-0.471817 z'/%3E%3C/svg%3E") no-repeat center / contain;
    pointer-events: none;
}
.vc-theme-herbalife .vc-head,
.vc-theme-herbalife .vc-contacts { position: relative; z-index: 1; }
.vc-theme-herbalife .vc-name { color: #1b3e35; font-weight: 800; }
.vc-theme-herbalife .vc-company { font-weight: 700; }
/* Long details stop short of the mark. */
.vc-theme-herbalife .vc-contacts { max-width: 62cqw; }
.vc-theme-herbalife .vc-contacts li::before {
    content: "";
    display: inline-block;
    width: 1.1cqw;
    height: 1.1cqw;
    margin-right: 2cqw;
    border-radius: 50%;
    background: #3c9e43;
    vertical-align: .12em;
}

/* Small previews (design picker): the corners shouldn't dominate. */
.vc-mini .vc { border-radius: .4rem; box-shadow: 0 1px 4px rgba(0, 0, 0, .2); }

/*
 * The pile. In the wallet's pile a card shows its top 26cqw (of 64.7): the next card covers the
 * rest. The name and the role belong in the top 25cqw, so that they stay readable above the
 * edge of the next card and its shadow. Every design keeps them there for a name on one line
 * (a name on two lines pushes the role below it: the editor says so, see PILE_SAFE in
 * card-editor.js; keep it equal to the number here, and 26 equal to .vc-stack's overlap).
 *
 * The editor shows the whole card, and below it a small pile of two cards (.ev-pile-demo), which
 * is how the card will lie in the wallet. The pile is only a picture.
 */
.ev-card-preview {
    max-width: 26rem; /* as wide as the pile below it */
}

/* 26cqw of the card in front, then a strip of the next one; the aspect-ratio follows the card's width. */
.ev-pile-demo {
    width: 100%;
    max-width: 26rem;
    aspect-ratio: 100 / 38;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(#000 78%, transparent);
    mask-image: linear-gradient(#000 78%, transparent);
}
.ev-pile-demo .vc-stack { padding-top: .5rem; }
.ev-pile-demo .vc-stack > .vc-item,
.ev-pile-demo .vc-stack > .vc-item:hover { cursor: default; transform: none; }
.vc.ev-pile-next { --vc-bg: #e5e7eb; }
.dark .vc.ev-pile-next { --vc-bg: #374151; }
.ev-pile-next .vc-face::before { display: none; }

/*
 * The editor's form as an accordion of native <details name="ev-sections">: Information, Design,
 * More. One is open at a time, the heading says what is in a closed one, and the chevron is drawn
 * here (the browser's own marker is hidden). It works without script, too.
 */
.ev-acc {
    border: 1px solid #cbd5e1;
    border-radius: .75rem;
}
.dark .ev-acc { border-color: #4b5563; }
/* A tinted bar, darker than the page it lies on: it reads as something to press. */
.ev-acc-head {
    display: flex;
    align-items: center;
    gap: .75rem;
    min-height: 3rem; /* a thumb-sized row */
    padding: .5rem 1rem;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
    border-radius: calc(.75rem - 1px);
    background: #e2e8f0;
    transition: background-color .12s;
}
.ev-acc-head:hover { background: #cbd5e1; }
.dark .ev-acc-head { background: #374151; }
.dark .ev-acc-head:hover { background: #4b5563; }
.ev-acc[open] > .ev-acc-head {
    border-radius: calc(.75rem - 1px) calc(.75rem - 1px) 0 0;
    border-bottom: 1px solid #cbd5e1;
}
.dark .ev-acc[open] > .ev-acc-head { border-bottom-color: #4b5563; }
.ev-acc-head::-webkit-details-marker { display: none; }
.ev-acc-head::after {
    content: "";
    flex: none;
    width: .55rem;
    height: .55rem;
    margin: 0 .25rem 0 auto;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
    transition: transform .15s;
}
.ev-acc[open] > .ev-acc-head::after { transform: translateY(1px) rotate(-135deg); }
.ev-acc-head:focus-visible { outline: 3px solid #93c5fd; outline-offset: -3px; }
/* What the part holds, while it is closed (a part that is open shows it). */
.ev-acc-sum {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 400;
    font-size: .875rem;
    color: #475569;
}
.dark .ev-acc-sum { color: #d1d5db; }
.ev-acc[open] .ev-acc-sum { display: none; }
.ev-acc-body { padding: 1rem; }

/* The small "?" beside a label: a short explanation that is shown only when asked for. */
.ev-help {
    position: relative;
    flex: none;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 1px solid #94a3b8;
    border-radius: 50%;
    background: transparent;
    color: #475569;
    font-size: .8rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
}
.dark .ev-help { border-color: #9ca3af; color: #d1d5db; }
.ev-help[aria-expanded="true"] { background: #2563eb; border-color: #2563eb; color: #ffffff; }
/* The target is thumb-sized though the circle is not. */
.ev-help::after { content: ""; position: absolute; inset: -.55rem; }

/*
 * The designs to choose from in the editor. Three to a row where the form is wide. Where it is narrow
 * (a phone, a tablet's half of the page) they are two rows that scroll sideways, a little of the
 * next column showing: the whole list no longer takes the better part of a screen, and the colours
 * below it are in view together with the design that is chosen. It goes by the width of the form
 * itself (a container query), not the screen's.
 */
.ev-designs { container-type: inline-size; }
.ev-design-list {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .75rem;
}
@container (max-width: 24rem) {
    .ev-design-list {
        position: relative; /* the offsetParent card-editor.js scrolls by */
        grid-template-columns: none;
        grid-template-rows: repeat(2, auto);
        grid-auto-flow: column;
        /* Two columns and a third of the next one, whatever the width, so that the cut-off edge says "there is more". */
        grid-auto-columns: clamp(7rem, calc((100cqw - 6px - .75rem) / 2.35), 8.5rem);
        overflow-x: auto;
        overscroll-behavior-x: contain; /* the end of the list is not a swipe back to the last page */
        scroll-snap-type: x proximity;
        scrollbar-width: thin;
        /* The chosen design's ring would be cut by the edge of the scroller without room around it. */
        padding: 3px 3px .6rem;
        margin: -3px -3px 0;
    }
    .ev-design-list > label { scroll-snap-align: start; }
}

/*
 * The card and the pile at their real size, in a native <dialog> opened from the small preview.
 * The card is as wide as in the wallet (26rem at most, or the screen less a margin).
 */
.ev-full {
    box-sizing: border-box;
    width: min(26rem, calc(100vw - 2rem));
    max-height: calc(100dvh - 2rem);
    padding: 0;
    border: 0;
    border-radius: 1rem;
    overflow: auto;
    background: #f3f4f6;
    color: #111827;
}
.dark .ev-full { background: #111827; color: #f3f4f6; }
.ev-full::backdrop { background: rgba(0, 0, 0, .6); }
.ev-full-body { position: relative; padding: 1rem; }
.ev-full-card .vc { max-width: none; }
.ev-full .ev-pile-demo { max-width: none; }
.ev-full-close {
    position: absolute;
    top: .5rem;
    right: .5rem;
    width: 2.75rem;
    height: 2.75rem;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: inherit;
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
}

/*
 * A card of the size it has in the wallet, kept out of sight (not display: none, which has no
 * layout to measure): the editor measures the name and the role on it. The width is the card's own:
 * 26rem at most, or the screen's less the page's side space.
 */
.ev-ruler {
    position: fixed;
    top: 0;
    left: -9999px;
    width: min(26rem, calc(100vw - 2rem));
    visibility: hidden;
    pointer-events: none;
}

/*
 * Colour choice in the editor (card-editor.js): the ready-made palettes as round chips, then the
 * colours themselves in the browser's own colour picker. Both are as big as a thumb (2.75rem).
 */
.ev-swatch {
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .22);
}
.dark .ev-swatch { box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .35); }
.ev-swatch[aria-pressed="true"] { outline: 3px solid #2563eb; outline-offset: 2px; }
.dark .ev-swatch[aria-pressed="true"] { outline-color: #93c5fd; }

/* A colour of the card, as a button that opens its sliders (.ev-picker). */
.ev-slot {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font-size: .875rem;
    cursor: pointer;
}
.ev-color {
    flex: none;
    width: 2.75rem;
    height: 2.75rem;
    border: 1px solid #9ca3af;
    border-radius: .5rem;
}
.ev-slot[aria-expanded="true"] .ev-color { outline: 3px solid #2563eb; outline-offset: 2px; }
.dark .ev-slot[aria-expanded="true"] .ev-color { outline-color: #93c5fd; }

/* The sliders of one colour: hue, saturation, lightness, and its code. The track shows the way the colour would go. */
.ev-picker {
    margin-top: .75rem;
    padding: .75rem;
    border: 1px solid #d1d5db;
    border-radius: .5rem;
}
.dark .ev-picker { border-color: #4b5563; }
.ev-picker > * + * { margin-top: .35rem; }
.ev-range {
    -webkit-appearance: none;
    appearance: none;
    display: block;
    width: 100%;
    height: 2rem; /* a thumb-sized row, the track itself is thinner */
    margin: 0;
    background: transparent;
    cursor: pointer;
    touch-action: pan-y; /* sideways drags the slider, up and down still scrolls the page */
}
.ev-range::-webkit-slider-runnable-track {
    height: .8rem;
    border-radius: 99px;
    background: var(--track);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .25);
}
.ev-range::-moz-range-track {
    height: .8rem;
    border-radius: 99px;
    background: var(--track);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .25);
}
.ev-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 1.75rem;
    height: 1.75rem;
    margin-top: -.475rem;
    border: 3px solid #ffffff;
    border-radius: 50%;
    background: transparent;
    box-shadow: 0 0 0 1.5px #111827, 0 1px 4px rgba(0, 0, 0, .5);
}
.ev-range::-moz-range-thumb {
    width: 1.5rem;
    height: 1.5rem;
    border: 3px solid #ffffff;
    border-radius: 50%;
    background: transparent;
    box-shadow: 0 0 0 1.5px #111827, 0 1px 4px rgba(0, 0, 0, .5);
}

/*
 * The preview stays in view while the form is filled in, below the top bar, which is fixed.
 *
 * Wide screen: a column of its own beside the form, the card above the pile.
 *
 * Phone: a strip across the top of the page, the card and the pile side by side and small (the
 * sizes follow the card's width, so nothing else changes), so that it leaves room for the fields,
 * the on-screen keyboard included. It reaches the screen's edges and has the page's own colour
 * (body: bg-gray-100, dark:bg-gray-900): the form scrolls under it and must not show through.
 *
 * A phone held sideways is wide but short: it has the column, with the same two small previews
 * (a card above a pile would be taller than the screen).
 */
@media (min-width: 768px) {
    .ev-preview-col {
        position: sticky;
        top: calc(var(--bar-h) + var(--sat) + 1rem);
    }
}
@media (max-width: 767px) {
    .ev-preview-col {
        position: sticky;
        top: calc(var(--bar-h) + var(--sat));
        z-index: 20;
        margin: 0 calc(-1 * max(1rem, var(--sar))) 0 calc(-1 * max(1rem, var(--sal)));
        padding: .5rem max(1rem, var(--sar)) .6rem max(1rem, var(--sal));
        background: #f3f4f6;
        box-shadow: 0 6px 10px -6px rgba(0, 0, 0, .4);
    }
    .dark .ev-preview-col { background: #111827; }
    /* A field brought into view by focus must not stop under the strip (about 9.5rem tall). */
    html:has(.ev-preview-col) { scroll-padding-top: calc(var(--bar-h) + var(--sat) + 10.5rem); }
}
/* Where the preview is already as large as a card gets there is nothing to enlarge: shown in the small layout below. */
.ev-preview-full { display: none; }
@media (max-width: 767px), (max-height: 600px) {
    .ev-preview-grid {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        gap: .75rem;
        align-items: start;
    }
    .ev-preview-grid h3 { margin: 0 0 .25rem; font-size: .7rem; line-height: 1.1; }
    .ev-preview-hint { display: none; }
    /* The pile is shorter than the card beside it: the button goes in the room that leaves. */
    .ev-preview-full {
        display: block;
        width: 100%;
        min-height: 2.25rem;
        margin-top: .35rem;
        padding: .25rem .5rem;
        border: 1px solid #9ca3af;
        border-radius: .5rem;
        background: transparent;
        color: inherit;
        font-size: .75rem;
        line-height: 1.2;
        cursor: pointer;
    }
}
/* A phone with the keyboard open has about 400px of screen: the two previews shrink to leave the fields room. */
@media (max-width: 767px) and (max-height: 520px) {
    .ev-preview-grid { grid-template-columns: repeat(2, minmax(0, 8.5rem)); justify-content: center; }
    html:has(.ev-preview-col) { scroll-padding-top: calc(var(--bar-h) + var(--sat) + 7.5rem); }
}

/* ── Wallet layouts ──────────────────────────────────────────────────── */

/* Every card in the list is the same size: one fixed column width, one fixed proportion. */
.vc-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(17rem, 22rem));
    justify-content: center;
    gap: 1.5rem 1.25rem;
}

.vc-grid .vc { max-width: none; }

/*
 * A pile of cards: each one covers all but the top of the one before it, so
 * the top ~26% of every card shows. The sizes follow the card width
 * (--w), which is why the pile is a container itself: cqw on .vc-item is
 * measured against .vc-stack.
 */
.vc-stack {
    container-type: inline-size;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-top: .5rem;
}

.vc-stack > .vc-item {
    --w: min(100cqw, 26rem);
    width: var(--w);
    /* card height is 55/85 = 0.647 of the width; 0.26 of the width stays visible */
    margin-top: calc(var(--w) * -0.387);
    transition: transform .15s ease;
}

/* first-of-type, not first-child: Alpine's <template x-for> sits before the items. */
.vc-stack > .vc-item:first-of-type { margin-top: 0; }
.vc-stack > .vc-item:hover,
.vc-stack > .vc-item:focus-visible { transform: translateY(-.5rem); }
/* The card that is open (it keeps its ghost in the pile) rises a little further out of it than a hover does. */
.vc-stack > .vc-item.vc-ghost,
.vc-stack > .vc-item.vc-ghost:hover { transform: translateY(-1rem); }

/* A faint shadow along the top edge, falling on the card underneath. */
.vc-stack .vc {
    box-shadow: 0 -.6rem .6rem -.4rem rgba(0, 0, 0, .5), 0 1px 2px rgba(0, 0, 0, .08);
}

/* The empty wallet shows a blank card that says what to do; its text scales with the card like any other. */
.vc-empty { margin-inline: auto; }
.vc-empty .vc-face { justify-content: center; gap: 4cqw; }
.vc-empty-msg {
    margin: 0;
    font-size: 4.8cqw;
    line-height: 1.3;
    font-weight: 600;
}
.vc-empty-note {
    margin: 0;
    font-size: 3.4cqw;
    line-height: 1.35;
    color: var(--vc-muted);
}
.vc-empty-note a { color: var(--vc-accent); text-decoration: underline; }

.vc-item {
    cursor: pointer;
    border-radius: 1rem;
    /* Holding a card must not select its text or bring up the browser's own menu. */
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}

/*
 * Privacy mode (privacy.js, wallet.js lock/unlock): the list is blurred, and a frosted veil covers the whole
 * screen, the texture too, so it is plain that everything is covered; only the "Show" button on it lifts it.
 * The cards under it are inert in the view, so nothing reaches them by tap or keyboard. The cards' own blur is
 * what keeps them unreadable where backdrop-filter is missing (the veil's blur is the same look, not the safeguard).
 * The veil sits under the top bar (40, with the menu button above it) and the search bar (40), so those stay
 * where they are; of the search bar only the button for the person's own card is left.
 */
.ev-shield-body { transition: filter .2s ease; }
.ev-shielded .ev-shield-body {
    filter: blur(6px) brightness(.9);
    pointer-events: none;
    -webkit-user-select: none;
    user-select: none;
}
.ev-shield-veil {
    position: fixed;
    inset: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(17, 24, 39, .35);
    -webkit-backdrop-filter: blur(5px);
    backdrop-filter: blur(5px);
}
/* Nothing to scroll to under the veil (wallet.js takes it to the top first). */
body:has(.ev-shielded) { overflow: hidden; }
/* The search bar steps aside; its button, the one to the person's own card, stays within reach. */
.ev-wallet:has(.ev-shielded) .ev-searchbar {
    background: none;
    border-top: 0;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    pointer-events: none;
}
.ev-wallet:has(.ev-shielded) .ev-fab { pointer-events: auto; }
.ev-shield-pill {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: .75rem 1.5rem;
    border-radius: 9999px;
    background: #2563eb;
    color: #fff;
    font-weight: 600;
    box-shadow: 0 .25rem 1rem rgba(0, 0, 0, .35);
    cursor: pointer;
}
.ev-shield-pill:hover { background: #1d4ed8; }
.ev-shield-pill:focus-visible { outline: none; box-shadow: 0 0 0 3px #fff, 0 0 0 6px #2563eb; }
@media (prefers-reduced-motion: reduce) {
    .ev-shield-body { transition: none; }
}

/*
 * Peek: hold a card in the list and a copy rises to the front over the blurred page, larger
 * and with its note; let go and it flies back to its place (wallet.js, peekStart/peekRelease).
 * The flight is a Web Animation on .ev-peek-card, the idle float on .ev-peek-float; the rest is here.
 * It is only a picture for the pointer, so it never takes a click.
 */
.ev-peek {
    position: fixed;
    inset: 0;
    z-index: 58;
    pointer-events: none;
}
.ev-peek-dim {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, .5);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    animation: ev-fade-in .25s ease-out;
}
.ev-peek.ev-leaving .ev-peek-dim { animation: ev-fade-out .26s ease-in forwards; }

/* The card and its note are placed by wallet.js (layoutPeek): where the card lies in the list. */
.ev-peek-stage { position: absolute; inset: 0; }
.ev-peek-card { position: absolute; left: 0; top: 0; width: min(94vw, 28rem); }
.ev-peek-float { position: relative; }
.ev-peek-card .vc {
    max-width: none;
    width: 100%;
    box-shadow:
        0 38px 70px -14px rgba(0, 0, 0, .75),
        0 14px 28px -8px rgba(0, 0, 0, .5),
        0 0 0 1px rgba(255, 255, 255, .1);
}

/* A glint that sweeps across the card as it lands. */
.ev-peek-shine {
    position: absolute;
    inset: 0;
    border-radius: 1rem;
    overflow: hidden;
    pointer-events: none;
}
.ev-peek-shine::before {
    content: "";
    position: absolute;
    top: -25%;
    bottom: -25%;
    left: 0;
    width: 40%;
    background: linear-gradient(105deg, transparent 0%, rgba(255, 255, 255, .6) 50%, transparent 100%);
    transform: translateX(-140%) skewX(-14deg);
    animation: ev-shine .9s .35s ease-out forwards;
}
@keyframes ev-shine {
    to { transform: translateX(360%) skewX(-14deg); }
}

.ev-peek-info {
    position: absolute;
    left: 0;
    top: 0;
    box-sizing: border-box;
    width: min(88vw, 26rem);
    padding: .6rem .9rem;
    border-radius: .75rem;
    background: rgba(17, 24, 39, .72);
    border: 1px solid rgba(255, 255, 255, .14);
    color: #f3f4f6;
    font-size: .9rem;
    line-height: 1.35;
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    animation: ev-peek-info-in .3s .22s both ease-out;
}
.ev-peek-info p { margin: 0; }
.ev-peek-info .ev-peek-note {
    margin-top: .3rem;
    color: #d1d5db;
    white-space: pre-line;
    overflow-wrap: anywhere;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.ev-peek.ev-leaving .ev-peek-info { animation: ev-peek-info-out .18s ease-in forwards; }
/* Above the card, the note comes and goes from the other side. */
.ev-peek-info.ev-above { animation-name: ev-peek-info-in-up; }
.ev-peek.ev-leaving .ev-peek-info.ev-above { animation: ev-peek-info-out-up .18s ease-in forwards; }
@keyframes ev-peek-info-in-up {
    from { opacity: 0; transform: translateY(-14px); }
    to   { opacity: 1; transform: none; }
}
@keyframes ev-peek-info-out-up {
    from { opacity: 1; transform: none; }
    to   { opacity: 0; transform: translateY(-8px); }
}
@keyframes ev-peek-info-in {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}
@keyframes ev-peek-info-out {
    from { opacity: 1; transform: none; }
    to   { opacity: 0; transform: translateY(8px); }
}
@media (prefers-reduced-motion: reduce) {
    .ev-peek-shine { display: none; }
    .ev-peek-dim, .ev-peek-info { animation-duration: .01s; animation-delay: 0s; }
}
.vc-item:focus-visible { outline: 3px solid #93c5fd; outline-offset: 2px; }

/* ── QR page ─────────────────────────────────────────────────────────── */

.qr-page {
    /* The padding (with the room kept for the bar below) counts into the height: otherwise the
       content is centred in a box that reaches under the bar and the page scrolls for nothing. */
    box-sizing: border-box;
    margin: 0;
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.25rem;
    /* The bottom is kept free for the fixed bar of buttons. */
    padding: max(1.5rem, var(--sat)) max(1.5rem, var(--sar)) calc(5.5rem + var(--sab)) max(1.5rem, var(--sal));
    background: #ffffff;
    color: #111827;
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    text-align: center;
}

/*
 * An installed iOS app draws under the status bar with white icons, which
 * vanish on this white page: a dark strip the height of the inset sits behind
 * them. In an ordinary browser --sat is 0 and the strip has no height.
 */
.qr-page::before {
    content: "";
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--sat);
    background: #111827;
}

#qr svg {
    width: min(80vw, 46vh, 26rem);
    height: auto;
    image-rendering: pixelated;
}

/*
 * The owner's cards under the code, side by side: a swipe moves to the next one and it
 * snaps to the middle (public/js/qr-page.js puts that card's code above). The row is
 * as wide as the screen, so the neighbours show at the edges and hint at the swipe.
 * With one card nothing overflows and it looks as a lone card always did.
 */
.qr-page { overflow-x: clip; }
.qr-cards {
    position: relative; /* a card's offsetLeft is measured from here */
    display: flex;
    gap: 1rem;
    box-sizing: border-box;
    width: 100vw;
    /* Room before the first and after the last card, so each can sit in the middle (half a card is min(40vw, 11rem)). */
    padding: .25rem calc(50vw - min(40vw, 11rem));
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain; /* the end of the row must not start the browser's "back" swipe */
    scrollbar-width: none;
}
.qr-cards::-webkit-scrollbar { display: none; }

/* It is on display: a stray tap must not dial or write to anyone. */
.qr-card {
    flex: none;
    width: min(80vw, 22rem);
    scroll-snap-align: center;
    scroll-snap-stop: always; /* a hard swipe moves one card, not all of them */
    transition: opacity .2s, transform .2s;
}
.qr-card .vc { max-width: none; }
.qr-card a { pointer-events: none; }

/*
 * The owner's name for the card, above its right edge. It only tells the cards apart (when
 * their details are alike), so it is small and grey, not something to look at. One line, cut
 * with an ellipsis.
 */
.qr-card-label {
    margin: 0 0 .3rem;
    overflow: hidden;
    text-align: right;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: .75rem;
    line-height: 1.2;
    color: #6b7280;
}

/* The card whose code is on screen stands out; a tap on another one brings it to the middle. */
.qr-cards-many .qr-card:not(.qr-card-on) { opacity: .45; transform: scale(.94); cursor: pointer; }

/* The dots also serve a mouse, which cannot swipe. */
.qr-dots { display: flex; gap: .25rem; }
.qr-dots[hidden] { display: none; }
.qr-dots button {
    position: relative;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}
.qr-dots button::before {
    content: "";
    position: absolute;
    inset: .45rem;
    border-radius: 50%;
    background: #d1d5db;
}
.qr-dots button[aria-current="true"]::before { background: #2563eb; }
@media (prefers-reduced-motion: reduce) {
    .qr-card { transition: none; }
}

.qr-sub { margin: 0; color: #6b7280; font-size: .85rem; }

/* Manual entry: the digits take the QR code's place (the button in the bar below swaps them). */
.qr-code {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .6rem;
    width: min(86vw, 26rem);
}
.qr-code[hidden] { display: none; }
.qr-code-label { margin: 0; color: #6b7280; font-size: .9rem; }
.qr-code-digits {
    margin: 0;
    /* Nine digits and two spaces on one line: about 7.3em, which has to fit in the box's 86vw. */
    font: 700 clamp(1.8rem, 10.5vw, 3.2rem) / 1.1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    letter-spacing: .06em;
    white-space: nowrap;
}
.qr-code-digits.qr-code-expired { color: #9ca3af; text-decoration: line-through; }
.qr-code-msg { margin: 0; color: #b91c1c; font-size: .9rem; }
.qr-code-msg a { color: inherit; text-decoration: underline; }

/* Fixed to the bottom edge, clear of the gesture bar. */
.qr-actions {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    box-sizing: border-box;
    display: flex;
    justify-content: center;
    gap: .75rem;
    padding: .75rem max(1rem, var(--sar)) max(.75rem, var(--sab)) max(1rem, var(--sal));
    background: #ffffff;
    border-top: 1px solid #e5e7eb;
}
.qr-actions .qr-btn {
    flex: 1;
    max-width: 14rem;
    box-sizing: border-box;
    text-align: center;
    /* The buttons are as tall as the taller one ("Ръчно въвеждане" wraps on a phone),
       so the label of the other would sit at the top. Scoped here: #qr-code-new uses
       .qr-btn too and is hidden with [hidden], which a display rule here would override. */
    display: flex;
    align-items: center;
    justify-content: center;
}

.qr-btn {
    padding: .6rem 1.25rem;
    border: 1px solid #2563eb;
    border-radius: .5rem;
    background: #2563eb;
    color: #fff;
    font: inherit;
    text-decoration: none;
    cursor: pointer;
}
.qr-btn-ghost { background: transparent; color: #2563eb; }

/* A short note over the page (public/js/share-link.js): "Link copied". Above the bar of the QR page. */
.ev-toast {
    position: fixed;
    left: 50%;
    bottom: calc(var(--sab) + 6.5rem);
    transform: translateX(-50%);
    z-index: 80;
    box-sizing: border-box;
    max-width: calc(100% - 2rem);
    padding: .6rem 1rem;
    border-radius: .5rem;
    background: #111827;
    color: #fff;
    font-size: .9rem;
    text-align: center;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .25);
}
.ev-toast[hidden] { display: none; }
