/* ==========================================================================
   veam-frame.css - the common style for a content page framed inside a
   Player2 menu panel.

   Player2 opens Download, About, Recommendations, Beta and Influencers the
   same way: an iframe filling a panel whose own background has been cleared
   (see .menu-panel.download-active and friends in Players/Player2/css/player.css).
   A cleared panel shows the player's own backdrop, but that backdrop stops at
   the frame edge, so every framed page has to repaint the sky itself. That is
   what the first half of this file is, lifted from landing/download.html so
   the two stay identical. Change a tile size or a keyframe here and you must
   change it there and in player.css by the same pixels.

   The second half is the page furniture: tokens, a wrap, headings, cards,
   buttons and form controls, so a new framed page does not invent its own
   palette. The prefix is vf- (veam frame). Pages still own their own layout.

   Usage:
     <html lang="en" data-theme="dark">
     <link rel="stylesheet" href="/css/veam-frame.css">
     <body>
       <div class="ns-stars"></div><div class="ns-twinkling"></div>
       <div class="ns-sky"></div><div class="ns-clouds"></div>
       <div class="vf-page"> ... </div>

   plus the theme bootstrap script (read localStorage before first paint, then
   follow the parent's toggle through a storage event). See Beta/Default.aspx.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html { background: #000; }
/* Light mode is the same day sky Player2 paints. A framed page sits in a panel
   whose background has been cleared, so a transparent canvas here
   lets the playing veam show straight through the panel. The parent html sky
   stops at the frame edge, so repeat it here -- the same reason dark mode
   repeats the starfield instead of relying on transparency. Keep in step with
   --sky-wash in player.css. */
html[data-theme="light"] {
    --sky-wash: linear-gradient(180deg, #d9e4ef 0%, #e6edf5 30%, #f5f7fa 58%, #eef0f3 80%, #e8eaee 100%);
    --cloud-far:
        radial-gradient(7.5% 9.5% at 31% 24%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0.60) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(6.5% 8.5% at 36% 22.5%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0.60) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(4% 5.5% at 39.5% 25.5%, rgba(255,255,255,0.88) 0%, rgba(255,255,255,0.58) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(10% 5.5% at 34.5% 27%, rgba(255,255,255,0.88) 0%, rgba(255,255,255,0.58) 45%, rgba(255,255,255,0) 80%),
        radial-gradient(6.5% 8.5% at 72% 57%, rgba(255,255,255,0.88) 0%, rgba(255,255,255,0.58) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(5.5% 7% at 76.5% 55.5%, rgba(255,255,255,0.88) 0%, rgba(255,255,255,0.58) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(9% 5% at 74.5% 59.5%, rgba(255,255,255,0.86) 0%, rgba(255,255,255,0.56) 45%, rgba(255,255,255,0) 80%),
        radial-gradient(5.5% 7.5% at 10% 76%, rgba(255,255,255,0.86) 0%, rgba(255,255,255,0.56) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(4.5% 6% at 14% 75%, rgba(255,255,255,0.86) 0%, rgba(255,255,255,0.56) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(8% 4.5% at 12% 78.5%, rgba(255,255,255,0.84) 0%, rgba(255,255,255,0.55) 45%, rgba(255,255,255,0) 80%),
        radial-gradient(11.5% 5.5% at 34.5% 28.5%, rgba(150,168,190,0.28) 0%, rgba(150,168,190,0.12) 50%, rgba(150,168,190,0) 82%),
        radial-gradient(10% 5% at 74.5% 61%, rgba(150,168,190,0.26) 0%, rgba(150,168,190,0.11) 50%, rgba(150,168,190,0) 82%),
        radial-gradient(9% 4.5% at 12% 80%, rgba(150,168,190,0.24) 0%, rgba(150,168,190,0.10) 50%, rgba(150,168,190,0) 82%);
    --cloud-near:
        radial-gradient(6.6% 9.1% at 14% 17%, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.66) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(7.3% 9.9% at 19.3% 15.5%, rgba(255,255,255,0.95) 0%, rgba(255,255,255,0.66) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(4.4% 6.1% at 23.7% 18.5%, rgba(255,255,255,0.93) 0%, rgba(255,255,255,0.64) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(11% 6.1% at 17.6% 20.2%, rgba(255,255,255,0.92) 0%, rgba(255,255,255,0.64) 45%, rgba(255,255,255,0) 80%),
        radial-gradient(5.9% 8.4% at 56% 47%, rgba(255,255,255,0.93) 0%, rgba(255,255,255,0.65) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(6.6% 9.1% at 61% 45.5%, rgba(255,255,255,0.93) 0%, rgba(255,255,255,0.65) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(3.7% 5.3% at 64.7% 48.4%, rgba(255,255,255,0.91) 0%, rgba(255,255,255,0.63) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(9.5% 5.3% at 59.5% 50%, rgba(255,255,255,0.90) 0%, rgba(255,255,255,0.63) 45%, rgba(255,255,255,0) 80%),
        radial-gradient(6.6% 9.1% at 83% 76%, rgba(255,255,255,0.94) 0%, rgba(255,255,255,0.66) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(7.3% 9.9% at 88.3% 74.5%, rgba(255,255,255,0.94) 0%, rgba(255,255,255,0.66) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(4.4% 6.1% at 92% 77.5%, rgba(255,255,255,0.92) 0%, rgba(255,255,255,0.64) 45%, rgba(255,255,255,0) 78%),
        radial-gradient(11% 6.1% at 86.6% 79.2%, rgba(255,255,255,0.91) 0%, rgba(255,255,255,0.64) 45%, rgba(255,255,255,0) 80%),
        radial-gradient(13.2% 6.1% at 17.6% 21.7%, rgba(150,168,190,0.40) 0%, rgba(150,168,190,0.18) 50%, rgba(150,168,190,0) 82%),
        radial-gradient(11.7% 5.3% at 59.5% 51.4%, rgba(150,168,190,0.35) 0%, rgba(150,168,190,0.15) 50%, rgba(150,168,190,0) 82%),
        radial-gradient(12% 6.1% at 85% 80.7%, rgba(150,168,190,0.38) 0%, rgba(150,168,190,0.17) 50%, rgba(150,168,190,0) 82%);
    background-color: #e8eaee;
    background-image: var(--sky-wash);
    background-repeat: no-repeat;
    background-attachment: fixed;
    background-size: auto;
}

/* -- Sky motion: two-speed parallax -- */
/* Drift is driven by transform on an oversized strip, not by
   background-position, so the browser composites it instead of repainting a
   full-viewport layer every frame. Each strip is exactly one tile wider than
   the viewport and travels exactly one tile, which is what makes the loop
   seamless: change a background-size and you MUST change the matching
   keyframe and the matching negative inset by the same pixels.
   Two layers at very different speeds is the point. One sheet at a constant
   speed reads as a printed backdrop on rails. Same layers, tiles and timings
   as the block of the same name in Player2/css/player.css. Keep them in step. */
.ns-stars::before,
.ns-twinkling::before,
.ns-clouds::before,
.ns-clouds::after {
    content: '';
    position: absolute;
    background-repeat: repeat;
    pointer-events: none;
    will-change: transform;
}

.ns-stars,
.ns-twinkling,
.ns-sky,
.ns-clouds {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    overflow: hidden; /* clip the oversized strips */
    pointer-events: none;
}

/* Dark: the 0.34 black veil stays on the element so it covers the whole
   viewport; only the 2000px star tile moves, and barely. Far layer. */
.ns-stars {
    background: rgba(0,0,0,.34);
    z-index: -2;
}

.ns-stars::before {
    inset: 0 -2000px 0 0;
    background-image: url('../images/nightsky/stars.png');
    background-size: 2000px 1375px;
    animation: ns-drift-stars 880s linear infinite;
}

@keyframes ns-drift-stars {
    from { transform: translate3d(0,0,0); }
    to   { transform: translate3d(-2000px,0,0); }
}

/* Near layer. Same diagonal and the same 50px/s as the old
   background-position keyframe (-10000px/5000px over 200s), restated as one
   100px tile every 2s so it can composite. */
.ns-twinkling {
    background: transparent;
    z-index: -1;
}

.ns-twinkling::before {
    inset: -100px -100px -100px -100px;
    background-image: url('../images/nightsky/twinkling.png');
    background-size: 100px 100px;
    animation: ns-drift-twink 2s linear infinite;
}

@keyframes ns-drift-twink {
    from { transform: translate3d(0,0,0); }
    to   { transform: translate3d(-100px,50px,0); }
}

html[data-theme="light"] .ns-stars,
html[data-theme="light"] .ns-twinkling {
    display: none;
}

/* -- Day sky (light theme) -- */
/* .ns-sky is never used: the wash goes on the html canvas so transparent
   elements pick it up without a backdrop-filter. The clouds cannot live
   there too, because a canvas background is not composited and so could
   only ever hold still. */
.ns-sky {
    display: none;
}

.ns-clouds {
    display: none;
    z-index: -2;
}

html[data-theme="light"] .ns-clouds {
    display: block;
    background: transparent;
}

/* Far: small, faint, ~2.3px/s -- about the drift rate of the star field, so
   the two themes read as the same sky at different hours. */
html[data-theme="light"] .ns-clouds::before {
    inset: 0 -900px 0 0;
    background-image: var(--cloud-far);
    background-size: 900px 620px;
    opacity: 0.50;
    animation: ns-drift-far 400s linear infinite;
}

/* Near: large, bright, ~9px/s. Four times the far layer, which is about the
   most separation you can use before the near layer looks driven. */
html[data-theme="light"] .ns-clouds::after {
    inset: 0 -1500px 0 0;
    background-image: var(--cloud-near);
    background-size: 1500px 900px;
    opacity: 0.95;
    animation: ns-drift-near 167s linear infinite;
}

@keyframes ns-drift-far {
    from { transform: translate3d(0,0,0); }
    to   { transform: translate3d(-900px,0,0); }
}

@keyframes ns-drift-near {
    from { transform: translate3d(0,0,0); }
    to   { transform: translate3d(-1500px,0,0); }
}

@media (prefers-reduced-motion: reduce) {
    .ns-stars::before,
    .ns-twinkling::before,
    .ns-clouds::before,
    .ns-clouds::after {
        animation: none;
    }
}

/* ==========================================================================
   Page furniture
   ========================================================================== */

:root {
    /* Dark is the default: Player2 opens dark, and a page that paints light
       first and then flips is the exact flash the theme= query param on the
       About frame exists to avoid. */
    --vf-text:        #ffffff;
    --vf-text-dim:    rgba(255,255,255,0.72);
    --vf-text-faint:  rgba(255,255,255,0.5);
    --vf-card:        rgba(255,255,255,0.05);
    --vf-card-hover:  rgba(255,255,255,0.09);
    --vf-border:      rgba(255,255,255,0.12);
    --vf-border-soft: rgba(255,255,255,0.07);
    --vf-rule:        rgba(255,255,255,0.1);
    --vf-input:       rgba(255,255,255,0.06);
    --vf-input-text:  #ffffff;
    --vf-placeholder: rgba(255,255,255,0.4);

    /* A framed page is its OWN document, so player.css's color-scheme and its
       *::-webkit-scrollbar block stop at the frame edge and never reach here.
       Without these the panel scrolled with the platform default: a 15px white
       bar down the right of a starfield. Measured on
       /players/player2/?action=influencers, where the framed document reported
       color-scheme normal and zero scrollbar rules while the player's own
       document reported dark.
       color-scheme is the half that CSS cannot reach: the caret, the textarea
       resize grip, autofill, and any scrollbar Firefox draws for a control. */
    color-scheme: dark;

    /* Not player.css's solid --bg-primary track. The panel behind this frame is
       cleared so the sky and a playing veam show through, and an opaque track
       would be a grey stripe painted over the stars. A faint tint still reads as
       a gutter and keeps the sky. */
    --vf-scrollbar-track:      rgba(255,255,255,0.04);
    --vf-scrollbar-thumb:      rgba(255,255,255,0.18);
    --vf-scrollbar-thumb-hover: rgba(255,255,255,0.3);

    /* The brand blue, used for primary buttons and the numerals in a sequence.
       #183488 is the Veamcast navy and #183466 its dark companion: the same
       pair as the BlueMetalBrush still painting the landing and channel pages,
       so a filled surface here matches the rest of the site. White on it is
       11:1, up from 3.5:1 on the sky blue this replaced. */
    --vf-accent:       #183488;
    --vf-accent-hover: #183466;
    --vf-accent-deep:  #132950;
    --vf-accent-ink:   #ffffff;

    /* A brighter tone of the same hue, for strokes only: a focus ring, a hover
       border, a link hover. The navy is a fill colour - against the night sky
       it is 1.4:1, so a focus ring painted with it would not be there at all. */
    --vf-accent-line:  #2f5ada;
    --vf-accent-quiet: rgba(47,90,218,0.24);

    /* Two brighter stops of the same navy, used only as the middle of the
       gradient below. A two-stop ramp reads as a flat panel with one edge
       shaded; the brightest tone in the MIDDLE is what makes a surface read as
       curved metal, and that is the difference between these buttons and the
       selected header menu item in Player2 they are meant to match. White on
       --vf-accent-lift is 8.2:1, so the label is still above AA at the
       lightest point of the sweep. */
    --vf-accent-lift:  #2347a8;
    --vf-accent-rise:  #1c3c96;

    /* The same blue as a gradient, for the filled surfaces only: a primary
       button and the numerals in a sequence. Five stops in the shape Player2
       uses for .menu-btn.active - dark edge, accent, lift, rise, dark edge -
       so the sweep returns to where it started and the surface looks lit from
       the middle. One definition serves both themes (the var() inside resolves
       against whichever theme block is active). Borders and focus rings keep
       the flat accent, which is all a border can paint. */
    --vf-accent-grad: linear-gradient(135deg,
        var(--vf-accent-hover) 0%,
        var(--vf-accent) 25%,
        var(--vf-accent-lift) 50%,
        var(--vf-accent-rise) 75%,
        var(--vf-accent-hover) 100%);
    --vf-accent-grad-hover: linear-gradient(135deg,
        var(--vf-accent) 0%,
        var(--vf-accent-lift) 30%,
        var(--vf-accent-lift) 55%,
        var(--vf-accent-rise) 80%,
        var(--vf-accent) 100%);

    /* The glass trim that comes with that gradient, lifted from Player2: a
       white-to-transparent sheen over the top of the fill, a bright hairline
       border, and the glow the selected menu item sits in. */
    --vf-accent-sheen: linear-gradient(170deg,
        rgba(255,255,255,0.28) 0%,
        rgba(255,255,255,0.08) 25%,
        transparent 50%,
        rgba(60,120,220,0.06) 75%,
        rgba(30,80,180,0.10) 100%);
    --vf-accent-edge:  rgba(255,255,255,0.40);
    --vf-accent-glow:  rgba(35,71,168,0.45);

    /* The warm lamp. Kept to one job: the beam under a headline. */
    --vf-lamp:  #e8b44a;
    --vf-error: #ff8a80;
    --vf-ok:    #4ade80;

    --vf-sans: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    --vf-measure: 38rem;
    --vf-radius: 12px;
}

html[data-theme="light"] {
    --vf-text:        #111111;
    --vf-text-dim:    rgba(0,0,0,0.66);
    --vf-text-faint:  rgba(0,0,0,0.45);
    --vf-card:        rgba(255,255,255,0.62);
    --vf-card-hover:  rgba(255,255,255,0.82);
    --vf-border:      rgba(0,0,0,0.1);
    --vf-border-soft: rgba(0,0,0,0.06);
    --vf-rule:        rgba(0,0,0,0.1);
    --vf-input:       rgba(255,255,255,0.85);
    --vf-input-text:  #111111;
    --vf-placeholder: rgba(0,0,0,0.42);

    color-scheme: light;
    --vf-scrollbar-track:      rgba(0,0,0,0.04);
    --vf-scrollbar-thumb:      rgba(0,0,0,0.18);
    --vf-scrollbar-thumb-hover: rgba(0,0,0,0.3);

    /* Same navy ramp: it was already dark enough to carry white text, and the
       pale sky here means it can carry the strokes as well. */
    --vf-accent:       #183488;
    --vf-accent-hover: #183466;
    --vf-accent-deep:  #132950;
    --vf-accent-line:  #183488;
    --vf-accent-quiet: rgba(24,52,136,0.14);

    /* The fill and its sheen are identical in both themes, same as Player2.
       Only the trim changes: a white hairline disappears into a pale page, and
       a pure glow has nothing dark to glow against, so the border goes to a
       tone of the navy and the shadow carries a real drop. */
    --vf-accent-edge: rgba(24,52,136,0.55);
    --vf-accent-glow: rgba(24,52,136,0.28);

    --vf-lamp:  #b9812a;
    --vf-error: #a3332a;
    --vf-ok:    #1b7f46;
}

body {
    margin: 0;
    padding: 0;
    min-height: 100vh;
    font-family: var(--vf-sans);
    font-size: 16px;
    line-height: 1.6;
    color: var(--vf-text);
    /* Transparent, not a colour: the panel behind this frame is cleared so a
       playing veam shows through it. The sky layers above are what paint. */
    background: transparent;
    -webkit-text-size-adjust: 100%;
}

a { color: inherit; }
:focus-visible { outline: 2px solid var(--vf-accent-line); outline-offset: 3px; }

/* Global scrollbar. The same block as "Global scrollbar" in
   Players/Player2/css/player.css, with vf- token names: these are two documents
   showing one surface, so the bar in the panel has to be the bar in the player.
   Change one and change the other. Modern Chrome honours scrollbar-width and
   ignores the ::-webkit- rules when it does; those stay for the browsers that
   do not, which is the arrangement player.css already ships. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--vf-scrollbar-thumb) var(--vf-scrollbar-track);
}
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-track { background: var(--vf-scrollbar-track); }
*::-webkit-scrollbar-thumb { background: var(--vf-scrollbar-thumb); border-radius: 4px; }
*::-webkit-scrollbar-thumb:hover { background: var(--vf-scrollbar-thumb-hover); }

/* The content column. position/z-index put it in front of the fixed sky
   layers, which sit at z-index -1 and -2. */
.vf-page {
    position: relative;
    z-index: 1;
    max-width: 68rem;
    margin: 0 auto;
    padding: 36px 20px 64px;
}
@media (max-width: 640px) { .vf-page { padding: 24px 16px 48px; } }

.vf-h1 {
    margin: 0 0 10px;
    font-size: clamp(1.9rem, 4.4vw, 2.9rem);
    font-weight: 300;
    line-height: 1.08;
    letter-spacing: -0.02em;
}
.vf-h2 {
    margin: 0 0 12px;
    font-size: clamp(1.3rem, 2.6vw, 1.8rem);
    font-weight: 500;
    line-height: 1.2;
    letter-spacing: -0.015em;
}
.vf-sub {
    margin: 0 0 28px;
    font-size: 1.0625rem;
    color: var(--vf-text-dim);
    max-width: var(--vf-measure);
}
.vf-quiet { color: var(--vf-text-faint); }
.vf-measure { max-width: var(--vf-measure); }

/* The lamp-light beam under a headline: the one warm gesture on a page. */
.vf-beam {
    display: block;
    width: min(20rem, 60%);
    height: 4px;
    margin: 0 0 18px;
    border-radius: 3px;
    background: linear-gradient(90deg, var(--vf-lamp) 0%, rgba(232,180,74,0.5) 55%, rgba(232,180,74,0) 100%);
}

/* Glass card. Glass only reads as glass over something, which is why the
   panel behind this frame is cleared rather than painted. */
.vf-card {
    background: var(--vf-card);
    border: 1px solid var(--vf-border);
    border-radius: var(--vf-radius);
    padding: 20px 22px;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}
a.vf-card {
    display: block;
    text-decoration: none;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}
a.vf-card:hover { background: var(--vf-card-hover); border-color: var(--vf-accent-line); }

.vf-card-title { display: block; font-size: 1.0625rem; font-weight: 600; margin-bottom: 4px; }
.vf-card-desc  { display: block; font-size: 0.9375rem; color: var(--vf-text-dim); }

/* Buttons. .vf-btn is the quiet one, .vf-btn-primary the accent fill. */
.vf-btn {
    display: inline-block;
    font: inherit;
    font-size: 1rem;
    font-weight: 600;
    text-align: center;
    text-decoration: none;
    color: var(--vf-text);
    background: var(--vf-card);
    border: 1px solid var(--vf-border);
    border-radius: 8px;
    padding: 12px 22px;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}
.vf-btn:hover { background: var(--vf-card-hover); border-color: var(--vf-accent-line); }
/* The one filled control on a page, built as the same material as a selected
   header menu item in Player2 (.menu-btn.active): the five-stop sweep, a glass
   sheen over the top of it, a bright hairline, the glow it sits in, and the
   text-shadow plus letter-spacing that make the label sit ON the surface
   instead of in front of it. background-image is what paints the fill: the flat
   background-color underneath is the fallback anywhere the gradient does not
   render, and it is the colour the hover transition can actually animate.
   position+overflow are for the two pseudo-elements, which are the sheen and
   the bottom glow line; both are pointer-events:none so neither eats a click. */
.vf-btn-primary {
    position: relative;
    overflow: hidden;
    color: var(--vf-accent-ink);
    background-color: var(--vf-accent);
    background-image: var(--vf-accent-grad);
    border-color: var(--vf-accent-edge);
    letter-spacing: 0.5px;
    text-shadow: 0 1px 3px rgba(0,0,0,0.5);
    box-shadow:
        0 0 18px var(--vf-accent-glow),
        0 4px 20px var(--vf-accent-glow),
        0 1px 3px rgba(0,0,0,0.4),
        inset 0 1px 0 rgba(255,255,255,0.25);
    transition: background-color 0.15s ease, border-color 0.15s ease,
                box-shadow 0.3s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* Glass sheen: bright at the top edge, gone by halfway down. It paints over the
   label as well as the fill, which is deliberate - that is what gives the top
   of the text the same lit look the menu item has. */
.vf-btn-primary::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: var(--vf-accent-sheen);
    pointer-events: none;
}
/* Bottom glow line, the same 3px bar that marks a selected menu item. */
.vf-btn-primary::after {
    content: '';
    position: absolute;
    bottom: -1px;
    left: 15%;
    width: 70%;
    height: 3px;
    border-radius: 2px;
    background: linear-gradient(90deg, transparent, var(--vf-accent-edge), var(--vf-accent-lift), var(--vf-accent-edge), transparent);
    filter: blur(0.5px);
    pointer-events: none;
}
.vf-btn-primary:hover {
    background-color: var(--vf-accent-lift);
    background-image: var(--vf-accent-grad-hover);
    border-color: var(--vf-accent-edge);
    color: var(--vf-accent-ink);
    transform: translateY(-2px);
    box-shadow:
        0 0 28px var(--vf-accent-glow),
        0 8px 32px var(--vf-accent-glow),
        0 1px 3px rgba(0,0,0,0.5),
        inset 0 1px 0 rgba(255,255,255,0.30);
}
.vf-btn-primary:active {
    transform: translateY(0) scale(0.98);
    transition-duration: 0.1s;
    box-shadow:
        0 0 20px var(--vf-accent-glow),
        inset 0 2px 6px rgba(0,0,0,0.5),
        inset 0 -1px 0 rgba(255,255,255,0.08);
}
@media (prefers-reduced-motion: reduce) {
    .vf-btn-primary { transition: none; }
    .vf-btn-primary:hover, .vf-btn-primary:active { transform: none; }
}
.vf-btn[disabled] { opacity: 0.55; cursor: default; }
.vf-btn-block { display: block; width: 100%; }

/* Form controls */
.vf-field { margin-bottom: 12px; }
.vf-field label { display: block; font-size: 0.9375rem; font-weight: 500; margin-bottom: 5px; }
.vf-field .vf-opt { font-weight: 400; color: var(--vf-text-faint); }
.vf-field input,
.vf-field textarea {
    width: 100%;
    font: inherit;
    font-size: 1rem;
    color: var(--vf-input-text);
    background: var(--vf-input);
    border: 1px solid var(--vf-border);
    border-radius: 8px;
    padding: 10px 12px;
}
.vf-field textarea { min-height: 4.5rem; resize: vertical; line-height: 1.5; }
.vf-field input::placeholder,
.vf-field textarea::placeholder { color: var(--vf-placeholder); opacity: 1; }
.vf-field input:focus,
.vf-field textarea:focus {
    outline: none;
    border-color: var(--vf-accent-line);
    box-shadow: 0 0 0 3px var(--vf-accent-quiet);
}

/* Status message for a form result. */
.vf-status {
    display: none;
    margin: 0 0 16px;
    padding: 12px 14px;
    border-left: 4px solid var(--vf-border);
    border-radius: 8px;
    background: var(--vf-card);
    font-size: 0.9375rem;
    line-height: 1.5;
    color: var(--vf-text-dim);
}
.vf-status.is-shown { display: block; }
.vf-status b { display: block; font-size: 1.0625rem; margin-bottom: 3px; color: var(--vf-text); }
.vf-status.is-ok { border-color: var(--vf-ok); }
.vf-status.is-error { border-color: var(--vf-error); }
.vf-status.is-error b { color: var(--vf-error); }

/* Honeypot: off-screen for people and for autofill, still fillable by a bot. */
.vf-trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.vf-footer {
    margin-top: 40px;
    padding-top: 20px;
    border-top: 1px solid var(--vf-rule);
    font-size: 0.8125rem;
    line-height: 1.7;
    color: var(--vf-text-faint);
}
.vf-footer p { margin: 0 0 4px; }
