/* ==========================================================================
   Zenh link hub
   Nova cosmic identity, same palette as recraft.fr.
   Author: zenh
   ========================================================================== */

/* ---------- Polices, servies depuis le site ---------- */
/* Sous-ensemble latin uniquement : la page n'ecrit rien en dehors. */
@font-face {
    font-family: 'Manrope';
    src: url('../fonts/manrope-latin.woff2') format('woff2');
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Bricolage Grotesque';
    src: url('../fonts/bricolage-latin.woff2') format('woff2');
    font-weight: 600 800;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
    /* Palette EasyCraft : indigo profond, l'or du logo comme seule couleur
       d'action. Le violet ne sert plus que de lueur d'ambiance, jamais a
       designer ce qui se clique. */
    --primary: #4b3fd6;
    --primary-light: #7d72ff;
    --primary-dark: #2a2299;
    --primary-glow: rgba(75, 63, 214, 0.28);
    --accent: #ffd84d;
    --bg-dark: #0a0a1e;
    --bg-card: #12122c;
    --bg-card-hover: #191938;
    --text-primary: #f4f4ff;
    --text-secondary: #a0a0c8;
    --text-muted: #8a8ab4;
    --border-color: #1d1d3e;
    --border-light: #2a2a55;
    --nova-grad: linear-gradient(100deg, var(--accent), #ffb020);
    --font-display: 'Bricolage Grotesque', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-main: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-spring: cubic-bezier(0.34, 1.32, 0.64, 1);
    --radius: 14px;
}

/* ---------- Base ---------- */
* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    min-height: 100vh;
    background-color: var(--bg-dark);
    color: var(--text-primary);
    font-family: var(--font-main);
    font-size: 1rem;
    line-height: 1.5;
    overflow-x: hidden;
}

/* Starfield: two layered radial-gradient tile patterns, pure CSS. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -2;
    pointer-events: none;
    background-image:
        radial-gradient(1px 1px at 25px 45px, rgba(232, 232, 240, 0.55) 50%, transparent 51%),
        radial-gradient(1.4px 1.4px at 130px 170px, rgba(255, 216, 77, 0.4) 50%, transparent 51%);
    background-size: 190px 190px, 270px 270px;
}

/* Soft violet orb behind the profile header. */
body::after {
    content: "";
    position: fixed;
    top: -140px;
    left: 50%;
    width: 460px;
    height: 460px;
    transform: translateX(-50%);
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(circle, rgba(255, 216, 77, 0.20), transparent 62%),
        radial-gradient(circle, rgba(75, 63, 214, 0.30), transparent 72%);
}

.page {
    max-width: 480px;
    margin: 0 auto;
    padding: 2.2rem 1rem 1.6rem;
}

a { color: var(--primary-light); }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 4px;
}

/* ---------- Profile header ---------- */
.profile {
    text-align: center;
    margin-bottom: 1.4rem;
    /* The entrance animation applies a transform, which makes this block its
       own stacking context. The cards below do the same, and with no explicit
       level the later ones win and paint over the contact popover. Naming a
       level here keeps the popover above them. */
    position: relative;
    z-index: 20;
}

.avatar-ring {
    position: relative;
    z-index: 0;
    display: inline-block;
    padding: 3px;
    border-radius: 50%;
    /* Le logo porte deja son anneau jaune : en dessiner un second ici
       ferait deux cercles concentriques. */
    background: transparent;
}

/* Soft halo behind the avatar, drifting slowly for a subtle sense of life.
   Sits inside the ring's own stacking context so it never bleeds behind the
   page background. Composited: only transform and opacity animate. */
.avatar-ring::before {
    content: "";
    position: absolute;
    inset: -16px;
    z-index: -1;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(255, 216, 77, 0.45), transparent 68%);
    animation: avatar-float 7s ease-in-out infinite;
    will-change: transform, opacity;
    pointer-events: none;
}

@keyframes avatar-float {
    0%, 100% { transform: translateY(-5px) scale(0.97); opacity: 0.75; }
    50%      { transform: translateY(5px) scale(1.06); opacity: 1; }
}

.avatar-ring img {
    display: block;
    width: 96px;
    height: 96px;
    border-radius: 50%;
    border: 0;
    object-fit: cover;
}

.profile-name {
    margin: 0.7rem 0 0.25rem;
    font-family: var(--font-display);
    font-size: 1.7rem;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--text-primary);
}

@keyframes name-sheen {
    to { background-position: 200% center; }
}

.profile-bio {
    margin: 0 auto;
    max-width: 34ch;
    color: var(--text-secondary);
    font-size: 0.95rem;
}

/* ---------- Contact call to action ---------- */
/* Rendered only when the owner has filled the email field in the panel.
   A pill in the Nova palette, gold mail glyph, lifting on hover like the
   cards and social pills so every interactive element reads the same way.
   The button opens the menu below it, so the wrapper is the anchor point. */
.contact {
    position: relative;
    display: inline-block;
}

.profile-contact {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    margin: 0.9rem 0 0;
    padding: 0.5rem 1.15rem;
    appearance: none;
    -webkit-appearance: none;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background: var(--bg-card);
    color: var(--text-primary);
    font-family: var(--font-display);
    font-size: 0.86rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    line-height: 1.5;
    text-decoration: none;
    cursor: pointer;
    transition: transform 0.25s var(--ease-spring), border-color 0.2s ease,
                box-shadow 0.2s ease, background 0.2s ease;
}

.profile-contact svg {
    width: 16px;
    height: 16px;
    color: var(--accent);
}

.profile-contact:hover,
.profile-contact:focus-visible {
    transform: translateY(-2px);
    border-color: var(--primary-light);
    background: var(--bg-card-hover);
    box-shadow: 0 8px 22px rgba(0, 0, 0, 0.4), 0 0 20px var(--primary-glow);
}

.profile-contact:active {
    transform: translateY(0) scale(0.97);
}

/* Contact popover. Opened by the button above it, one way to reach the
   address per row. Width is capped against the viewport so it still fits on
   a 375px screen, and it is centred under its button. */
.contact-menu {
    position: absolute;
    top: calc(100% + 0.5rem);
    left: 50%;
    z-index: 5;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 12.5rem;
    max-width: min(17rem, calc(100vw - 2rem));
    padding: 0.35rem;
    border: 1px solid var(--border-light);
    border-radius: 12px;
    background: var(--bg-card);
    box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55), 0 0 22px var(--primary-glow);
    text-align: left;
    transform: translateX(-50%);
    animation: menu-in 0.18s var(--ease-out) both;
}

/* The base rule sets display: flex, so the hidden attribute needs saying. */
.contact-menu[hidden] {
    display: none;
}

@keyframes menu-in {
    from { opacity: 0; transform: translateX(-50%) translateY(-6px); }
    to   { opacity: 1; transform: translateX(-50%); }
}

.contact-menu a,
.contact-menu button {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    margin: 0;
    padding: 0.5rem 0.65rem;
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--text-primary);
    font-family: var(--font-main);
    font-size: 0.86rem;
    font-weight: 600;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.18s ease, color 0.18s ease;
}

.contact-menu a:hover,
.contact-menu a:focus-visible,
.contact-menu button:hover,
.contact-menu button:focus-visible {
    background: rgba(139, 92, 246, 0.16);
    color: var(--text-primary);
}

/* The ring would sit on top of the popover border otherwise. */
.contact-menu a:focus-visible,
.contact-menu button:focus-visible {
    outline-offset: -2px;
}

/* Copy confirmation inside the menu: a gold label pushed to the right edge,
   the row keeps its own text so nothing jumps. */
.contact-menu .js-copy.copied {
    background: rgba(251, 191, 36, 0.1);
    color: var(--accent);
}

.contact-menu .js-copy.copied::after {
    content: "Copié !";
    margin-left: auto;
    font-size: 0.74rem;
    font-weight: 800;
    letter-spacing: 0.02em;
}

/* ---------- Social pills (row above the footer) ---------- */
.socials-bottom {
    display: flex;
    justify-content: center;
    gap: 0.7rem;
    margin: 2.1rem 0 0;
    padding: 0;
    list-style: none;
}

.social-pill {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    color: var(--text-primary);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    transition: transform 0.25s var(--ease-spring), border-color 0.2s ease, box-shadow 0.2s ease;
}

.social-pill svg {
    width: 20px;
    height: 20px;
}

.social-pill:hover,
.social-pill:focus-visible {
    transform: translateY(-2px);
    border-color: var(--primary-light);
    box-shadow: 0 0 18px var(--primary-glow);
}

/* ---------- Link cards ---------- */
.links {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.card {
    position: relative;
    border-radius: var(--radius);
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    transition: transform 0.3s var(--ease-spring), border-color 0.25s var(--ease-out), box-shadow 0.25s var(--ease-out);
}

.card:hover,
.card:focus-within {
    transform: translateY(-3px);
    border-color: var(--border-light);
    background: var(--bg-card-hover);
    box-shadow: 0 12px 34px rgba(0, 0, 0, 0.5), 0 0 26px var(--primary-glow);
}

/* The whole card fills with a ring, so tuck the anchor's outline just inside
   the rounded edge instead of letting it float outside the card. */
.card-link:focus-visible {
    outline-offset: -4px;
    border-radius: var(--radius);
}

.card-link {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    padding: 0.8rem 0.9rem;
    color: inherit;
    text-decoration: none;
    border-radius: inherit;
}

/* Copy mode. The card copies a value instead of opening a URL, so it is a
   button element. Everything a button brings of its own is reset here: it has
   to be indistinguishable from a link card, and the hover lift and glow keep
   coming from .card itself. */
.card-link-copy {
    position: relative;
    width: 100%;
    margin: 0;
    appearance: none;
    -webkit-appearance: none;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

/* The copy glyph takes the chevron's place and its measurements. */
.card-link-copy > svg {
    flex: none;
    width: 18px;
    height: 18px;
    color: var(--text-muted);
    transition: color 0.2s ease;
}

.card:hover .card-link-copy > svg {
    color: var(--accent);
}

/* The copy glyph sits in the chevron's slot and carries its class for size and
   colour, but nothing here navigates, so it must not slide sideways on hover
   the way the chevron does. */
.card:hover .card-chevron-copy {
    transform: none;
}

/* Copied confirmation on a full card. A chip is small enough to be covered by
   a label, a card is not: the content stays readable and a short badge takes
   the glyph's place at the right edge. */
.card-link-copy.copied > svg {
    opacity: 0;
}

.card-link-copy.copied::after {
    content: "Copié !";
    position: absolute;
    top: 50%;
    right: 0.6rem;
    padding: 0.12rem 0.5rem;
    border: 1px solid var(--accent);
    border-radius: 999px;
    /* Opaque, so a long title running under the badge never shows through. */
    background: var(--bg-card-hover);
    color: var(--accent);
    font-size: 0.7rem;
    font-weight: 800;
    letter-spacing: 0.02em;
    line-height: 1.5;
    white-space: nowrap;
    transform: translateY(-50%);
    box-shadow: 0 0 0 4px var(--bg-card-hover), 0 0 16px rgba(251, 191, 36, 0.2);
    pointer-events: none;
}

.card-thumb {
    flex: none;
    width: 56px;
    height: 56px;
    border-radius: 12px;
    object-fit: cover;
    /* A very subtle neutral tint, never the near-black --bg-dark: an uploaded
       transparent logo shows the card colour through it and blends in instead
       of sitting on a dark square. Opaque photo thumbs cover it entirely. */
    background: rgba(255, 255, 255, 0.04);
}

.card-body { min-width: 0; flex: 1; }

.card-title {
    display: block;
    font-family: var(--font-display);
    font-size: 0.98rem;
    font-weight: 700;
    letter-spacing: -0.01em;
}

.card-subtitle {
    display: block;
    margin-top: 0.1rem;
    color: var(--text-secondary);
    font-size: 0.85rem;
}

.card-chevron {
    flex: none;
    width: 18px;
    height: 18px;
    color: var(--text-muted);
    transition: transform 0.25s var(--ease-out), color 0.2s ease;
}

.card:hover .card-chevron {
    transform: translateX(3px);
    color: var(--accent);
}

/* Discreet partner tag, top right corner. */
.tag {
    position: absolute;
    top: -0.55em;
    right: 12px;
    padding: 0.1rem 0.55rem;
    border: 1px solid var(--border-light);
    border-radius: 999px;
    background: var(--bg-dark);
    color: var(--text-muted);
    /* The smallest type on the page carried the commercial disclosure, which
       is the one label that has to stay readable at arm length on a phone. */
    font-size: 0.7rem;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    pointer-events: none;
}

/* ---------- Link chips ---------- */
/* A chip sits under a card. The template emits exactly one kind of chip:
     .card-chip-promo carries a promo code, always copies it on click, and is
     an anchor through the counted /go/{slug}-promo route when the owner also
     gave the offer a page.
   .card-chip-link and .code-chip are legacy aliases, kept only so a page
   generated before the promo rework still renders until it is saved again.
   The pill is keyed on .card-chip and never on .js-copy: that marker also
   rides on a copy-mode card and on a contact menu row, which are not pills. */
.card-chip,
.card-chip-link,
.code-chip {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0 0.9rem 0.8rem 4.7rem;
    padding: 0.3rem 0.72rem;
    border: 1px dashed var(--border-light);
    border-radius: 999px;
    background: transparent;
    color: var(--text-secondary);
    font-family: var(--font-main);
    font-size: 0.78rem;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
    transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease,
                transform 0.14s var(--ease-spring);
}

.card-chip strong,
.code-chip strong {
    color: var(--accent);
    letter-spacing: 0.05em;
}

.card-chip svg,
.card-chip-link svg,
.code-chip svg {
    width: 13px;
    height: 13px;
    flex: none;
}

.card-chip:hover,
.card-chip:focus-visible,
.code-chip:hover,
.code-chip:focus-visible {
    color: var(--text-primary);
    border-color: var(--accent);
    background: rgba(251, 191, 36, 0.06);
}

/* Link mode reads as a small tappable button: solid edge, violet accent. */
.card-chip-link {
    border-style: solid;
    background: rgba(139, 92, 246, 0.08);
    color: var(--text-primary);
}

.card-chip-link svg {
    color: var(--primary-light);
}

.card-chip-link:hover,
.card-chip-link:focus-visible {
    border-color: var(--primary-light);
    background: rgba(139, 92, 246, 0.16);
    box-shadow: 0 0 16px var(--primary-glow);
}

/* A promo chip sits right under a full width card. A miss-tap lands on the
   card, which navigates away and takes the code with it, so the chip needs a
   comfortable target of its own without growing visually out of place. */
/* The clickable area used to end where the link box ended. On a card carrying
   a promo code that left the whole bottom strip inert, and the click heatmap
   showed people tapping it and getting nothing. The link now covers the entire
   card through a transparent overlay, so nothing changes visually while every
   part of the rectangle answers. The promo chip is lifted back above it, since
   it owns its own action and must not be swallowed. */
.card-link::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    border-radius: inherit;
}

.card-chip {
    min-height: 40px;
    padding-block: 0.5rem;
    z-index: 2;
}

/* Discount badge on a promo code chip, e.g. -10%. Solid gold pill so the
   saving reads at a glance next to the code. */
.card-chip-percent {
    display: inline-flex;
    align-items: center;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    background: var(--accent);
    color: #2a1a00;
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 0.02em;
    line-height: 1.4;
}

/* Promo code chip. Keeps the dashed pill of the copy mode: clicking it always
   copies the code. When the owner also gave a URL it is rendered as an anchor
   through the counted /go/ route, and it has to read as tappable, so it gains
   the violet tint of the link mode while keeping its dashed edge at rest. */
a.card-chip-promo {
    background: rgba(139, 92, 246, 0.08);
    color: var(--text-primary);
}

a.card-chip-promo:hover,
a.card-chip-promo:focus-visible {
    border-style: solid;
    border-color: var(--primary-light);
    background: rgba(139, 92, 246, 0.16);
    box-shadow: 0 0 16px var(--primary-glow);
}

/* Springy press feedback on every chip mode. */
.card-chip:active,
.card-chip-link:active,
.code-chip:active {
    transform: scale(0.95);
}

/* Copied confirmation: the tracker adds .copied for ~1.5s. The pill's own
   content fades to transparent and a French label is laid over it, so the
   box keeps its size and nothing reflows. Small pills only: a full card gets
   its own badge instead, see .card-link-copy.copied. */
.card-chip.copied,
.code-chip.copied {
    border-style: solid;
    border-color: var(--accent);
    color: transparent;
}

.card-chip.copied svg,
.code-chip.copied svg,
.card-chip.copied strong,
.code-chip.copied strong,
.card-chip.copied .card-chip-percent {
    color: transparent;
    opacity: 0;
}

.card-chip.copied::after,
.code-chip.copied::after {
    content: "Copié !";
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent);
    font-weight: 800;
    letter-spacing: 0.03em;
    pointer-events: none;
}

/* ---------- Featured card (Re:Craft) ---------- */
.card-featured {
    /* isolate contains the pulse pseudo's stacking context, so its glow sits
       behind the card without a z-index that could escape the layout. */
    isolation: isolate;
    border: 1px solid transparent;
    background:
        linear-gradient(var(--bg-card-hover), var(--bg-card-hover)) padding-box,
        var(--nova-grad) border-box;
}

/* Soft glow that breathes around the featured border. Only opacity animates,
   so it stays on the compositor and never triggers layout. */
.card-featured::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    box-shadow: 0 0 30px var(--primary-glow);
    opacity: 0.6;
    animation: featured-pulse 3.8s ease-in-out infinite;
    pointer-events: none;
}

@keyframes featured-pulse {
    0%, 100% { opacity: 0.4; }
    50%      { opacity: 0.95; }
}

.card-featured:hover,
.card-featured:focus-within {
    background:
        linear-gradient(var(--bg-card-hover), var(--bg-card-hover)) padding-box,
        var(--nova-grad) border-box;
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55), 0 0 40px rgba(139, 92, 246, 0.4);
}

.card-featured .card-link { padding: 1.1rem 1rem; }

.card-featured .card-thumb {
    width: 72px;
    height: 72px;
    border-radius: 14px;
}

.card-featured .card-title { font-size: 1.15rem; }

.chip-featured {
    display: inline-block;
    margin-bottom: 0.3rem;
    padding: 0.12rem 0.55rem;
    border-radius: 999px;
    background: var(--nova-grad);
    color: #14061f;
    font-family: var(--font-display);
    font-size: 0.62rem;
    font-weight: 800;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

/* ---------- Footer (minimal: mark + year) ---------- */
.footer {
    margin-top: 1.6rem;
    text-align: center;
    color: var(--text-muted);
    font-size: 0.8rem;
}

.footer-mark {
    font-family: var(--font-display);
    font-weight: 800;
    letter-spacing: 0.04em;
    color: var(--text-secondary);
}

/* ---------- Entrance animation, staggered fade-up ---------- */
.profile,
.card,
.socials-bottom,
.footer {
    animation: fade-up 0.55s var(--ease-out) both;
}

.links .card:nth-child(1) { animation-delay: 0.10s; }
.links .card:nth-child(2) { animation-delay: 0.16s; }
.links .card:nth-child(3) { animation-delay: 0.22s; }
.links .card:nth-child(4) { animation-delay: 0.28s; }
.links .card:nth-child(5) { animation-delay: 0.34s; }
.links .card:nth-child(6) { animation-delay: 0.40s; }
.socials-bottom { animation-delay: 0.46s; }
.footer { animation-delay: 0.52s; }

/* Fade in with a slight rise and scale; transform/opacity only. */
@keyframes fade-up {
    from {
        opacity: 0;
        transform: translateY(16px) scale(0.985);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* ---------------------------------------------------------------------- */
/* Attention cues. Each one fires briefly, once per cycle, and the cycles
   are deliberately out of phase so the page never twitches all at once.
   Nothing here moves layout: transforms and background position only. */

/* The MON SERVEUR pill gives a short shake, the eye catches motion before
   it reads text, so this is what pulls attention to the main card. */
@keyframes chip-attention {
    0%, 92%, 100% { transform: rotate(0) scale(1); }
    94%           { transform: rotate(-3.5deg) scale(1.06); }
    96%           { transform: rotate(3.5deg) scale(1.06); }
    98%           { transform: rotate(-2deg) scale(1.02); }
    99%           { transform: rotate(1deg) scale(1); }
}

.chip-featured {
    animation: chip-attention 11s ease-in-out 3s infinite;
}

/* A light sweeps across the featured card, the way a highlight travels over
   a physical surface. Clipped by the border radius on its own box, so no
   overflow rule is needed and the outer glow stays intact. */
/* The band rides on transform alone, which the compositor animates without
   repainting, and it travels at a constant speed along a path far longer than
   the card. It is therefore off screen at both ends and never appears to stop
   or jump: the previous version animated background-position and held a pose
   for half its cycle, which read as a stutter. */
@keyframes featured-sheen {
    from { transform: translateX(-150%) skewX(-12deg); }
    to   { transform: translateX(1600%) skewX(-12deg); }
}

/* Clipped by the link box, which sits inside the card border, so the outer
   glow around the card is untouched. Skipped on a copy card, whose own ::after
   carries the copied confirmation. */
.card-featured .card-link {
    position: relative;
    overflow: hidden;
}

.card-featured .card-link:not(.card-link-copy)::after {
    content: "";
    position: absolute;
    top: -30%;
    bottom: -30%;
    left: 0;
    width: 42%;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.11), transparent);
    transform: translateX(-150%) skewX(-12deg);
    animation: featured-sheen 7s linear 1.2s infinite;
    pointer-events: none;
}

/* The discount is the strongest selling point on a partner card, so the
   badge gets its own beat, offset from the two above. */
@keyframes badge-pulse {
    0%, 84%, 100% { transform: scale(1); }
    89%           { transform: scale(1.14); }
    94%           { transform: scale(1); }
}

.card-chip-percent {
    animation: badge-pulse 9s ease-in-out 5s infinite;
}

@media (prefers-reduced-motion: reduce) {
    .profile, .card, .socials-bottom, .footer,
    .profile-name,
    .avatar-ring::before,
    .card-featured::before,
    .card-featured .card-link::after,
    .chip-featured,
    .card-chip-percent { animation: none; }
    /* The popover appears at once, still centred under its button. */
    .contact-menu { animation: none; transform: translateX(-50%); }
    .card, .social-pill, .card-chevron,
    .card-link-copy > svg,
    .contact-menu a, .contact-menu button,
    .profile-contact, .card-chip, .card-chip-link, .code-chip { transition: none; }
    .card:hover, .card:focus-within,
    .social-pill:hover, .social-pill:focus-visible,
    .profile-contact:hover, .profile-contact:focus-visible { transform: none; }
    /* Keep the glows as calm static states rather than removing them. */
    .avatar-ring::before { transform: none; opacity: 0.85; }
    .card-featured::before { opacity: 0.6; }
}

/* ---------- Slightly wider screens ---------- */
@media (min-width: 480px) {
    .page { padding-top: 3rem; }
    .profile-name { font-size: 1.9rem; }
}

/* Honeypot anchor: physically present for HTML parsers, absent for people.
   Any visitor that follows it is discarded from the statistics. */
.lh-skip {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}
