/*******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2026, Jean-David Gadina - www.xs-labs.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the Software), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

/*******************************************************************************
 * Design tokens
 ******************************************************************************/

:root
{
    --bg:            #05070d;
    --bg-elevated:   #090c14;
    --panel:         #0e1420;
    --panel-2:       #111927;
    --border:        #1c2536;
    --border-strong: #28344a;

    --text:          #eef2f8;
    --text-dim:      #9aa6b8;
    --text-faint:    #7a8698;

    --accent:        #3b82f6;
    --accent-bright: #5b9dff;
    --accent-soft:   rgba( 59, 130, 246, 0.12 );
    --accent-line:   rgba( 59, 130, 246, 0.30 );

    /* Darker accent for white text on a solid fill ( meets WCAG AA contrast ) */
    --accent-deep:   #2563eb;

    /* Secondary "nebula" accent — used sparingly ( hero keyword, CTA glow ) */
    --accent-2:      #b45cd8;

    /* Nebula-purple tints for the sponsor button and support block */
    --sponsor-soft:  rgba( 180, 92, 216, 0.12 );
    --sponsor-line:  rgba( 180, 92, 216, 0.34 );

    --radius:        16px;
    --maxw:          1180px;

    --font:          -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text",
                     "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --mono:          "SF Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/*******************************************************************************
 * Reset
 ******************************************************************************/

*
{
    box-sizing: border-box;
    margin:     0;
    padding:    0;
}

html
{
    scroll-behavior:    smooth;

    /* Offset anchor jumps by the sticky header height so targets aren't hidden behind it */
    scroll-padding-top: 120px;

    /* Clip horizontal overflow at the root as well as on `body`. The decorative glow
       pseudo-elements — .hero::before, .hero-shot::before, .highlight-media::before —
       bleed a little past the viewport by design and are meant to be clipped at its
       edge. iOS Safari doesn't reliably honour a body-only overflow-x, so the page
       could be panned sideways to reveal that bleed. `clip` clips without creating a
       scroll container, so it removes the sideways drag without breaking the sticky
       header or vertical scrolling ( which `overflow-x: hidden` here would ). */
    overflow-x:         clip;
}

body
{
    background:               var( --bg );
    color:                    var( --text );
    font-family:              var( --font );
    line-height:              1.6;
    -webkit-font-smoothing:   antialiased;
    -moz-osx-font-smoothing:  grayscale;
    overflow-x:               clip;
}

a
{
    color:           inherit;
    text-decoration: none;
}

/* Visible focus ring for keyboard navigation */
a:focus-visible
{
    outline:        2px solid var( --accent-bright );
    outline-offset: 3px;
}

/* Skip link — hidden off-screen until focused via the keyboard */
.skip-link
{
    position:      absolute;
    left:          16px;
    top:           -100px;
    z-index:       50;
    padding:       10px 18px;
    border-radius: 10px;
    background:    var( --accent );
    color:         #fff;
    font-size:     0.9rem;
    font-weight:   600;
    transition:    top 0.2s ease;
}

.skip-link:focus
{
    top: 14px;
}

/* The main landmark receives focus from the skip link; no ring needed */
#main:focus
{
    outline: none;
}

/* Visually hidden, but still read by assistive tech ( e.g. "opens in a new tab" hints ) */
.sr-only
{
    position:    absolute;
    width:       1px;
    height:      1px;
    padding:     0;
    margin:      -1px;
    overflow:    hidden;
    clip:        rect( 0, 0, 0, 0 );
    clip-path:   inset( 50% );
    white-space: nowrap;
    border:      0;
}

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

.wrap
{
    max-width: var( --maxw );
    margin:    0 auto;
    padding:   0 28px;
    position:  relative;
    z-index:   1;
}

/*******************************************************************************
 * Cosmic backdrop
 *
 * A faint, fixed nebula wash and star field sit behind every section, so the
 * hero's atmosphere carries down the whole page instead of stopping below the
 * fold. Colour is spread across the top, middle and bottom of the viewport, so
 * every scroll depth keeps some glow, and the whole wash drifts and breathes on
 * a slow loop ( see `nebula-drift`; stilled under reduced motion ). Both layers
 * live at z-index 0; sections and the footer sit at z-index 1 above them
 * ( see `section` and `.site-footer` ).
 ******************************************************************************/

body::before
{
    content:        "";
    position:       fixed;
    inset:          0;
    z-index:        0;
    pointer-events: none;
    background:
        radial-gradient( 70% 55% at 25%  3%, rgba( 120,  62, 155, 0.20 ), transparent 60% ),
        radial-gradient( 65% 50% at 80%  5%, rgba(  46,  92, 165, 0.17 ), transparent 62% ),
        radial-gradient( 55% 45% at 10% 48%, rgba(  46,  92, 165, 0.10 ), transparent 66% ),
        radial-gradient( 55% 48% at 92% 54%, rgba( 120,  60, 140, 0.10 ), transparent 68% ),
        radial-gradient( 60% 50% at 32% 92%, rgba(  90,  60, 150, 0.10 ), transparent 68% ),
        radial-gradient( 55% 48% at 85% 88%, rgba( 120,  60, 140, 0.08 ), transparent 70% );
    animation:      nebula-drift 55s ease-in-out infinite;
    will-change:    transform;
}

/* Slow, organic drift for the nebula wash — a gentle translate paired with a
   breathing scale. The scale stays above 1, so the fixed layer always overfills
   the viewport and no edge is ever exposed as it shifts. */
@keyframes nebula-drift
{
    0%
    {
        transform: translate3d( 0, 0, 0 ) scale( 1.08 );
    }

    50%
    {
        transform: translate3d( -1.8%, 1.2%, 0 ) scale( 1.16 );
    }

    100%
    {
        transform: translate3d( 0, 0, 0 ) scale( 1.08 );
    }
}

/* Subtle film grain over the whole page — smooths banding in the dark gradients
   and adds a filmic texture. Static and pointer-events: none, so it never
   intercepts input. The noise is a self-contained SVG ( feTurbulence ). */
body::after
{
    content:          "";
    position:         fixed;
    inset:            0;
    z-index:          40;
    pointer-events:   none;
    opacity:          0.05;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.sky-stars
{
    position:       fixed;
    inset:          0;
    z-index:        0;
    overflow:       hidden;
    pointer-events: none;
}

/* Occasional shooting star — a fading diagonal streak with a trailing tail.
   Launched on a random timer in JS and removed after it animates. */
.shooting-stars
{
    position:       fixed;
    inset:          0;
    z-index:        0;
    overflow:       hidden;
    pointer-events: none;
}

.shooting-star
{
    position:      absolute;
    width:         2px;
    height:        2px;
    border-radius: 50%;
    background:    #fff;
    box-shadow:    0 0 6px 1px rgba( 255, 255, 255, 0.85 );
    opacity:       0;
    transform:     rotate( var( --angle, 40deg ) ) translateX( 0 );
    animation:     shoot var( --dur, 1.1s ) ease-in forwards;
}

.shooting-star::before
{
    content:    "";
    position:   absolute;
    top:        50%;
    right:      100%;
    width:      110px;
    height:     1.5px;
    transform:  translateY( -50% );
    background: linear-gradient( to left, rgba( 255, 255, 255, 0.8 ), transparent );
}

@keyframes shoot
{
    0%
    {
        opacity:   0;
        transform: rotate( var( --angle, 40deg ) ) translateX( 0 );
    }

    12%
    {
        opacity: 1;
    }

    100%
    {
        opacity:   0;
        transform: rotate( var( --angle, 40deg ) ) translateX( var( --dist, 420px ) );
    }
}

/* Konami easter egg: a flying saucer that drifts in, roams closer, hovers, then
   darts away. A fixed layer like the shooting stars, but — unlike them — it sits
   on top of the page content ( z-index above the header and scroll bar ), so the
   craft flies over everything. Stays click-through ( pointer-events: none ).
   Built and removed in JS — one of the two payloads the Konami code picks
   between. */
.ufo
{
    position:       fixed;
    inset:          0;
    z-index:        100;
    overflow:       hidden;
    pointer-events: none;
}

/* Outer element: flies the craft through a wandering path — drifting in from
   an edge, closing in as it roams, holding a hover near the middle, then
   darting back off screen. Each waypoint's position ( --x0…--xe, --y0…--ye )
   and scale ( --s0…--se ) are set in JS, so every visit traces a different
   route. Keeping the path here leaves the bob transform on the craft itself
   free of interference. */
.ufo-flight
{
    position:  absolute;
    top:       0;
    left:      0;
    width:     var( --size, 130px );
    animation: ufo-visit var( --dur, 10s ) ease-in-out forwards;
}

/* Inner element: the craft itself, bobbing gently on its own timer with a soft
   glow. The parent's scale carries into the bob, so it floats more visibly the
   closer the craft gets. */
.ufo-craft
{
    display:   block;
    width:     100%;
    height:    auto;
    filter:    drop-shadow( 0 0 10px rgba( 102, 202, 220, 0.45 ) );
    animation: ufo-bob 2.6s ease-in-out infinite;
}

/* Drifts in far and small ( scale ), roams through two waypoints while growing
   closer, holds a hover ( 65%–85%, identical transform ), then accelerates off
   screen — the ease-in set at 85% makes that last dart quick. */
@keyframes ufo-visit
{
    0%
    {
        opacity:   0;
        transform: translate( var( --x0, -25vw ), var( --y0, 12vh ) ) scale( var( --s0, 0.35 ) );
    }

    6%
    {
        opacity: 1;
    }

    28%
    {
        transform: translate( var( --x1, 32vw ), var( --y1, 28vh ) ) scale( var( --s1, 0.6 ) );
    }

    52%
    {
        transform: translate( var( --x2, 56vw ), var( --y2, 22vh ) ) scale( var( --s2, 0.88 ) );
    }

    65%
    {
        transform: translate( var( --xh, 46vw ), var( --yh, 36vh ) ) scale( var( --sh, 1.1 ) );
    }

    85%
    {
        transform:                 translate( var( --xh, 46vw ), var( --yh, 36vh ) ) scale( var( --sh, 1.1 ) );
        animation-timing-function: cubic-bezier( 0.55, 0, 0.9, 0.35 );
    }

    94%
    {
        opacity: 1;
    }

    100%
    {
        opacity:   0;
        transform: translate( var( --xe, 120vw ), var( --ye, 8vh ) ) scale( var( --se, 0.8 ) );
    }
}

@keyframes ufo-bob
{
    0%,
    100%
    {
        transform: translateY( -6px );
    }

    50%
    {
        transform: translateY( 6px );
    }
}

/*******************************************************************************
 * Shared type helpers
 ******************************************************************************/

.eyebrow
{
    font-family:    var( --mono );
    font-size:      0.74rem;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color:          var( --accent-bright );
}

.section-head
{
    text-align: center;
    max-width:  640px;
    margin:     0 auto 56px;
}

.section-head h2
{
    font-size:      clamp( 1.7rem, 3.6vw, 2.5rem );
    font-weight:    700;
    letter-spacing: -0.02em;
    line-height:    1.25;
    margin-bottom:  12px;
}

.section-head p
{
    color:     var( --text-dim );
    font-size: 1.05rem;
}

section
{
    position: relative;
    z-index:  1;
}

/*******************************************************************************
 * Icons
 ******************************************************************************/

.svg-icon
{
    display:   block;
    flex:      none;
    max-width: 100%;
}

/*******************************************************************************
 * Scroll progress bar
 ******************************************************************************/

.scroll-progress
{
    position:         fixed;
    top:              0;
    left:             0;
    z-index:          60;
    width:            100%;
    height:           3px;
    transform:        scaleX( 0 );
    transform-origin: left;
    background:       linear-gradient( 90deg, var( --accent-bright ), var( --accent-2 ) );
    pointer-events:   none;
}

/*******************************************************************************
 * Header bar
 ******************************************************************************/

.site-header
{
    position:                sticky;
    top:                     0;
    z-index:                 20;
    background:              rgba( 5, 7, 13, 0.45 );
    -webkit-backdrop-filter: blur( 14px ) saturate( 140% );
    backdrop-filter:         blur( 14px ) saturate( 140% );
    border-bottom:           1px solid rgba( 255, 255, 255, 0.05 );
}

.site-header .wrap
{
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    gap:             18px;
    padding-top:     26px;
    padding-bottom:  26px;
    transition:      padding 0.25s ease;
}

/* Compact header once the page is scrolled ( class toggled in JS ) */
.site-header.scrolled .wrap
{
    padding-top:    8px;
    padding-bottom: 8px;
}

.site-header.scrolled .brand
{
    gap: 12px;
}

.site-header.scrolled .brand img
{
    width:  38px;
    height: 38px;
}

.site-header.scrolled .brand-name
{
    font-size: 1.12rem;
}

.site-header.scrolled .brand-tag
{
    font-size: 0.75rem;
}

.site-header.scrolled .github-link
{
    padding:   6px 14px;
    font-size: 0.84rem;
}

.site-header.scrolled .github-link .svg-icon
{
    width:  16px;
    height: 16px;
}

.brand
{
    display:     flex;
    align-items: center;
    gap:         15px;
}

.brand-text
{
    display:         flex;
    flex-direction:  column;
    justify-content: center;
}

.brand img
{
    width:      54px;
    height:     54px;
    flex:       none;
    filter:     drop-shadow( 0 6px 16px rgba( 0, 0, 0, 0.55 ) );
    transition: width 0.25s ease, height 0.25s ease;
}

.brand .brand-name
{
    font-size:      1.4rem;
    font-weight:    700;
    letter-spacing: -0.01em;
    line-height:    1.15;
    transition:     font-size 0.25s ease;
}

.brand .brand-tag
{
    font-size:   0.86rem;
    line-height: 1.2;
    color:       var( --text-dim );
    transition:  font-size 0.25s ease;
}

.github-link
{
    display:      inline-flex;
    align-items:  center;
    gap:          9px;
    font-size:    0.9rem;
    font-weight:  500;
    color:        var( --text );
    padding:      9px 17px;
    border-radius: 999px;
    border:       1px solid var( --border-strong );
    background:   rgba( 255, 255, 255, 0.02 );
    transition:   border-color 0.2s ease, background 0.2s ease, transform 0.2s ease, padding 0.25s ease, font-size 0.25s ease;
}

.github-link:hover
{
    border-color: var( --accent-line );
    background:   var( --accent-soft );
    transform:    translateY( -1px );
}

.github-link .svg-icon
{
    width:      18px;
    height:     18px;
    transition: width 0.25s ease, height 0.25s ease;
}

.gh-stars
{
    padding-left: 13px;
    margin-left:  2px;
    border-left:  1px solid var( --border-strong );
    font-size:    0.85rem;
    font-weight:  600;
    color:        var( --accent-bright );

    /* Keep "star + count" on a single line: as a flex item its min-content lets
       it break at the space when the header is tight, which stacks the count
       under the star. */
    white-space:  nowrap;
}

/* Likewise, the pill labels must never wrap to two lines when they are shown
   ( above the collapse breakpoint ) — they either fit on one line or are
   hidden by the responsive rules. */
.gh-text,
.sponsor-text
{
    white-space: nowrap;
}

/* The header's two secondary actions ( sponsor + GitHub ) sit together on the
   right, opposite the brand. `flex: none` keeps them at their natural size so
   that, at very narrow widths ( or high browser zoom ), they never get squeezed
   into slivers — the header overflows/scrolls instead of mangling the pills. */
.header-actions
{
    display:     flex;
    align-items: center;
    gap:         12px;
    flex:        none;
}

.sponsor-link,
.github-link
{
    flex: none;
}

/* Secondary "Sponsor" button — a nebula-purple counterpart to the GitHub pill.
   Deliberately quieter than the primary download CTA. */
.sponsor-link
{
    display:       inline-flex;
    align-items:   center;
    gap:           9px;
    font-size:     0.9rem;
    font-weight:   500;
    color:         var( --text );
    padding:       9px 17px;
    border-radius: 999px;
    border:        1px solid var( --sponsor-line );
    background:    var( --sponsor-soft );
    transition:    border-color 0.2s ease, background 0.2s ease, transform 0.2s ease, padding 0.25s ease, font-size 0.25s ease;
}

.sponsor-link:hover
{
    border-color: var( --accent-2 );
    background:    rgba( 180, 92, 216, 0.18 );
    transform:    translateY( -1px );
}

.sponsor-link .svg-icon
{
    width:      16px;
    height:     16px;
    transition: width 0.25s ease, height 0.25s ease;
}

/* Compact sponsor pill once the page is scrolled, mirroring the GitHub pill. */
.site-header.scrolled .sponsor-link
{
    padding:   6px 14px;
    font-size: 0.84rem;
}

.site-header.scrolled .sponsor-link .svg-icon
{
    width:  15px;
    height: 15px;
}

/*******************************************************************************
 * Hero
 ******************************************************************************/

.hero
{
    position: relative;
    padding:  40px 0 96px;
}

/* Nebula glow recreated with layered radial gradients */
.hero::before
{
    content:        "";
    position:       absolute;
    inset:          -140px -10% 0;
    z-index:        0;
    pointer-events: none;
    background:
        radial-gradient( 46% 60% at 72% 36%, rgba( 150, 70, 160, 0.30 ), transparent 70% ),
        radial-gradient( 40% 52% at 84% 30%, rgba( 196, 92, 110, 0.24 ), transparent 72% ),
        radial-gradient( 50% 58% at 60% 52%, rgba( 40,  86, 150, 0.30 ), transparent 72% ),
        radial-gradient( 38% 44% at 30% 24%, rgba( 46,  70, 132, 0.22 ), transparent 72% );
    filter:         blur( 6px );
    opacity:        0.9;
}

/* Twinkling, glowing star field */
.hero-stars
{
    position:       fixed;
    inset:          0;
    z-index:        0;
    overflow:       hidden;
    pointer-events: none;
}

.hero-star
{
    position:      absolute;
    border-radius: 50%;
    opacity:       var( --min );
    animation:     twinkle var( --dur ) ease-in-out var( --delay ) infinite;
}

/* Diffraction spikes on the brighter stars */
.hero-star.spikes::before,
.hero-star.spikes::after
{
    content:   "";
    position:  absolute;
    top:       50%;
    left:      50%;
    transform: translate( -50%, -50% );
}

.hero-star.spikes::before
{
    width:      750%;
    height:     1px;
    background: linear-gradient( to right, transparent, rgba( 255, 255, 255, 0.7 ), transparent );
}

.hero-star.spikes::after
{
    width:      1px;
    height:     750%;
    background: linear-gradient( to bottom, transparent, rgba( 255, 255, 255, 0.7 ), transparent );
}

@keyframes twinkle
{
    0%, 100%
    {
        opacity:   var( --min );
        transform: scale( 1 );
    }

    50%
    {
        opacity:   var( --max );
        transform: scale( 1.25 );
    }
}

.hero .wrap
{
    display:               grid;
    grid-template-columns: 1fr 1.12fr;
    align-items:           center;
    gap:                   56px;
}

.hero-copy h1
{
    font-size:      clamp( 2.3rem, 4.4vw, 3.4rem );
    font-weight:    800;
    letter-spacing: -0.025em;
    line-height:    1.25;
    margin-bottom:  22px;
}

/* Gradient accent on the hero keyword. Falls back to a solid colour where
   background-clip: text is unsupported, so the word is never invisible. */
.accent-word
{
    color: var( --accent-bright );
}

@supports ( -webkit-background-clip: text ) or ( background-clip: text )
{
    .accent-word
    {
        background:              linear-gradient( 120deg, var( --accent-bright ), var( --accent-2 ) );
        -webkit-background-clip: text;
        background-clip:         text;
        -webkit-text-fill-color: transparent;
    }
}

.hero-copy p.lede
{
    font-size:     clamp( 1.02rem, 1.6vw, 1.18rem );
    color:         var( --text-dim );
    max-width:     460px;
    margin-bottom: 32px;
}

/* Buttons */
.btn-row
{
    display:   flex;
    flex-wrap: wrap;
    gap:       14px;
}

.btn
{
    display:       inline-flex;
    align-items:   center;
    gap:           11px;
    font-weight:   600;
    font-size:     0.98rem;
    padding:       0 22px;
    height:        52px;
    border-radius: 13px;
    transition:    transform 0.2s ease, background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease;
}

.btn .svg-icon
{
    flex: none;
}

.btn-primary
{
    background: linear-gradient( 135deg, var( --accent ), var( --accent-2 ) );
    color:      #fff;
    box-shadow: 0 12px 30px -12px rgba( 59, 130, 246, 0.7 );
}

.btn-primary:hover
{
    filter:    brightness( 1.08 );
    transform: translateY( -2px );
}

.btn-appstore
{
    background: var( --panel-2 );
    color:      var( --text );
    border:     1px solid var( --border-strong );
}

.btn-appstore:hover
{
    border-color: var( --accent-line );
    background:    var( --panel );
    transform:     translateY( -2px );
}

.btn-appstore .store-label
{
    display:        flex;
    flex-direction: column;
    line-height:    1.1;
    text-align:     left;
}

.btn-appstore .store-label small
{
    font-size:      0.64rem;
    font-weight:    500;
    letter-spacing: 0.02em;
    color:          var( --text-dim );
}

.btn-appstore .store-label strong
{
    font-size:   1.02rem;
    font-weight: 600;
}

/* System requirements */
.sys-req
{
    display:     inline-flex;
    align-items: center;
    gap:         8px;
    font-size:   0.86rem;
    color:       var( --text-faint );
}

.hero-meta
{
    display:     flex;
    flex-wrap:   wrap;
    align-items: center;
    gap:         14px 18px;
    margin-top:  24px;
}

.badge-oss
{
    font-size:      0.78rem;
    font-weight:    600;
    color:          var( --accent-bright );
    background:     var( --accent-soft );
    border:         1px solid var( --accent-line );
    padding:        5px 12px;
    border-radius:  999px;
}

.sys-req .svg-icon
{
    width:   15px;
    height:  15px;
    opacity: 0.8;
}

/* Hero screenshot */
.hero-shot
{
    position:  relative;
    isolation: isolate;
}

.hero-shot .window-frame
{
    box-shadow:
        0 50px 120px -40px rgba( 0,   0,   0,   0.9 ),
        0 0    0     1px   rgba( 255, 255, 255, 0.03 );
}

/*******************************************************************************
 * Feature card grids (icon + title + text)
 ******************************************************************************/

.feature-section
{
    padding: 84px 0;
}

.feature-section-alt
{
    /* Elevated tint kept slightly translucent, so the fixed starfield shows through */
    background:    rgba( 9, 12, 20, 0.85 );
    border-top:    1px solid rgba( 255, 255, 255, 0.05 );
    border-bottom: 1px solid rgba( 255, 255, 255, 0.05 );
    border-image:  linear-gradient( 90deg, transparent, var( --border-strong ), transparent ) 1;
}

.card-grid
{
    display:         flex;
    flex-wrap:       wrap;
    justify-content: center;
    gap:             36px 22px;
}

.feature-card
{
    flex:          0 1 200px;
    text-align:    center;
    padding:       20px 16px;
    position:      relative;
    border-radius: var( --radius );
    background:    transparent;
    border:        1px solid transparent;
    transition:    transform 0.25s ease, background 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
}

.feature-card:hover
{
    transform:    translateY( -5px );
    background:   var( --accent-soft );
    border-color: var( --accent-line );
    box-shadow:   0 14px 30px -22px rgba( 0, 0, 0, 0.55 );
}

.feature-card .icon
{
    width:         64px;
    height:        64px;
    margin:        0 auto 20px;
    padding:       14px;
    display:       grid;
    place-items:   center;
    border-radius: var( --radius );
    background:    var( --accent-soft );
    border:        1px solid var( --accent-line );
    transition:    background 0.25s ease, border-color 0.25s ease;
}

.feature-card .icon .svg-icon
{
    width:  100%;
    height: 100%;
}

.feature-card:hover .icon
{
    background:   var( --accent-line );
    border-color: var( --accent-bright );
}

/* Gentle blue→purple hue sweep across each icon row, echoing the nebula accents
   ( --accent → --accent-2 ) for a bit more life. The filter rotates the icon
   tile only — tint, border and glyph — so the card's hover fill stays blue. The
   first card keeps the base blue ( 0deg, no rule needed ). */
.card-grid .feature-card:nth-child( 2 ) .icon
{
    filter: hue-rotate( 18deg );
}

.card-grid .feature-card:nth-child( 3 ) .icon
{
    filter: hue-rotate( 36deg );
}

.card-grid .feature-card:nth-child( 4 ) .icon
{
    filter: hue-rotate( 54deg );
}

.card-grid .feature-card:nth-child( 5 ) .icon
{
    filter: hue-rotate( 72deg );
}

.feature-card h3
{
    font-size:     1.04rem;
    font-weight:   600;
    margin-bottom: 9px;
}

.feature-card p
{
    font-size:   0.9rem;
    color:       var( --text-dim );
    line-height: 1.55;
    max-width:   200px;
    margin:      0 auto;
}

/* Framed like the sibling icon tiles ( same fill, border, radius and hover ) and
   kept to the SAME 64px height, so it sits in the icon row without towering over
   the glyph tiles. The formats pack into a compact 4-column grid inside. */
.format-chips
{
    display:               grid;
    grid-template-columns: repeat( 4, 1fr );
    place-content:         center;
    gap:                   4px;
    width:                 fit-content;
    max-width:             100%;
    height:                64px;
    margin:                0 auto 20px;
    padding:               0 10px;
    border-radius:         var( --radius );
    background:            var( --accent-soft );
    border:                1px solid var( --accent-line );
    transition:            background 0.25s ease, border-color 0.25s ease;
}

.feature-card:hover .format-chips
{
    background:   var( --accent-line );
    border-color: var( --accent-bright );
}

.format-chips span
{
    font-family:    var( --mono );
    font-size:      0.58rem;
    font-weight:    600;
    letter-spacing: 0.02em;
    text-align:     center;
    padding:        2px 5px;
    border-radius:  5px;
    background:     rgba( 59, 130, 246, 0.22 );
    border:         1px solid var( --accent-line );
    color:          var( --accent-bright );
}

/* "Soon" badge */
.soon-badge
{
    display:        inline-block;
    margin:         11px 0 0;
    font-size:      0.62rem;
    font-weight:    700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color:          #fff;
    background:     var( --accent-deep );
    padding:        3px 9px;
    border-radius:  999px;
}

/*******************************************************************************
 * Feature highlight blocks (title + text + screenshot placeholder)
 ******************************************************************************/

.highlights
{
    padding: 40px 0 96px;
}

.highlight
{
    display:               grid;
    grid-template-columns: 1fr 1.15fr;
    align-items:           center;
    gap:                   64px;
    padding:               48px 0;
}

.highlight + .highlight
{
    border-top:   1px solid var( --border );
    border-image: linear-gradient( 90deg, transparent, var( --border-strong ), transparent ) 1;
}

.highlight.reverse
{
    grid-template-columns: 1.15fr 1fr;
}

.highlight.reverse .highlight-media
{
    order: -1;
}

.highlight-copy .eyebrow
{
    display:       block;
    margin-bottom: 14px;
}

.highlight-copy h2
{
    font-size:      clamp( 1.5rem, 3vw, 2.1rem );
    font-weight:    700;
    letter-spacing: -0.02em;
    line-height:    1.25;
    margin-bottom:  14px;
}

.highlight-copy > p
{
    color:         var( --text-dim );
    font-size:     1.04rem;
    margin-bottom: 22px;
}

.highlight-list
{
    list-style: none;
    display:    grid;
    gap:        12px;
}

.highlight-list li
{
    display:     flex;
    gap:         12px;
    align-items: flex-start;
    color:       var( --text-dim );
    font-size:   0.96rem;
}

.highlight-list li .svg-icon
{
    flex:       none;
    width:      19px;
    height:     19px;
    margin-top: 2px;
}

.highlight-list li strong
{
    color:       var( --text );
    font-weight: 600;
}

/* macOS-style window frame used as a screenshot placeholder */
.window-frame
{
    border-radius: 14px;
    overflow:      hidden;
    border:        1px solid var( --border-strong );
    background:    var( --panel );
    box-shadow:
        0 40px 90px -40px rgba( 0,   0,   0,   0.9 ),
        0 0    0    1px   rgba( 255, 255, 255, 0.02 );
}

.window-frame .titlebar
{
    display:       flex;
    align-items:   center;
    gap:           8px;
    padding:       12px 15px;
    background:    var( --panel-2 );
    border-bottom: 1px solid var( --border );
}

.window-frame .titlebar i
{
    width:         12px;
    height:        12px;
    border-radius: 50%;
    display:       block;
}

.window-frame .titlebar i:nth-child( 1 )
{
    background: #ff5f57;
}

.window-frame .titlebar i:nth-child( 2 )
{
    background: #febc2e;
}

.window-frame .titlebar i:nth-child( 3 )
{
    background: #28c840;
}

/* Placeholder canvas inside the frame — swap for a real <img> later */
.shot-placeholder
{
    aspect-ratio: 16 / 10;
    display:      grid;
    place-items:  center;
    gap:          14px;
    text-align:   center;
    color:        var( --text-faint );
    background:
        radial-gradient( 60% 80% at 50% 40%, rgba( 59, 130, 246, 0.08 ), transparent 70% ),
        repeating-linear-gradient( 45deg, rgba( 255, 255, 255, 0.015 ) 0 14px, transparent 14px 28px ),
        var( --bg-elevated );
}

.shot-placeholder .svg-icon
{
    width:   46px;
    height:  46px;
    opacity: 0.45;
}

.shot-placeholder span
{
    font-family:    var( --mono );
    font-size:      0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* When a real screenshot is dropped in */
.window-frame .shot-img
{
    width:   100%;
    display: block;
}

/* Soft accent bloom behind each screenshot, so the app windows read as lit by the
   surrounding sky rather than pasted onto black. `isolation` keeps the glow's
   negative z-index local to each wrapper, so it stays behind its own frame only. */
.highlight-media
{
    position:  relative;
    isolation: isolate;
}

.hero-shot::before,
.highlight-media::before
{
    content:        "";
    position:       absolute;
    inset:          -16% -8%;
    z-index:        -1;
    pointer-events: none;
    background:     radial-gradient( ellipse at 50% 50%, rgba( 59, 130, 246, 0.40 ), rgba( 59, 130, 246, 0.10 ) 45%, transparent 72% );
    filter:         blur( 44px );
}

/*******************************************************************************
 * "Built by Astrophotographers" block
 ******************************************************************************/

.built
{
    padding: 72px 0;
}

.built-panel
{
    display:                 grid;
    grid-template-columns:   auto 1fr;
    align-items:             center;
    gap:                     48px;
    padding:                 48px 52px;
    border-radius:           var( --radius );
    border:                  1px solid var( --border-strong );
    background:
        radial-gradient( 120% 140% at 0% 0%, rgba( 40, 86, 150, 0.18 ), transparent 55% ),
        linear-gradient( 180deg, rgba( 14, 20, 32, 0.72 ), rgba( 9, 12, 20, 0.72 ) );
    -webkit-backdrop-filter: blur( 20px ) saturate( 140% );
    backdrop-filter:         blur( 20px ) saturate( 140% );
    box-shadow:              0 30px 80px -50px rgba( 0, 0, 0, 0.9 );
}

.built-photo
{
    margin:    0;
    flex:      none;
    min-width: 0;
}

.photo-frame
{
    /* Fixed stage: its size stays constant whatever the photo's aspect, so the
       heading and text beside it never shift as the gallery cycles. It is an
       <a> linking the current photo to AstroBin ( href updated per image ). */
    display:   block;
    position:  relative;
    width:     380px;
    height:    250px;
    max-width: 100%;
    margin:    0 auto;
    overflow:  hidden;
}

/* Each photo keeps its own aspect ratio — never cropped, never letterboxed. It
   is scaled to fit the stage and centred, with the frame drawn tight around the
   image itself, so any spare room simply shows the panel. Two layers crossfade
   ( see installBuiltGallery ). */
.photo-frame .photo-layer
{
    position:      absolute;
    inset:         0;
    margin:        auto;
    max-width:     100%;
    max-height:    100%;
    width:         auto;
    height:        auto;
    border-radius: 14px;
    border:        1px solid var( --border-strong );
    box-shadow:    0 24px 60px -30px rgba( 0, 0, 0, 0.9 );
    opacity:       0;
    transition:    opacity 0.8s ease;
}

.photo-frame .photo-layer.is-active
{
    opacity: 1;
}

/* "View on AstroBin" affordance — centred over the photo, revealed on hover or
   keyboard focus of the frame. Purely visual ( the whole frame is the link ). */
.photo-view
{
    position:                absolute;
    z-index:                 2;
    top:                     50%;
    left:                    50%;
    transform:               translate( -50%, -50% ) scale( 0.96 );
    display:                 inline-flex;
    align-items:             center;
    gap:                     8px;
    padding:                 8px 14px;
    border-radius:           999px;
    background:              rgba( 5, 7, 13, 0.66 );
    -webkit-backdrop-filter: blur( 8px );
    backdrop-filter:         blur( 8px );
    border:                  1px solid rgba( 255, 255, 255, 0.18 );
    font-size:               0.78rem;
    font-weight:             600;
    color:                   var( --text );
    white-space:             nowrap;
    opacity:                 0;
    transition:              opacity 0.25s ease, transform 0.25s ease;
    pointer-events:          none;
}

.photo-frame:hover .photo-view,
.photo-frame:focus-visible .photo-view
{
    opacity:   1;
    transform: translate( -50%, -50% ) scale( 1 );
}

.photo-view .svg-icon
{
    width:  15px;
    height: 15px;
}

.built-photo figcaption
{
    margin-top:     12px;
    min-height:     1.1em;
    font-family:    var( --mono );
    font-size:      0.72rem;
    letter-spacing: 0.05em;
    color:          var( --text-faint );
    text-align:     center;
    transition:     opacity 0.35s ease;
}

.built-panel h2
{
    font-size:      clamp( 1.5rem, 3vw, 2.1rem );
    font-weight:    700;
    letter-spacing: -0.02em;
    line-height:    1.25;
    margin-bottom:  14px;
}

.built-panel p
{
    color:     var( --text-dim );
    font-size: 1.06rem;
    max-width: 560px;
}

/*******************************************************************************
 * Call to action
 ******************************************************************************/

.cta
{
    position:   relative;
    overflow:   hidden;
    text-align: center;
    padding:    72px 0;
}

/* Soft glow behind the closing call to action */
.cta::before
{
    content:        "";
    position:       absolute;
    left:           50%;
    top:            50%;
    transform:      translate( -50%, -50% );
    width:          760px;
    max-width:      120%;
    height:         420px;
    z-index:        0;
    pointer-events: none;
    background:
        radial-gradient( 55% 65% at 40% 50%, rgba(  59, 130, 246, 0.15 ), transparent 70% ),
        radial-gradient( 50% 60% at 64% 52%, rgba( 180,  92, 216, 0.11 ), transparent 72% );
    filter:         blur( 6px );
}

.cta .wrap
{
    position: relative;
    z-index:  1;
}

.cta-icon
{
    width:  128px;
    height: 128px;
    margin: 0 auto 22px;
    filter: drop-shadow( 0 10px 26px rgba( 0, 0, 0, 0.6 ) );
}

.cta h2
{
    font-size:      clamp( 1.9rem, 4vw, 2.8rem );
    font-weight:    700;
    letter-spacing: -0.02em;
    line-height:    1.25;
    margin-bottom:  14px;
}

.cta p
{
    color:     var( --text-dim );
    font-size: 1.08rem;
    max-width: 520px;
    margin:    0 auto 34px;
}

.cta .btn-row
{
    justify-content: center;
}

.cta .sys-req
{
    margin-top: 20px;
}

/*******************************************************************************
 * Sponsor — a quiet support band just above the footer. Kept deliberately
 * understated so it never competes with the download call to action above it.
 ******************************************************************************/

.sponsor
{
    padding:    16px 0 64px;
    text-align: center;
}

.sponsor-panel
{
    max-width:     560px;
    margin:        0 auto;
    padding:       40px 36px;
    border:        1px solid var( --sponsor-line );
    border-radius: var( --radius );
    background:    var( --sponsor-soft );
}

.sponsor-heart
{
    width:  40px;
    height: 40px;
    margin: 0 auto 14px;
    filter: drop-shadow( 0 6px 16px rgba( 180, 92, 216, 0.35 ) );
}

.sponsor-panel h2
{
    font-size:      1.5rem;
    font-weight:    700;
    letter-spacing: -0.01em;
    margin-bottom:  10px;
}

.sponsor-panel p
{
    color:         var( --text-dim );
    font-size:     1rem;
    max-width:     440px;
    margin:        0 auto 26px;
}

/* Outline purple button — a secondary counterpart to the primary download CTA.
   `nowrap` keeps the label on one line so it never blows past the button's
   fixed height on the narrowest phones. */
.btn-sponsor
{
    background:  transparent;
    color:       var( --text );
    border:      1px solid var( --sponsor-line );
    white-space: nowrap;
}

.btn-sponsor:hover
{
    border-color: var( --accent-2 );
    background:    rgba( 180, 92, 216, 0.18 );
    transform:    translateY( -2px );
}

/*******************************************************************************
 * Footer
 ******************************************************************************/

.site-footer
{
    position:                relative;
    z-index:                 1;
    background:              rgba( 5, 7, 13, 0.45 );
    -webkit-backdrop-filter: blur( 14px ) saturate( 140% );
    backdrop-filter:         blur( 14px ) saturate( 140% );
    border-top:              1px solid var( --border );
    border-image:            linear-gradient( 90deg, transparent, var( --border-strong ), transparent ) 1;
    padding:                 36px 0;
}

.site-footer .wrap
{
    display:         flex;
    align-items:     center;
    justify-content: space-between;
    flex-wrap:       wrap;
    gap:             18px;
}

.footer-brand
{
    display:     flex;
    align-items: center;
    gap:         12px;
    font-weight: 600;
    line-height: 1.3;
}

.footer-brand img
{
    width:  54px;
    height: 54px;
    flex:   none;
    filter: drop-shadow( 0 4px 12px rgba( 0, 0, 0, 0.5 ) );
}

.footer-brand .ft-sub
{
    font-weight: 400;
    font-size:   0.84rem;
    color:       var( --text-faint );
}

.footer-right
{
    display:        flex;
    flex-direction: column;
    align-items:    flex-end;
    gap:            14px;
}

.footer-social
{
    display:     flex;
    align-items: center;
    gap:         18px;
}

.site-footer a.footer-link
{
    display:     inline-flex;
    align-items: center;
    gap:         8px;
    font-size:   0.88rem;
    color:       var( --text-dim );
    transition:  color 0.2s ease;
}

.site-footer a.footer-link:hover
{
    color: var( --text );
}

.site-footer a.footer-link .svg-icon
{
    width:      18px;
    height:     18px;
    opacity:    0.62;
    transition: opacity 0.2s ease;
}

.site-footer a.footer-link:hover .svg-icon
{
    opacity: 1;
}

.site-footer .copyright
{
    font-size: 0.84rem;
    color:     var( --text-faint );
}

.site-footer .copyright a
{
    color:      var( --text-dim );
    transition: color 0.2s ease;
}

.site-footer .copyright a:hover
{
    color: var( --text );
}

/*******************************************************************************
 * Scroll reveal
 ******************************************************************************/

/* Scoped to .js so content stays visible when JavaScript is unavailable */
.js .reveal
{
    opacity:          0;
    transform:        translateY( 24px );
    transition:       opacity 0.7s ease, transform 0.7s ease;

    /* Cards inside a grid get a small incremental delay ( set in JS ) so a row
       cascades in instead of appearing all at once; other reveals default to 0s */
    transition-delay: var( --reveal-delay, 0s );
}

.js .reveal.in
{
    opacity:   1;
    transform: none;
}

/*******************************************************************************
 * Responsive
 ******************************************************************************/

/* Below this width the brand plus two labelled action pills ( sponsor + GitHub,
   the latter also carrying its star count ) no longer fit on one line, so both
   pills collapse to their icons. Above it, the labels have room to breathe. */
@media ( max-width: 860px )
{
    .github-link .gh-text,
    .sponsor-link .sponsor-text
    {
        display: none;
    }

    .github-link,
    .sponsor-link
    {
        padding: 9px;
    }

    .header-actions
    {
        gap: 10px;
    }
}

@media ( max-width: 980px )
{
    .hero .wrap
    {
        grid-template-columns: 1fr;
        gap:                   44px;
    }

    .hero-copy
    {
        text-align: center;
    }

    .hero-copy p.lede
    {
        margin-left:  auto;
        margin-right: auto;
    }

    .btn-row
    {
        justify-content: center;
    }

    .hero-meta
    {
        justify-content: center;
    }

    .highlight,
    .highlight.reverse
    {
        grid-template-columns: 1fr;
        gap:                   32px;
    }

    .highlight .highlight-media,
    .highlight.reverse .highlight-media
    {
        order: 0;
    }

    .built-panel
    {
        grid-template-columns: 1fr;
        text-align:            center;
        padding:               40px 32px;
    }

    .built-panel .built-photo
    {
        margin: 0 auto;
    }

    /* Fluid stage on narrow screens — 100% of the column, capped at 380px, so it
       can never force the panel wider than the viewport. */
    .photo-frame
    {
        width:     100%;
        max-width: 380px;
    }

    .built-panel p
    {
        margin-left:  auto;
        margin-right: auto;
    }
}

@media ( max-width: 620px )
{
    .wrap
    {
        padding: 0 20px;
    }

    .brand .brand-tag
    {
        display: none;
    }

    /* Tighten the support band's panel so its button never has to wrap on the
       narrowest phones ( see the 860px block for the header pill collapse ). */
    .sponsor-panel
    {
        padding: 32px 22px;
    }

    .btn
    {
        width:           100%;
        justify-content: center;
    }

    /* A strict two-column grid on phones. The desktop layout uses flex-wrap, whose
       line-breaking depends on each card's flex-basis plus the gap, so a half-width
       basis sits right at the 100% boundary and sub-pixel rounding drops some cards
       onto their own row ( an inconsistent two-up / one-up mix ). An explicit grid
       with `minmax( 0, 1fr )` tracks gives two equal columns regardless of content
       or rounding; a lone final card is centred at one column's width. */
    .card-grid
    {
        display:               grid;
        grid-template-columns: repeat( 2, minmax( 0, 1fr ) );
        gap:                   28px 18px;
    }

    .card-grid .feature-card:last-child:nth-child( odd )
    {
        grid-column:  1 / -1;
        justify-self: center;
        max-width:    calc( 50% - 9px );
    }

    /* A half-width phone card is too narrow for the formats tile: it either
       overflows or grows taller than the sibling icon tiles, breaking the row
       alignment. Give it its own full-width row instead, so nothing sits beside
       it to misalign — the tile keeps its normal 64px height with room to spare.
       The remaining four cards then form two clean rows, so the last-child /
       full-width rule is reset here to keep this section's grid tidy. */
    .feature-section-alt .card-grid .feature-card:first-child
    {
        grid-column: 1 / -1;
    }

    .feature-section-alt .card-grid .feature-card:last-child:nth-child( odd )
    {
        grid-column:  auto;
        justify-self: auto;
        max-width:    none;
    }

    .footer-right
    {
        align-items: flex-start;
    }
}

/* On the very narrowest phones the brand, both action pills and the star count
   no longer fit on one line, so the decorative star badge is dropped — the
   GitHub link keeps its icon, which is what matters at this size. */
@media ( max-width: 360px )
{
    .gh-stars
    {
        display: none;
    }
}

@media ( prefers-reduced-motion: reduce )
{
    html
    {
        scroll-behavior: auto;
    }

    .js .reveal
    {
        opacity:    1;
        transform:  none;
        transition: none;
    }

    .hero-star
    {
        animation: none;
        opacity:   var( --max );
    }

    /* Still the nebula wash — no drift, and back to its natural, unscaled size */
    body::before
    {
        animation: none;
        transform: none;
    }

    .btn:hover,
    .github-link:hover,
    .feature-card:hover
    {
        transform: none;
    }
}
