/*
 * Poppa — site styles.
 *
 * The palette is the one already deployed on privacy.html, unchanged, so the
 * two pages read as one site rather than as a landing page and a legal
 * afterthought. The sky stops are taken from the app's own daytime sky.
 *
 * Text does not sit on the bare background anywhere. Over the pale floor of
 * the gradient an outline keeps text technically legible and it still reads as
 * floating, so blocks of it go on a plate — the same rule the app's UI follows.
 */

:root {
    --sky-top: #4A6BC7;
    --sky-mid: #6BB8E0;
    --sky-low: #9ED4DE;
    --sky-floor: #D1BA85;
    --ink: #122142;
    --ink-soft: #3C4A66;
    --amber: #EB9E24;
    --plate: #FFFFFF;
    --plate-soft: rgba(255, 255, 255, 0.92);
    --link: #1F5FA8;
    /* One scrim value, used by everything that sits light-on-sky. 0.62 is the
       smallest that clears 4.5:1 against BOTH the lightest gradient stop
       (5.92:1) and a measured bubble highlight (4.95:1). 0.55 passed the stop
       at 4.95:1 and failed the highlight at 4.04:1 -- the canvas is what is
       really behind this text, so the highlight is the case that counts.
       ⚠️ backdrop-filter: blur() is not a substitute: blur redistributes a
       highlight, it does not darken it, so composite luminance is unchanged. */
    --scrim: rgba(18, 33, 66, 0.62);
    --radius: 18px;
    --shadow: 0 18px 45px rgba(18, 33, 66, 0.18);
    --measure: 34rem;
}

* { box-sizing: border-box; }

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

body {
    margin: 0;
    min-height: 100vh;
    /* Also the fallback: if WebGL is unavailable the canvas never reveals
       itself and this gradient is the background, at the same four stops. */
    background: linear-gradient(180deg,
        var(--sky-top) 0%,
        var(--sky-mid) 42%,
        var(--sky-low) 78%,
        var(--sky-floor) 100%);
    background-attachment: fixed;
    color: var(--ink);
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
    line-height: 1.65;
}

#bubble-field {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    z-index: 0;
    /* The field is scenery. Every pointer event is handled on window, so the
       canvas itself must never intercept a click meant for a link. */
    pointer-events: none;
}

.page {
    position: relative;
    z-index: 1;
    padding: 0 1rem 3rem;
}

a { color: var(--link); }
/* ⚠️ The focus ring is ink with a cream halo, NOT the amber accent.
   outline-offset puts the ring 3px OUTSIDE the element -- i.e. on the sky --
   and amber measures 1.01:1 against --sky-mid, meaning the ring around the
   Google Play button was the same luminance as the sky behind it. Ink is
   3.19:1 at its worst stop and 13.55:1 over a bubble highlight; the halo is
   what keeps the pair visible on the dark store button too. */
a:focus-visible,
button:focus-visible {
    outline: 3px solid var(--ink);
    outline-offset: 3px;
    box-shadow: 0 0 0 6px #FFFCF5;
    border-radius: 6px;
}

.skip-link {
    position: fixed;
    left: -9999px;
    top: 0;
    background: var(--plate);
    color: var(--ink);
    padding: 0.7rem 1rem;
    border-radius: 0 0 var(--radius) 0;
    z-index: 5;
}
.skip-link:focus { left: 0; }

/* ---------------------------------------------------------------- Hero */

.hero {
    max-width: 46rem;
    margin: 0 auto;
    padding: clamp(2.5rem, 8vw, 4.5rem) 0 clamp(1.5rem, 5vw, 3rem);
    text-align: center;
}

.hero__logo {
    display: block;
    width: min(20rem, 68vw);
    height: auto;
    margin: 0 auto clamp(1rem, 3vw, 1.6rem);
    filter: drop-shadow(0 10px 26px rgba(18, 33, 66, 0.28));
}

/* 🚨 The h1 gets a scrim, not a text-shadow, and this was measured wrong once.
   WCAG gives a text-shadow no credit at all, and light text on this sky does
   not clear 3:1 at ANY viewport: 2.43:1 at its best, 1.56:1 at worst. The trap
   is that the lightest part of the gradient is NOT the cream floor -- it is
   --sky-low #9ED4DE at 78% height (luminance 0.596 against the floor's 0.504),
   and the second palette puts #ADD4D9 there, lighter still. Measuring against
   the floor gives a comfortable number for the wrong stop.
   Worse, the canvas is what is actually behind this text: a bubble's specular
   highlight rendered at rgb(225,239,249) drops #FFFCF5 to 1.14:1, so the
   failure is intermittent, which is harder to notice than a constant one. */
.hero__tagline {
    display: inline-block;
    margin: 0 0 1rem;
    padding: 0.28em 0.7em;
    font-size: clamp(2rem, 7vw, 3.2rem);
    line-height: 1.15;
    color: #FFFCF5;
    background: var(--scrim);
    border-radius: var(--radius);
}

.hero__subline {
    max-width: var(--measure);
    margin: 0 auto;
    padding: 0.85rem 1.2rem;
    background: var(--plate-soft);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    font-size: clamp(1rem, 2.4vw, 1.12rem);
}

/* Store buttons.
   The store marks are INLINE SVG rather than Google's and Apple's downloadable
   badge images: same recognisable glyph, but nothing to fetch or commit, it
   scales to any size without a second asset, and the Apple mark inherits
   currentColor so it stays right on any button background. The wordmarks are
   set in the site's own type, which is why these are not the official badges
   and should not be described as them.

   A button with no href renders identically but is not clickable and not
   focusable. The App Store one carries its state in visible text instead. */
.stores {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    justify-content: center;
    margin: 1.4rem 0 0;
}

.store-btn {
    display: inline-flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 0.6rem;
    min-width: 11rem;
    padding: 0.55rem 1.35rem;
    border-radius: 12px;
    background: #122142;
    color: #FFFCF5;
    text-decoration: none;
    box-shadow: 0 10px 24px rgba(18, 33, 66, 0.32);
    border: 1px solid rgba(255, 255, 255, 0.22);
    transition: transform 120ms ease, box-shadow 120ms ease;
}

/* Keep the hero focused on the store that matches a mobile visitor. Other
   platform links are deliberately left unmarked so they remain available
   lower on the page. */
html[data-platform="android"] [data-platform-link="ios"],
html[data-platform="ios"] [data-platform-link="android"] {
    display: none;
}

.store-btn[href]:hover,
.store-btn[href]:focus-visible {
    transform: translateY(-2px);
    box-shadow: 0 14px 30px rgba(18, 33, 66, 0.4);
}

/* Muted by colour rather than by opacity. opacity dimmed the small line --
   which already carries its own 0.82 -- to a compounded 0.508, taking it to
   4.17:1 over the sky. And because this is an <a> with no href it is not a
   real disabled control, so the WCAG exemption for disabled UI does not cover
   it. Flat, full-opacity, still visibly not the primary button. */
.store-btn--soon {
    background: #40506E;
    cursor: default;
}

.store-btn__icon {
    flex: 0 0 auto;
    width: 1.65rem;
    height: 1.65rem;
}

.store-btn__text {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

.store-btn__small {
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0.82;
    line-height: 1.4;
}

/* The "Coming soon" line is the state, so it must not be the quietest thing on
   the button -- it keeps full opacity where the "Get it on" label is dimmed.
   Declared AFTER the base rule, or the 0.82 there wins on source order. */
.store-btn--soon .store-btn__small {
    opacity: 1;
    font-weight: 600;
}

.store-btn__big {
    font-size: 1.12rem;
    font-weight: 600;
    line-height: 1.2;
}

.hint {
    margin: 1.4rem auto 0;
    max-width: var(--measure);
    padding: 0.55rem 1rem;
    border-radius: 999px;
    background: var(--scrim);
    color: #FFFCF5;
    font-size: 0.9rem;
    backdrop-filter: blur(3px);
}
.hint[hidden] { display: none; }

/* ------------------------------------------------------------ Sections */

.section {
    max-width: 58rem;
    margin: 0 auto clamp(1rem, 3vw, 1.6rem);
    background: var(--plate);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: clamp(1.3rem, 4vw, 2.2rem);
}

.section h2 {
    margin: 0 0 0.5rem;
    font-size: clamp(1.25rem, 3.4vw, 1.6rem);
    line-height: 1.25;
}

.section p { margin: 0 0 0.8rem; max-width: var(--measure); }
.section p:last-child { margin-bottom: 0; }

.section__lead {
    font-size: clamp(1.02rem, 2.4vw, 1.12rem);
    color: var(--ink-soft);
    margin-bottom: clamp(1rem, 3vw, 1.5rem) !important;
}

/* The four mechanics: text and its screenshot side by side, stacking on a
   phone. A grid rather than offsets, so a fifth one added later needs no
   retyped coordinates. */
.steps {
    display: grid;
    gap: clamp(1rem, 3vw, 1.6rem);
    grid-template-columns: 1fr;
}
@media (min-width: 44rem) {
    .steps { grid-template-columns: repeat(3, 1fr); }
}

/* On a phone each step is a compact row -- shot beside its words. Stacked
   full-width, portrait screenshots make the page about twice as long as it
   needs to be and the reader scrolls past the words to reach the next one. */
.step {
    display: grid;
    /* Fluid rather than a fixed 6.5rem: held fixed, the thumbnail was still a
       104px postage stamp at 700px wide, dwarfed by its own caption. */
    grid-template-columns: clamp(6.5rem, 22vw, 11rem) 1fr;
    align-items: center;
    column-gap: 1rem;
    margin: 0;
}

.step figcaption {
    margin: 0;
    color: var(--ink-soft);
}

.step__shot {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 12px;
    box-shadow: 0 10px 26px rgba(18, 33, 66, 0.22);
    background: var(--sky-mid);
}

/* With room, the shot goes above its words -- side by side in a three-column
   grid leaves the screenshots too small to read anything in. */
@media (min-width: 44rem) {
    .step {
        display: flex;
        flex-direction: column;
        gap: 0.9rem;
        align-items: center;
        text-align: center;
    }
    .step__shot {
        max-width: 11rem;
    }
}

/* --------------------------------------------------------- Plain list */

/* Named for its shape, not its audience. It was `.plain`, which was the
   framing the copy has since dropped: Poppa is a bubble popping game with
   nothing to win, and the page is not addressed to somebody buying it for
   somebody else. */

.plain {
    background: linear-gradient(180deg, #FFFFFF 0%, #FFF6DF 100%);
    border-left: 5px solid var(--amber);
}

.plain ul {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: 0.75rem;
}
@media (min-width: 44rem) {
    .plain ul { grid-template-columns: repeat(2, 1fr); }
}

.plain li {
    margin: 0;
    padding-left: 1.6rem;
    position: relative;
    max-width: var(--measure);
}
.plain li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.62em;
    width: 0.62rem;
    height: 0.62rem;
    border-radius: 50%;
    background: var(--amber);
}

.closing {
    max-width: 46rem;
    margin: clamp(1.4rem, 4vw, 2.2rem) auto;
    text-align: center;
    font-size: clamp(1.05rem, 2.8vw, 1.3rem);
    line-height: 1.5;
    color: var(--ink);
    background: var(--plate-soft);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: clamp(1.1rem, 3.5vw, 1.8rem);
}

/* -------------------------------------------------------------- Footer */

/* ⚠️ The footer gets a plate, not a text-shadow.
   The background is fixed to the viewport AND there is a canvas of drifting
   bubbles over it, so the footer can end up over any stop of the gradient --
   including the near-white cream floor -- with a pale bubble passing behind it.
   Light text with a shadow was tried and vanished against the bottom stop;
   dark ink fails against the top one. Neither colour is safe on its own,
   because the thing behind it moves. A plate is the only version that holds. */
.site-footer {
    max-width: 58rem;
    margin: 1.2rem auto 0;
    padding: 1rem 1.2rem;
    border-radius: var(--radius);
    background: var(--scrim);
    backdrop-filter: blur(3px);
    text-align: center;
    font-size: 0.88rem;
    color: #FFFCF5;
}
.site-footer p { margin: 0.35rem 0; }

/* Studio badge: links back to the company site. The mark is a dark-background
   lockup -- red outlines with black letter interiors and a cyan strapline --
   so it keeps its own background and gets a rounded chip. Cutting the black
   out and putting it on white leaves the letters hollow and the strapline
   almost invisible; light-background variants and that caveat are recorded in
   the lynetech-website repo under site/assets/brand/. */
.studio-badge { display: inline-block; line-height: 0; border: 0; border-radius: 10px; overflow: hidden; opacity: 0.82; transition: opacity 0.2s ease; }
.studio-badge:hover, .studio-badge:focus-visible { opacity: 1; }
.studio-badge img { display: block; width: 190px; height: auto; }

.site-footer a { color: #FFFCF5; text-decoration-thickness: 1px; text-underline-offset: 2px; }

/* --------------------------------------------------------- Preferences */

@media (prefers-reduced-motion: reduce) {
    /* The bubble field checks this itself and stills its own drift; this only
       has to stop anything CSS is animating. */
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

@media print {
    #bubble-field, .hint { display: none; }
    body { background: #fff; color: #000; }
    .section, .closing, .hero__subline { box-shadow: none; border: 1px solid #ccc; }
}
