/* ---------------------------------------------------------------------------
   themes.css — page colour.

   Loads after profile-2.css / profile-3.css on profile pages. It used to hold
   23 named themes, each a hand-written block of ~40 tokens; picking a colour
   meant picking whichever skin was nearest. It is now driven by three values
   the owner sets — --page-bg, --pill-colour, --pill-alpha — and everything else
   on the page is derived from those.

   The three inputs arrive as inline custom properties on .profile-body, set
   server-side by page_colour_config() in app.py. The ink and muted colours
   come from there too: a name may pick any of 16 million backgrounds, so the
   text on top has to stay readable for all of them, and that is a measurement
   rather than a decoration. See "ink flips" below.

   Components still read the --p-* contract, so this file never has to know
   what a profile is made of — it only decides what colour it is.
--------------------------------------------------------------------------- */

/* ---------- the token set, derived ---------- */

.profile-body {
    /* Inherited from the body inline style. The fallbacks are the blood
       defaults so the page still renders if a var() ever fails to resolve. */
    --p-body-bg: var(--page-bg, #000000);
    --p-pill: var(--pill-colour, #c41e1e);
    --p-pill-a: var(--pill-alpha, 0.18);

    /* Ink flips with the background rather than with a theme name. */
    --p-ink: var(--page-ink, #f5f0ef);
    --p-muted: var(--page-muted, #9b8d88);
    --p-danger: #ff8a8a;
    color-scheme: dark;

    /* Lines are the pill colour at low alpha, so they belong to the same
       family as the pills instead of being a fixed grey that clashes with a
       chosen background. */
    --p-line: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 1.6));
    --p-line-accent: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 4));

    /* The accent IS the pill colour. Everything that used to be "red because
       it is blood" is now "the colour the owner picked". */
    --p-accent: var(--p-pill);
    --p-accent-text: var(--on-pill, #ffffff);
    --p-accent-soft: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 2.2));
    --p-accent-faint: rgb(from var(--p-pill) r g b / var(--p-pill-a));
    --p-accent-halo: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 0.7));
    --p-accent-glow: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 1.4));
    /* Deep/dark are the accent over the page background, so a solid button
       built from them sits on the page rather than floating above it. */
    --p-accent-deep: color-mix(in oklab, var(--p-pill) 72%, var(--p-body-bg));
    --p-accent-dark: color-mix(in oklab, var(--p-pill) 42%, var(--p-body-bg));

    /* Surfaces. Every panel is a wash of the pill colour over the page, which
       is what makes the transparency dial meaningful: at 18 the pills read as
       tinted glass, at 60 they read as slabs, and at 0 the page is bare. */
    --p-link-bg: rgb(from var(--p-pill) r g b / var(--p-pill-a));
    --p-link-hover-bg: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 1.9));
    --p-item-bg: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 0.55));
    --p-item-line: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 1.2));
    --p-btn-bg: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 1.3));
    --p-input-bg: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 0.9));
    --p-icon-bg: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 0.8));
    --p-music-bg: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 1.1));
    --p-music-cover-bg: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 2));

    /* Glass over a video wants the opposite of a tint: the clip has to stay
       visible, so this darkens whatever is behind the panels instead of
       colouring it. */
    --p-glass-tint: rgb(0 0 0 / .34);
    --p-glass-blur: 14px;
    --p-glass-sat: 1.15;
    --p-glass-edge: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 2));
    --p-video-brightness: .5;
    --p-video-saturate: .92;

    --p-player-bg: linear-gradient(180deg, var(--p-accent), var(--p-accent-dark));
    --p-player-border: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 2.4));

    --p-avatar-bg: radial-gradient(circle at 30% 20%,
                                   rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * 2)),
                                   rgb(from var(--p-body-bg) r g b / .92));
    --p-avatar-border: var(--p-line-accent);
    --p-avatar-initial: var(--p-ink);
    --p-avatar-radius: 50%;
    --p-avatar-ring: 0 0 0 6px var(--p-accent-halo);
    --p-avatar-shadow: 0 14px 44px rgb(0 0 0 / .7);

    --p-editor-bg: color-mix(in srgb, var(--p-body-bg) 88%, transparent);
    --p-scanline: rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * .5));
    --p-shadow: 0 10px 30px rgb(0 0 0 / .4);

    --p-radius-sm: 10px;
    --p-radius-md: 14px;
    --p-radius-lg: 16px;
    --p-link-radius: var(--p-radius-md);

    /* The ambient wash is the pill colour, barely there. */
    --p-ambient-bg:
        radial-gradient(1100px 460px at 50% -12%,
                        rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * .55)),
                        transparent 62%),
        radial-gradient(900px 420px at 100% 100%,
                        rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * .4)),
                        transparent 60%);
    --p-ambient-opacity: .6;
    --p-ambient-dur: 13s;

    /* The page image is a vignette in the owner's colour. It has to sit under
       an opaque --p-body-bg, so it is a background-image, not a second
       background-color. */
    --p-body-image:
        radial-gradient(1200px 500px at 50% -10%,
                        rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * .5)),
                        transparent 60%),
        radial-gradient(900px 400px at 100% 20%,
                        rgb(from var(--p-pill) r g b / calc(var(--p-pill-a) * .35)),
                        transparent 60%);

    /* Banner scrims dim the owner's picture so the name stays readable. */
    --p-banner-scrim-1: color-mix(in srgb, var(--p-body-bg) 78%, transparent);
    --p-banner-scrim-2: color-mix(in srgb, var(--p-body-bg) 90%, transparent);
}

/* ---------- ink flips ---------- */

/* A pale background inverts the site-wide dark tokens, not just --p-ink. The
   display name, the footer headings, the editor labels and the legal titles
   all read --foreground directly, and leaving them on the dark value produced
   a measured contrast ratio of 1.0 — a pale name on a pale page. This is the
   same class the light themes needed, reached by luminance instead of by
   name. */
.profile-body.page-light {
    --foreground: var(--p-ink);
    --muted-foreground: var(--p-muted);
    color-scheme: light;
    --p-shadow: 0 10px 26px rgb(0 0 0 / .12);
    --p-avatar-shadow: 0 14px 40px rgb(0 0 0 / .16);
    --p-glass-tint: rgb(255 255 255 / .42);
    --p-glass-edge: rgb(255 255 255 / .7);
    --p-ambient-opacity: .34;
    --p-banner-scrim-1: color-mix(in srgb, var(--p-body-bg) 62%, transparent);
    --p-banner-scrim-2: color-mix(in srgb, var(--p-body-bg) 78%, transparent);
}

/* ---------- components that have to follow the ink ---------- */

.profile-body.page-light .display,
.profile-body.page-light .profile-handle,
.profile-body.page-light .footer h3,
.profile-body.page-light .footer-brand .brand-word,
.profile-body.page-light .bb-panel-title,
.profile-body.page-light .bb-group summary,
.profile-body.page-light .bb-label,
.profile-body.page-light .legal h2,
.profile-body.page-light .copyright {
    color: var(--p-ink);
}

.profile-body.page-light .profile-sub,
.profile-body.page-light .bb-hint,
.profile-body.page-light .footer-tagline {
    color: var(--p-muted);
}

.profile-body.page-light .stat-tile,
.profile-body.page-light .bb-btn,
.profile-body.page-light .stat {
    color: var(--p-ink);
}

.profile-body.page-light .start-btn,
.profile-body.page-light .pill-solid {
    color: var(--on-pill);
}

/* The selection highlight and scrollbar chrome were tuned for a dark page. */
.profile-body.page-light ::selection {
    background: rgb(from var(--p-pill) r g b / .3);
    color: var(--p-ink);
}

/* ---------- the pill, done properly ---------- */

/* A pill is a tinted pane of glass over the page, with a hairline in its own
   colour and a soft bloom behind it. The transparency dial is what moves it
   between "tint" and "slab", so nothing here is a fixed colour. */
.profile-body .profile-link {
    position: relative;
    border-color: var(--p-line);
    background: var(--p-link-bg);
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / .05),
        0 6px 22px -14px var(--p-accent-glow);
    transition:
        border-color .18s ease,
        background .18s ease,
        transform .18s var(--ui-ease, cubic-bezier(.2, .7, .2, 1)),
        box-shadow .18s ease;
    overflow: hidden;
}

.profile-body .profile-link:hover {
    border-color: var(--p-line-accent);
    background: var(--p-link-hover-bg);
    transform: translateY(-1px);
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / .07),
        0 10px 28px -14px var(--p-accent-glow);
}

/* The specular edge. Kept on its own layer so a scanline effect can stack on
   top of it rather than replace it. */
.profile-body .profile-link::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    box-shadow: inset 0 0 0 -20px rgb(255 255 255 / .5);
}

.profile-body .profile-link-card {
    background: var(--p-item-bg);
    border-color: var(--p-item-line);
}

.profile-body .profile-link-card:hover {
    background: var(--p-link-hover-bg);
    border-color: var(--p-line-accent);
}

/* A pill at 0% has no fill, so its border and its glow would be the only
   thing left — a row of hard outlines, which reads as a broken page rather
   than a deliberate one. At zero the pills drop to bare text: still focusable,
   still links, no longer pretending to be panes. The class is set server-side
   from the stored alpha, so this costs nothing at runtime. */
.profile-body.pill-flat .profile-link {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    padding-left: 0;
    padding-right: 0;
}

.profile-body.pill-flat .profile-link:hover {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
    transform: none;
    color: var(--p-accent);
}

/* Between 0 and ~6 the fill is too faint to carry a border, so the border has
   to stand in for the colour instead. The ramp is the other direction: the
   more solid the pill, the more the hairline gets out of its way. */
.profile-body.pill-flat .profile-link-card {
    padding-left: 0;
    padding-right: 0;
}

/* ---------- ambient layer ---------- */

/* Unchanged in behaviour from the themed version: a slow drift of the owner's
   colour behind the content, dimmed further under a banner so the picture
   still reads. */
.profile-body .p-ambient {
    position: fixed;
    inset: 0;
    /* Above the banner and the video (both z-index 0) and below `.main`,
       which is later in the DOM at the same index. Kept non-negative for the
       reason spelled out at the top of profile-2.css: a negative index here
       would need an `isolation: isolate` on the body, and that broke the
       cursor-trail canvas compositing. */
    z-index: 1;
    pointer-events: none;
    background: var(--p-ambient-bg, none);
    opacity: var(--p-ambient-opacity, .6);
    animation: p-ambient-drift var(--p-ambient-dur, 13s) ease-in-out infinite alternate;
}

body.has-banner .p-ambient { opacity: calc(var(--p-ambient-opacity, .6) * .5); }

@keyframes p-ambient-drift {
    from { transform: translate3d(-1.5%, -1%, 0) scale(1); }
    to { transform: translate3d(1.5%, 1.5%, 0) scale(1.06); }
}

@media (prefers-reduced-motion: reduce) {
    .profile-body .p-ambient { animation: none; }
}

/* ---------- glass ---------- */

/* The three glass levels stay discrete — "frost a bit more" only means
   something as a coherent step — but the tint is now a fixed dark scrim
   rather than a colour, so an owner picking a pale yellow still gets a
   readable page over busy art. Each level is a complete treatment: blur,
   saturate, how far the backdrop dims, and the hairline. The levels apply to
   any background the page has — a still picture, an animated picture or a
   clip — which is what the .has-banner / .has-video pair below encodes. */
body.has-video.glass-soft,
body.has-banner.glass-soft {
    --p-video-brightness: .62;
    --p-glass-blur: 6px;
    --p-glass-tint: rgb(0 0 0 / .30);
    --p-glass-sat: 1.05;
    --p-glass-edge: rgb(255 255 255 / .14);
}
body.has-video.glass-frosted,
body.has-banner.glass-frosted {
    --p-video-brightness: .48;
    --p-glass-blur: 14px;
    --p-glass-tint: rgb(0 0 0 / .44);
    --p-glass-sat: 1.15;
    --p-glass-edge: rgb(255 255 255 / .20);
}
body.has-video.glass-crystal,
body.has-banner.glass-crystal {
    --p-video-brightness: .34;
    --p-glass-blur: 24px;
    --p-glass-tint: rgb(0 0 0 / .60);
    --p-glass-sat: 1.25;
    --p-glass-edge: rgb(255 255 255 / .28);
}

/* Scoped to a page that actually has a background on purpose. A page with no
   media has nothing to frost, so paying for a backdrop-filter there is both a
   visual regression and a compositing cost with nothing behind it. */
body.has-video:not(.glass-none) .profile-link,
body.has-video:not(.glass-none) .music-bar,
body.has-banner:not(.glass-none) .profile-link,
body.has-banner:not(.glass-none) .music-bar {
    background: var(--p-glass-tint);
    border-color: var(--p-glass-edge);
    backdrop-filter: blur(var(--p-glass-blur, 10px)) saturate(var(--p-glass-sat, 1.1));
    -webkit-backdrop-filter: blur(var(--p-glass-blur, 10px)) saturate(var(--p-glass-sat, 1.1));
}

/* "None" means no blur, so it has to take the blur away rather than just
   decline to add it. Last in the file, because it is an override of the rule
   above and of the panel blur in profile-2.css. */
body.has-video.glass-none .profile-link,
body.has-video.glass-none .music-bar,
body.has-banner.glass-none .profile-link,
body.has-banner.glass-none .music-bar {
    background: color-mix(in srgb, var(--p-body-bg) 88%, transparent);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

/* ---------- accessibility ---------- */

/* A page is not finished because it is legible; it is finished because it
   can be operated. These are the two that matter on a colour-customisable
   page, where a name can pick their own contrast. */

@media (prefers-reduced-motion: reduce) {
    .profile-body .profile-link,
    .profile-body .profile-link-card { transition: none; }
}

/* Forced colours: the owner's chosen colours are dropped by the OS, so the
   panel has to find its edges from the system palette instead of from a
   tint that is no longer there. */
@media (forced-colors: active) {
    .profile-body .profile-link,
    .profile-body .profile-link-card {
        border-color: CanvasText;
        forced-color-adjust: none;
    }
}

/* ---------- no theme classes ---------- */

/* There is no .theme-* selector in this file any more. A name that still
   carries one in its body class from a cached page simply has no effect,
   because nothing matches — which is the point of retiring the picker
   without a redirect. */