/* Every page on tappcounter.app. Three pages shared the same tokens, type scale
   and goblin layers by copy-paste, which meant a colour could be corrected in one
   and stay wrong in the other two. */

/* Inter, the same four faces the apps bundle and register (AppFonts.swift), so the
   site, the app and the shared link preview all set in one voice. Reusing the app's
   own files rather than a webfont host is also what keeps them identical.

   swap, not block: the page stays readable the instant it arrives and reflows into
   Inter when it lands. Licensed OFL — see fonts/OFL.txt. */
@font-face {
    font-family: 'Inter';
    src: url('/fonts/Inter-Regular.ttf') format('truetype');
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: 'Inter';
    src: url('/fonts/Inter-Medium.ttf') format('truetype');
    font-weight: 500;
    font-display: swap;
}
@font-face {
    font-family: 'Inter';
    src: url('/fonts/Inter-SemiBold.ttf') format('truetype');
    font-weight: 600;
    font-display: swap;
}
@font-face {
    font-family: 'Inter';
    src: url('/fonts/Inter-Bold.ttf') format('truetype');
    font-weight: 700;
    font-display: swap;
}

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

:root {
    --bg: #0a0a0a;
    --text: #f5f5f5;
    /* Body copy sits brighter than the muted UI grey: at 1rem over long lines
       #8a8a8a's 5.7:1 reads tiring, and this lands at 7.5:1. */
    --body: #a0a0a0;
    --muted: #8a8a8a;
    --legal: #555;
    --sep: #3a3a3a;
    --rule: #1e1e1e;

    /* The app's accent is #8000FF. As TEXT it only reaches 3.1:1 on black, which
       fails for anything but large type, so links use a lightened cast; washes and
       glows keep the raw hue and carry their own alpha. */
    --purple: #b47cff;
    --accent: 128, 0, 255;

    /* One frame shape for every screenshot and clip. capture.sh owns the real
       number (SHOT_W / SHOT_H) and writes assets at exactly this ratio. */
    --shot-ratio: 880 / 478;

    --font: 'Inter', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;

    /* The only motion this file still defines: the cross-fade between two stills,
       which belongs to the page rather than to any component. The value is not
       invented — it is Material's SpringSlowEffects token (damping 1.0, stiffness
       800) simulated and sampled into linear(), the one CSS easing that can carry
       a spring's shape. Critically damped, because an opacity that overshoots
       reads as a flicker. Everything else on this page that moves is a Material
       component moving itself. */
    --picture-fade: linear(0.0000, 0.0817, 0.2432, 0.4131, 0.5622, 0.6821, 0.7737, 0.8414, 0.8901, 0.9247, 0.9488, 0.9654, 0.9767, 0.9845, 0.9897, 0.9931, 0.9955, 0.9970, 0.9980, 0.9987, 0.9992, 0.9995, 0.9996, 0.9998, 0.9998, 1.0000);
    --picture-fade-time: 0.40s;
}

html { background-color: var(--bg); }

body {
    font-family: var(--font);
    background-color: var(--bg);
    color: var(--text);
    line-height: 1.6;
    /* viewport-fit=cover puts the page under the status bar, so the safe area has
       to come back as padding or the first line sits beneath the clock. */
    padding: calc(64px + env(safe-area-inset-top)) 24px
             calc(48px + env(safe-area-inset-bottom));
}

/* The purple bloom behind the hero, opted into by the landing page alone: privacy
   and support are documents, and a wash behind a legal heading only tints the text
   it sits under.

   It starts at ZERO alpha and blooms in BELOW the top edge, which is not a style
   choice. Measured on device: iOS Safari fills the strip behind the status bar with
   the body's background COLOUR only, flat, never the image — so a wash carrying any
   purple in its first row drew a hard line where that strip ended (10,10,10 against
   31,8,54). Fading in from nothing makes row one equal the strip by definition. The
   vertical radius must stay SMALLER than the centre offset; that is what holds the
   top row at alpha 0. */
.landing {
    background-image: radial-gradient(ellipse 65% 340px at 50% 360px,
                      rgba(var(--accent), 0.34), rgba(var(--accent), 0) 100%);
    background-repeat: no-repeat;
}

/* display back to inline: the vendored framework turns every bare <a> into an
   inline-flex button box, which stacked the footer's "Privacy · Support" pair.
   Components that want a box (store badges, the home link) say so by class. */
a { color: var(--purple); text-decoration: none; display: inline; }
a:hover { text-decoration: underline; }

:focus-visible { outline: 2px solid var(--purple); outline-offset: 3px; }

.sep { margin: 0 8px; color: var(--sep); }

/* ------------------------------------------------------------------- goblins
   The setup screen's stickers, ported to the web. goblin.js builds the layers and
   sizes them from --goblin-height, so the art scales here in CSS rather than in
   the script. */

[data-goblins] {
    display: flex;
    align-items: center;
    justify-content: center;
    --goblin-height: 150px;
}

.goblin-figure {
    position: relative;
    flex: none;
    /* Contains the ink layer's multiply blend, so it darkens the sticker and not
       the page behind it. */
    isolation: isolate;
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5));
}

/* All three layers fill the figure exactly, so the tinted outline lands on the
   drawing rather than drifting against a fixed box. */
.goblin-figure > * {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    pointer-events: none;
}

/* The player colour painted through the outline's silhouette — the web equivalent
   of the app's colorMultiply on the sticker outline. */
.goblin-tint {
    opacity: 0.5;
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
}

.goblin-ink { mix-blend-mode: multiply; }

/* ------------------------------------------------------- privacy and support
   Both are the same object: a titled document with a way back home. */

.doc {
    max-width: 640px;
    margin: 0 auto;
}

.doc .home {
    display: inline-block;
    margin-bottom: 32px;
    font-size: 0.9rem;
    font-weight: 600;
}

.doc h1 {
    font-size: 2.6rem;
    font-weight: 700;
    letter-spacing: -0.035em;
    line-height: 1.08;
    text-wrap: balance;
    margin-bottom: 10px;
}

.doc .subtitle {
    color: var(--muted);
    font-size: 1.1rem;
    margin-bottom: 56px;
}

.doc h2 {
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin: 48px 0 14px;
}

.doc p,
.doc ul {
    color: var(--body);
    font-size: 1.02rem;
    line-height: 1.65;
    margin-bottom: 14px;
}

.doc ul { padding-left: 24px; }
.doc li { margin-bottom: 6px; }

/* Emphasis inside body copy: the name of a service, or the word that carries the
   sentence. Brightening to full text colour is the whole effect. */
.doc strong { color: var(--text); }

/* Elevation, not colour: a purple tint here read as a solid slab rather than a
   panel. A neutral lift states "raised" and nothing else. */
.doc .card {
    background: rgba(255, 255, 255, 0.045);
    border-left: 3px solid var(--purple);
    padding: 20px;
    border-radius: 6px;
    margin: 24px 0;
}

.doc .card strong {
    color: var(--purple);
    display: block;
    margin-bottom: 8px;
    font-size: 1.05rem;
}

.doc .card p:last-child { margin-bottom: 0; }
.doc .card a { font-weight: 600; }

.doc [data-goblins] { margin-top: 56px; }

.doc .colophon {
    margin-top: 40px;
    padding-top: 28px;
    border-top: 1px solid var(--rule);
    color: var(--legal);
    font-size: 0.85rem;
    text-align: center;
}

@media (max-width: 600px) {
    body { padding-top: calc(48px + env(safe-area-inset-top)); }
    .doc h1 { font-size: 2.1rem; }
    [data-goblins] { --goblin-height: 120px; }
}
