/*
  TapPulse product chrome theme — dashboard, login/register, account management.
  Deliberately its own dark/glass identity, independent from a tenant's brand colors
  (those only apply to their public /u/{slug} card). Distinct from Kobexa's light,
  minimal look on purpose: dark canvas, glass surfaces, a live violet->cyan gradient.
*/

:root {
    --tp-bg: #0a0a12;
    --tp-bg-elevated: #12121c;
    --tp-surface: rgba(255, 255, 255, 0.045);
    --tp-surface-hover: rgba(255, 255, 255, 0.08);
    --tp-border: rgba(255, 255, 255, 0.09);
    --tp-text: #f5f5f8;
    --tp-text-muted: #9a9aad;
    --tp-accent-1: #8b5cf6;
    --tp-accent-2: #22d3ee;
    --tp-gradient: linear-gradient(135deg, var(--tp-accent-1), var(--tp-accent-2));
    --tp-success: #34d399;
    --tp-success-bg: rgba(52, 211, 153, 0.12);
    --tp-danger: #f87171;
    --tp-danger-bg: rgba(248, 113, 113, 0.12);
    --tp-warning: #f59e0b;
    --tp-warning-bg: rgba(245, 158, 11, 0.12);
    --tp-radius-lg: 20px;
    --tp-radius: 14px;
    --tp-radius-sm: 10px;
    --tp-shadow: 0 20px 60px -20px rgba(0, 0, 0, 0.6);
    --tp-font: "Sora", "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }

html, body {
    background: var(--tp-bg);
    color: var(--tp-text);
    font-family: var(--tp-font);
    margin: 0;
}

body {
    background-image:
        radial-gradient(circle at 15% -10%, rgba(139, 92, 246, 0.25), transparent 45%),
        radial-gradient(circle at 90% 10%, rgba(34, 211, 238, 0.16), transparent 40%);
    background-attachment: fixed;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

a { color: inherit; }

/* ---------- Top navigation ---------- */
/* backdrop-filter directly on .tp-topbar used to make Chrome treat the header's own box as
   the containing block for ANY position:fixed descendant (the mobile nav drawer included)
   instead of the viewport — the drawer would render pinned to the header's own ~70px
   height instead of the full screen. Moving the blur to a ::before layer behind the
   content keeps the same frosted look without the header itself carrying backdrop-filter,
   so position:fixed children resolve against the viewport like they're supposed to. */
.tp-topbar {
    position: sticky;
    top: 0;
    /* .tp-topbar (position:sticky + z-index) forms its own stacking context, and the mobile
       drawer (.tp-nav-group) lives INSIDE it — so its own z-index of 50 only wins against
       siblings inside the topbar, not against .tp-nav-overlay (z-index:40) sitting outside
       it in the root stacking context. With the topbar itself left at a low z-index, the
       overlay painted OVER the whole header once the drawer was open, which is why every
       link underneath looked "disabled": taps were landing on the overlay, not the links.
       Raising the topbar above the overlay makes its entire subtree — burger, drawer, and
       all its links — win the stacking comparison as a whole. */
    z-index: 45;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 18px 32px;
    border-bottom: 1px solid var(--tp-border);
}
.tp-topbar::before {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(10, 10, 18, 0.7);
    backdrop-filter: blur(16px);
    z-index: -1;
}

.tp-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-weight: 800;
    font-size: 1.15rem;
    text-decoration: none;
    letter-spacing: -0.02em;
}

.tp-brand-mark {
    width: 20px;
    height: 30px;
    display: inline-block;
    vertical-align: middle;
}

/* Groups the desktop links + burger so they sit together, flush right — .tp-topbar's
   justify-content:space-between then only has two items to place (the brand and this whole
   group), instead of spacing three items apart with the group left stranded in the middle. */
.tp-topbar-right { display: flex; align-items: center; gap: 12px; }

/* Visible only ≥781px: Edit Profile and Analytics, the only two things kept out of the
   Menu drawer on desktop — everything else (How it works, Plans, the account avatar,
   Dashboard, Design card, Leads, Reviews, vCard Settings, Settings, Sign out) lives in the
   drawer only, at every width. Hidden below 781px so the topbar never has to fit these
   alongside the brand mark and burger on a narrow screen; the drawer carries its own copy
   of both (see .tp-mobile-only-link) for mobile instead.
   nav.tp-topbar-links (not just .tp-topbar-links): this element also carries the plain
   .tp-nav class for its link styling, and .tp-nav's own display:flex is declared LATER in
   this file — at equal specificity the later rule wins on a tie, so a bare class selector
   here would silently lose the "display:none" battle below 781px. The element-type
   qualifier raises this rule's specificity just enough to always win instead. */
nav.tp-topbar-links {
    display: none;
    gap: 4px;
}
@media (min-width: 781px) {
    nav.tp-topbar-links { display: flex; align-items: center; }
}

/* The drawer's own Edit Profile/Analytics rows exist only for mobile, where nothing shows
   outside — hidden ≥781px since .tp-topbar-links already covers that case there. */
@media (min-width: 781px) {
    .tp-mobile-only-link { display: none !important; }
}

/* Anonymous visitors on desktop keep the original plain horizontal nav — there's little
   enough there (two marketing links + Log in) that a menu would be overkill on a wide
   screen. .tp-topbar's own justify-content:space-between is what pushes it to the right,
   same as the drawer trigger does for signed-in users. Hidden on mobile, where the same
   content (plus Sign up and the language switcher) doesn't fit comfortably in the topbar —
   .tp-burger-anon/.tp-nav-group-anon below take over there instead. */
.tp-topbar-anon-nav { display: none; align-items: center; gap: 4px; flex-wrap: wrap; }
@media (min-width: 781px) {
    .tp-topbar-anon-nav { display: flex; }
}
.tp-topbar-anon-nav > .tp-nav:first-child {
    padding-right: 14px;
    margin-right: 10px;
    border-right: 1px solid var(--tp-border);
}

/* Anonymous mobile's own burger — .tp-burger already shows it by default; this just adds
   the desktop-hide, the mirror image of .tp-topbar-anon-nav above (which takes over at
   781px+ instead). .tp-burger.tp-burger-anon (not just .tp-burger-anon): the base .tp-burger
   rule is declared later in this file, so at equal specificity it would otherwise win the
   tie and this override would be silently ignored above 781px — combining both classes
   raises this rule's specificity just enough to always win instead. */
@media (min-width: 781px) {
    .tp-burger.tp-burger-anon { display: none; }
}

/* Log in + Sign up side by side in the anonymous drawer, instead of each stacking as its
   own full-width row like the rest of the menu's links. */
.tp-anon-auth-buttons { display: flex; gap: 8px; margin: 4px 0; }
.tp-anon-auth-buttons .tp-btn { flex: 1; }

.tp-nav {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

/* :not(.tp-btn) so a Registrarse-style button nested in .tp-nav keeps its own
   background/color instead of this generic link reset winning on specificity. */
.tp-nav a:not(.tp-btn), .tp-nav button:not(.tp-btn) {
    color: var(--tp-text-muted);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 600;
    padding: 8px 14px;
    border-radius: 999px;
    transition: color .15s ease, background .15s ease;
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
}

.tp-nav a:not(.tp-btn):hover, .tp-nav button:not(.tp-btn):hover { color: var(--tp-text); background: var(--tp-surface); }

/* ---------- Menu drawer (right-to-left slide-in, every viewport width) ---------- */
.tp-burger {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    width: auto;
    height: 40px;
    background: none;
    border: none;
    padding: 0 4px;
    cursor: pointer;
    flex-shrink: 0;
    z-index: 60;
}
.tp-burger-icon {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 5px;
    width: 22px;
    flex-shrink: 0;
}
.tp-burger-icon span {
    display: block;
    width: 22px;
    height: 2px;
    background: var(--tp-text);
    border-radius: 2px;
    transition: background .15s ease;
}
/* Hidden while the drawer is open — the drawer's own round (X) close button is the only
   close affordance shown at that point, at every viewport width (no morph-into-X here,
   and no burger left showing next to/behind it either). */
.tp-burger.is-open { display: none; }
.tp-burger-label { display: none; font-weight: 700; font-size: .88rem; color: var(--tp-text); white-space: nowrap; }
@media (min-width: 781px) {
    .tp-burger-label { display: inline; }
}

.tp-nav-overlay {
    display: none;
    position: fixed;
    /* Starts below the topbar too, for the same reason as .tp-nav-group above — otherwise
       this dark scrim would sit over the topbar's own always-visible content, even though
       it doesn't literally hide it, and dim it in a way that reads as "disappearing". */
    inset: var(--tp-topbar-h, 72px) 0 0 0;
    background: rgba(5, 5, 12, .6);
    opacity: 0;
    transition: opacity .2s ease;
    z-index: 40;
}
.tp-nav-overlay.is-open { display: block; opacity: 1; }

body.tp-no-scroll { overflow: hidden; }

.tp-nav-group {
    position: fixed;
    /* Starts below the topbar (site.js measures its real height into this variable) rather
       than at 0 — the topbar's own Edit Profile/Analytics group must stay visible and
       usable while the drawer is open, not get covered by it. */
    top: var(--tp-topbar-h, 72px);
    right: 0;
    bottom: 0;
    width: min(392px, 100vw);
    background: var(--tp-bg-elevated);
    border-left: 1px solid var(--tp-border);
    display: flex;
    flex-direction: column;
    align-items: stretch;
    /* No padding/overflow here anymore — .tp-nav-mobile-header and .tp-nav-mobile-body below
       split those responsibilities so the header can stay put while only the body scrolls. */
    margin: 0;
    transform: translateX(100%);
    transition: transform .25s ease;
    z-index: 50;
    overflow: hidden;
}
.tp-nav-group.is-open { transform: translateX(0); }

/* The JS-measured --tp-topbar-h (site.js) can land a few pixels off on mobile — measured too
   late relative to a reflow, a rounding difference, whatever the cause, the result was a
   visible gap between the topbar and the drawer/overlay below it. The mobile topbar's
   content never varies (just the brand mark + burger, always the same size) unlike desktop's
   variable-width Edit Profile/Analytics group, so a fixed, measured-once value is safe and
   more reliable here than relying on the runtime measurement at all. */
@media (max-width: 780px) {
    .tp-nav-group { top: 65px; }
    .tp-nav-overlay { inset: 65px 0 0 0; }
}

.tp-nav-mobile-header {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 20px 20px 16px;
    border-bottom: 1px solid var(--tp-border);
    font-size: 1.15rem;
    font-weight: 800;
    color: var(--tp-text);
}
.tp-nav-mobile-close {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    border-radius: 50%;
    color: var(--tp-text-muted);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
}
.tp-nav-mobile-close:hover { color: var(--tp-text); background: var(--tp-surface-hover); }

/* Everything below the pinned header — the account card, the page links, sign out —
   scrolls on its own, so the header (and its close button) is never scrolled out of view. */
.tp-nav-mobile-body {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px 20px 24px;
}

.tp-nav-group .tp-nav {
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    width: 100%;
}
.tp-nav-group .tp-nav a:not(.tp-btn), .tp-nav-group .tp-nav button:not(.tp-btn) {
    padding: 13px 12px;
    font-size: 1rem;
    text-align: left;
    border-radius: 10px;
}
.tp-nav-group .tp-btn { width: 100%; }
.tp-nav-group form { width: 100%; }

/* The account avatar now lives only inside the drawer (it's no longer duplicated in the
   topbar) — it grows into a bordered "account card" row (initial + email inline, dropdown
   flush underneath) rather than the compact circle-only look defined further down, which
   would otherwise apply. */
.tp-nav-group .tp-avatar-menu {
    width: 100%;
    margin: 4px 0 14px;
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius);
    padding: 4px;
}
.tp-nav-group .tp-avatar-btn {
    width: 100%;
    justify-content: flex-start;
    padding: 10px 8px;
    border-radius: 10px;
}
.tp-nav-group .tp-avatar-btn-email { display: inline; color: var(--tp-text); font-weight: 600; font-size: .88rem; word-break: break-all; }
.tp-nav-group .tp-avatar-dropdown {
    position: static;
    margin-top: 4px;
    box-shadow: none;
    border: none;
    border-top: 1px solid var(--tp-border);
    background: none;
    border-radius: 0;
    padding: 4px 0 0;
}
.tp-nav-group .tp-avatar-dropdown-email { display: none; }

/* ---------- Icon + label list (Dashboard, Edit Profile, ...) ---------- */
.tp-nav-icon { display: block; flex-shrink: 0; color: var(--tp-text-muted); }
.tp-nav-mobile-link {
    display: flex !important;
    align-items: center;
    gap: 12px;
}

/* Self-contained — no .tp-nav/.tp-nav-group ancestor requirement — so it looks right
   wherever it's placed. It's rendered as a direct child of .tp-nav-mobile-body (not wrapped
   in a <nav class="tp-nav">), so relying on ancestor-scoped link styling the way other rows
   used to left it with no styling at all: a bare, underlined default link. */
.tp-lang-switch {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--tp-text-muted);
    text-decoration: none;
    font-size: .85rem;
    font-weight: 700;
    padding: 6px 12px;
    border-radius: 999px;
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    transition: color .15s ease, background .15s ease;
}
.tp-lang-switch:hover { color: var(--tp-text); background: var(--tp-surface-hover); }

.tp-lang-flag { display: flex; flex-shrink: 0; border-radius: 2px; overflow: hidden; line-height: 0; }
.tp-lang-flag svg { display: block; }

/* Full-width menu row instead of the small pill above, matching the drawer's other items —
   set by _Header.cshtml's anonymous mobile drawer via ViewData["LangSwitcherInMenu"]. */
.tp-lang-switch.tp-nav-mobile-link {
    width: 100%;
    padding: 13px 12px;
    font-size: 1rem;
    border-radius: 10px;
    background: none;
    border: none;
}
.tp-nav-mobile-link.is-active {
    background: var(--tp-surface);
    color: var(--tp-text) !important;
}
.tp-nav-mobile-link.is-active .tp-nav-icon { color: var(--tp-text); }

/* Separated from the rest of the list, like the reference's divider above "Sign out". */
.tp-nav-mobile-signout {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--tp-border);
}
.tp-nav-mobile-signout .tp-nav-mobile-link { color: var(--tp-danger); }
.tp-nav-mobile-signout .tp-nav-mobile-link:hover { background: var(--tp-danger-bg); }

/* ---------- Account avatar menu (hover shows the email, click opens the dropdown) ---------- */
.tp-avatar-menu { position: relative; margin-left: 4px; }

/* Not scoped under .tp-nav — the avatar renders as a direct child of .tp-nav-group (no
   <nav> wrapper), so requiring that ancestor meant this never matched and the button fell
   back to the browser's own default (white/bordered) look. */
.tp-avatar-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0;
    background: none;
    border: none;
    justify-content: center;
    flex-shrink: 0;
    cursor: pointer;
    font-family: inherit;
}

.tp-avatar-btn-initial {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--tp-gradient);
    color: #fff;
    font-weight: 800;
    font-size: .85rem;
    line-height: 1;
    flex-shrink: 0;
}
.tp-avatar-btn:hover .tp-avatar-btn-initial { opacity: .85; }

/* Hidden on desktop — the circle relies on its native title-attribute tooltip for the
   email on hover instead; only the mobile layout above turns this back on. */
.tp-avatar-btn-email { display: none; }

.tp-avatar-dropdown {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    min-width: 240px;
    background: var(--tp-bg-elevated);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius);
    box-shadow: var(--tp-shadow);
    padding: 8px;
    z-index: 55;
}
.tp-avatar-dropdown[hidden] { display: none; }

.tp-avatar-dropdown-email {
    padding: 8px 12px 12px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--tp-border);
    color: var(--tp-text-muted);
    font-size: .8rem;
    word-break: break-all;
}

.tp-avatar-dropdown a,
.tp-avatar-dropdown button {
    display: block;
    width: 100%;
    text-align: left;
    padding: 10px 12px;
    border-radius: 8px;
    background: none;
    border: none;
    color: var(--tp-text);
    text-decoration: none;
    font-size: .88rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
}
.tp-avatar-dropdown a:hover, .tp-avatar-dropdown button:hover { background: var(--tp-surface-hover); }
.tp-avatar-dropdown form { margin: 0; }

/* ---------- Notification bell (topbar, authenticated tenants only) ---------- */
.tp-notif { position: relative; flex-shrink: 0; }

.tp-notif-btn {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    background: none;
    border: none;
    color: var(--tp-text-muted);
    cursor: pointer;
}
.tp-notif-btn:hover { background: var(--tp-surface); color: var(--tp-text); }

.tp-notif-badge {
    position: absolute;
    top: 2px;
    right: 2px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--tp-danger);
    color: #fff;
    font-size: .64rem;
    font-weight: 800;
    line-height: 16px;
    text-align: center;
}

.tp-notif-panel {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    width: 320px;
    max-width: calc(100vw - 32px);
    background: var(--tp-bg-elevated);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius);
    box-shadow: var(--tp-shadow);
    z-index: 55;
    overflow: hidden;
}

.tp-notif-panel-header {
    padding: 12px 16px;
    font-weight: 700;
    font-size: .9rem;
    color: var(--tp-text);
    border-bottom: 1px solid var(--tp-border);
}

.tp-notif-panel-body { max-height: 320px; overflow-y: auto; }

.tp-notif-loading, .tp-notif-empty {
    padding: 20px 16px;
    color: var(--tp-text-muted);
    font-size: .85rem;
    text-align: center;
}

.tp-notif-list { list-style: none; margin: 0; padding: 4px; }

.tp-notif-item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    border-radius: 8px;
}
.tp-notif-item:hover { background: var(--tp-surface-hover); }

.tp-notif-item-dot {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-top: 6px;
}
.tp-notif-item-dot-new { background: #22c55e; }
.tp-notif-item-dot-returned { background: #f59e0b; }

.tp-notif-item-text { display: flex; flex-direction: column; gap: 2px; font-size: .85rem; color: var(--tp-text); line-height: 1.4; }
.tp-notif-item-time { font-size: .74rem; color: var(--tp-text-muted); }

.tp-notif-panel-footer {
    display: block;
    text-align: center;
    padding: 12px 16px;
    border-top: 1px solid var(--tp-border);
    color: var(--tp-accent-2);
    font-weight: 600;
    font-size: .82rem;
    text-decoration: none;
}
.tp-notif-panel-footer:hover { background: var(--tp-surface-hover); }

/* ---------- Layout shell ---------- */
.tp-shell {
    max-width: 1054px;
    width: 100%;
    margin: 0 auto;
    padding: 40px 24px 80px;
    flex: 1;
}

.tp-eyebrow {
    color: var(--tp-accent-2);
    text-transform: uppercase;
    letter-spacing: .12em;
    font-size: .72rem;
    font-weight: 700;
    margin: 0 0 6px;
}

h1, h2, h3 { font-family: var(--tp-font); letter-spacing: -0.02em; font-weight: 700; }
h1 { font-size: 1.9rem; margin: 0 0 4px; }
h2 { font-size: 1.25rem; }
.tp-subtitle {
    color: var(--tp-text-muted);
    margin: 17px 0px 28px;
    font-size: .95rem;
    line-height: 26px;
}

/* ---------- Cards & stat tiles ---------- */
.tp-card {
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius-lg);
    padding: 24px;
    backdrop-filter: blur(10px);
}

/* minmax(0, 1fr), not a bare 1fr: a plain 1fr track has an implicit min-width of "auto",
   meaning it refuses to shrink below its content's intrinsic width — a date input's native
   segments + calendar-icon button are wide enough that this forced a .tp-grid-2 column (and
   everything inside it) wider than its fair share of the row on mobile, spilling past the
   card's edge instead of respecting the column width. minmax(0, 1fr) removes that floor. */
.tp-grid { display: grid; gap: 16px; }
.tp-grid-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
.tp-grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.tp-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 780px) { .tp-grid-5, .tp-grid-4, .tp-grid-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
@media (max-width: 520px) { .tp-grid-5, .tp-grid-4, .tp-grid-2 { grid-template-columns: minmax(0, 1fr); } }

/* Some .tp-grid-2 usages lay out two whole .tp-card sections side by side (e.g. the vCard
   Settings work/home address cards, or the personal-info/notes cards) rather than a row of
   plain fields. Squeezing two full cards into half-width columns down to 520px (like a
   field row can tolerate) left little room for their own nested inputs — such as the
   Birthday/Anniversary date pickers, which have real native minimum width — and those
   inputs visibly overflowed their half-width card. These stack to one column much sooner. */
@media (max-width: 900px) { .tp-grid-2.vcard-card-pair { grid-template-columns: minmax(0, 1fr); } }

.tp-stat {
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius);
    padding: 20px;
}

.tp-stat-value {
    font-size: 2rem;
    font-weight: 800;
    background: var(--tp-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    line-height: 1;
}

.tp-stat-label { color: var(--tp-text-muted); font-size: .82rem; margin-top: 8px; }

/* ---------- Analytics dashboard ---------- */
.tp-period-card { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 18px 24px; }
.tp-period-card select.tp-select { width: auto; min-width: 180px; }

.tp-stat-trend { display: flex; align-items: center; gap: 4px; font-size: .78rem; font-weight: 700; margin-top: 10px; }
.tp-stat-trend-label { color: var(--tp-text-muted); font-weight: 500; }
.tp-stat-trend.tp-trend-up { color: var(--tp-success); }
.tp-stat-trend.tp-trend-down { color: var(--tp-danger); }

.tp-perf-stat { background: var(--tp-surface); border: 1px solid var(--tp-border); border-radius: var(--tp-radius); padding: 20px 24px; display: flex; align-items: center; gap: 18px; }
.tp-perf-stat-ring { flex-shrink: 0; }
.tp-perf-stat-value { font-size: 1.7rem; font-weight: 800; color: var(--tp-text); line-height: 1; }
.tp-perf-stat-label { font-weight: 700; font-size: .92rem; margin: 2px 0 4px; }
.tp-perf-stat-desc { color: var(--tp-text-muted); font-size: .8rem; }

.tp-chart-wrap { position: relative; height: 280px; margin-top: 8px; }
.tp-chart-legend { display: flex; gap: 20px; justify-content: center; margin-top: 14px; flex-wrap: wrap; }
.tp-chart-legend-item { display: inline-flex; align-items: center; gap: 8px; font-size: .82rem; color: var(--tp-text-muted); font-weight: 600; }
.tp-chart-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }

.tp-analytics-tabs { display: flex; gap: 8px; margin: 32px 0 16px; flex-wrap: wrap; }
.tp-analytics-tab {
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    color: var(--tp-text-muted);
    font-family: inherit;
    font-weight: 700;
    font-size: .85rem;
    padding: 10px 18px;
    border-radius: 999px;
    cursor: pointer;
    transition: color .15s ease, background .15s ease, border-color .15s ease;
}
.tp-analytics-tab:hover { color: var(--tp-text); }
.tp-analytics-tab.is-active { background: var(--tp-gradient); color: #fff; border-color: transparent; }
.tp-analytics-panel[hidden] { display: none; }

.tp-platform-list { display: flex; flex-direction: column; gap: 16px; }
.tp-platform-row { display: grid; grid-template-columns: 28px minmax(0, 1fr) minmax(120px, 2fr) 48px; align-items: center; gap: 12px; }
.tp-platform-icon { width: 28px; height: 28px; border-radius: 50%; background: color-mix(in srgb, var(--tp-accent-1) 18%, transparent); color: var(--tp-accent-1); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.tp-platform-icon svg { width: 15px; height: 15px; }
.tp-platform-label { font-size: .88rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tp-platform-bar-track { height: 8px; border-radius: 999px; background: var(--tp-surface); overflow: hidden; }
.tp-platform-bar-fill { display: block; height: 100%; border-radius: 999px; background: var(--tp-gradient); }
.tp-platform-pct { font-size: .82rem; color: var(--tp-text-muted); font-weight: 700; text-align: right; font-variant-numeric: tabular-nums; }
@media (max-width: 520px) {
    .tp-platform-row { grid-template-columns: 24px minmax(0, 1fr) 40px; }
    .tp-platform-bar-track { grid-column: 1 / -1; }
}

.tp-activity-feed { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.tp-activity-item { display: flex; align-items: center; gap: 14px; padding: 14px 0; border-bottom: 1px solid var(--tp-border); }
.tp-activity-item:last-child { border-bottom: none; }
.tp-activity-icon { width: 36px; height: 36px; border-radius: 50%; background: color-mix(in srgb, var(--tp-accent-2) 16%, transparent); color: var(--tp-accent-2); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.tp-activity-icon svg { width: 17px; height: 17px; }
.tp-activity-text { flex: 1; min-width: 0; font-size: .88rem; font-weight: 600; }
.tp-activity-time { color: var(--tp-text-muted); font-size: .78rem; white-space: nowrap; flex-shrink: 0; }

/* ---------- Buttons ---------- */
.tp-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 20px;
    border-radius: 999px;
    font-weight: 700;
    font-size: .9rem;
    text-decoration: none;
    border: 1px solid transparent;
    cursor: pointer;
    font-family: inherit;
    transition: transform .12s ease, opacity .15s ease, background .15s ease;
}
.tp-btn:hover { transform: translateY(-1px); }

.tp-btn-primary { background: var(--tp-gradient); color: #0a0a12; }
.tp-btn-primary:hover { opacity: .92; }

.tp-btn-ghost { background: var(--tp-surface); color: var(--tp-text); border-color: var(--tp-border); }
.tp-btn-ghost:hover { background: var(--tp-surface-hover); }
.tp-btn:disabled { opacity: .4; cursor: not-allowed; pointer-events: none; }

.tp-btn-danger { background: var(--tp-danger-bg); color: var(--tp-danger); border-color: rgba(248,113,113,.3); }
.tp-btn-sm { padding: 8px 14px; font-size: .8rem; }

/* ---------- Forms ---------- */
/* ---------- vCard Settings: icon-badged section cards ---------- */
.vcard-section { margin-bottom: 16px; }
.vcard-section-header { display: flex; align-items: center; gap: 14px; margin-bottom: 20px; }
.vcard-section-icon {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--tp-accent-1) 18%, transparent);
    color: var(--tp-accent-1);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.vcard-section-title { font-weight: 700; font-size: 1rem; margin: 0; }
.vcard-section-subtitle { color: var(--tp-text-muted); font-size: .82rem; margin: 2px 0 0; }
.vcard-hint { color: var(--tp-text-muted); font-size: .8rem; margin: 4px 0 0; }

/* min-width:0 overrides the browser default of min-width:auto on grid/flex items, which
   otherwise refuses to shrink a cell below its content's intrinsic width — input[type=date]
   has enough intrinsic width (day/month/year segments + the calendar-icon button) that a
   .tp-field sitting in a narrow .tp-grid-2 column on mobile got stretched wider than the
   column itself, spilling the input out past its card's edge. */
.tp-field { margin-bottom: 18px; min-width: 0; }
.tp-field label { display: block; font-size: .85rem; font-weight: 600; color: var(--tp-text-muted); margin-bottom: 6px; }

/* Deliberately NOT targeting input[type=...].form-control / textarea.form-control /
   select.form-select here even though they'd match: those Bootstrap-named classes belong
   exclusively to the compiled Identity pages, styled in the shim further down with their
   own (light-theme) look — letting both rules target the same elements just turns into a
   specificity fight (this rule's `background` shorthand quietly resetting the shim's
   background-repeat/position/size, which is exactly the bug that caused the auth page's
   icons to tile instead of sitting still). */
.tp-input, .tp-textarea, .tp-select {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    background: var(--tp-bg-elevated);
    border: 1px solid var(--tp-border);
    color: var(--tp-text);
    padding: 12px 14px;
    border-radius: var(--tp-radius-sm);
    font-family: inherit;
    font-size: .92rem;
    outline: none;
    transition: border-color .15s ease;
}
/* Native mobile date pickers (Chrome/Safari on real phones) render their own internal
   day/month/year segments + calendar icon with a minimum width the OS widget enforces
   regardless of our CSS width — desktop browsers use a much thinner control so this never
   shows up there. overflow:hidden stops that internal content from visually spilling past
   the input's own box; the smaller padding/font on narrow screens gives the segments more
   room so they rarely need to be clipped in the first place. */
.tp-input[type="date"], .tp-input[type="datetime-local"], .tp-input[type="time"] {
    min-width: 0;
    overflow: hidden;
}
/* iOS Safari auto-zooms the whole page in on focus for any text input under 16px — and since
   Add a link's dialog calls .focus() on its URL field the instant it opens, that zoom used to
   kick in immediately and (since it's a programmatic, not a pinch, zoom) never auto-reset when
   the dialog closed, leaving the page stuck zoomed in — both scrollbars appearing, gone only on
   a manual reload. 16px is the documented no-zoom threshold, so every field gets it on phones. */
@media (max-width: 780px) {
    .tp-input, .tp-textarea, .tp-select { font-size: 16px; }
}
@media (max-width: 480px) {
    .tp-input[type="date"], .tp-input[type="datetime-local"], .tp-input[type="time"] {
        padding-left: 8px;
        padding-right: 6px;
        font-size: .82rem;
    }
}
.tp-input:focus, .tp-textarea:focus, .tp-select:focus {
    border-color: var(--tp-accent-1);
    box-shadow: 0 0 0 3px rgba(139, 92, 246, .18);
}
.form-control:focus, .form-select:focus {
    border-color: var(--tp-accent-1);
    box-shadow: 0 0 0 3px rgba(139, 92, 246, .18);
}

input[type="color"].form-control-color {
    width: 100%;
    height: 44px;
    padding: 4px;
    background: var(--tp-bg-elevated);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius-sm);
}

.tp-check { display: flex; align-items: center; gap: 10px; font-size: .9rem; color: var(--tp-text-muted); }

/* ---------- Table ---------- */
.tp-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.tp-table th {
    text-align: left;
    color: var(--tp-text-muted);
    font-weight: 600;
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    padding: 10px 14px;
    border-bottom: 1px solid var(--tp-border);
}
.tp-table td { padding: 14px; border-bottom: 1px solid var(--tp-border); }
.tp-table tr:last-child td { border-bottom: none; }

/* A slim, themed scrollbar for any horizontally-scrolling table wrapper — used as a
   graceful fallback on narrow desktop widths instead of the clunky default OS bar. */
.tp-table-scroll {
    overflow-x: auto;
    scrollbar-width: thin;
    scrollbar-color: var(--tp-border) transparent;
}
.tp-table-scroll::-webkit-scrollbar { height: 8px; }
.tp-table-scroll::-webkit-scrollbar-track { background: transparent; }
.tp-table-scroll::-webkit-scrollbar-thumb { background: var(--tp-border); border-radius: 999px; }
.tp-table-scroll::-webkit-scrollbar-thumb:hover { background: var(--tp-text-muted); }

.tp-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 8px;
    border: 1px solid var(--tp-border);
    background: transparent;
    color: var(--tp-text-muted);
    cursor: pointer;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
    flex-shrink: 0;
}
.tp-icon-btn:hover { background: var(--tp-surface-hover); color: var(--tp-text); border-color: var(--tp-accent-1); }

/* ---------- Leads table: kept to 5 columns so it fits desktop widths without scrolling ---------- */
.tp-leads-table td:first-child { max-width: 220px; }
.tp-leads-note { color: var(--tp-text-muted); font-size: .78rem; font-style: italic; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tp-leads-contact { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tp-leads-contact-secondary { color: var(--tp-text-muted); font-size: .82rem; margin-top: 2px; }
.tp-leads-activity { display: flex; flex-wrap: wrap; gap: 4px; max-width: 220px; }
.tp-filter-row { display: flex; gap: 10px; flex-wrap: wrap; }
.tp-badge-accent { color: var(--tp-accent-1); border-color: color-mix(in srgb, var(--tp-accent-1) 30%, transparent); }
.tp-activity-none { color: var(--tp-text-muted); font-size: .8rem; }
.tp-select-auto { width: auto; }
.tp-muted-sm { color: var(--tp-text-muted); font-size: .85rem; }
.tp-pagination-row { display: flex; align-items: center; justify-content: space-between; margin-top: 16px; flex-wrap: wrap; gap: 10px; }
.tp-chart-dot-views { background: #8b5cf6; }
.tp-chart-dot-clicks { background: #22d3ee; }
.tp-clicks-by-link-heading { margin-top: 32px; font-size: 1rem; }

/* ---------- Dashboard home: unconfigured banner, stat grid, QR/public-link card ---------- */
.tp-alert-card-unconfigured { margin-bottom: 20px; display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; border-color: var(--tp-accent-1); }
.tp-unconfigured-desc { color: var(--tp-text-muted); font-size: .85rem; margin: 4px 0 0; }
.tp-stat-link { text-decoration: none; display: block; }
.tp-stat-value.is-danger { background: none; -webkit-text-fill-color: var(--tp-danger); color: var(--tp-danger); }
.tp-qr-card { margin-top: 20px; display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap; }
.tp-qr-info { display: flex; align-items: center; gap: 16px; min-width: 0; }
.tp-qr-image { border-radius: 10px; background: #fff; padding: 6px; flex-shrink: 0; }
.tp-qr-hint { color: var(--tp-text-muted); font-size: .82rem; margin-bottom: 4px; }
.tp-qr-url-link { font-weight: 700; word-break: break-all; overflow-wrap: anywhere; }

/* ---------- Admin users grid: tap a row to manage it in a dialog ---------- */
.tp-user-row { cursor: pointer; transition: background .12s ease; }
.tp-user-row:hover, .tp-user-row:focus-visible { background: var(--tp-surface-hover); }
.tp-user-row:focus-visible { outline: 2px solid var(--tp-accent-1); outline-offset: -2px; }

/* showModal() blocks clicks on the page behind a dialog but, on its own, does nothing to
   stop the page behind it from being touch-scrolled on mobile — every .tp-dialog shares this
   fix so the background never shifts while any of them (Add a link, print detail, admin user
   edit, Exchange Contact, ...) is open. */
html:has(dialog[open]), body:has(dialog[open]) { overflow: hidden; }

.tp-dialog {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    margin: 0;
    width: min(420px, calc(100vw - 48px));
    max-height: min(600px, calc(100vh - 48px));
    overflow-y: auto;
    background: var(--tp-bg-elevated);
    color: var(--tp-text);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius-lg);
    padding: 28px;
    box-shadow: var(--tp-shadow);
}
.tp-dialog::backdrop { background: rgba(5, 5, 12, .7); }
.tp-dialog-close {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    border-radius: 50%;
    color: var(--tp-text-muted);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
}
.tp-dialog-close:hover { color: var(--tp-text); background: var(--tp-surface-hover); }

/* On a real phone every .tp-dialog (Add a link, edit a user, the print detail dialog, ...)
   becomes the same right-to-left slide-in panel as the site's own mobile nav drawer
   (.tp-nav-group) instead of a centered popup — full height, edge-to-edge, sliding from the
   right. Desktop is untouched (no rule outside this query gives .is-open any meaning there),
   so this only ever changes how the dialog opens on mobile. The class itself is added a frame
   after showModal() by each dialog's own script — see admin-print.js, admin-users.js, and the
   inline script in Dashboard/Profile.cshtml — the same pattern _PublicLayout's own drawer and
   the "Exchange Contact" dialog already use. */
@media (max-width: 780px) {
    .tp-dialog {
        top: 0;
        left: auto;
        right: 0;
        bottom: 0;
        width: min(392px, 100vw);
        height: 100%;
        max-height: 100%;
        border-radius: 0;
        border-top: none;
        border-bottom: none;
        border-right: none;
        transform: translateX(100%);
        transition: transform .25s ease;
    }
    .tp-dialog.is-open { transform: translateX(0); }
    .tp-dialog::backdrop { opacity: 0; transition: opacity .25s ease; }
    .tp-dialog.is-open::backdrop { opacity: 1; }
}

/* ---------- Dashboard "Add a link": searchable, categorized platform picker ---------- */
.link-catalog-group { margin-bottom: 18px; }
.link-catalog-group:last-child { margin-bottom: 0; }
.link-catalog-heading {
    color: var(--tp-text-muted);
    text-transform: uppercase;
    letter-spacing: .06em;
    font-size: .7rem;
    font-weight: 700;
    margin: 0 0 8px;
}
.link-catalog-rows { display: flex; flex-wrap: wrap; gap: 8px; }
.link-catalog-row {
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    color: var(--tp-text);
    padding: 8px 14px;
    border-radius: 999px;
    font-size: .85rem;
    font-family: inherit;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}
.link-catalog-row:hover { background: var(--tp-surface-hover); border-color: var(--tp-accent-1); }

/* ---------- Print queue / history: tap a row to manage it in a dialog ---------- */
/* Same layout, larger, inside the shared dialog (#tpPrintDialog / _PrintDialog.cshtml) — its
   pixel positions are set from the tapped card's data-* attributes by admin-print.js. */
.detail-canvas {
    width: 100%;
    aspect-ratio: 85.6 / 54;
    border-radius: 16px;
    position: relative;
    overflow: hidden;
    box-shadow: 0 20px 50px -20px rgba(0,0,0,.5);
}
.detail-logo { position: absolute; transform: translate(-50%, -50%); filter: drop-shadow(0 4px 10px rgba(0,0,0,.35)); }
.detail-text { position: absolute; transform: translate(-50%, -50%); color: #fff; font-weight: 700; font-size: 1rem; white-space: nowrap; text-shadow: 0 2px 6px rgba(0,0,0,.4); }
.detail-plate { position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 14px; background: rgba(0,0,0,.28); color: #fff; font-weight: 700; text-align: center; }
.detail-meta { margin: 20px 0 0; }
.detail-meta dt { color: var(--tp-text-muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; margin-top: 12px; }
.detail-meta dt:first-child { margin-top: 0; }
.detail-meta dd { margin: 2px 0 0; font-size: .92rem; }

/* Mobile: .print-detail-layout gets no rules here, so it stays plain block flow — the card
   on top, the data stacked below it, exactly as before. Desktop widens the shared .tp-dialog
   (only for this one, via .tp-dialog-wide — #tpUserDialog elsewhere keeps its normal narrower
   width) and turns the layout into a row: card on the left, every field + the mark-as-printed
   action on the right. */
@media (min-width: 781px) {
    .tp-dialog-wide { width: min(760px, calc(100vw - 48px)); max-height: min(620px, calc(100vh - 48px)); }
    .print-detail-layout { display: flex; gap: 28px; align-items: flex-start; }
    .print-detail-visual { flex: 0 0 300px; }
    .print-detail-info { flex: 1; min-width: 0; }
    .print-detail-info .detail-meta { margin-top: 0; }
}

/* ---------- Alerts & badges ---------- */
.tp-alert { border-radius: var(--tp-radius-sm); padding: 12px 16px; font-size: .88rem; margin-bottom: 20px; }
.tp-alert-success { background: var(--tp-success-bg); color: var(--tp-success); }
.tp-alert-danger { background: var(--tp-danger-bg); color: var(--tp-danger); }

.tp-badge {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    font-size: .74rem;
    font-weight: 700;
    color: var(--tp-text-muted);
}

.tp-empty { color: var(--tp-text-muted); padding: 40px 0; text-align: center; }

/* A status column packed into a narrow table cell next to several other columns made the
   plain .tp-badge text read as cramped on both desktop and mobile (Dashboard/Leads) — this
   variant adds a colored dot (scannable even before the text registers), more generous
   padding, and guarantees the label never wraps mid-word inside a squeezed cell. */
.tp-status-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px 5px 9px;
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 700;
    white-space: nowrap;
    line-height: 1.4;
}
.tp-status-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.tp-status-badge.is-pending { background: var(--tp-danger-bg); color: var(--tp-danger); }
.tp-status-badge.is-pending .tp-status-dot { background: var(--tp-danger); }
.tp-status-badge.is-followedup { background: var(--tp-success-bg); color: var(--tp-success); }
.tp-status-badge.is-followedup .tp-status-dot { background: var(--tp-success); }
.tp-status-badge.is-success { background: var(--tp-success-bg); color: var(--tp-success); }
.tp-status-badge.is-success .tp-status-dot { background: var(--tp-success); }
.tp-status-badge.is-warning { background: var(--tp-warning-bg); color: var(--tp-warning); }
.tp-status-badge.is-warning .tp-status-dot { background: var(--tp-warning); }
.tp-status-badge.is-neutral { background: var(--tp-surface); color: var(--tp-text-muted); }
.tp-status-badge.is-neutral .tp-status-dot { background: var(--tp-text-muted); }

/* ---------- Admin > NFC cards ---------- */
.tp-generated-codes { border: 1px dashed var(--tp-accent-1); border-radius: var(--tp-radius); padding: 16px; margin-bottom: 20px; }
.tp-generated-code-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--tp-border); flex-wrap: wrap; }
.tp-generated-code-row:last-child { border-bottom: none; }
.tp-mono { font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; }
.tp-write-status { font-size: .78rem; margin-top: 4px; }
.tp-write-status.is-success { color: var(--tp-success); }
.tp-write-status.is-error { color: var(--tp-danger); }
.tp-quantity-field { max-width: 160px; }
.tp-addcard-input { flex: 1; min-width: 220px; text-transform: uppercase; }
.tp-active-cards-list { display: flex; flex-direction: column; gap: 8px; }
.tp-active-card-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 14px; border: 1px solid var(--tp-border); border-radius: var(--tp-radius-sm); flex-wrap: wrap; }

/* ---------- Table filter bars (search + selects, filters rows already on the page) ----------
   Right-aligned and sized to their content rather than stretched full-width — reads as a
   compact toolbar tucked against the table's own edge instead of a big form of its own. */
.tp-filter-bar { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; padding: 10px 16px; }
.tp-search-input { width: 200px; max-width: 100%; }
.tp-align-self-end { align-self: flex-end; }

/* Centers the whole "Support" page as one narrow column in the middle of .tp-shell —
   matching the same margin:0 auto idiom .tp-auth-form-inner already uses for the login/
   signup cards — instead of it sitting flush left like the wider multi-column dashboard
   forms (VCardSettings, Profile) do. The card itself resets back to left-aligned text so
   labels/inputs read normally; only the surrounding header copy stays centered. */
.tp-support-page { max-width: 560px; margin: 0 auto; text-align: center; }
.tp-support-page .tp-subtitle { margin-left: auto; margin-right: auto; }
.tp-support-intro { color: var(--tp-text-muted); font-size: .88rem; line-height: 1.6; max-width: 460px; margin: 0 auto 12px; }
.tp-support-card { text-align: left; margin-top: 20px; }

/* ---------- Marketing content: steps, features, FAQ ---------- */
.tp-section { margin-top: 64px; }
.tp-section-head { text-align: center; max-width: 560px; margin: 0 auto 32px; }

.tp-steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; counter-reset: tp-step; }
@media (max-width: 900px) { .tp-steps { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .tp-steps { grid-template-columns: 1fr; } }
.tp-steps-with {
    grid-template-columns: 1fr;
    max-width: 1040px;
}
.tp-step {
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius);
    padding: 22px;
}
.tp-step-num {
    width: 32px; height: 32px;
    border-radius: 10px;
    background: var(--tp-gradient);
    color: #0a0a12;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800;
    margin-bottom: 14px;
}
.tp-step h3 { font-size: 1rem; margin: 0 0 6px; }
.tp-step p { color: var(--tp-text-muted); font-size: .85rem; margin: 0; line-height: 1.5; }

.tp-features { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
@media (max-width: 900px) { .tp-features { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .tp-features { grid-template-columns: 1fr; } }

.tp-feature { background: var(--tp-surface); border: 1px solid var(--tp-border); border-radius: var(--tp-radius); padding: 22px; }
.tp-feature-icon {
    width: 40px; height: 40px;
    border-radius: 12px;
    background: rgba(139, 92, 246, .15);
    color: var(--tp-accent-2);
    display: flex; align-items: center; justify-content: center;
    margin-bottom: 14px;
}
.tp-feature h3 { font-size: 1rem; margin: 0 0 6px; }
.tp-feature p { color: var(--tp-text-muted); font-size: .85rem; margin: 0; line-height: 1.5; }

.tp-faq { max-width: 720px; margin: 0 auto; display: flex; flex-direction: column; gap: 10px; }
.tp-faq details {
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius);
    padding: 16px 20px;
}
.tp-faq summary {
    cursor: pointer;
    font-weight: 700;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.tp-faq summary::-webkit-details-marker { display: none; }
.tp-faq summary::after { content: "+"; color: var(--tp-accent-2); font-size: 1.3rem; font-weight: 400; }
.tp-faq details[open] summary::after { content: "−"; }
.tp-faq p { color: var(--tp-text-muted); font-size: .88rem; line-height: 1.6; margin: 12px 0 0; }

.tp-cta-band {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 40px;
    padding: 56px 48px;
    border-radius: var(--tp-radius-lg);
    background:
        radial-gradient(circle at 78% 45%, rgba(139, 92, 246, .35), transparent 55%),
        radial-gradient(circle at 95% 70%, rgba(34, 211, 238, .2), transparent 45%),
        #0a0a12;
    overflow: hidden;
    flex-wrap: wrap;
}
.tp-cta-band-text { flex: 1 1 320px; }
.tp-cta-band-text h2 { font-size: 1.8rem; margin: 0 0 12px; color: #fff; }
.tp-cta-band-text p { color: var(--tp-text-muted); margin: 0 0 26px; font-size: .95rem; }
.tp-cta-band-image { flex: 1 1 320px; text-align: center; }
.tp-cta-band-image img { max-width: 100%; max-height: 340px; }

.tp-btn-light { background: #fff; color: #0a0a12; }
.tp-btn-light:hover { opacity: .9; }

/* ---------- Pricing ---------- */
.tp-pricing { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; max-width: 760px; margin: 0 auto; }
@media (max-width: 700px) { .tp-pricing { grid-template-columns: 1fr; } }

.tp-plan {
    background: var(--tp-surface);
    border: 1px solid var(--tp-border);
    border-radius: var(--tp-radius-lg);
    padding: 32px;
    position: relative;
}
.tp-plan-featured { border-color: var(--tp-accent-1); box-shadow: 0 0 0 1px var(--tp-accent-1), var(--tp-shadow); }
.tp-plan-badge {
    position: absolute; top: -12px; right: 24px;
    background: var(--tp-gradient); color: #0a0a12;
    font-size: .72rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em;
    padding: 4px 12px; border-radius: 999px;
}
.tp-plan h3 { font-size: 1.1rem; margin: 0 0 4px; }
.tp-plan-price { font-size: 2.2rem; font-weight: 800; margin: 12px 0 4px; }
.tp-plan-price small { font-size: .9rem; color: var(--tp-text-muted); font-weight: 600; }
.tp-plan-desc { color: var(--tp-text-muted); font-size: .85rem; margin: 0 0 20px; }
.tp-plan ul { list-style: none; padding: 0; margin: 0 0 24px; display: flex; flex-direction: column; gap: 10px; }
.tp-plan li { font-size: .88rem; color: var(--tp-text); display: flex; align-items: center; gap: 8px; }
.tp-plan li::before { content: "✓"; color: var(--tp-accent-2); font-weight: 800; }
.tp-current-badge { display: inline-block; margin-bottom: 14px; }

/* ---------- Footer ---------- */
.tp-footer {
    border-top: 1px solid var(--tp-border);
    margin-top: 80px;
    padding: 48px 32px 24px;
}
.tp-footer-inner { max-width: 1040px; margin: 0 auto; }
.tp-footer-grid {
    display: grid;
    grid-template-columns: 1.4fr repeat(4, 1fr);
    gap: 24px;
    padding-bottom: 32px;
}
/* Stays 2 columns down to the smallest phone widths — each link group (Product, Account,
   Legal, Contact) keeps its own block instead of collapsing into one long single-column
   stack, which made the groups harder to tell apart at a glance. */
@media (max-width: 800px) {
    .tp-footer-grid { grid-template-columns: repeat(2, 1fr); }
    /* Brand/logo block sits on its own full-width row above the 2-column link groups
       instead of sharing a grid cell with one of them. */
    .tp-footer-brand-col { grid-column: 1 / -1; margin-bottom: 8px; }
}

.tp-footer-brand { display: flex; align-items: center; gap: 10px; font-weight: 800; margin-bottom: 10px; text-decoration: none; }
.tp-footer-tagline { color: var(--tp-text-muted); font-size: .85rem; max-width: 240px; line-height: 1.5; }

.tp-footer-col h4 { font-size: .8rem; text-transform: uppercase; letter-spacing: .06em; color: var(--tp-text-muted); margin: 0 0 14px; }
.tp-footer-col ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.tp-footer-col a { color: var(--tp-text); text-decoration: none; font-size: .88rem; }
.tp-footer-col a:hover { color: var(--tp-accent-2); }

.tp-footer-bottom {
    border-top: 1px solid var(--tp-border);
    padding-top: 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    color: var(--tp-text-muted);
    font-size: .8rem;
}

/* ---------- Auth pages (Identity area) ---------- */
/* Same split-screen structure as the reference (form left, product shot right) but built
   from TapPulse's own dark palette — the site's usual violet->cyan-on-navy look, not the
   reference's white panels. body is already a flex column (see the global `body` rule
   above) with its own dark gradient background-image, so .tp-auth-split stays transparent
   as a flex:1 item and lets that show through, same as every other page. */
.tp-auth-split {
    flex: 1;
    min-height: 0;
    display: flex;
}
.tp-auth-form-col {
    flex: 1 1 480px;
    max-width: 560px;
    display: flex;
    align-items: center;
    padding: 48px 64px;
}
.tp-auth-form-inner { width: 100%; max-width: 380px; margin: 0 auto; }
.tp-auth-back {
    display: inline-block;
    color: var(--tp-text-muted);
    font-size: .85rem;
    text-decoration: none;
    margin-bottom: 28px;
}
.tp-auth-back:hover { color: var(--tp-text); }

.tp-auth-image-col {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 40px;
}
.tp-auth-image-col img {
    max-width: 300px;
    width: 100%;
    filter: drop-shadow(0 40px 70px -25px rgba(0, 0, 0, .6));
}
@media (max-width: 860px) {
    .tp-auth-image-col { display: none; }
    .tp-auth-form-col { max-width: none; flex-basis: auto; padding: 40px 24px; }
}

.tp-auth-form-inner h1 { color: var(--tp-text); font-size: 1.7rem; margin: 0 0 6px; }
/* The compiled Identity pages use <h2>/<h3> for their own sub-headings (e.g. "Use a local
   account to log in.", "Use another service to log in.") rather than <p>. */
.tp-auth-form-inner h2, .tp-auth-form-inner h3 { color: var(--tp-text); font-size: .95rem; font-weight: 600; margin: 0 0 16px; }
.tp-auth-form-inner p { color: var(--tp-text-muted); font-size: .92rem; }
.tp-auth-form-inner a { color: var(--tp-accent-2); text-decoration: none; font-weight: 600; }
.tp-auth-form-inner a:hover { text-decoration: underline; }
.tp-auth-form-inner hr { border: none; border-top: 1px solid var(--tp-border); margin: 24px 0; }

/*
  Compatibility shim for the compiled ASP.NET Core Identity UI pages (Login, Register,
  Manage account, ...). Those Razor Pages ship inside a NuGet package and render fixed
  Bootstrap class names (form-control, form-floating, btn btn-primary, form-check-input)
  that we can't rename — so we style those exact classes here instead of pulling in all
  of Bootstrap. Safe to scope broadly: nothing else in the app uses these class names,
  our own pages all use the tp-* prefix.
*/
/* Same markup Bootstrap's floating-label pattern expects (input, then label, overlapping
   via absolute position) — but flipped with flex column-reverse into a plain caption-above-
   box look (like "EMAIL" / "PASSWORD" in the reference) instead of an animated overlap, since
   we can't reorder the compiled page's actual HTML. */
.form-floating { display: flex; flex-direction: column-reverse; gap: 7px; margin-bottom: 20px; }
.form-floating > label {
    position: static;
    padding: 0;
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--tp-text-muted);
    transform: none;
    pointer-events: none;
}
.form-floating > .form-control {
    height: 52px;
    padding: 0 16px 0 44px;
    background: var(--tp-bg-elevated);
    border: 1px solid var(--tp-border);
    border-radius: 12px;
    color: var(--tp-text);
    font-size: .95rem;
    background-repeat: no-repeat;
    background-position: 14px center;
    background-size: 18px;
}
.form-floating > .form-control::placeholder { color: var(--tp-text-muted); }
input[type="email"].form-control {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%239a9aad' stroke-width='1.6'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M2.25 6.75c0-.414.336-.75.75-.75h18c.414 0 .75.336.75.75v10.5a.75.75 0 01-.75.75H3a.75.75 0 01-.75-.75V6.75z'/%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M2.25 7.5l9.75 6.75 9.75-6.75'/%3E%3C/svg%3E");
}
input[type="password"].form-control {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%239a9aad' stroke-width='1.6'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M16.5 10.5V7.5a4.5 4.5 0 10-9 0v3m-.75 0h10.5a1.5 1.5 0 011.5 1.5v7.5a1.5 1.5 0 01-1.5 1.5h-9a1.5 1.5 0 01-1.5-1.5v-7.5a1.5 1.5 0 011.5-1.5z'/%3E%3C/svg%3E");
    background-size: 16px;
}

.form-check { display: flex; align-items: center; gap: 8px; margin: 14px 0; font-size: .88rem; color: var(--tp-text-muted); }
.form-check-input { width: 16px; height: 16px; accent-color: var(--tp-accent-1); }
.form-check-label { margin: 0; }

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 20px;
    border-radius: 999px;
    font-weight: 700;
    font-size: .9rem;
    text-decoration: none;
    border: 1px solid transparent;
    cursor: pointer;
    font-family: inherit;
    transition: transform .12s ease, opacity .15s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn-primary { background: var(--tp-gradient); color: #0a0a12; }
.btn-primary:hover { opacity: .92; }
.btn-primary::after { content: "\2192"; font-weight: 800; }
.btn-lg { padding: 15px 22px; font-size: 1rem; }
.w-100 { width: 100%; }
.mb-3 { margin-bottom: 1rem; }

.text-danger { color: var(--tp-danger); font-size: .82rem; display: block; margin-top: 4px; }
.validation-summary-errors ul { list-style: none; padding: 0; margin: 0; color: var(--tp-danger); font-size: .85rem; }

/* ---------- Login / Register (our own pages, not the compiled Identity UI) ---------- */
.tp-auth-field { margin-bottom: 20px; }
.tp-auth-field label {
    display: block;
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--tp-text-muted);
    margin-bottom: 7px;
}
.tp-input-wrap { position: relative; }
.tp-input-wrap .tp-input { padding-left: 44px; height: 52px; border-radius: 12px; }
.tp-input-wrap .tp-input-icon {
    position: absolute;
    left: 14px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--tp-text-muted);
    pointer-events: none;
}
.tp-input-wrap .tp-pw-toggle {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    padding: 6px;
    color: var(--tp-text-muted);
    cursor: pointer;
    display: flex;
    border-radius: 8px;
}
.tp-input-wrap .tp-pw-toggle:hover { color: var(--tp-text); background: var(--tp-surface); }
.tp-input-wrap input.tp-input { padding-right: 44px; }

.tp-auth-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: -6px 0 22px; flex-wrap: wrap; }
.tp-auth-row .tp-check { margin: 0; font-size: .85rem; }
.tp-auth-row a { font-size: .85rem; color: var(--tp-accent-2); text-decoration: none; font-weight: 600; }
.tp-auth-row a:hover { text-decoration: underline; }

.tp-auth-submit { width: 100%; padding: 15px 22px; font-size: 1rem; margin-top: 4px; }

.tp-auth-divider {
    text-align: center;
    text-transform: uppercase;
    letter-spacing: .08em;
    font-size: .72rem;
    font-weight: 700;
    color: var(--tp-text-muted);
    border-top: 1px solid var(--tp-border);
    padding-top: 24px;
    margin-top: 28px;
}
.tp-auth-cta { display: block; text-align: center; margin-top: 12px; font-weight: 700; }
button.tp-auth-cta {
    width: 100%;
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: var(--tp-accent-2);
    cursor: pointer;
}
button.tp-auth-cta:hover { text-decoration: underline; }

.tp-input-code {
    height: 60px;
    text-align: center;
    font-size: 1.6rem;
    font-weight: 700;
    letter-spacing: .5em;
    font-family: 'Courier New', monospace;
}

/* ---------- Small utility classes ----------
   Stand-ins for the one-off style="..." attributes views used to carry inline (spacing,
   flex layout, text tone) — kept here, in one place, instead of scattered per element. */
.tp-mb-0 { margin-bottom: 0; }
.tp-mt-6 { margin-top: 6px; }
.tp-mt-8 { margin-top: 8px; }
.tp-mt-10 { margin-top: 10px; }
.tp-mt-12 { margin-top: 12px; }
.tp-mt-14 { margin-top: 14px; }
.tp-mt-16 { margin-top: 16px; }
.tp-mt-20 { margin-top: 20px; }
.tp-mt-24 { margin-top: 24px; }
.tp-mt-32 { margin-top: 32px; }
.tp-mt-36 { margin-top: 36px; }
.tp-mt-40 { margin-top: 40px; }
.tp-mb-8 { margin-bottom: 8px; }
.tp-mb-12 { margin-bottom: 12px; }
.tp-mb-14 { margin-bottom: 14px; }
.tp-mb-16 { margin-bottom: 16px; }

.tp-w-full { width: 100%; }
.tp-text-right { text-align: right; }
.tp-text-center { text-align: center; }
.tp-nowrap { white-space: nowrap; }
.tp-min-w-0 { min-width: 0; }
.tp-flex-shrink-0 { flex-shrink: 0; }
.tp-hidden { display: none; }
.tp-inline-block { display: inline-block; }
.tp-block { display: block; }

.tp-back-link { font-size: .82rem; }
.tp-heading-sm { font-size: 1rem; }
.tp-text-xs { font-size: .75rem; }
.tp-text-sm { font-size: .82rem; }

.tp-field-label { display: block; font-size: .85rem; font-weight: 600; color: var(--tp-text-muted); margin-bottom: 6px; }
.tp-hint-text { color: var(--tp-text-muted); font-size: .8rem; }
.tp-hint-text-sm { color: var(--tp-text-muted); font-size: .78rem; }
.tp-muted { color: var(--tp-text-muted); }
.tp-muted-link { color: inherit; text-decoration: underline; }

.tp-flex { display: flex; }
.tp-flex-wrap { flex-wrap: wrap; }
.tp-flex-center { align-items: center; }
.tp-flex-between { justify-content: space-between; }
.tp-flex-end { justify-content: flex-end; }
.tp-flex-col { flex-direction: column; }
.tp-gap-4 { gap: 4px; }
.tp-gap-6 { gap: 6px; }
.tp-gap-8 { gap: 8px; }
.tp-gap-10 { gap: 10px; }
.tp-gap-12 { gap: 12px; }
.tp-gap-16 { gap: 16px; }
.tp-gap-20 { gap: 20px; }
.tp-flex-1 { flex: 1; }
.tp-inline { display: inline; }
.tp-m-0 { margin: 0; }
.tp-grid-align-start { align-items: start; }
.tp-checkbox-sm { width: 15px; height: 15px; }
.tp-checkbox-md { width: 16px; height: 16px; }
.tp-card-pad-tight { padding: 8px 0; }
.tp-card-pad-compact { padding: 8px 24px; }
.tp-dialog-title { margin: 0 0 16px; font-size: 1.15rem; }
.tp-field-hint-inline { color: var(--tp-text-muted); font-size: .78rem; margin: 6px 0 0; }

/* ---------- Card Designer: drag-to-position logo/text on a live card preview ----------
   The canvas's gradient and the logo/text's saved position are the only genuinely dynamic,
   per-tenant values here — dashboard-card-designer.js applies those three via style.* from
   data-* attributes on init (the same way it already updates them live while dragging),
   so this view itself carries no inline style. */
.designer-layout { display: flex; gap: 32px; flex-wrap: wrap; align-items: flex-start; }
.designer-canvas-col { flex: 1 1 420px; }
.designer-controls-col { flex: 1 1 280px; max-width: 340px; }

.card-canvas {
    width: 100%;
    max-width: 420px;
    aspect-ratio: 85.6 / 54;
    margin: 0 auto;
    border-radius: 16px;
    position: relative;
    overflow: hidden;
    box-shadow: 0 20px 50px -20px rgba(0,0,0,.5);
    user-select: none;
}

.card-name-plate {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: 10px 14px;
    background: rgba(0,0,0,.28);
    color: #fff;
    font-weight: 700;
    font-size: clamp(.8rem, 3.2vw, 1rem);
    text-align: center;
}

.card-logo {
    position: absolute;
    width: 80px;
    transform: translate(-50%, -50%);
    cursor: grab;
    touch-action: none;
    filter: drop-shadow(0 4px 10px rgba(0,0,0,.35));
}
.card-logo:active { cursor: grabbing; }

.card-text {
    position: absolute;
    transform: translate(-50%, -50%);
    cursor: grab;
    touch-action: none;
    color: #fff;
    font-weight: 700;
    font-size: .95rem;
    white-space: nowrap;
    text-shadow: 0 2px 6px rgba(0,0,0,.4);
    padding: 4px 8px;
}
.card-text:active { cursor: grabbing; }
.card-text[hidden] { display: none; }

/* Base .dropzone (padding, hover state, hint text) lives with Dashboard Profile's photo/
   cover uploaders below, shared by every dropzone site-wide; the card designer's own logo
   dropzone only needs a bit more breathing room, via this padding-only modifier. */
.dropzone.dropzone-lg { padding: 28px 16px; }

.scale-row { display: flex; align-items: center; gap: 10px; }
.scale-row input[type="range"] { flex: 1; }

.tp-print-card-heading { font-size: 1rem; margin: 0 0 8px; }
.tp-print-meta { color: var(--tp-text-muted); font-size: .8rem; margin: 0 0 12px; }
.tp-print-meta-sm { color: var(--tp-text-muted); font-size: .78rem; margin: 12px 0 0; }
.tp-print-hint { color: var(--tp-text-muted); font-size: .78rem; margin: 0 0 14px; }

/* ---------- Dashboard Profile: photo/cover uploaders, slug editor, social links ----------
   .avatar-preview/.cover-preview's background-image is the one genuinely dynamic value here
   (the tenant's own uploaded photo/cover) — dashboard-profile.js sets it from data-photo-url/
   data-cover-url on init, so this view carries no inline style either. */
.dropzone {
    border: 2px dashed var(--tp-border);
    border-radius: var(--tp-radius);
    padding: 20px 16px;
    text-align: center;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}
.dropzone:hover, .dropzone.drag-over { border-color: var(--tp-accent-1); background: var(--tp-surface); }
.dropzone p { margin: 0; color: var(--tp-text-muted); font-size: .85rem; }

.image-preview-row { display: flex; align-items: center; gap: 16px; margin-bottom: 12px; }
.avatar-preview {
    width: 64px; height: 64px; border-radius: 50%;
    background: var(--tp-bg-elevated) center/cover no-repeat;
    border: 1px solid var(--tp-border);
    flex-shrink: 0;
}
.cover-preview {
    width: 100%; max-width: 340px; height: 80px; border-radius: var(--tp-radius-sm);
    background: var(--tp-bg-elevated) center/cover no-repeat;
    border: 1px solid var(--tp-border);
    margin-bottom: 12px;
}

.slug-row { display: flex; align-items: center; gap: 0; }
.slug-prefix {
    padding: 12px 0 12px 14px;
    background: var(--tp-bg-elevated);
    border: 1px solid var(--tp-border);
    border-right: none;
    border-radius: var(--tp-radius-sm) 0 0 var(--tp-radius-sm);
    color: var(--tp-text-muted);
    font-size: .92rem;
    white-space: nowrap;
}
.slug-row .tp-input { border-radius: 0 var(--tp-radius-sm) var(--tp-radius-sm) 0; }

.link-toggle-form { display: inline-flex; align-items: center; gap: 6px; margin: 0; }
.tp-profile-url-label { margin: 0 0 16px; }
.tp-card-subheading { font-size: 1rem; margin: 0 0 12px; }
.tp-table tr.tp-row-dimmed { opacity: .55; }
.tp-link-url-cell { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tp-comment-cell { max-width: 280px; }
.tp-card-narrow { max-width: 480px; }
.tp-admin-explanation { color: var(--tp-text-muted); font-size: .78rem; max-width: 640px; margin-top: 16px; }
.tp-tap-hint { color: var(--tp-text-muted); font-size: .78rem; margin-top: 16px; }
.tp-role-note { color: var(--tp-text-muted); font-size: .78rem; margin-top: 10px; }
.tp-badge-success { color: var(--tp-success); border-color: rgba(52,211,153,.3); }
.tp-badge-danger { color: var(--tp-danger); border-color: rgba(248,113,113,.3); }
.tp-dialog-title-tight { margin: 0 0 4px; font-size: 1.15rem; }
.tp-dialog-subtitle { color: var(--tp-text-muted); font-size: .85rem; margin: 0 0 20px; }
.tp-hr { border: none; border-top: 1px solid var(--tp-border); margin: 20px 0; }
.tp-pre-line { white-space: pre-line; }
.tp-max-w-480 { max-width: 480px; }
.tp-cta-center { text-align: center; margin-top: 48px; }
.tp-hero { text-align: center; padding: 64px 32px; }
.tp-hero-title { font-size: 2.4rem; }
.tp-hero-subtitle { max-width: 700px; margin-left: auto; margin-right: auto; }
.tp-hero-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.tp-hero-image { max-width: 520px; width: 100%; border-radius: 20px; box-shadow: 0 30px 70px -30px rgba(0,0,0,.6); }
.tp-mb-40 { margin-bottom: 40px; }
.tp-plans-subtitle { text-align: center; max-width: 480px; margin-left: auto; margin-right: auto; }
.tp-plans-alert { max-width: 480px; margin: 0 auto 24px; }
.tp-nav-mobile-link-admin { color: var(--tp-accent-2); }
.tp-addlink-heading { font-size: 1rem; margin: 0 0 4px; }
.tp-addlink-hint { color: var(--tp-text-muted); font-size: .82rem; margin: 0 0 14px; }
