/* GENERATED by BuildTools/build-name-styles.mjs from BuildTools/name-styles.css — do not edit.
   Faces and effects together. Linked directly by /voyager for its Name styles
   demo. Run: npm run build:namestyles */

/* ══════════════════════════════════════════════════════════════════════
   Display name styles (Voyager)  —  GENERATED BLOCK, DO NOT HAND-EDIT
   Source: BuildTools/name-styles.css, written by BuildTools/build-name-styles.mjs into
   both cometfare-chat-unified.css (for /home) and gooberland.components.css
   (for /chat, prefixed `.gb-shell`). Change the source and re-run the tool;
   editing one copy by hand is how the two drift.

   Everything here is driven by four custom properties set inline per user by
   buildDisplayNameMarkup — --cf-name-a through --cf-name-d, the display-name
   palette. No rule encodes a specific user's colour, so one rule serves
   everyone. Stops C and D are optional and fall back to A and B, which is why
   a two-colour profile still renders every effect correctly.
   ══════════════════════════════════════════════════════════════════════ */

/* ── fonts ─────────────────────────────────────────────────────────── */
/* Families come from the Google Fonts <link> on chat.html / home.html.
   The em nudges are optical: these faces do not share a cap height, so a
   flat size makes Pixelify Sans look tiny next to Cherry Bomb One.

   ⚠️ font-family is !important, the size nudges deliberately are not. Two
   context rules already reach this span at higher specificity — the self
   card's `font: inherit` and the reply-composer's 11px — and the shorthand
   would silently swallow the user's chosen face. A context that wants an
   11px name should still get 11px; it should not get to override which
   font the user picked. Per CLAUDE.md, raise specificity or use
   !important rather than reordering.

   Each id is the typeface's real name, lowercased and de-spaced — the picker
   labels it the same way, so what the owner reads is what the CSS asks the
   font service for. The old invented ids (tempo, sakura, vampyre…) still
   resolve through NAME_FONT_ALIASES in app-data.js and in both renderers.

   ⚠️ `default` NAMES A FACE NOW — it is not `inherit` any more (2026-08-24).
   It used to resolve to whatever the page font was, which made it Quicksand on
   both surfaces and meant Quicksand could not also be OFFERED: picking it would
   have been indistinguishable from picking nothing. The default face is Outfit,
   the face the profile cards were briefly declaring for themselves; that
   per-card override is gone, so the display name on a profile, the author name
   on a message header and the picker all read from this one line.

   The `default` ID itself is kept (it is what an unset profile stores, and
   every existing one would be orphaned by a rename). Two companions sit under
   it: `outfit` is the same face declared explicitly — deliberately IDENTICAL in
   size and weight, so picking it looks exactly like leaving it unset — and
   `quicksand` is the old default's face, now a real choice.

   ⚠️ default carries NO !important, on purpose. It is the fallback the context
   rules are allowed to beat (the self card's `font: inherit`, the reply
   composer's 11px), which is the behaviour `inherit` used to give for free.
   `outfit` DOES carry it, like every other named face — that is the difference
   between "I didn't choose" and "I chose this". */
.cf-name-font-default { font-family: "Outfit", "Quicksand", system-ui, sans-serif; }
.cf-name-font-outfit { font-family: "Outfit", "Quicksand", system-ui, sans-serif !important; }
.cf-name-font-quicksand { font-family: "Quicksand", system-ui, sans-serif !important; }
.cf-name-font-zillaslab { font-family: "Zilla Slab", Georgia, serif !important; font-weight: 600; }
.cf-name-font-cherrybomb { font-family: "Cherry Bomb One", "Quicksand", cursive !important; font-size: 0.96em; }
.cf-name-font-chicle { font-family: "Chicle", "Quicksand", cursive !important; font-size: 1.02em; }
.cf-name-font-museomoderno { font-family: "MuseoModerno", "Quicksand", sans-serif !important; font-weight: 600; }
.cf-name-font-almendra { font-family: "Almendra", Georgia, serif !important; font-weight: 700; font-size: 1.04em; }
.cf-name-font-pixelify { font-family: "Pixelify Sans", ui-monospace, monospace !important; font-size: 1.12em; letter-spacing: 0.01em; }
.cf-name-font-grenzegotisch { font-family: "Grenze Gotisch", Georgia, serif !important; font-weight: 600; font-size: 1.12em; }
.cf-name-font-playpen { font-family: "Playpen Sans", "Quicksand", cursive !important; font-weight: 600; font-size: 0.94em; }
.cf-name-font-orbitron { font-family: "Orbitron", "Quicksand", sans-serif !important; font-weight: 700; font-size: 0.92em; letter-spacing: 0.01em; }
.cf-name-font-newrocker { font-family: "New Rocker", Georgia, cursive !important; font-size: 1.08em; }

/* New Rocker is a blackletter: its strokes swell and taper inside every glyph,
   so at name size the counters close up and the word reads as one dark shape.
   A hairline outline in a DARKENED cut of the user's own colour redraws those
   edges without introducing a second colour to the design — color-mix derives
   it from stop A, so there is nothing extra to store and it follows the palette
   automatically.

   paint-order is what keeps the stroke on the OUTSIDE of the glyph: without it
   the browser paints the stroke over the fill, centred on the outline, and eats
   half the letter. It lives in the font section on purpose — Toon and Pop set
   their own stroke further down and, being later at equal specificity, win. */
.cf-name-font-newrocker {
    -webkit-text-stroke: 1px color-mix(in srgb, var(--cf-name-a, currentColor) 45%, #000);
    paint-order: stroke fill;
}
.cf-name-font-kalam { font-family: "Kalam", "Quicksand", cursive !important; font-weight: 700; font-size: 1.06em; }

/* ── effects ───────────────────────────────────────────────────────── */

.cf-name-fx-solid { color: var(--cf-name-a, currentColor); }

/* Gradient and Prism are the same four-stop sweep at two speeds: Prism is the
   quick one. Both need a box for background-clip, hence inline-block.
   Repeating stop A at the end makes the loop seamless — without it the pan
   snaps back visibly every cycle.

   ⚠️ The padding/negative-margin pair is what fixes the clipped render. With
   `background-clip: text` the glyphs are painted through the element's own
   box, so anything that overhangs it — a descender, New Rocker's swashes,
   Grenze Gotisch's tail — is cut off mid-stroke. The padding grows the paint
   box; the matching negative margin gives the space straight back, so the name
   still sits where it did in the line. Remove one without the other and either
   the letters clip again or every name row gets taller.

   ⚠️ THE SEAMLESS LOOP. `background-position` in percent does NOT mean "this
   far along the image" — it means "this far through the travel the image has
   room for", i.e. 100% moves the background by (imageWidth − boxWidth). So
   with a 300%-wide image, animating 0%→100% shifts by 2 box widths across a
   3-box-wide tile: two thirds of the way through the pattern, and the wrap
   snaps. No percentage end value fixes that on its own — for any size S the
   shift is (S−1)/S of a tile, never a whole one.

   The fix is in the IMAGE, not the animation: each gradient below lists its
   palette TWICE and closes on stop A again, so one image holds two identical
   cycles. At background-size: 200% the image is 2 box widths, one cycle is
   exactly 1 box width, and a 0%→100% pan travels exactly (2−1) = one whole
   cycle. Start and end frames are then pixel-identical and the loop is
   invisible.

   Anything that changes the stop count MUST keep the doubling and the closing
   stop, or the cut comes straight back. */
.cf-name-fx-gradient,
.cf-name-fx-prism {
    background-size: 200% 100%;
    background-position: 0% 50%;
    background-repeat: repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    display: inline-block;
    line-height: 1.15;
    padding: 0.1em 0.08em 0.18em;
    margin: -0.1em -0.08em -0.18em;
}

/* Gradient: the four-stop palette, laid down twice. */
.cf-name-fx-gradient {
    background-image: linear-gradient(90deg,
        var(--cf-name-a, currentColor) 0%,
        var(--cf-name-b, var(--cf-name-a, currentColor)) 12.5%,
        var(--cf-name-c, var(--cf-name-a, currentColor)) 25%,
        var(--cf-name-d, var(--cf-name-b, currentColor)) 37.5%,
        var(--cf-name-a, currentColor) 50%,
        var(--cf-name-b, var(--cf-name-a, currentColor)) 62.5%,
        var(--cf-name-c, var(--cf-name-a, currentColor)) 75%,
        var(--cf-name-d, var(--cf-name-b, currentColor)) 87.5%,
        var(--cf-name-a, currentColor) 100%);
}

/* Prism is the FIVE-stop one — that extra colour is the whole difference
   between it and Gradient, along with the speed. Same doubling: five stops
   twice, closing on A, at even tenths. */
.cf-name-fx-prism {
    background-image: linear-gradient(90deg,
        var(--cf-name-a, currentColor) 0%,
        var(--cf-name-b, var(--cf-name-a, currentColor)) 10%,
        var(--cf-name-c, var(--cf-name-a, currentColor)) 20%,
        var(--cf-name-d, var(--cf-name-b, currentColor)) 30%,
        var(--cf-name-e, var(--cf-name-c, currentColor)) 40%,
        var(--cf-name-a, currentColor) 50%,
        var(--cf-name-b, var(--cf-name-a, currentColor)) 60%,
        var(--cf-name-c, var(--cf-name-a, currentColor)) 70%,
        var(--cf-name-d, var(--cf-name-b, currentColor)) 80%,
        var(--cf-name-e, var(--cf-name-c, currentColor)) 90%,
        var(--cf-name-a, currentColor) 100%);
}

/* A browser with no background-clip: text paints nothing at all here, because
   the fill colour is transparent. Falling back to stop A is the difference
   between a plain name and an invisible one. */
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
    .cf-name-fx-gradient,
    .cf-name-fx-prism {
        color: var(--cf-name-a, currentColor);
        -webkit-text-fill-color: var(--cf-name-a, currentColor);
    }
}

/* ⚠️ The animation NAME comes from an inherited custom property, and that is
   the switch /chat uses to hold every name still until you hover the thing it
   belongs to (see the `cf:name-motion` block in gooberland.components.css).
   It has to be animation-name and not animation-play-state: pausing RESUMES
   from where it stopped, and the rule is that motion always restarts from the
   beginning. Swapping the name none → cfNamePan starts a genuinely new
   animation at frame zero, which is exactly the asked-for behaviour.
   Anywhere that sets nothing — /home, the profile preview — gets the default
   and animates as it always did. */
@media (prefers-reduced-motion: no-preference) {
    .cf-name-fx-gradient { animation: var(--cf-name-pan, cfNamePan) 7s linear infinite; }
    .cf-name-fx-prism { animation: var(--cf-name-pan, cfNamePan) 3s linear infinite; }
}

@keyframes cfNamePan {
    from { background-position: 0% 50%; }
    to { background-position: 100% 50%; }
}

.cf-name-fx-neon {
    color: var(--cf-name-a, currentColor);
    text-shadow:
        0 0 4px var(--cf-name-b, var(--cf-name-a, currentColor)),
        0 0 10px var(--cf-name-b, var(--cf-name-a, currentColor)),
        0 0 22px var(--cf-name-b, var(--cf-name-a, currentColor));
}

/* ⚠️ Neon + a heavy display face is the one unreadable combination in the
   grid. Cherry Bomb One and Chicle are near-touching by design, and Neon's
   glow is drawn from EVERY glyph edge — including the edges facing each
   other — so the gaps fill in and the letters melt into one blob. A 1px dark
   outline redraws the gap the glow closed. It is deliberately dark rather
   than the page background: this name renders on cards, banners and chat
   rows, and a background-coloured halo would only be invisible on one of
   them. Only these two faces need it; a stroke on the lighter faces just
   makes them look inked. */
.cf-name-fx-neon.cf-name-font-cherrybomb,
.cf-name-fx-neon.cf-name-font-chicle {
    -webkit-text-stroke: 1px color-mix(in srgb, var(--cf-name-a, currentColor) 22%, #000);
    paint-order: stroke fill;
}

/* ── the two layered effects ────────────────────────────────────────────
   Toon and Pop are both TWO copies of the same word: the <span> itself, and a
   ::before that repeats it out of `data-text`. That attribute is emitted by
   buildDisplayNameMarkup (both copies — home.html and
   Chat/features/profiles/index.js) for these two effects only. Without it the
   pseudo-element renders empty and the effect silently degrades to its base
   layer, which is the failure to look for if one of them ever goes flat.

   The pseudo-element sits in the same box via inset:0, so it needs the parent
   to be positioned and inline-block. `isolation: isolate` keeps Pop's
   z-index:-1 shadow layer inside the name instead of dropping it behind
   whatever card the name is sitting on.

   Both animate ON HOVER ONLY, by request: a chat list of thirty names each
   running its own 4s loop is a wall of movement. At rest they are still. */

/* Toon: white letters with the user's colour welling up from the bottom, and
   the outline in that same colour darkened. Hovering shimmers the fill by
   sliding the ramp vertically. color-mix derives the outline shade from the
   chosen colour, so there is no second stored field to keep in sync. */
.cf-name-fx-toon {
    position: relative;
    display: inline-block;
    isolation: isolate;
    font-weight: 700;
    line-height: 1.15;
    padding: 0.1em 0.08em 0.18em;
    margin: -0.1em -0.08em -0.18em;
    color: color-mix(in srgb, var(--cf-name-a, #ff6fb7) 40%, #200310);
    -webkit-text-stroke: 0.7px color-mix(in srgb, var(--cf-name-a, #ff6fb7) 40%, #200310);
    paint-order: stroke fill;
}

.cf-name-fx-toon::before {
    content: attr(data-text);
    position: absolute;
    inset: 0;
    padding: inherit;
    background-image: linear-gradient(to top,
        var(--cf-name-b, var(--cf-name-a, #ff6fb7)) 0%,
        var(--cf-name-a, #ff6fb7) 34%,
        #ffffff 78%);
    background-size: 100% 220%;
    background-position: 50% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    -webkit-text-stroke: 0;
    pointer-events: none;
}

/* Pop: white letters over a coloured copy of themselves, offset down-right.
   Hovering drifts the two layers out of sync, which is the "pop". */
.cf-name-fx-pop {
    position: relative;
    display: inline-block;
    isolation: isolate;
    font-weight: 700;
    line-height: 1.15;
    padding: 0.1em 0.08em 0.18em;
    margin: -0.1em -0.08em -0.18em;
    color: #ffffff;
}

.cf-name-fx-pop::before {
    content: attr(data-text);
    position: absolute;
    inset: 0;
    z-index: -1;
    padding: inherit;
    color: var(--cf-name-a, currentColor);
    background-image: linear-gradient(to left bottom,
        var(--cf-name-b, var(--cf-name-a, currentColor)),
        var(--cf-name-a, currentColor));
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    transform: translate(0.055em, 0.055em);
    pointer-events: none;
}

/* Toon and Pop are still at rest by default and wake on hovering the NAME —
   that is what /home and the profile card do. The custom properties give a
   container a second way in: /chat sets them on a hovered message so the whole
   row wakes the name, without the pointer having to find the name itself. */
@media (prefers-reduced-motion: no-preference) {
    .cf-name-fx-toon::before { animation: var(--cf-name-toon, none) 4s ease-in-out infinite; }
    .cf-name-fx-pop { animation: var(--cf-name-pop-main, none) 4s ease-in-out infinite; }
    .cf-name-fx-pop::before { animation: var(--cf-name-pop-shadow, none) 4s ease-in-out infinite; }

    .cf-name-fx-toon:hover::before { animation-name: cfNameToon; }
    .cf-name-fx-pop:hover { animation-name: cfNamePopMain; }
    .cf-name-fx-pop:hover::before { animation-name: cfNamePopShadow; }
}

@keyframes cfNameToon {
    0%, 100% { background-position: 50% 100%; }
    50% { background-position: 50% 0%; }
}

/* The two Pop keyframes deliberately move by different amounts: identical
   motion would just slide the whole name, and the effect is the gap opening
   and closing between the layers. */
@keyframes cfNamePopMain {
    0%, 100% { transform: translate(0, 0); }
    50% { transform: translate(-0.03em, -0.04em); }
}

@keyframes cfNamePopShadow {
    0%, 100% { transform: translate(0.055em, 0.055em); }
    50% { transform: translate(0.12em, 0.13em); }
}

/* Gummy is per-letter, so the palette is applied by buildDisplayNameMarkup to
   one <span class="cf-name-letter"> per character rather than by a rule here.
   This block only carries the shared treatment. */
/* No white top highlight. It was meant to read as a bevel, but a 1px light
   shadow under every glyph edge reads as an outline instead — the "white
   border around Gummy" it kept getting reported as. The soft dark shadow
   alone still lifts the letters off the surface. */
/* ⚠️ The `color` line is a SAFETY NET, not the effect. Gummy's real colour is
   per-letter and arrives on .cf-name-letter below. But this rule is the only
   thing standing between a Gummy name and NO colour at all on any path that
   does not emit the letter spans — a renderer bailing out, a surface that
   flattens them (see cf:name-dm-opener), an older cached bundle. Every other
   effect resolves back to stop A through the var chain; without this line
   Gummy was the single effect that could come out uncoloured, which is exactly
   how it kept getting reported as "Gummy does nothing". */
.cf-name-fx-gummy {
    font-weight: 700;
    color: var(--cf-name-a, currentColor);
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

.cf-name-fx-gummy .cf-name-letter {
    /* The per-letter colour arrives as an inline custom property so the
       markup carries no rule of its own. */
    color: var(--cf-letter, currentColor);
}

/* ── end display name styles ───────────────────────────────────────── */
