/* =====================================================================================
   GROUP PERSONALITY - the inside of the house (task 2817, theme build stage 3 of 5)
   =====================================================================================

   Kris's house model (memory coterie-theme-direction-decision-2026-10): the LOOK is the outside
   (palettes.css + looks.css), a group's PERSONALITY is the inside, game boards are locked.

   A group's own content area carries class "gx" plus data-layer="group" and an inline style that
   PersonalityStyles (Coterie.Client/Services) writes: every colour of Coterie.Shared
   PersonalityPalette - the r3 rule, already corrected to AA in the member's look - for BOTH modes,
   as --gpd-<token> (day) and --gpn-<token> (night). This file picks the active mode's set and maps
   it onto the app's semantic tokens, ON THAT ELEMENT ONLY:
     .gx-p  the group's personality ("Group style" / "Group style, no photo"): every token;
     .gx-m  the member's "My look" view, and a group with no personality: only the tokens r3's
            tweakPalette moves (accent and its ink, soft accent, inks, link, input line, focus).
   The layout root (top bar, navigation) never carries these, so the frame is never recoloured.
   data-layer="group" on the .gx element makes palettes.css re-declare its derived tokens
   (color-mix tints, chip and nav fills, status inks) there, so they follow the personality.

   SPECIFICITY: html[data-mode="day"] .gx-p is (0,2,1), the same as palettes.css's mode blocks;
   this file loads after palettes.css (index.html), so it wins the tie. Focus, error and success
   are the look's hues, pushed to AA on the group's surface by the rule - a group cannot pick them.

   The --gpd-* / --gpn-* properties exist only as runtime inline styles; they are read here without
   a fallback on purpose (CssCustomPropertyDefinitionTests exempts that family).
   ===================================================================================== */

/* ── Curated heading fonts (r3 GFONTS). Nunito and Bricolage Grotesque ship with the looks
      (looks.css); these four are self-hosted here, SIL OFL 1.1 (THIRD-PARTY-NOTICES.md). ── */
@font-face {
    font-family: 'Gabarito';
    font-style: normal;
    font-display: swap;
    font-weight: 400 900;
    src: url(../fonts/gabarito/gabarito-latin-wght-normal.780dd129f5.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Gabarito';
    font-style: normal;
    font-display: swap;
    font-weight: 400 900;
    src: url(../fonts/gabarito/gabarito-latin-ext-wght-normal.d38497efbc.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Fredoka';
    font-style: normal;
    font-display: swap;
    font-weight: 300 700;
    src: url(../fonts/fredoka/fredoka-latin-wght-normal.99d6c78e04.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Fredoka';
    font-style: normal;
    font-display: swap;
    font-weight: 300 700;
    src: url(../fonts/fredoka/fredoka-latin-ext-wght-normal.18a1723c87.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Young Serif';
    font-style: normal;
    font-display: swap;
    font-weight: 400;
    src: url(../fonts/young-serif/young-serif-latin-400-normal.6ba68b9927.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Young Serif';
    font-style: normal;
    font-display: swap;
    font-weight: 400;
    src: url(../fonts/young-serif/young-serif-latin-ext-400-normal.9e6eb8e5e3.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Schibsted Grotesk';
    font-style: normal;
    font-display: swap;
    font-weight: 400 900;
    src: url(../fonts/schibsted-grotesk/schibsted-grotesk-latin-wght-normal.4c8b93f431.woff2) format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Schibsted Grotesk';
    font-style: normal;
    font-display: swap;
    font-weight: 400 900;
    src: url(../fonts/schibsted-grotesk/schibsted-grotesk-latin-ext-wght-normal.ee9eeb232c.woff2) format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* ── The group's personality, NIGHT ── */
html:not([data-mode="day"]) .gx-p {
    --bg-color: var(--gpn-bg);
    --bg2: var(--gpn-bg2);
    --surface-color: var(--gpn-surface);
    --surface-hover: var(--gpn-raise);
    --border-color: var(--gpn-line);
    --input-line: var(--gpn-input-line);
    --text-primary: var(--gpn-ink);
    --text-secondary: var(--gpn-ink2);
    --accent-primary: var(--gpn-accent);
    --text-on-accent: var(--gpn-on-accent);
    --text-on-accent-rgb: var(--gpn-on-accent-rgb);
    --accent-hover-bg: var(--gpn-accent-hover);
    --accent-soft: var(--gpn-accent-soft);
    --link: var(--gpn-link);
    --focus-ring: var(--gpn-focus);
    --game-grid: var(--gpn-grid);
    --game-lex: var(--gpn-lex);
    --game-lattice: var(--gpn-lattice);
    --game-span: var(--gpn-span);
    --game-domain: var(--gpn-domain);
    --tier-easy: var(--gpn-easy);
    --tier-medium: var(--gpn-med);
    --tier-hard: var(--gpn-hard);
    --tier-expert: var(--gpn-expert);
    --color-error-text: var(--gpn-err);
    --color-success-text: var(--gpn-ok);
    --gx-accent2: var(--gpn-accent2);
    --gx-on-accent2: var(--gpn-on-accent2);
    --gx-bubble: var(--gpn-bubble);
    --gx-on-bubble: var(--gpn-on-bubble);
    --gx-banner: var(--gpn-banner);
    --gx-banner2: var(--gpn-banner2);
    --gx-on-banner: var(--gpn-on-banner);
    --gx-banner-focus: var(--gpn-banner-focus);
    --gx-pattern: var(--gpn-pattern, none);
}

/* ── The group's personality, DAY ── */
html[data-mode="day"] .gx-p {
    --bg-color: var(--gpd-bg);
    --bg2: var(--gpd-bg2);
    --surface-color: var(--gpd-surface);
    --surface-hover: var(--gpd-raise);
    --border-color: var(--gpd-line);
    --input-line: var(--gpd-input-line);
    --text-primary: var(--gpd-ink);
    --text-secondary: var(--gpd-ink2);
    --accent-primary: var(--gpd-accent);
    --text-on-accent: var(--gpd-on-accent);
    --text-on-accent-rgb: var(--gpd-on-accent-rgb);
    --accent-hover-bg: var(--gpd-accent-hover);
    --accent-soft: var(--gpd-accent-soft);
    --link: var(--gpd-link);
    --focus-ring: var(--gpd-focus);
    --game-grid: var(--gpd-grid);
    --game-lex: var(--gpd-lex);
    --game-lattice: var(--gpd-lattice);
    --game-span: var(--gpd-span);
    --game-domain: var(--gpd-domain);
    --tier-easy: var(--gpd-easy);
    --tier-medium: var(--gpd-med);
    --tier-hard: var(--gpd-hard);
    --tier-expert: var(--gpd-expert);
    --color-error-text: var(--gpd-err);
    --color-success-text: var(--gpd-ok);
    --gx-accent2: var(--gpd-accent2);
    --gx-on-accent2: var(--gpd-on-accent2);
    --gx-bubble: var(--gpd-bubble);
    --gx-on-bubble: var(--gpd-on-bubble);
    --gx-banner: var(--gpd-banner);
    --gx-banner2: var(--gpd-banner2);
    --gx-on-banner: var(--gpd-on-banner);
    --gx-banner-focus: var(--gpd-banner-focus);
    --gx-pattern: var(--gpd-pattern, none);
}

/* ── Translucent tints inside a personality. palettes.css draws chips, row headers and glass
      buttons as a few percent of the INK over the card (--glass-bg, --tier-badge-bg, ...). The rule
      holds text at 4.6:1 on the group's card and hover row - not on the card darkened by a tint - and
      on a saturated card (Neon Party's magenta) a 3-4% ink wash took secondary and tier text to
      4.3-4.5:1. Inside a group these fills are the group's hover row, an opaque colour the rule
      checks every ink against (r3 GROUP_PAIRS "on hover / active row"). ── */
.gx-p {
    --glass-bg: var(--surface-hover);
    --glass-input-bg: var(--surface-hover);
    --tier-badge-bg: var(--surface-hover);
    --inset-bg: var(--surface-hover);
    --selected-bg: var(--surface-hover);
}

/* ── "My look": the member's look with the group's accent (r3 tweakPalette). A group with no
      personality is a .gx-m with no inline style: every fallback below is the look's own value. ── */
html:not([data-mode="day"]) .gx-m {
    --accent-primary: var(--gpn-accent, var(--lh-accent));
    --text-on-accent: var(--gpn-on-accent, var(--lh-on-accent));
    --text-on-accent-rgb: var(--gpn-on-accent-rgb, var(--look-on-accent-rgb));
    --accent-hover-bg: var(--gpn-accent-hover, var(--look-accent-hover));
    --accent-soft: var(--gpn-accent-soft, var(--lh-accent-soft));
    --text-primary: var(--gpn-ink, var(--lh-ink));
    --text-secondary: var(--gpn-ink2, var(--lh-ink2));
    --link: var(--gpn-link, var(--lh-link));
    --input-line: var(--gpn-input-line, var(--lh-input-line));
    --focus-ring: var(--gpn-focus, var(--lh-focus));
    --gx-banner: var(--gpn-banner, var(--lh-accent-soft));
    --gx-banner-focus: var(--gpn-banner-focus, var(--focus-ring));
    --gx-bubble: var(--gpn-accent, var(--lh-accent));
    --gx-on-bubble: var(--gpn-on-accent, var(--lh-on-accent));
}

html[data-mode="day"] .gx-m {
    --accent-primary: var(--gpd-accent, var(--lh-accent));
    --text-on-accent: var(--gpd-on-accent, var(--lh-on-accent));
    --text-on-accent-rgb: var(--gpd-on-accent-rgb, var(--look-on-accent-rgb));
    --accent-hover-bg: var(--gpd-accent-hover, var(--look-accent-hover));
    --accent-soft: var(--gpd-accent-soft, var(--lh-accent-soft));
    --text-primary: var(--gpd-ink, var(--lh-ink));
    --text-secondary: var(--gpd-ink2, var(--lh-ink2));
    --link: var(--gpd-link, var(--lh-link));
    --input-line: var(--gpd-input-line, var(--lh-input-line));
    --focus-ring: var(--gpd-focus, var(--lh-focus));
    --gx-banner: var(--gpd-banner, var(--lh-accent-soft));
    --gx-banner-focus: var(--gpd-banner-focus, var(--focus-ring));
    --gx-bubble: var(--gpd-accent, var(--lh-accent));
    --gx-on-bubble: var(--gpd-on-accent, var(--lh-on-accent));
}

/* ── The ground: a group page's own background, and its pattern. The pattern is a texture (at most
      1.5:1 on the ground, by the rule) and NO text sits on it: everything on a .gx-ground sits on a
      card, the banner or another solid fill (E2E PersonalityFidelityTests checks the rendered page). ── */
.gx-ground {
    background-color: var(--bg-color);
    color: var(--text-primary);
}

.gx-ground.pz {
    background-image: var(--gx-pattern, none);
}

/* The group's heading font (r3 .gx-h). "My look" and personality-less groups keep the look's. */
.gx-h {
    font-family: var(--gfont, var(--font-heading));
    font-weight: var(--gfw, var(--heading-weight));
}

/* ── The group's PHOTO (task 2818, r3 .gx.ph-bg / .banner-photo / .scrim-plate). Shown only in the
      member's "Group style" view (PersonalityStyles adds .ph, and .ph-bg when the photo is the page's
      background). The component sets --gx-photo to the photo it fetched with the sign-in; the focal
      points --gx-fx/--gx-fy (desktop) and --gx-fxp/--gx-fyp (phone) come with the personality.
      Text NEVER sits on raw pixels: cards on a photo ground are frosted surface at --gx-frost and the
      banner's text sits on a black scrim at --gx-scrim-a - both alphas solved by PersonalityPalette
      against a pure white and a pure black pixel, so they hold for any photo. The photo ground is
      dimmed like r3's (20% by day, 45% by night). ── */
html:not([data-mode="day"]) .gx-p.ph {
    --gx-frost: var(--gpn-frost);
    --gx-scrim-a: var(--gpn-scrim-a);
    --gx-dim: 0.45;
}

html[data-mode="day"] .gx-p.ph {
    --gx-frost: var(--gpd-frost);
    --gx-scrim-a: var(--gpd-scrim-a);
    --gx-dim: 0.2;
}

.gx-ground.ph-bg {
    background-image: linear-gradient(rgba(0, 0, 0, var(--gx-dim)), rgba(0, 0, 0, var(--gx-dim))), var(--gx-photo, none);
    background-size: cover;
    background-position: var(--gx-fx, 50%) var(--gx-fy, 50%);
}

/* Cards on the photo: a frosted surface (r3 .gx.ph-bg .card). Modals sit on their own scrim, not on the photo. */
.gx-ground.ph-bg .glass-card:not(.modal-card) {
    background: color-mix(in srgb, var(--surface-color) var(--gx-frost), transparent);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
}

@media (max-width: 576px) {
    .gx-ground.ph-bg {
        background-position: var(--gx-fxp, 50%) var(--gx-fyp, 50%);
    }
}

/* ── The floating chat: the member's own bubbles in the group's bubble colour. ── */
html:not([data-mode="day"]) .gx-chat {
    --gx-bubble: var(--gpn-bubble);
    --gx-on-bubble: var(--gpn-on-bubble);
}

html[data-mode="day"] .gx-chat {
    --gx-bubble: var(--gpd-bubble);
    --gx-on-bubble: var(--gpd-on-bubble);
}

/* ── The personality editor's and the view choice's modal (in the member's LOOK: the editor's own
      controls keep the look's structure, r3). Same shape as GroupDashboard's modals. ── */
.gx-modal-overlay {
    position: fixed;
    inset: 0;
    background: var(--scrim-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 1rem;
}

.gx-modal-card {
    width: 100%;
    max-width: 30rem;
    max-height: calc(100dvh - 2rem);
    overflow-y: auto;
    background: var(--surface-color);
    color: var(--text-primary);
    border: var(--card-border-width, 1px) solid var(--border-color);
    border-radius: var(--radius-card);
    box-shadow: var(--card-shadow);
    padding: 1.5rem;
}

.gx-modal-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 1rem;
}

/* =====================================================================================
   THE ROOM AROUND A GAME (task 2831, r3 "a loud room around a calm board")
   =====================================================================================

   GroupGameRoom (Components) wraps a group game page. The group shows up AROUND the board - a small
   banner above it, the group's ground behind it - and the personality STOPS at the board's frame:

     .gx-room          display: contents, NO .gx class: it remaps no token. Its inline style only
                       declares the --gpd-* / --gpn-* properties, which no board rule reads. The board
                       therefore resolves every token from the member's look (and their board theme),
                       exactly as outside a group.
     .gx-room-ground   a `gx gx-p` SIBLING of the board: the group's colours and pattern, a layer behind
                       the page content (never an ancestor of the board).
     .gx-room-banner   a `gx gx-p|gx-m` SIBLING: the small group banner on its own solid fill.
     .gx-room > .app-container   the board's container: the look's card colour with ONE frame ring in
                       the group's accent (an outline, so no layout moves).

   No text sits on the group's pattern: everything on the ground is the banner (solid fill) or the
   board's container (a solid card). The top bar and navigation are outside the article and untouched.
   "My look" and a group with no personality paint no ground (no .gx-room-ground is rendered).
   ===================================================================================== */
:root {
    --room-banner-box-h: 3.5rem;
    --room-banner-gap: 0.5rem;
}

.gx-room {
    display: contents;
}

/* The ground fills the page content area (the article). The article is positioned (the ground's
   containing block) but is NOT a stacking context: a stacking context here would cap every fixed
   overlay inside a game page (win card, reset confirm: z 1000-10001) below the sidebar (1050) and the
   nav scrim (1040). The ground is z-index 0 and comes first in tree order; the banner and the board's
   container are positioned with z-index auto, so they paint above it without creating a context. */
article:has(> .gx-room > .gx-room-ground) {
    position: relative;
}

.gx-room-banner {
    position: relative;
}

:where(.gx-room > .app-container) {
    position: relative;
}

.gx-room-ground {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
}

/* A FIXED footprint like the solo-daily banner (app.css --solo-banner-h): every game already subtracts
   --solo-banner-h from its height budget, and the two banners never coexist (this one is signed in,
   that one is signed out). The box never grows: one line, the name ellipsised. */
.gx-room-banner {
    box-sizing: border-box;
    height: var(--room-banner-box-h);
    margin: 0 0 var(--room-banner-gap);
}

.gx-room-banner > .gx {
    height: 100%;
}

.gx-room-banner .gx-banner.gx-room-banner-box.small {
    box-sizing: border-box;
    height: 100%;
    margin: 0;
    padding: 0 14px;
    flex-wrap: nowrap;
    gap: 10px;
    overflow: hidden;
}

.gx-room-banner .gx-banner.gx-room-banner-box.small .gx-emoji {
    width: 32px;
    height: 32px;
    font-size: 19px;
}

.gx-room-banner .gx-banner.gx-room-banner-box.small .gx-banner-text {
    flex: 1 1 0;
}

.gx-room-banner .gx-banner.gx-room-banner-box.small .gx-h {
    font-size: 20px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.gx-room-banner .gx-banner.gx-room-banner-box.small .gx-sub {
    margin-top: 0;
    font-size: 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The ribbon's bars and the bunting need room that the 56px box has to give up from the content. */
.gx-room-banner .gx-banner.gx-room-banner-box.small.banner-ribbon {
    box-shadow: inset 0 4px 0 var(--gx-accent2), inset 0 -4px 0 var(--gx-accent2);
}

.gx-room-banner .gx-banner.gx-room-banner-box.small.banner-bunting {
    padding-bottom: 12px;
}

.gx-room-banner .gx-banner.gx-room-banner-box.small.banner-bunting::after {
    bottom: 3px;
    height: 8px;
    background-size: 14px 8px;
}

/* The reservation is there iff the banner is rendered, and not at 500px tall or less - the games' own
   short-viewport line (the same complement app.css draws for the solo banner). */
@media (height > 500px) {
    .page:has(.gx-room-banner) {
        --solo-banner-h: calc(var(--room-banner-box-h) + var(--room-banner-gap));
    }
}

@media (max-height: 500px) {
    .gx-room-banner {
        display: none;
    }
}

/* The board's container: a solid card in the PLAYER'S look (never the group's), so nothing in it sits
   on the group's ground. The frame is the group's accent (r3 --frame), the one place it touches. */
.gx-room > .app-container {
    background-color: var(--surface-color);
    border-radius: var(--radius-card);
}

html:not([data-mode="day"]) .gx-room-framed > .app-container {
    outline: 3px solid var(--gpn-accent, transparent);
}

html[data-mode="day"] .gx-room-framed > .app-container {
    outline: 3px solid var(--gpd-accent, transparent);
}

/* The PHOTO banner inside the fixed 56px room banner (task 2831): the photo strip stays one line high, with
   the words on a black scrim plate that is only as wide as its words, so the photo shows beside it. The
   plate keeps GroupBanner's scrim alpha (text never sits on the photo itself). */
.gx-room-banner .gx-banner.gx-room-banner-box.small.banner-photo {
    min-height: 0;
    align-items: center;
    padding: 6px 8px;
}

.gx-room-banner .banner-photo .scrim-plate {
    flex: 0 1 auto;
    max-width: 100%;
    min-width: 0;
    height: 100%;
    box-sizing: border-box;
    padding: 0 12px;
    gap: 10px;
    overflow: hidden;
}
