/*
    Sweep — landing page.
    Kept apart from styles.css, which dresses the privacy and support pages.
    Those are utility documents and read as such; this is the front door.

    Everything here comes from the app's own style guide (STYLE_GUIDE.md in
    the app repository): the iridescent palette is the app icon's gradient,
    the card radius is the app's 26pt, and the hero's fade uses the same mask
    stops as IridescentBackdrop — solid, 0.9 at 40%, gone by the bottom.
*/

:root {
    /* The iridescent palette, as the app defines it. */
    --sky: 120, 217, 255;
    --silver: 232, 235, 245;
    --blue: 51, 107, 240;
    --deep: 18, 38, 107;

    /* Category tints, dark-appearance values — the app is dark only. */
    --screenshots: #CFE0F5;
    --whatsapp: #96D9F5;
    --recordings: #5CD1F5;
    --duplicates: #3B82F6;
    --large-photos: #6366F1;
    --large-videos: #9061F9;
    --live-photos: #C084FC;
    --downloaded: #E0A0FC;

    --ink: #FFFFFF;
    --ink-dim: rgba(255, 255, 255, 0.62);
    --ink-faint: rgba(255, 255, 255, 0.40);
    --card: rgba(255, 255, 255, 0.055);
    --card-edge: rgba(255, 255, 255, 0.10);
    --radius: 26px;
}

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

body {
    /* The app ships dark only, so the page does too — no light variant. */
    background: #000;
    color: var(--ink);
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
                 'Helvetica Neue', Arial, sans-serif;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}

/* Figures and headlines are rounded in the app; ui-rounded is the same face
   on Apple platforms and degrades to the system font everywhere else. */
.display {
    font-family: ui-rounded, 'SF Pro Rounded', system-ui, -apple-system, sans-serif;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.1;
}

.wrap { max-width: 960px; margin: 0 auto; padding: 0 24px; }

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

.hero { position: relative; padding: 88px 0 72px; text-align: center; }

/*
    The mesh gradient, approximated in layers: silver at the crown, sky either
    side of it, the icon's blue through the middle, and deep navy before the
    page's black. A real MeshGradient bends the bands; stacked radials get
    close enough at this size, and cost nothing to render.

    The drift below is IridescentSurface's, translated. In the app both interior
    rows of the mesh move in y across the full width and the centre column also
    moves in x — the note there is that holding the row ends fixed and moving
    only the centre "nails the bands in place and reads as no movement at all",
    so every band here moves bodily rather than bulging from the middle.
*/
.aurora {
    position: absolute;
    inset: -120px 0 auto 0;
    height: 760px;
    pointer-events: none;
    /* The app's own mask stops, so the colour ends as the page rather than on
       an edge. Masking the parent takes the drifting layers with it. */
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, rgba(0,0,0,0.9) 40%, transparent 100%);
            mask-image: linear-gradient(to bottom, #000 0%, rgba(0,0,0,0.9) 40%, transparent 100%);
    /* Both needed now that the layers inside move. A mask repeats by default,
       so a layer drifting past the bottom edge would meet the opaque top of the
       next tile and reappear below the fade instead of ending in it. And the
       clip keeps the 20% overhang doing its job — covering the frame — without
       painting outside it. */
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    overflow: hidden;
}

.aurora div { position: absolute; inset: 0; }

/*
    The x-drifting bands are 140% wide, overhanging 20% each side. Translating a
    whole layer sideways is not what the mesh does — there the outer columns
    hold their x precisely so the fill always covers its frame — and a layer at
    the container's own width slid 14% leaves a dark strip down that edge. The
    overhang is what pins the frame instead: 20% of slack against 14% of travel.

    Their horizontal radii are divided by that 1.4 so the blobs keep the
    absolute width they had before the box grew — 92% of the hero is 66% of a
    box half again as wide, and 120% is 86%. The vertical radii are untouched,
    the box being no taller.

    Qualified with .aurora because the reset above is a class plus a type and
    outspecifies a bare class: as `.band-x` alone the overhang silently lost to
    `inset: 0`, and the strip down the edge came back.
*/
.aurora .band-x { inset: 0 -20%; }

/* The crown alone does not drift. Its silver is the mesh's top row, whose
   corners are pinned in the app so the fill always covers its frame — and it
   sits directly behind the app icon, where a travelling highlight would pull
   the eye off the one thing the hero is for. */
.crown  { background: radial-gradient(58% 42% at 50% 4%,  rgba(var(--silver), 0.55), transparent 70%); }
.sky-l  .veil { background: radial-gradient(70% 50% at 18% 10%, rgba(var(--sky), 0.42), transparent 72%); }
.sky-r  .veil { background: radial-gradient(70% 50% at 84% 14%, rgba(var(--sky), 0.32), transparent 72%); }
.core   { background: radial-gradient(66% 62% at 50% 34%, rgba(var(--blue), 0.50), transparent 76%); }
.deep   { background: radial-gradient(86% 80% at 50% 58%, rgba(var(--deep), 0.62), transparent 82%); }

/*
    Amplitudes are the app's, as fractions of the surface: ±0.09 in y, ±0.14 in
    x for the centre column. Percentages in a translate resolve against the
    layer's own box, so they stay proportional at any width, exactly as the
    mesh's normalised points do.

    A layer needing both axes nests, because one element has one transform: the
    outer box carries x, the inner y, each on its own clock.
*/
@keyframes drift-y {
    0%, 100% { transform: translateY(calc(var(--amp) * -1)); }
    50%      { transform: translateY(var(--amp)); }
}

@keyframes drift-x {
    0%, 100% { transform: translateX(calc(var(--amp) * -1)); }
    50%      { transform: translateX(var(--amp)); }
}

/*
    Periods are 2π/ω for the app's own frequencies — 0.41, 0.47, 0.53, 0.29,
    0.61 and 0.34 rad/s — which is what keeps them mutually prime enough that
    the surface never settles into a visible loop. The negative delays are the
    app's phase offsets: they start each band part-way through rather than
    lining every one up at the same extreme on load.

    ease-in-out is a triangle wave with softened corners, not a true sine, but
    at a 15-second period nothing in the difference is visible.
*/
.band-y, .band-x { animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1); }
.band-y { --amp: 9%;  animation-name: drift-y; animation-iteration-count: infinite; }
/* 10% of a box 1.4× the hero's width is the app's 14% of the surface. */
.band-x { --amp: 10%; animation-name: drift-x; animation-iteration-count: infinite; }

.sky-l  { animation-duration: 15.3s; animation-delay:  -1.0s; }
.sky-r  { animation-duration: 13.4s; animation-delay:  -5.2s; }
.core-x { animation-duration: 21.7s; animation-delay:  -7.1s; }
.core-y { animation-duration: 11.9s; animation-delay:  -3.9s; }
.deep-x { animation-duration: 18.5s; animation-delay:  -2.2s; }
.deep-y { animation-duration: 10.3s; animation-delay:  -6.4s; }

/* Only the content is lifted above the gradient. Written as `.hero > *`
   this matched .aurora too — same specificity, later in the file — which
   dropped the absolutely-positioned gradient back into normal flow, where
   its 760px pushed the whole hero down the page. */
.hero > .wrap { position: relative; z-index: 1; }

.app-icon {
    width: 132px; height: 132px;
    border-radius: 29.5%;           /* the squircle, near enough at this size */
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
    margin-bottom: 26px;
}

.hero h1 { font-size: clamp(2.6rem, 7vw, 4rem); margin-bottom: 14px; }

.lede {
    font-size: clamp(1.05rem, 2.4vw, 1.3rem);
    color: var(--ink-dim);
    max-width: 34ch;
    margin: 0 auto 34px;
}

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

.cta { display: flex; flex-direction: column; align-items: center; gap: 14px; }

/*
    The one thing on the page to press, so it is the one solid shape: light on
    a dark hero is the strongest contrast the palette has, and the glass pills
    everywhere else read as surface next to it rather than competing.

    No TestFlight logo on it. That mark is Apple's, from the same marketing
    guidelines as the App Store badge below, and redrawing Apple's artwork is
    exactly what the pill under it refuses to do — the words carry it.
*/
.btn-primary {
    display: inline-flex; align-items: center;
    padding: 15px 30px;
    border-radius: 999px;
    background: linear-gradient(160deg, #FFFFFF, rgb(var(--silver)));
    color: rgb(var(--deep));
    font-size: 1.02rem; font-weight: 600;
    text-decoration: none;
    /* The glow is the icon's blue, so the button sits in the hero's gradient
       rather than on top of it. */
    box-shadow: 0 14px 34px rgba(var(--blue), 0.34);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.btn-primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 18px 44px rgba(var(--sky), 0.36);
}

.btn-primary:active { transform: translateY(0); }

/*
    Status, not a call to action — it lost the button's weight when the beta
    took the primary slot, and it is a div rather than a link because there is
    no listing to open yet.

    Still deliberately not Apple's "Download on the App Store" badge: that
    artwork is Apple's, comes from their marketing guidelines, and shouldn't be
    redrawn. When the app is live, swap this element for the official badge
    linking to the listing.
*/
.badge-soon {
    display: inline-flex; align-items: center;
    padding: 9px 18px;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid var(--card-edge);
    font-size: 0.85rem; font-weight: 500;
    color: var(--ink-dim);
    backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
}

.cta-note { font-size: 0.9rem; color: var(--ink-faint); }

/* The line runs to two on a phone. Held together at the phrases, it breaks
   after a separator rather than through the middle of "iOS 18 or later". */
.cta-note span { white-space: nowrap; }

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

section.band { padding: 64px 0; }

h2 {
    font-size: clamp(1.5rem, 3.4vw, 2rem);
    margin-bottom: 10px;
}

.band > .wrap > p.intro {
    color: var(--ink-dim);
    max-width: 56ch;
    margin-bottom: 34px;
}

/* ---- Stat row ----------------------------------------------------------- */

/*
    A KPI row: four headline numbers, which is the case where the number is
    the form rather than something to plot. Uncarded on purpose — the tiles
    below are cards, and a second card style here would set the figures at the
    same weight as the feature list. A rule above each column gives the
    structure a card would have, without the surface.
*/
.stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 14px 20px;
}

.stat {
    border-top: 1px solid var(--card-edge);
    padding-top: 14px;
}

/*
    Nothing here is colour-coded. These are four independent figures rather
    than series to be told apart, so there is no identity for a hue to carry —
    the values wear ink like the rest of the page's text.

    Rounded, because the app sets its figures in the rounded face and this is
    the same face the headings already use — the brand's own sans, not a
    display cut brought in to decorate a number.

    No tabular-nums: equal-width digits are for columns that align vertically,
    and at this size they leave a number like 154 looking gappy.
*/
.stat__value {
    font-size: clamp(1.9rem, 4.6vw, 2.5rem);
    line-height: 1.1;
    letter-spacing: -0.02em;
    margin-bottom: 6px;
}

.stat__label {
    font-size: 0.88rem;
    color: var(--ink-dim);
    line-height: 1.45;
}

.stats-note {
    color: var(--ink-dim);
    max-width: 56ch;
    margin-top: 30px;
}

/* Named because the numbers are someone else's work, and a page that makes a
   point of saying how it knows things should say where these came from. */
.sources {
    margin-top: 14px;
    font-size: 0.8rem;
    color: var(--ink-faint);
    max-width: 56ch;
}

/* ---- Category grid ----------------------------------------------------- */

.finds {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 14px;
}

.find {
    background: var(--card);
    border: 1px solid var(--card-edge);
    border-radius: var(--radius);
    padding: 20px;
    backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
}

/* The glyph carries the category's own colour, exactly as the tile's does in
   the app — but the name is always there too, so nothing depends on colour
   alone. Drawn as inline SVG rather than set in SF Symbols, which are
   licensed for Apple-platform UI and cannot be shipped on a web page. */
.find .icon {
    display: block;
    margin-bottom: 12px;
    /* No glow. A stroked glyph blooms into itself and loses its own shape —
       the swatch it replaced could carry one because it had no detail to
       lose. */
}

.find h3 { font-size: 1rem; margin-bottom: 5px; color: var(--ink); }
.find p  { font-size: 0.9rem; color: var(--ink-dim); line-height: 1.5; }

/* ---- Two-up feature cards ---------------------------------------------- */

/* This band's heading runs straight into the cards — it lost the intro
   paragraph that used to hold them apart, so the gap lives here instead.
   Collapses with the intro's own margin if a band ever regains one. */
.pair {
    margin-top: 30px;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px;
}

.card {
    background: var(--card);
    border: 1px solid var(--card-edge);
    border-radius: var(--radius);
    padding: 28px;
    backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);
}

.card h3 { font-size: 1.15rem; margin-bottom: 8px; }
.card p  { color: var(--ink-dim); font-size: 0.96rem; }

/* ---- Closing call to action -------------------------------------------- */

/* The hero's button is several screens away by the time anyone reaches the
   bottom, and scrolling back up to it is a thing people don't do. Centred,
   unlike the other bands, because it is one line and a button rather than
   something to read. */
.closing { text-align: center; }
.closing .intro { margin-left: auto; margin-right: auto; }
.closing .btn-primary { margin-top: 4px; }

/* ---- Privacy note ------------------------------------------------------ */

/* Not a card. The promise is the page speaking plainly rather than another
   panel of features, and the glass it used to sit in put it on a level with
   the tiles above — which is exactly the wrong level for the one claim the
   reader is being asked to take on trust. The band's own padding gives it the
   room the card's inset used to. */
.promise { text-align: center; }

.promise h2 { margin-bottom: 12px; }
.promise p { color: var(--ink-dim); max-width: 52ch; margin: 0 auto; }

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

footer {
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    padding: 34px 0 48px;
    text-align: center;
    font-size: 0.9rem;
    color: var(--ink-faint);
}

footer nav { margin-bottom: 12px; }

footer a {
    color: var(--ink-dim);
    text-decoration: none;
    margin: 0 12px;
    border-bottom: 1px solid transparent;
    transition: color 0.2s ease, border-color 0.2s ease;
}

footer a:hover { color: var(--ink); border-bottom-color: var(--card-edge); }

/* Focus is visible for anyone driving the page from a keyboard. */
a:focus-visible {
    outline: 2px solid var(--recordings);
    outline-offset: 3px;
    border-radius: 4px;
}

/*
    The app pauses every animation under Reduce Motion, and forces elapsed time
    to zero rather than freezing a frame, so a surface holds one settled shape
    instead of an arbitrary one. Dropping the animation does the same thing here
    for free: sin(0) is no offset, and an untransformed layer is no offset, so
    the drift resolves to the same settled mesh either way.
*/
@media (prefers-reduced-motion: reduce) {
    * { animation: none !important; transition: none !important; }
}

/* ---- Narrow screens ----------------------------------------------------- */

/*
    Two up, because that is what the app's own dashboard does on an iPhone —
    an adaptive grid with a 160pt minimum, which lands on two columns. Stacked
    one per card the section ran to four screens and stopped reading as a grid
    at all.
*/
@media (max-width: 560px) {
    .finds { grid-template-columns: repeat(2, 1fr); gap: 10px; }
    .find { padding: 16px; border-radius: 20px; }
    .find .icon { width: 24px; height: 24px; margin-bottom: 10px; }
    .find h3 { font-size: 0.94rem; }
    .find p { font-size: 0.82rem; line-height: 1.45; }
    .hero { padding: 64px 0 56px; }
    section.band { padding: 48px 0; }
    .promise { padding: 32px 22px; }
}
