/* css/fun-panel.css — sidebar fun panel (Phase 1).

   Deliberately contains NO form-field rules: the panel renders no form fields
   at all, so it never has to fight the global paint in css/v2-tokens.css
   (~line 600), which slams a background onto every field on every
   body.app-v2-enabled page through a ~(0,5,2) :is() selector plus !important.
   scripts/check-fun-panel.mjs enforces the absence — with CSS comments
   stripped first, so this paragraph can neither satisfy nor defeat it. */

#fun-panel {
    margin-bottom: 1rem;
    /* js/dashboard-v2.js's assignRailSectionMetadata() re-appends the rail
       cards it knows about — 'profile-actions' then 'team-stats' — in a fixed
       order, and anything it does not know about (this slot) is left behind
       and ends up ABOVE them. Verified in a browser: the slot rendered as the
       sidebar's SECOND child, above the welcome card. #sidebar is a flex
       column on desktop (md:flex + flex-col), which is the only width the
       panel ever shows at, so `order` puts it back under Team Stats
       declaratively — and unlike a DOM move it survives the next re-order. */
    order: 99;
}

/* The rail paints every one of its direct children:

     @media (min-width: 768px) { #sidebar > div, #on-leave-sidebar > div {
         border-radius: 8px !important; margin-bottom: 10px !important;
         padding: 10px !important; } }              (css/style.css)

   — (1,0,1) and !important, so the plain `#fun-panel` rule above silently
   loses, exactly like the global input paint documented in CLAUDE.md. Found by
   reading the COMPUTED style off the live element (margin-bottom came back as
   10px, not 1rem), never by trusting the rule.

   This slot is a bare wrapper, not a card — `.fun-card` inside it is the card —
   so it must not take the rail's padding and radius on top of the card's own.
   `#sidebar > #fun-panel` is (2,0,0), which beats (1,0,1) among !important
   declarations. */
#sidebar > #fun-panel {
    margin-bottom: 1rem !important;
    padding: 0 !important;
    border-radius: 0 !important;
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
}

/* And the one that actually decides whether the panel exists at all.

   js/dashboard-v2.js's assignRailSectionMetadata() stamps
   data-dashboard-v2-section on EVERY left-rail child, and anything it does not
   recognise (i.e. anything that is not Team Stats) is labelled
   'profile-actions' — which css/dashboard-v2.css then hides outright:

     body.dashboard-v2-enabled .dashboard-v2-left-rail
       > [data-dashboard-v2-section="profile-actions"] { display: none !important; }

   So a correctly gated, correctly mounted fun panel rendered NOTHING on the
   real dashboard: computed display was `none` while every unit and static
   check was green. Setting the attribute ourselves does not help — that
   function clears and re-stamps it on every rail update — so the fix has to be
   CSS. (0,3,1) is beaten by (2,0,1) below.

   `:not([hidden])` is load-bearing: the slot ships hidden and the host re-hides
   it when a widget throws, and a bare `display: block !important` would defeat
   both. */
#sidebar > #fun-panel:not([hidden]) {
    display: block !important;
}

.fun-card {
    background: var(--dashboard-v2-panel-strong, #171a1f);
    border: 1px solid var(--dashboard-v2-border, #374151);
    border-radius: 0.75rem;
    padding: 0.75rem;
    color: var(--dashboard-v2-text, #e5e7eb);
}

.fun-card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    font-size: 0.8125rem;
    font-weight: 700;
}

.fun-card__title { display: flex; align-items: center; gap: 0.375rem; }
.fun-card__note { font-size: 0.75rem; line-height: 1.5; color: var(--dashboard-v2-muted, #a8b1bd); min-height: 1rem; }
.fun-card__attempts { font-size: 0.75rem; color: var(--dashboard-v2-muted, #a8b1bd); }

.fun-canvas { display: block; width: 100%; height: 150px; }

.fun-btn {
    border-radius: 0.5rem;
    border: 1px solid rgba(99, 102, 241, 0.4);
    background: rgba(99, 102, 241, 0.15);
    color: #e0e7ff;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.375rem 0.75rem;
    cursor: pointer;
    transition: background 0.15s ease;
}
.fun-btn:hover { background: rgba(99, 102, 241, 0.3); }
.fun-btn[disabled] { opacity: 0.5; cursor: default; }

.fun-board { list-style: none; margin: 0.5rem 0 0; padding: 0; font-size: 0.75rem; }
.fun-board li { display: flex; justify-content: space-between; gap: 0.5rem; padding: 0.125rem 0; }
.fun-board li[data-me="1"] { color: #a5b4fc; font-weight: 700; }

@media (prefers-reduced-motion: reduce) {
    .fun-canvas { animation: none !important; }
}

.fun-word-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 0.25rem;
    margin: 0.25rem 0;
}
.fun-word-cell {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(55, 65, 81, 0.9);
    border-radius: 0.25rem;
    font-size: 0.875rem;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--dashboard-v2-text, #e5e7eb);
}
.fun-word-cell[data-state="hit"],  .fun-word-tile[data-state="hit"]  { background: rgba(52, 211, 153, 0.35); border-color: rgba(52, 211, 153, 0.6); }
.fun-word-cell[data-state="near"], .fun-word-tile[data-state="near"] { background: rgba(251, 191, 36, 0.30); border-color: rgba(251, 191, 36, 0.55); }
.fun-word-cell[data-state="miss"], .fun-word-tile[data-state="miss"] { background: rgba(55, 65, 81, 0.45); }
/* A refused guess is marked on the row itself. The note alone is small grey
   text under the card, and a refusal there read as "Enter does nothing". */
.fun-word-cell[data-invalid] { border-color: rgba(248, 113, 113, 0.9); box-shadow: inset 0 0 0 1px rgba(248, 113, 113, 0.45); }
.fun-card__note[data-tone="warn"] { color: #fca5a5; }
/* The card is a typing target, like a text field, so it shows it is live on a
   MOUSE click too. :focus-visible alone never matched: Chromium does not apply
   it after a mouse click on a div, and a click is the only way anyone focuses
   this card. Reproduced with a real click, not assumed. */
[data-word-card]:focus { outline: 2px solid rgba(99, 102, 241, 0.7); outline-offset: 2px; }
/* The square the next letter goes into. Faintly marked always, so an untouched
   card still says where to start; a blinking caret once the card has focus, so
   "type now" is unmissable. There is no text field to draw one for us. The
   caret height is in em, not %: a percentage height on a flex item of an
   aspect-ratio box is not reliably definite, and would collapse to nothing. */
.fun-word-cell[data-active] { border-color: rgba(129, 140, 248, 0.55); }
[data-word-card]:focus .fun-word-cell[data-active] {
    border-color: rgba(165, 180, 252, 0.95);
    box-shadow: inset 0 0 0 1px rgba(129, 140, 248, 0.45);
}
[data-word-card]:focus .fun-word-cell[data-active]::after {
    content: '';
    width: 2px;
    height: 1.1em;
    border-radius: 1px;
    background: #c7d2fe;
    animation: fun-word-caret 1.05s steps(1, end) infinite;
}
@keyframes fun-word-caret { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
    [data-word-card]:focus .fun-word-cell[data-active]::after { animation: none; }
}

/* The a-z tracker under the word grid — the puzzle's only hint. Derived from
   played guesses alone, so it never reveals an unguessed letter. */
.fun-word-keys {
    display: grid;
    grid-template-columns: repeat(13, 1fr);
    gap: 2px;
    margin: 0.1875rem 0 0;
}
.fun-word-keys__label { margin: 0.375rem 0 0; font-size: 0.625rem; color: #9ca3af; }
/* Measured in a real 280px sidebar column: at 9px/#6b7280 the strip rendered
   perfectly and was still missed completely — it read as a smudge under the
   grid rather than as the puzzle's hint. Bigger and brighter, and paired with
   the legend below, because a colour code nobody can read teaches nothing. */
.fun-word-key {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3px 0;
    border-radius: 3px;
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--dashboard-v2-muted, #a8b1bd);
    background: rgba(55, 65, 81, 0.55);
}
.fun-word-key[data-state="hit"]  { background: rgba(52, 211, 153, 0.55); color: #ecfdf5; }
.fun-word-key[data-state="near"] { background: rgba(251, 191, 36, 0.50); color: #fffbeb; }
/* Dimmest of the four, but still readable: at #4b5563 the letter disappeared
   into the chip, so the legend's third example looked like an empty box and
   taught nothing. */
.fun-word-key[data-state="miss"] { background: #111827; color: #a8b1bd; }

/* How to play. A colour key alone never taught the game: it described squares
   that were still grey and never said there was a hidden word. So before the
   first guess the card states the goal and walks through one example guess;
   after it (data-mode="key") only the key stays, because the goal is known and
   the board's colours now mean something. paint() sets the mode. */
.fun-word-help { margin: 0.5rem 0 0; font-size: 0.6875rem; line-height: 1.35; color: #cbd5e1; }
.fun-word-help[data-mode="none"] { display: none; }
.fun-word-help[data-mode="key"] .fun-word-help__intro,
.fun-word-help[data-mode="key"] .fun-word-legend__eg { display: none; }
.fun-word-help p { margin: 0 0 0.25rem; }
.fun-word-help__title {
    font-size: 0.625rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #a5b4fc;
}
.fun-word-example { display: flex; gap: 3px; margin: 0.125rem 0 0.375rem; }
.fun-word-tile {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border: 1px solid rgba(55, 65, 81, 0.9);
    border-radius: 3px;
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    color: #e5e7eb;
}

/* What the colours mean. One colour per line, never a wrapped row: a row broke
   one-then-two in the 280px column and read as a single run-on sentence. The
   swatches are colour only — sample letters inside them ("b wrong spot") were
   read as a rule about the letter B; the example's letters sit BESIDE them, and
   only while the example above is on screen. */
.fun-word-legend {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 3px;
    margin: 0;
    padding: 0;
}
.fun-word-legend__item { display: flex; align-items: center; gap: 6px; }
.fun-word-legend__eg { flex: 0 0 1.75rem; font-weight: 700; color: #f3f4f6; white-space: nowrap; }
.fun-word-swatch {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 2px;
    flex: 0 0 auto;
}
.fun-word-swatch[data-state="hit"]  { background: rgba(52, 211, 153, 0.75); }
.fun-word-swatch[data-state="near"] { background: rgba(251, 191, 36, 0.75); }
.fun-word-swatch[data-state="miss"] { background: rgba(17, 24, 39, 0.95); box-shadow: inset 0 0 0 1px rgba(107, 114, 128, 0.7); }

/* Light theme. Every rule above is written for the dark card, and pale-on-dark
   text (#cbd5e1 help, #a5b4fc title, #f3f4f6 example letters) all but vanishes
   on the white one — so the game carries its own paired palette: every result
   state, the tracker, the worked example, the legend swatches and the caret.
   The swatches reuse the board's own fill + edge so the key looks like what it
   explains. */
html[data-theme="light"] :is(.fun-word-cell, .fun-word-tile) {
    border-color: #64748b;
    background: #f8fafc;
    color: #0f172a;
}
html[data-theme="light"] .fun-word-key { color: #475569; background: #e2e8f0; }
html[data-theme="light"] :is(.fun-word-cell, .fun-word-tile, .fun-word-key)[data-state="hit"] {
    color: #166534; background: #dcfce7; border-color: #15803d;
}
html[data-theme="light"] :is(.fun-word-cell, .fun-word-tile, .fun-word-key)[data-state="near"] {
    color: #92400e; background: #fef3c7; border-color: #b45309;
}
html[data-theme="light"] :is(.fun-word-cell, .fun-word-tile, .fun-word-key)[data-state="miss"] {
    color: #334155; background: #e2e8f0; border-color: #64748b;
}
html[data-theme="light"] .fun-word-swatch[data-state="hit"]  { background: #dcfce7; box-shadow: inset 0 0 0 1px #15803d; }
html[data-theme="light"] .fun-word-swatch[data-state="near"] { background: #fef3c7; box-shadow: inset 0 0 0 1px #b45309; }
html[data-theme="light"] .fun-word-swatch[data-state="miss"] { background: #e2e8f0; box-shadow: inset 0 0 0 1px #64748b; }
/* The light base border above outranks the dark-theme [data-active] rule, so
   the next-letter square needs its own light marking or it disappears. */
html[data-theme="light"] .fun-word-cell[data-active] { border-color: #818cf8; }
html[data-theme="light"] [data-word-card]:focus .fun-word-cell[data-active] {
    border-color: #4338ca;
    box-shadow: inset 0 0 0 1px rgba(67, 56, 202, 0.35);
}
html[data-theme="light"] [data-word-card]:focus .fun-word-cell[data-active]::after { background: #4338ca; }
html[data-theme="light"] .fun-word-help { color: #334155; }
html[data-theme="light"] .fun-word-help__title { color: #4338ca; }
html[data-theme="light"] .fun-word-legend__eg { color: #0f172a; }
html[data-theme="light"] .fun-word-keys__label { color: #475569; }
/* The refused-word row: the light base border above outranks the dark
   [data-invalid] rule, and #fca5a5 warning text is ~2:1 on white. */
html[data-theme="light"] .fun-word-cell[data-invalid] {
    border-color: #dc2626;
    box-shadow: inset 0 0 0 1px rgba(220, 38, 38, 0.35);
}
html[data-theme="light"] .fun-card__note[data-tone="warn"] { color: #b91c1c; }
html[data-theme="light"] .fun-board li[data-me="1"] { color: #4338ca; }
html[data-theme="light"] .fun-btn { color: #4338ca; background: #eef2ff; border-color: #6366f1; }

/* Hide/Show: lets the game recede while the queue is being worked. */
.fun-word-toggle {
    color: var(--dashboard-v2-muted, #a8b1bd);
    background: transparent;
    border: 1px solid var(--dashboard-v2-border, #374151);
    border-radius: 6px;
    min-height: 28px;
    padding: 2px 6px;
    font-size: 12px;
    cursor: pointer;
}
.fun-word-content[hidden] { display: none; }

/* ── Tic-Tac-Toe ─────────────────────────────────────────────────────────────
   Buttons only — deliberately no input/select/textarea rules anywhere in this
   file. v2-tokens.css paints every form field on an app-v2 page through a
   ~(0,5,2) :is() selector with !important, and fighting it is how the admin
   contrast bug burned three attempts. A board made of <button>s never enters
   that fight. */
.fun-xo-board {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 4px;
    margin: 0.375rem 0 0;
}
.fun-xo-cell {
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(99, 102, 241, 0.28);
    border-radius: 6px;
    background: rgba(30, 41, 59, 0.65);
    color: #e2e8f0;
    font-size: 1.125rem;
    font-weight: 800;
    line-height: 1;
    cursor: pointer;
    transition: background 120ms ease, border-color 120ms ease, transform 120ms ease;
}
.fun-xo-cell:hover:not(:disabled) {
    background: rgba(51, 65, 85, 0.9);
    border-color: rgba(129, 140, 248, 0.55);
}
.fun-xo-cell:active:not(:disabled) { transform: scale(0.96); }
/* A taken square is not a dead control — it still shows its mark at full
   strength. Only the EMPTY squares dim when it is not your turn, so the board
   reads as "you cannot move yet", never as "this game is over". */
.fun-xo-cell:disabled { cursor: default; }
.fun-xo-cell[data-mark="empty"]:disabled { opacity: 0.55; }
.fun-xo-cell[data-mark="x"] { color: #f87171; border-color: rgba(248, 113, 113, 0.45); }
.fun-xo-cell[data-mark="o"] { color: #60a5fa; border-color: rgba(96, 165, 250, 0.45); }
.fun-xo-cell[data-win="1"] {
    background: rgba(52, 211, 153, 0.22);
    border-color: rgba(52, 211, 153, 0.7);
}
.fun-xo-turn { font-size: 0.6875rem; color: #9ca3af; }
.fun-xo-lobby { margin-top: 0.375rem; }
.fun-xo-hint,
.fun-xo-invite {
    margin: 0 0 0.25rem;
    font-size: 0.6875rem;
    color: #9ca3af;
}
.fun-xo-invite { color: #fbbf24; }
.fun-xo-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}
.fun-xo-actions + .fun-xo-actions { margin-top: 4px; }
.fun-xo-btn {
    padding: 3px 8px;
    border: 1px solid rgba(99, 102, 241, 0.35);
    border-radius: 999px;
    background: rgba(30, 41, 59, 0.7);
    color: #cbd5e1;
    font-size: 0.625rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease;
}
.fun-xo-btn:hover { background: rgba(67, 56, 202, 0.5); color: #eef2ff; }
.fun-xo-btn--go {
    border-color: rgba(52, 211, 153, 0.5);
    background: rgba(16, 185, 129, 0.22);
    color: #d1fae5;
}
.fun-xo-btn--go:hover { background: rgba(16, 185, 129, 0.38); }
