/* Colourblind variant. Activated by <body class="cb">. The shared base
   hues swap to the canonical Okabe-Ito CVD-safe set (mirrors STYLES[].cb in
   app/constants.py); shape/pattern channels (data-pattern + glyphs) carry
   meaning so colour is never load-bearing. Satisfies the spec's hard
   "usable colourblind variant" requirement.

   NOTE: never write a glob asterisk immediately followed by a slash inside
   a CSS comment — that two-char sequence closes the comment early and
   silently corrupts the whole stylesheet (it killed the entire body.cb
   variable block for a long time). Say "role / status aliases" with spaces.

   IMPORTANT — the role and status aliases DO NOT "follow automatically".
   They are declared in anni.css ":root" as a base-hue var(), so they are
   resolved on :root (the html element), where the base hue is still the
   default palette (body.cb only swaps the base hues on <body>, a different
   element). The resolved default then inherits into the cards — i.e. CB
   mode would keep showing the bright default. The fix: re-declare every
   alias here on body.cb so its base-hue var() re-resolves against the
   swapped hues and inherits correctly (this is also why the role dark
   aliases are re-declared below). Any new base-hue alias added to anni.css
   MUST be mirrored here too (a static guard in test_colourblind.py
   enforces it). */

body.cb {
  --c-red:     #D55E00;  /* vermillion */
  --c-yellow:  #F0E442;  /* yellow */
  --c-green:   #009E73;  /* bluish green */
  --c-blue:    #0072B2;  /* blue */
  --c-cyan:    #000000;  /* black (Okabe-Ito) — sky-blue was too near blue */
  --c-magenta: #CC79A7;  /* reddish purple */
  --c-grey:    #999999;

  /* Re-resolve the role / status aliases against the swapped --c-* (see the
     IMPORTANT note above — without this they stay baked to the :root
     default and CB mode shows the bright palette). Borders + the role glyph
     swatch read these, so this is what makes them verbatim Okabe-Ito. */
  --role-primary:    var(--c-red);
  --role-secondary:  var(--c-yellow);
  --role-tertiary:   var(--c-magenta);
  --role-healer:     var(--c-green);
  --role-tank:       var(--c-blue);
  --role-fill:       var(--c-cyan);
  --role-unassigned: var(--c-grey);
  --st-gone:      var(--c-red);
  --st-offhard:   var(--c-green);
  --st-offsoft:   var(--c-yellow);
  --st-elsewhere: var(--c-blue);
  --st-world:     var(--c-cyan);
  --st-party:     var(--c-magenta);
  --st-unknown:   var(--c-grey);

  /* Role-chip BODIES adopt CVD-safe hues here too (the user dashboard's only
     palette element is the role chip, so cb must visibly change it). These
     are *darkened* Okabe-Ito shades — same hue family as the bright `--c-*`
     swatch above, but dark enough that the chip keeps plain **white** text
     (exactly like normal mode; no fragile outlined-text trick). White-on-bg
     contrast is ≥ ~4.7:1 for every one. */
  --role-primary-dark:    #8f3f00;  /* dark vermillion */
  --role-secondary-dark:  #5c5200;  /* dark gold (Okabe-Ito yellow) */
  --role-tertiary-dark:   #8a4e72;  /* dark reddish-purple */
  --role-healer-dark:     #00684c;  /* dark bluish-green */
  --role-tank-dark:       #00598c;  /* dark blue */
  --role-fill-dark:       #000000;  /* black (Okabe-Ito); white text is 21:1 */
  --role-unassigned-dark: #4d4d4d;  /* dark grey */
  /* Light halo tints — same hue family as the bright `--c-*` (CB) swatch,
     bright enough to read as a halo on the dark person-card backgrounds.
     Same role family as the `-dark` body shades above; capability dots'
     border uses these so the halo stays Okabe-Ito under body.cb instead of
     bleeding through to the default bright palette. */
  --role-primary-light:    #ff9966;  /* light vermillion */
  --role-secondary-light:  #fff099;  /* light gold */
  --role-tertiary-light:   #ebbed3;  /* light reddish-purple */
  --role-healer-light:     #66d6b3;  /* light bluish-green */
  --role-tank-light:       #66b3e0;  /* light blue */
}

/* Per-role card-background texture — a CB-only non-colour channel that
   survives greyscale. Okabe-Ito hues are tuned for protan/deutan/tritan but
   several collapse to similar luminance under achromatopsia (monochromacy),
   so the `--role-*-dark` background alone leaves roles indistinguishable on
   the person cards. A faint shape rhythm per role restores identifiability
   for that population. data-role is already on every .person at all times
   (see templates/macros/person.html); only CB activates the rule, matching
   how data-pattern works for statuses. ONLY background-image is set here so
   the inline `background-color: var(--role-*-dark)` underneath is untouched
   (and white text legibility, ≥ ~4.7:1, is preserved — the overlay alpha is
   < 10%). Unassigned cards stay flat — no texture maps to "no role".

   The same rule also targets the LEGEND role chips (the big labelled chips
   in `_board.html`, scoped by the `.legend` ancestor) so the legend acts as
   the key — without it the textures on cards have no glossary. The smaller
   cross-dashboard role chips emitted by the `role_bg` macro deliberately
   stay clean (they live outside `.legend`); chip-size texture reads as
   noise at that scale. */
body.cb .person[data-role="primary"],
body.cb .legend .role-chip[data-role="primary"] {
  background-image: repeating-linear-gradient(45deg,
    rgba(255,255,255,.07) 0 2px, transparent 2px 14px);
}
body.cb .person[data-role="secondary"],
body.cb .legend .role-chip[data-role="secondary"] {
  background-image: repeating-linear-gradient(135deg,
    rgba(255,255,255,.07) 0 2px, transparent 2px 14px);
}
body.cb .person[data-role="tertiary"],
body.cb .legend .role-chip[data-role="tertiary"] {
  background-image: repeating-linear-gradient(90deg,
    rgba(255,255,255,.07) 0 2px, transparent 2px 14px);
}
body.cb .person[data-role="healer"],
body.cb .legend .role-chip[data-role="healer"] {
  background-image:
    repeating-linear-gradient(45deg,
      rgba(255,255,255,.06) 0 1.5px, transparent 1.5px 12px),
    repeating-linear-gradient(135deg,
      rgba(255,255,255,.06) 0 1.5px, transparent 1.5px 12px);
}
body.cb .person[data-role="tank"],
body.cb .legend .role-chip[data-role="tank"] {
  background-image: radial-gradient(circle at center,
    rgba(255,255,255,.09) 1.4px, transparent 1.6px);
  background-size: 12px 12px;
}
body.cb .person[data-role="fill"],
body.cb .legend .role-chip[data-role="fill"] {
  background-image: repeating-linear-gradient(0deg,
    rgba(255,255,255,.07) 0 2px, transparent 2px 14px);
}

/* In CB mode, lean on text weight + the always-present glyph/label. */
body.cb .pill { font-weight: 700; }
body.cb .cb-toggle { text-decoration: underline; }

/* Capability dots: surface the per-role letter (P/S/M/H/T) so the role is
   readable without colour. The letter is in the DOM at all times (the macro
   never stops emitting it); only CB mode shows it. !important guards against
   any inline ``display`` ever sneaking in. */
body.cb .cap-letter { display: inline !important; }

/* Status border. CB OFF (default world state): a single SOLID coloured
   outline — colour is reliable for non-CVD users, so the dash/dot/double
   noise is dropped entirely. The `data-pattern` attribute is STILL emitted
   in the DOM at all times (the macros never stop) so the channel "exists
   from the start"; only CB activates it. */
.status-border { border-width: 3px; border-style: solid; }

/* CB ON: the non-colour pattern channel becomes load-bearing (the spec's
   hard rule). ONE uniform 4px family, most→least "present" (PARTY→GONE):
   double → solid → dash → dash-dash-dot → dash-dot → dot; dash-dot-dot =
   UNKNOWN. The colour is ALWAYS the verbatim `--stc` Okabe-Ito hue — only
   solid/double/dashed/dotted are native border-styles (they render
   border-color exactly); the composite dash-dot variants are a
   `repeating-linear-gradient` border-image fed by `var(--stc)`, so the line
   is still the exact hue (NO groove/ridge — those 3-D shade the colour).

   In CB-on the ring is drawn OUTSIDE the box (a ::after offset out by the
   ring width) instead of an inner border — it no longer eats into the card
   and reads as a halo. CB-off keeps the plain inner border (rule above,
   untouched: every ring rule here is scoped to body.cb + ::after). The ring
   is non-interactive (pointer-events:none) so drag/clicks pass through. */
body.cb .status-border { border: 0; position: relative; }
body.cb .status-border::after {
  content: ""; position: absolute; inset: -4px;
  border: 4px solid var(--stc, currentColor);
  border-image: none; border-radius: inherit; pointer-events: none;
}
/* Inside the .person stacking context (isolation: isolate, in anni.css), the
   ::after is a positioned z-auto descendant — which outranks an in-flow
   inline descendant like the .cap-dots stacking context. Result: the card's
   own ring paints OVER the popover where they overlap (the bottom edge of
   the card, where the popover starts). Pushing the ring to z:-1 puts it
   below all in-flow content within the same .person while leaving its
   spatial layout (inset:-4px, the outer halo) untouched — nothing else
   spatially overlaps the ring, so it stays fully visible. Scoped to
   `.person` so the legend's status-border (no popover, no isolation) keeps
   its default stacking. */
body.cb .person.status-border::after { z-index: -1; }
body.cb .status-border[data-pattern="solid"]::after  { border-style: solid; }
body.cb .status-border[data-pattern="double"]::after { border-style: double; }
body.cb .status-border[data-pattern="dash"]::after    { border-style: dashed; }
body.cb .status-border[data-pattern="dot"]::after     { border-style: dotted; }
/* Composites: native border-color is the solid fallback; border-image draws
   the dash/dot rhythm in the SAME verbatim colour (var(--stc)). */
body.cb .status-border[data-pattern="dash-dot"]::after {
  border-style: solid;
  border-image: repeating-linear-gradient(90deg,
    var(--stc) 0 12px, transparent 12px 17px,
    var(--stc) 17px 20px, transparent 20px 25px) 4;
}
body.cb .status-border[data-pattern="dash-dash-dot"]::after {
  border-style: solid;
  border-image: repeating-linear-gradient(90deg,
    var(--stc) 0 12px, transparent 12px 17px,
    var(--stc) 17px 29px, transparent 29px 34px,
    var(--stc) 34px 37px, transparent 37px 42px) 4;
}
body.cb .status-border[data-pattern="dash-dot-dot"]::after {
  border-style: solid;
  border-image: repeating-linear-gradient(90deg,
    var(--stc) 0 12px, transparent 12px 17px,
    var(--stc) 17px 20px, transparent 20px 25px,
    var(--stc) 25px 28px, transparent 28px 33px) 4;
}

/* The CB-on ring overhangs each box by the 4px ring width, so two adjacent
   rings span 8px — more than the default .45rem dropzone gap, and they bleed
   together. Widen the gaps in CB only (CB-off has an inner border, no
   overhang, so it keeps the tighter default spacing). */
body.cb .dropzone  { gap: .95rem; }
body.cb .legend-row { gap: 1rem; }
