/* ascendry-merge — the live site's style, the sandbox's structure.
 *
 * Every colour here is lifted from the running app, not approximated:
 *   tailwind.config.ts  neutral scale (teal-navy), amber re-tinted to brand gold,
 *                       brand.gold #dbb45a, brand.emerald #2dd4a7
 *   globals.css         bg-neutral-950 + the two radial brand glows
 *   InsightCard         severity: critical #ef4444 warning #f59e0b good #22c55e info #3b82f6
 * The type is the app's own system stack, not a display serif — this experiment
 * is about keeping the site looking like itself while it gets reorganised.
 */

:root {
  /* neutral — teal-navy slate, straight from the tailwind theme */
  --n50:#f3f6f6;  --n100:#e6edec; --n200:#cbd8d6; --n300:#a7bab7; --n400:#7e938f;
  --n500:#5f7571; --n600:#485d5a; --n700:#2f4444; --n800:#1c3030; --n900:#11212a;
  --n950:#0b181e;

  /* amber, re-tinted to the logo's bronze-gold */
  --a200:#eed9a3; --a300:#e6c87f; --a400:#dbb45a; --a500:#cc9d3a; --a700:#8a5f24;
  --a900:#3c2c13; --a950:#241a0c;

  --brand-gold:#dbb45a; --brand-emerald:#2dd4a7;
  --critical:#ef4444; --warning:#f59e0b; --good:#22c55e; --info:#3b82f6;

  --ground:var(--n950); --surface:var(--n900); --raised:var(--n800);
  /* The neutral ramp above is a teal-navy slate, which reads as green the moment
     a large flat area of it sits next to text. Panels and the page keep it — it is
     the theme. CARDS do not: a grid of them is the biggest expanse of flat colour
     on any screen, and at that size the tint stops being a tint. */
  --card:#1b1e20; --card-line:#2b3033; --card-sunk:#141719;
  /* How much raw class colour a NAME keeps. The rest is the theme's ink.
     Class colours are a game's brand palette, not a text palette — raw on a
     card, Shaman is 4.51:1 and Warlock 5.02:1, which reads as "that dark
     blue again". At 72% the worst on the palette clears 4.2 and every class
     still looks like itself. */
  --name-lift:72%;
  --line:var(--n800); --line-soft:#16282e;
  --ink:var(--n100); --ink-2:var(--n300); --ink-3:var(--n400);
  /* ONE accent, meaning "this is the one". The nav's current page, a pressed
     tab, a primary button — all the same yellow, because they are all saying the
     same thing. There are two yellows in this file and they were being used
     interchangeably: --a300 for selected pills, --brand-gold (a400) for primary
     buttons, so a Save button and the tab beside it were visibly different
     colours. --brand-gold stays what it is — the logo's bronze, for the crest
     and the marks — and stops standing in for "active". */
  --accent:var(--a300);
  /* ONE focus ring, and deliberately NOT the accent: a focused pill that is
     already selected would be a gold ring on a gold fill, which is no ring at
     all. Emerald is the other brand colour and reads against every surface here.
     Seven rules used to pick their own — a400, a300, brand-gold, emerald — so
     the ring changed colour as you tabbed across one screen. */
  --ring:var(--brand-emerald);

  /* class colours — canonical, as the app shows them on a dark ground */
  --c-warrior:#c79c6e; --c-paladin:#f58cba; --c-hunter:#abd473; --c-rogue:#fff569;
  --c-priest:#ffffff;  --c-shaman:#2b86e6;  --c-mage:#69ccf0;   --c-warlock:#9482c9;
  --c-druid:#ff7d0a;   --c-deathknight:#c41f3b;
  --swatch-edge:rgb(0 0 0/.5);

  /* item quality */
  --q-poor:#8d8478; --q-common:#e8e0d2; --q-uncommon:#4fd06a;
  --q-rare:#4aa8ff; --q-epic:#c07af0; --q-legendary:#ff9a3c;

  --radius:8px;
  --font:ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
         "Helvetica Neue", Arial, sans-serif;
  --mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

*, *::before, *::after { box-sizing:border-box; }
html { color-scheme:dark; }
body {
  margin:0; background:var(--ground); color:var(--ink);
  font:15px/1.6 var(--font); -webkit-font-smoothing:antialiased;
  /* the app's own brand glow — emerald from the runes, gold from the shield */
  background-image:
    radial-gradient(55rem 38rem at 50% -12%, rgba(45,212,167,.08), transparent 60%),
    radial-gradient(48rem 30rem at 100% 0%, rgba(219,180,90,.05), transparent 55%);
  background-attachment:fixed;
}
h1, h2, h3 { margin:0; font-weight:600; letter-spacing:-.01em; text-wrap:balance; }
/* Nothing clickable is underlined. The pointer cursor says it is a link, and
   an underline through a table full of names is noise. Anything that needs an
   affordance in running prose gets .link, which is coloured instead. */
a { color:inherit; text-decoration:none; }
.num { font-variant-numeric:tabular-nums; }

/* ── the backdrop ──────────────────────────────────────────────────────────
   The battle poster, sitting behind everything and going nowhere: fixed, so it
   reads as a backdrop the content moves over rather than a picture that scrolls
   with it.

   Heavily dimmed on purpose. It is a full-colour illustration and the app is a
   reading surface — every number, grade and name on top of it has to stay
   legible, so the image is held well below the text's contrast floor and a
   ground-coloured wash is laid over it. `pointer-events:none` keeps it out of
   the way of anything clickable. */
.backdrop { position:fixed; inset:0; z-index:-1; pointer-events:none;
            background:var(--ground); overflow:hidden; }
/* The art. Anchored right, because that is the side of the page the answer
   headline and its sentence do NOT occupy — the poster gets the empty half of
   the composition rather than sitting under the text. Desaturated a little so
   its sand and rust do not argue with the gold the UI uses for emphasis, and
   faded out down the page so it is a header illustration rather than wallpaper
   behind a table. */
.backdrop::before {
  content:""; position:absolute; inset:0;
  background:url("/ascendry-battle-bg-poster.svg") right top / cover no-repeat;
  filter:saturate(.72);
  opacity:.5;
  /* Dips through the middle and lifts again near the foot, because the painting
     has two halves worth seeing — the giant above and the fight on the ground
     below — and a mask that only faded downward threw the second one away. It
     comes back short of full, though: a short page leaves the whole lower half
     of the viewport exposed, and at full strength the picture stops being a
     backdrop and becomes the page. */
  -webkit-mask-image:linear-gradient(to bottom, #000 0%, rgb(0 0 0/.46) 42%,
                                     rgb(0 0 0/.52) 76%, rgb(0 0 0/.42) 100%);
          mask-image:linear-gradient(to bottom, #000 0%, rgb(0 0 0/.46) 42%,
                                     rgb(0 0 0/.52) 76%, rgb(0 0 0/.42) 100%);
}
/* The reading side. Not a left-to-right ramp — that darkened the whole left
   half, including the bottom corner where there is nothing to read. The scrim is
   a pool centred on the one block of text that sits directly on the backdrop:
   the eyebrow, the answer headline and its sentence. It falls away in every
   direction from there, which opens the top right AND the bottom left while
   still paying for the contrast exactly where it is owed. */
.backdrop::after {
  content:""; position:absolute; inset:0;
  background:
    /* The pool over the headline. */
    radial-gradient(82% 60% at 22% 16%,
      var(--ground) 0 30%, rgb(11 24 30/.78) 58%, rgb(11 24 30/.3) 82%, rgb(11 24 30/.1) 100%),
    /* And the rule that keeps the whole thing a backdrop: the art belongs in the
       gutters, so the centre column — where every panel and every short page's
       empty space sits — stays dark whatever the page length. Both edges stay
       open, which is what shows the giant on one side and the ground fight on
       the other. */
    linear-gradient(to right, transparent 0%, rgb(11 24 30/.5) 15%,
      rgb(11 24 30/.66) 46%, rgb(11 24 30/.5) 84%, transparent 100%);
}
/* A phone has no empty half — the text column is the whole width — so the art
   steps back to a suggestion behind the header. */
@media (max-width: 700px) {
  .backdrop::before { opacity:.4; background-position:62% top;
    -webkit-mask-image:linear-gradient(to bottom, #000 0%, transparent 46%);
            mask-image:linear-gradient(to bottom, #000 0%, transparent 46%); }
  .backdrop::after {
    background:linear-gradient(rgb(11 24 30/.34), rgb(11 24 30/.84) 30%, var(--ground) 58%);
  }
}

/* ── chrome ───────────────────────────────────────────────────────────────── */
.bar { position:sticky; top:0; z-index:20; display:flex; align-items:center; gap:14px;
       padding:11px 20px; background:rgb(11 24 30/.85); backdrop-filter:blur(8px);
       border-bottom:1px solid var(--line); flex-wrap:wrap; }
.wordmark { font-size:17px; font-weight:600; letter-spacing:-.02em; }
.spacer { flex:1 1 auto; }

.nav { display:flex; gap:4px; }
.nav a { text-decoration:none; color:var(--ink-3); padding:7px 13px; border-radius:6px;
         font-size:14px; font-weight:500; }
.nav a:hover { color:var(--ink); background:var(--raised); }
.nav a[aria-current="page"] { color:var(--n950); background:var(--a300); font-weight:600; }

.subwrap { border-bottom:1px solid var(--line); background:rgb(17 33 42/.5); }
.subnav { max-width:1140px; margin:0 auto; padding:0 20px; display:flex; gap:22px; flex-wrap:wrap; }
.subnav:empty { display:none; }
.subnav a { text-decoration:none; color:var(--ink-3); padding:11px 0 9px; font-size:14px;
            border-bottom:2px solid transparent; margin-bottom:-1px; }
.subnav a:hover { color:var(--ink); }
.subnav a[aria-current="page"] { color:var(--a300); border-bottom-color:var(--a300); font-weight:500; }
/* ── the section's pages, on a phone ───────────────────────────────────────
   Underlined tabs need a baseline to sit on, and a baseline needs a row that
   does not wrap. At 390px five of them wrapped into two lines of bare text with
   22px between them and nothing around them — no structure at all, just words.

   So on narrow screens they stop being tabs and become pills, which is the
   control this app already uses everywhere else for "pick one of these". They
   wrap, deliberately: a horizontal scroller would keep one tidy row by hiding
   half the section behind an edge, and these are the five things the section
   can show you. */
@media (max-width: 900px) {
  .subwrap { padding:2px 0 4px; }
  .subnav { gap:7px; padding:6px 14px; }
  .subnav a {
    padding:8px 13px; border:1px solid var(--n700); border-radius:999px;
    border-bottom-width:1px; margin-bottom:0; background:rgb(11 24 30/.5);
    font-size:13px; line-height:1.15;
  }
  .subnav a[aria-current="page"] {
    color:#17242b; background:var(--a300); border-color:var(--a300); font-weight:600;
  }
}

.ghost { border:1px solid var(--line); background:transparent; color:var(--ink-2);
         padding:6px 12px; border-radius:6px; font:500 13px var(--font); cursor:pointer; }
.ghost:hover { background:var(--raised); color:var(--ink); }
.ghost:focus-visible, .nav a:focus-visible, .subnav a:focus-visible, .tab:focus-visible,
.link:focus-visible, .down:focus-visible, summary:focus-visible {
  outline:2px solid var(--ring); outline-offset:2px; }

.page { max-width:1140px; margin:0 auto; padding:26px 20px 64px; }

/* ── page head ────────────────────────────────────────────────────────────── */
.eyebrow { font-size:12px; font-weight:500; letter-spacing:.06em; text-transform:uppercase;
           color:var(--ink-3); }
.answer { font-size:27px; line-height:1.25; margin:8px 0 8px; max-width:34ch; }
.answer em { font-style:normal; color:var(--a300); }
.answer.long { font-size:21px; max-width:60ch; }
.because { color:var(--ink-2); font-size:15px; max-width:74ch; margin:0; }
.because strong { color:var(--ink); font-weight:600; }

/* ── cards — the app's rounded panel on a slightly lifted surface ─────────── */
.stack { display:flex; flex-direction:column; gap:14px; margin-top:22px; }
.panel { background:rgb(17 33 42/.6); border:1px solid var(--line); border-radius:var(--radius); }
.panel-h { display:flex; align-items:center; gap:10px; padding:13px 16px 11px;
           border-bottom:1px solid var(--line); }
.panel-h h2 { font-size:15px; }
.panel-h .note { margin-left:auto; font-size:12.5px; color:var(--ink-3); }
.panel-b { padding:14px 16px 16px; }
.panel.crit { border-left:3px solid var(--critical); }
.panel.warn { border-left:3px solid var(--warning); }
.panel.good { border-left:3px solid var(--good); }
.panel.info { border-left:3px solid var(--info); }
/* WHICH TEAM this panel is about, in that team's own colour — the same --tc the
   team chips and pills read, so one team is one colour wherever it appears.
   `panel()` has set --tc and added .teamed since the accent was added; nothing
   ever drew it, so a weekly report for Tuesday and one for Thursday were the
   same anonymous box. Same 3px left border the tone classes use, because it is
   the app's established way of saying what a panel is about. */
.panel.teamed { border-left:3px solid var(--tc, var(--line)); }
/* A tone is a state and outranks a label: a critical panel about a team must
   still read as critical. */
.panel.teamed.crit { border-left-color:var(--critical); }
.panel.teamed.warn { border-left-color:var(--warning); }
.panel.teamed.good { border-left-color:var(--good); }
.panel.teamed.info { border-left-color:var(--info); }
.lozenge { display:none; }

/* ── chips, the app's rounded pill ────────────────────────────────────────── */
.chip { display:inline-flex; align-items:center; gap:5px; border-radius:5px; padding:3px 8px;
        font-size:12px; font-weight:500; white-space:nowrap; background:var(--raised);
        color:var(--ink-2); border:1px solid transparent; }
.chip.good { background:rgb(34 197 94/.14); color:#6ee7a8; border-color:rgb(34 197 94/.3); }
.chip.warn { background:rgb(245 158 11/.14); color:#fbbf24; border-color:rgb(245 158 11/.3); }
.chip.crit { background:rgb(239 68 68/.14); color:#fca5a5; border-color:rgb(239 68 68/.3); }
.chip.info { background:rgb(59 130 246/.14); color:#93c5fd; border-color:rgb(59 130 246/.3); }
.chip.mute { background:var(--raised); color:var(--ink-3); border-color:var(--line); }
/* A STANDING is a column, so every one of them gets the same box: the words are
   not the same length ("Core" against "At risk"), and a chip that hugs its text
   leaves the column ragged down one edge or the other. Sized in em off the chip's
   own type, so each theme lands where its own type does — and as a MINIMUM, so a
   longer standing added later widens rather than clips. */
.chip.stand { min-width:5.8em; justify-content:center; }
.chip.team { background:transparent; color:var(--tc,var(--ink-2)); border-color:var(--tc,var(--line)); }
.chip.team::before { content:""; width:7px; height:7px; border-radius:2px; background:var(--tc); }

.badge { display:inline-flex; align-items:center; gap:5px; padding:2px 7px; margin:0 4px 3px 0;
         border-radius:5px; background:var(--raised); color:var(--ink-2);
         border:1px solid var(--line); font-size:12px; font-weight:500; white-space:nowrap; }
.badge.good { background:rgb(34 197 94/.14); color:#6ee7a8; border-color:rgb(34 197 94/.3); }
.badge.info { background:rgb(59 130 246/.14); color:#93c5fd; border-color:rgb(59 130 246/.3); }
.badge.accent { background:rgb(219 180 90/.14); color:var(--a300); border-color:rgb(219 180 90/.3); }
.badgekey { display:flex; flex-wrap:wrap; gap:4px 6px; }

/* ── grades — the app colours these by band ───────────────────────────────── */
.grade { font-size:16px; font-weight:700; font-variant-numeric:tabular-nums; }
.grade.good { color:var(--good); } .grade.warn { color:var(--warning); }
.grade.crit { color:var(--critical); }
.grade-xl { font-size:44px; font-weight:800; line-height:1; }
.grade-xl.good { color:var(--good); } .grade-xl.warn { color:var(--warning); }
.grade-xl.crit { color:var(--critical); }
.grade-sub { display:block; margin-top:5px; text-align:center; font-size:12px; color:var(--ink-3); }
.verdict { display:flex; align-items:center; gap:20px; flex-wrap:wrap; }
.verdict p { margin:0; flex:1 1 260px; color:var(--ink-2); }

/* ── tables ───────────────────────────────────────────────────────────────── */
.scroll { overflow-x:auto; padding-left:3px; margin-left:-3px; }
.tbl { width:100%; border-collapse:collapse; font-size:14px; }
.tbl th { text-align:left; padding:0 16px 9px 0; color:var(--ink-3);
          font-size:11.5px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; }
.tbl td { padding:9px 16px 9px 0; border-top:1px solid var(--line); vertical-align:middle; }
.tbl tr:hover td { background:rgb(255 255 255/.035); }
.tbl .r { text-align:right; }
.tbl th:last-child, .tbl td:last-child { padding-right:0; }
.tbl th.sortable button { background:none; border:0; padding:0; cursor:pointer; color:inherit;
  font:inherit; letter-spacing:inherit; text-transform:inherit; display:inline-flex;
  /* Top-aligned, not centred: a two-line header like "MISSING ENCHANTS" centred
     its arrow between the lines, where it read as belonging to the column next
     to it rather than to its own. */
  align-items:flex-start; gap:6px; text-align:left; }
.tbl th.sortable.r button { text-align:right; }
.tbl th.sortable .arrow { line-height:1.45; }
.tbl th.sortable button:hover { color:var(--ink); }
.tbl th.sortable.r button { flex-direction:row-reverse; }
.tbl th.sortable .arrow { opacity:.35; }
.tbl th.sortable.on { color:var(--a300); }
.tbl th.sortable.on .arrow { opacity:1; }
.tbl td .scoped { margin-left:7px; }

.v-good { color:var(--good); } .v-warn { color:var(--warning); }
.v-crit { color:var(--critical); } .v-ok { color:var(--ink-2); } .v-mute { color:var(--ink-3); }
.scoped { font-size:12px; color:var(--ink-3); }
.role { font-size:11.5px; font-weight:500; letter-spacing:.05em; text-transform:uppercase;
        color:var(--ink-3); }
/* A spec shown WITH its glyph: the two are one label and must not be split by
   a line break or by whatever sits between them. */
.spec.marked { display:inline-flex; align-items:center; gap:5px;
                vertical-align:middle; white-space:nowrap; }
.spec { margin-left:8px; font-size:12px; color:var(--ink-3); }
.foot { margin:11px 0 0; color:var(--ink-3); font-size:13px; line-height:1.55; }
.key { display:flex; flex-wrap:wrap; gap:9px 26px; margin-top:12px; padding-top:10px;
       border-top:1px solid var(--line); color:var(--ink-3); font-size:12.5px; }
.key > span { display:inline-flex; align-items:center; gap:9px; }
.key b { color:var(--ink-2); }
.key .sw { width:10px; height:10px; border-radius:3px; border:1px solid var(--swatch-edge); }

/* ── class identity ───────────────────────────────────────────────────────── */
.who { display:inline-flex; align-items:center; gap:7px; text-decoration:none;
       color:color-mix(in oklab, var(--c-this, var(--ink)) var(--name-lift, 100%), var(--ink));
       font-weight:500; }
.who::before { content:""; width:8px; height:8px; border-radius:2px; flex:none;
               background:var(--c-this, var(--ink-3)); }
/* No hover underline anywhere: the pointer cursor is what says a thing can be
   clicked, and an underline that appears only on hover tells you nothing you
   have not already discovered by hovering. Emphasis on hover is a colour or a
   background, not a rule under the text. */
a.who:hover { color:var(--ink); }
.nm { color:color-mix(in oklab, var(--c-this, var(--ink)) var(--name-lift, 100%), var(--ink));
      font-weight:600; }
.n-warrior{--c-this:var(--c-warrior);} .n-paladin{--c-this:var(--c-paladin);}
.n-hunter{--c-this:var(--c-hunter);}   .n-rogue{--c-this:var(--c-rogue);}
.n-priest{--c-this:var(--c-priest);}   .n-shaman{--c-this:var(--c-shaman);}
.n-mage{--c-this:var(--c-mage);}       .n-warlock{--c-this:var(--c-warlock);}
.n-druid{--c-this:var(--c-druid);}     .n-deathknight{--c-this:var(--c-deathknight);}
.tagm { font-size:10.5px; font-weight:600; letter-spacing:.08em; text-transform:uppercase;
        padding:2px 6px; border-radius:4px; border:1px solid transparent; }
.tagm.main { color:var(--a300); border-color:rgb(219 180 90/.4); background:rgb(219 180 90/.12); }
.tagm.alt { color:var(--ink-3); border-color:var(--line); }
.charlist { display:flex; flex-direction:column; align-items:flex-start; gap:5px; }

/* ── controls ─────────────────────────────────────────────────────────────── */
/* Row gap larger than column gap. They are equal only if you never think about
   what happens when the strip wraps — and the roster's standing filter wraps to
   three rows on a phone, where 6px in both directions made one solid block of
   pills instead of three readable rows. */
.tabs { display:flex; gap:9px 6px; flex-wrap:wrap; align-items:center; margin-bottom:14px; }

/* Air above a pill row that follows the page's own words.
   .because sets margin:0 and .tabs had no margin-top, so every filter strip in
   the app sat flush against the sentence explaining the page — measured at zero
   pixels on every route, at both widths. These are adjacent-sibling rules on
   purpose: they lift the strips that follow the page header and leave the ones
   inside panels, which answer to their panel's own spacing, alone. */
.because + .tabs, .answer + .tabs, .foot + .tabs, .because + .filterbar,
.because + .switcher, .switcher + .foot,
/* A switcher above the week tabs: its own bottom margin is 4px, meant for the
   panel that usually follows, and against a row of tab buttons that reads as one
   crowded control instead of two. */
.switcher + .tabs { margin-top:16px; }
.tab { border:1px solid var(--line); background:transparent; color:var(--ink-3);
       /* Some tabs are ANCHORS — the person's pair, the night's pair, the team
          picker on "which raids count". A button has no underline to clear, so
          this was missed twice and patched per-container; here it covers all. */
       text-decoration:none;
       padding:6px 12px; border-radius:6px; cursor:pointer; font:500 13px var(--font);
       display:inline-flex; align-items:center; gap:6px; }
/* ── pills on a phone ──────────────────────────────────────────────────────
   Two changes, one reason. 31px is under the ~44px a thumb actually covers, and
   a strip that wraps to three rows needs those rows to look like rows. Every
   pill family gets the same treatment so a page does not present three
   different sizes of the same control. */
@media (max-width: 900px) {
  .tabs { gap:10px 7px; margin-bottom:16px; }
  .because + .tabs, .answer + .tabs, .foot + .tabs, .because + .filterbar,
  .because + .switcher, .switcher + .foot { margin-top:20px; }
  .tab { padding:9px 14px; font-size:13px; }
  .lens .tab { padding:7px 12px; font-size:12.5px; }
  .tabs.pulls .tab { padding:7px 12px; font-size:12.5px; }
}
.tab:hover { color:var(--ink); border-color:var(--n600); }
.tab[aria-pressed="true"] { color:var(--n950); background:var(--a300); border-color:var(--a300);
                            font-weight:600; }
.tab[disabled] { opacity:.35; cursor:default; }
.tab b { font-weight:600; }
/* ── which workspace you are reading ──────────────────────────────────────────
   Sits next to the wordmark because it qualifies it: this is Ascendry, showing
   THIS account. Quiet by default — it is a label most of the time and a control
   only when there is somewhere to switch to. */
.wspick { position:relative; display:inline-flex; }
.wsbtn {
  display:inline-flex; align-items:center; gap:7px; cursor:pointer;
  font:inherit; font-size:12.5px; font-weight:600; color:var(--ink-2);
  padding:5px 10px; border-radius:999px;
  border:1px solid var(--line); background:var(--raised);
}
.wsbtn:hover:not(:disabled) { border-color:var(--accent); color:var(--ink); }
/* One workspace and nothing to switch to: still says which, without pretending
   to be pressable. */
.wsbtn:disabled { cursor:default; opacity:.9; }
.wsdot { width:7px; height:7px; border-radius:999px; background:var(--brand-emerald); flex:none; }
.wsname { max-width:22ch; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.wsyou { color:var(--ink-3); font-weight:500; }
.wscaret { color:var(--ink-3); font-size:10px; }

.wsmenu {
  /* Anchored to the pill's LEFT edge, which is where it sits on both layouts —
     the control is left-aligned in its row on a phone and mid-header on desktop,
     so there is always room to its right and never a reason to flip it. */
  position:absolute; top:calc(100% + 6px); left:0; z-index:70; min-width:236px;
  padding:6px; border:1px solid var(--line); border-radius:10px;
  background:var(--surface); box-shadow:0 18px 46px rgb(0 0 0/.5);
}
.wshead { margin:6px 8px 4px; font-size:11px; text-transform:uppercase;
          letter-spacing:.06em; color:var(--ink-3); }
.wsrow {
  display:block; width:100%; text-align:left; cursor:pointer; font:inherit;
  font-size:13px; color:var(--ink); padding:7px 9px; border-radius:7px;
  border:1px solid transparent; background:transparent;
}
.wsrow:hover:not(:disabled) { background:var(--raised); border-color:var(--line); }
.wsrow.on { color:var(--ink-2); cursor:default; }
.wsfoot { margin:8px 8px 4px; font-size:11.5px; color:var(--ink-3); line-height:1.45; }

@media (max-width: 900px) {
  /* Behind the burger the header is tight; the name still fits and is still the
     thing most worth keeping. */
  .wsname { max-width:12ch; }
}

.lens { display:inline-flex; align-items:center; gap:6px; flex-wrap:wrap; }
.lens .tab { padding:4px 10px; font-size:12.5px; }
/* It sits directly above the list it filters, and with no margin the first card
   started immediately under it — a control and its results touching, reading as
   one object rather than as a thing that acts on another. */
.search { margin-bottom:14px; width:100%; max-width:320px; background:var(--n950); color:var(--ink);
          border:1px solid var(--line); border-radius:6px; padding:9px 11px; font:14px var(--font); }

.down { display:inline-flex; align-items:center; gap:7px; margin-top:12px; text-decoration:none;
        background:var(--a300); color:var(--n950); border:0; border-radius:6px; padding:8px 14px;
        font:600 13px var(--font); cursor:pointer; }
.down:hover { background:var(--a200); }
.down::after { content:"→"; }
.more { margin-top:20px; display:flex; gap:18px; flex-wrap:wrap; }
/* A link inside a sentence. It carries the accent rather than a rule under it —
   in body text there is nothing else to mark it out, and colour does the job an
   underline was doing without drawing a line through the paragraph. */
.link { color:var(--a400); text-decoration:none; cursor:pointer; }
.link:hover { color:var(--a200); }

details { border-top:1px solid var(--line); }
details:first-of-type { border-top:0; }
summary { list-style:none; cursor:pointer; padding:11px 0; display:flex; align-items:center; gap:10px; }
summary::-webkit-details-marker { display:none; }
summary::after { content:"›"; margin-left:auto; color:var(--ink-3); font-size:17px;
                 transition:transform .15s ease; }
details[open] summary::after { transform:rotate(90deg); }
.detail { padding:0 0 13px; color:var(--ink-2); font-size:14px; max-width:78ch; }
.detail .why { color:var(--ink-3); font-size:12.5px; margin-top:6px; }
@media (prefers-reduced-motion:reduce) { summary::after { transition:none; } }

/* ── bars ─────────────────────────────────────────────────────────────────── */
.bars { display:flex; flex-direction:column; gap:4px; }
.brow { display:grid; grid-template-columns:180px 1fr 86px; align-items:center; gap:12px;
        padding:4px 8px; }
/* Wraps rather than ellipses. The label column is 180px and some labels are
   240px; an ellipsis there loses the end of the very thing the bar is measuring,
   and a bar chart reads perfectly well with a two-line label. Rows only go
   uneven where a label is actually long, which is rare. */
.brow .lbl { font-size:14px; color:var(--ink-2); min-width:0;
             overflow-wrap:anywhere; }
.btrack { height:10px; background:var(--n800); border-radius:5px; overflow:hidden; position:relative; }
.bfill { display:block; height:100%; border-radius:5px; background:var(--brand-emerald);
         min-width:3px; max-width:100%; }
.bfill.best { background:var(--good); }
.bestmark { position:absolute; top:-2px; bottom:-2px; width:2px; background:var(--ink-2); }
.brow .val { text-align:right; font-size:14px; font-variant-numeric:tabular-nums; color:var(--ink); }
/* The value is a rate with its raw count trailing it — "96%" then "22/23" — and
   with nothing between them they printed as 96%22/23. The ichart sets its own
   wider spacing further down and keeps it. */
.brow .val .scoped { margin-left:7px; }
.brow.you { background:rgb(219 180 90/.1); border-radius:6px; }
.board .brow { grid-template-columns:24px 150px 1fr auto auto; }
.board .rank { text-align:right; font-size:12.5px; color:var(--ink-3); font-variant-numeric:tabular-nums; }
.brow.you .rank { color:var(--a300); }
.board .badges { display:flex; flex-wrap:wrap; justify-content:flex-end; max-width:300px; }
.board .pts { font-size:15px; font-weight:600; color:var(--a300); text-align:right; min-width:52px;
              font-variant-numeric:tabular-nums; }
.board .pts .unit { font-size:11px; font-weight:400; color:var(--ink-3); margin-left:4px; }

/* ── progression, calendar, loot ──────────────────────────────────────────── */
/* Columns, not a grid. These cards are wildly uneven — Karazhan has eleven
   bosses and Magtheridon has one — and in a grid every row is as tall as its
   tallest card, so Gruul's Lair sat next to Karazhan with three hundred pixels
   of nothing under it. Columns pack them by height instead, which leaves no
   holes at all. Reading runs down a column rather than across, which suits a
   list ordered by tier.

   `columns:300px` rather than a count: the browser fits as many as the width
   allows, so this is responsive without a single media query. */
.zones { columns:300px; column-gap:12px; }
.zone { break-inside:avoid; margin-bottom:12px; }
.zone { border:1px solid var(--line); border-radius:var(--radius); padding:12px 14px;
        background:rgb(11 24 30/.5); }
.zone h3 { font-size:13.5px; }
.zone .cnt { float:right; font-size:12.5px; color:var(--ink-3); font-weight:400; }
.zonebar { height:6px; background:var(--n800); border-radius:3px; overflow:hidden; margin:9px 0 6px; }
.zonebar i { display:block; height:100%; background:var(--brand-emerald); }
.bosslist { display:flex; flex-direction:column; }
.boss { display:flex; align-items:center; gap:9px; padding:5px 0; font-size:13.5px;
        border-top:1px solid var(--line-soft); }
.boss:first-child { border-top:0; }
/* ── the night's moments, one row per raider ───────────────────────────────
   Folded by default: a good night can be forty plays, and forty lines of prose
   is a wall nobody reads. The tally on the closed row is the summary — what kind
   of play, how many — so the list scans without opening anything. */
/* The bench, kept visually apart from the composition above it. Same panel —
   they are the same question — but a rule between them, because "who you can
   plan around" and "who might also show up" must not read as one list. */
.benchrow { margin-top:12px; padding-top:12px; border-top:1px solid var(--line); }
.momlist { display:flex; flex-direction:column; }
.mom { border-top:1px solid var(--line-soft); }
.mom:first-child { border-top:0; }
.momhead { display:flex; align-items:center; gap:9px; width:100%;
           padding:6px 0; background:none; border:0; cursor:pointer;
           font:inherit; color:inherit; text-align:left; }
.momhead:hover .momwho { color:var(--ink); }
.momcaret { flex:none; color:var(--ink-3); transition:transform .15s ease; }
.momhead[aria-expanded="true"] .momcaret { transform:rotate(90deg); }
.momwho { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis;
          white-space:nowrap; font-weight:600; }
.momtally { flex:none; display:flex; align-items:center; gap:9px;
            font-size:12px; color:var(--ink-3); font-variant-numeric:tabular-nums; }
.momkind { display:inline-flex; align-items:center; gap:4px; }
.momkind i { font-style:normal; }
.momtotal { border-radius:999px; background:var(--raised); color:var(--ink-2);
            padding:1px 8px; }
.momplays { display:flex; flex-direction:column; padding:2px 0 6px; }
/* display:flex OUTRANKS the [hidden] attribute, so a folded list stays
   fully drawn without this — the panel opened with every play showing and
   the caret claiming otherwise. */
.momplays[hidden] { display:none; }
.momplay { display:flex; align-items:flex-start; gap:9px; padding:5px 0 5px 26px;
           text-decoration:none; color:inherit; border-radius:6px; }
.momplay:hover { background:var(--raised); }
.momicon { flex:none; font-style:normal; line-height:1.2; }
.momtext { display:flex; flex-direction:column; gap:1px; min-width:0; }
.momtext b { font-weight:500; }
.momtext .scoped { font-size:12px; }
.boss .marks { display:inline-flex; gap:4px; flex:none; }
.tmark { width:9px; height:9px; border-radius:3px; flex:none; background:var(--tc,var(--ink-3));
         border:1px solid var(--swatch-edge); }
.tmark.off { background:transparent; border-color:var(--tc,var(--ink-3)); border-style:dashed; }
.boss.notpulled { color:var(--ink-3); }
.boss .n { margin-left:auto; font-size:12px; color:var(--ink-3); }
.bosslink { color:inherit; text-decoration:none; }
.bosslink:hover { color:var(--a300); }
.comp { display:flex; height:20px; border-radius:5px; overflow:hidden; margin-top:9px; }
.comp span { display:block; border-right:2px solid var(--surface); }
.comp span:last-child { border-right:0; }
.complegend { display:flex; gap:12px; flex-wrap:wrap; margin-top:8px; font-size:12.5px; color:var(--ink-3); }
.complegend i { display:inline-block; width:9px; height:9px; border-radius:2px; margin-right:5px; }

.cal-wrap { display:flex; flex-direction:column; gap:26px; }
.cal-m h3 { font-size:15px; margin-bottom:9px; }
.cal-m h3 .cnt { float:right; font-size:12.5px; color:var(--ink-3); font-weight:400; }
.cal { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:4px; }
.cal-h { padding:5px 0; text-align:center; color:var(--ink-3); font-size:11.5px;
         font-weight:600; letter-spacing:.05em; text-transform:uppercase; }
.cal-d { min-height:76px; padding:6px 7px; background:rgb(11 24 30/.6);
         border:1px solid var(--line-soft); border-radius:6px;
         display:flex; flex-direction:column; gap:4px; }
.cal-d.out { background:transparent; border-color:transparent; }
.cal-d.has { background:rgb(28 48 48/.5); border-color:var(--line); }
.cal-n { font-size:12.5px; color:var(--ink-3); font-variant-numeric:tabular-nums; }
.cal-d.has .cal-n { color:var(--ink); }
.cal-r { display:block; text-decoration:none; padding:3px 6px; border-radius:4px;
         border-left:3px solid var(--tc,var(--ink-3)); background:rgb(17 33 42/.9);
         color:var(--ink-2); font-size:12px; overflow:hidden; text-overflow:ellipsis;
         white-space:nowrap; }
.cal-r:hover { color:var(--ink); }
/* A seven-column month on a phone gives each day 38px. A raid's name cannot be
   read in that, and cutting it to "Serpentsh…" is worse than not printing it —
   so each raid becomes a bar in its team's colour, still tappable, still
   carrying the full name in its tooltip. The Nights list is where names are
   read; this view answers "which evenings did we raid". */
@media (max-width: 700px) {
  .cal-d { min-height:58px; padding:5px 4px; gap:3px; }
  .cal-r { font-size:0; padding:0; height:13px; border-left:0;
           background:var(--tc,var(--ink-3)); border-radius:3px; }
  .cal-r.offcount { background:transparent; border:1px dashed var(--tc,var(--ink-3)); }
  .cal-h { font-size:10px; letter-spacing:0; }
}

.lootrow { display:grid; grid-template-columns:minmax(240px,1fr) 2fr; gap:22px; align-items:start;
           padding:11px 0; border-top:1px solid var(--line-soft); }
.lootrow:first-child { border-top:0; }
.lootrow .loot-i { display:flex; flex-direction:column; gap:3px; }
.lootrow .cands { display:flex; flex-direction:column; gap:5px; }
/* Centred, not baseline-aligned. A candidate's name is an inline-flex box with
   the spec glyph inside it, so it exposes the flex box's baseline rather than the
   name's — the rank number and the name ended up a few pixels apart, which reads
   as a wobble down the column. Centring lines the glyph, the name, the number and
   the gain on one axis regardless of their differing heights. */
.lootrow .cand { display:grid; grid-template-columns:20px minmax(150px,auto) 52px 1fr;
                 gap:10px; align-items:center; opacity:.7; }
.lootrow .cand.first { opacity:1; }
/* The name cell when the character is somebody's alt: the marker sits with the
   name rather than beside it, so the row keeps its four columns and the item
   the candidate is replacing stays where it belongs. */
.lootrow .whoalt { display:inline-flex; align-items:center; gap:6px; min-width:0; }
.lootrow .whoalt .who { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.altmark {
  flex:none; font-size:10.5px; font-weight:600; letter-spacing:.04em;
  text-transform:uppercase; color:var(--ink-3); cursor:help;
  padding:1px 5px; border-radius:4px; border:1px solid var(--line);
}
.lootrow .pos { text-align:right; font-size:12px; color:var(--ink-3); }
.lootrow .cand.first .pos { color:var(--a300); }
.lootrow .gain { font-size:13.5px; font-weight:600; color:var(--ink-3); font-variant-numeric:tabular-nums; }
.lootrow .cand.first .gain { color:var(--good); }
.item-link { text-decoration:none; cursor:help; font-weight:600; }
.item-link:hover { color:var(--ink); }
.item-link.now { font-weight:400; font-size:12.5px; overflow:hidden; text-overflow:ellipsis;
                 white-space:nowrap; }
.q-poor{color:var(--q-poor);} .q-common{color:var(--q-common);} .q-uncommon{color:var(--q-uncommon);}
.q-rare{color:var(--q-rare);} .q-epic{color:var(--q-epic);} .q-legendary{color:var(--q-legendary);}

/* ── charts ───────────────────────────────────────────────────────────────── */
.plot { width:100%; height:auto; display:block; overflow:visible; }
/* `display:block` above beats the hidden ATTRIBUTE, which left an empty
   chart holding its full height open under the whole-raid view. */
.plot[hidden] { display:none; }
.plot .grid { stroke:var(--line); stroke-width:1; }
.plot .axis { stroke:var(--n700); stroke-width:1; }
.plot .tickt { fill:var(--ink-3); font:11px var(--font); }
.plot .axlabel { fill:var(--ink-3); font:11.5px var(--font); }
.plot .line { fill:none; stroke:var(--brand-emerald); stroke-width:2; stroke-linejoin:round; }
.plot .band { fill:var(--brand-emerald); opacity:.1; }
.plot .dot { fill:var(--brand-emerald); stroke:var(--surface); stroke-width:2; }
/* The ring that marks your own character. It had no rule at all, so an SVG
   circle did what an unstyled SVG circle does — filled itself solid black, over
   the dot it was supposed to be drawing attention to. Fill none, and the stroke
   comes from the inline class colour so the ring matches the dot inside it. */
.plot .youring { fill:none; stroke-width:2; }
.plot .cross { stroke:var(--ink-3); stroke-width:1; stroke-dasharray:2 3; opacity:0; }
.plot .phase { stroke:var(--a400); stroke-width:1; stroke-dasharray:2 4; opacity:.5; }
.plot .ev { stroke:var(--surface); stroke-width:1.5; cursor:help; }
.tick { display:flex; gap:10px; align-items:baseline; padding:7px 0; border-top:1px solid var(--line-soft); }
.tick:first-child { border-top:0; }
.tick .t { width:44px; flex:none; color:var(--ink-3); font-size:12.5px; font-variant-numeric:tabular-nums; }

/* ── tooltip ──────────────────────────────────────────────────────────────── */
#tip { position:fixed; z-index:9998; pointer-events:none; opacity:0; max-width:340px;
       background:var(--n900); border:1px solid var(--n700); border-radius:7px;
       padding:10px 12px; font-size:13px; line-height:1.5; color:var(--ink-2);
       box-shadow:0 12px 32px -12px rgb(0 0 0/.8); }
#tip[data-on="true"] { opacity:1; }
#tip .t { display:block; font-size:14px; font-weight:600; color:var(--ink); margin-bottom:4px; }
#tip .tip-meta { color:var(--ink-3); font-size:12px; margin-bottom:5px; }
.wowhead-tooltip, #wowhead-tooltip, div[class*="wowhead"] { z-index:9999 !important; }

.state { padding:36px 0 14px; color:var(--ink-3); }

/* ── waiting ─────────────────────────────────────────────────────────────────
   A sketch of the page that is coming, rather than a line of text on an empty
   screen. The shapes are the app own furniture — a panel, a head, some rows —
   because a wait that looks like the destination reads as loading, and a wait
   that looks like nothing reads as broken. Preparation spends nine seconds
   here, which is long enough for the difference to matter. */
/* ── the coach, over everything ──────────────────────────────────────────────
   A launcher pinned to the bottom right, and a panel that grows out of it. It
   lives outside .page on purpose: the questions belong to the screen underneath
   and change with it, but an answer being READ must survive that screen
   repainting under the reader. */
.coachdock { position:fixed; right:18px; bottom:18px; z-index:60;
             display:flex; flex-direction:column; align-items:flex-end; gap:10px; }
.coachbtn {
  display:inline-flex; align-items:center; gap:8px; cursor:pointer;
  font:inherit; font-size:13.5px; font-weight:600; color:var(--n950);
  /* Tighter on the left than the right: the face is a picture with its own
     margin built in, and matching the two visually leaves it looking adrift. */
  padding:7px 16px 7px 10px; border-radius:999px; border:1px solid var(--accent);
  /* --accent, NOT --brand-gold. They are two different yellows (#e6c87f against
     #dbb45a) and the app already spends one of them on "this is the thing you
     have selected" — the nav's current page, an active sort column, the open
     drawer item. The launcher is a persistent control in the corner of every
     screen, so on the deeper gold it read as a second, competing accent rather
     than as part of the same set. */
  background:var(--accent);
  box-shadow:0 6px 22px rgb(0 0 0/.45);
  transition:transform .12s, box-shadow .12s;
}
.coachbtn:hover { transform:translateY(-1px); box-shadow:0 8px 26px rgb(0 0 0/.55); }

/* Ordered ABOVE the launcher in the flex column so the panel opens upward, and
   the button never moves out from under the finger that just pressed it. */
.coachpanel {
  order:-1; width:min(420px, calc(100vw - 28px));
  max-height:min(70vh, 620px);
  /* A column, so header / questions / transcript can be REORDERED per shape
     rather than duplicated in the markup. Only .coachbody scrolls: pinning by
     layout beats position:sticky once the panel is a sheet. */
  display:flex; flex-direction:column; overflow:hidden;
  border:1px solid var(--line); border-radius:12px;
  background:var(--surface); box-shadow:0 18px 46px rgb(0 0 0/.5);
}
.coachhead {
  flex:none; display:flex; align-items:center; gap:10px;
  padding:12px 14px; border-bottom:1px solid var(--line);
  background:var(--surface); font-size:14px;
}
.coachhead b { flex:1 1 auto; min-width:0; }
/* His name, and under it what he is being asked about. Two lines because they
   are two different things — WHO you are talking to does not change as you move
   around the app, and what he would answer about does. */
.coachwho { display:flex; flex-direction:column; line-height:1.25; }
.coachwho span { font-size:12px; font-weight:400; color:var(--ink-3);
                 overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
/* The questions pin under the header. Sticky rather than merely first: an
   answer runs to a few hundred words, and the control you want next should not
   scroll away while you read the one you asked for. */
.coachasks {
  flex:none; padding:12px 14px; background:var(--surface);
  border-bottom:1px solid var(--line);
}
/* minmax(0,1fr), not 1fr: a bare `1fr` still floors the track at min-content, so
   one long hint would push the cards off the side of a phone. */
.coachasks .asks { grid-template-columns:minmax(0, 1fr); gap:6px; }
.coachasks .ask { padding:8px 11px; }
.coachasks .ask-q { font-size:13px; }
.coachasks .ask-h { font-size:12px; }
/* What is left of the week. Sits with the questions rather than the answers,
   because it is a fact about the BUTTONS — and it is deliberately quiet: at
   full allowance nobody needs to think about it. */
.coachleft { margin:9px 0 0; font-size:12px; color:var(--ink-3); line-height:1.5; }
.coachleft.out { color:var(--accent); }
/* The subscription in the sentence. It is an advert inside a tool, so it is a
   link in the running text rather than a button — the reader is mid-thought
   about a question they wanted answered, and a call to action shaped like one
   would be the app interrupting itself. */
.coachleft .link { color:var(--accent); font-weight:600; }
.coachleft .link:hover { text-decoration:underline; }

/* The one scrolling part, so the questions and the header cannot leave.
   `overscroll-behavior:contain` stops the SCROLL CHAIN: without it, reaching the
   end of an answer hands the gesture to the page underneath, so the sheet stops
   moving and the app scrolls away behind it. */
.coachbody { flex:1 1 auto; min-height:0; overflow:auto; overscroll-behavior:contain;
             padding:12px 14px 14px; }
/* A transcript with nothing in it takes no room. Only a VISITOR reaches this —
   everyone else gets the first-answer note — and for them the panel is an
   introduction, so an empty scroll region under it is a hole. It matters most on
   a phone, where the sheet gives the transcript min-height:50% above a
   max-height:34% ask region: without this the introduction is squeezed into a
   third of the screen under half a screen of nothing. */
.coachbody:empty { display:none; }

/* The coach himself — an illustration, /coach-wizard.png, sized by the markup.
   It replaced a drawn glyph that took its colours from the theme; the trade is
   deliberate and it is not free. He no longer re-tints per surface, and he does
   not survive being shrunk: under about 30px the art becomes a dark blob, which
   is why nothing here asks for one smaller. */
.coachface { flex:none; display:block; }

/* WHAT ORRIN IS, for somebody who has not signed in. Sits exactly where the
   question buttons sit for everyone else, so the panel does not change shape
   when they do sign in — the same box, filled with an introduction instead of
   controls. */
.coachintro { display:flex; flex-direction:column; gap:10px; }
.coachintro p { margin:0; font-size:13.5px; line-height:1.55; color:var(--ink-2); }
.coachintro p b { color:var(--ink); font-weight:600; }
.coachintro .btn { align-self:flex-start; margin-top:2px; }

/* THE BYLINE above a report he wrote. Deliberately quiet: it is attribution, so
   it sits at the weight of a panel note rather than competing with the writing
   under it. The face is round here and square in the launcher — a byline reads
   as a person, a button reads as a control. */
.byline {
  display:flex; align-items:center; gap:8px;
  margin:0 0 14px; padding-bottom:11px;
  border-bottom:1px solid var(--line-soft);
}
.byline .coachface { border-radius:50%; background:var(--raised); }
.byline b { font-size:13.5px; font-weight:600; color:var(--ink-2); }
.byline span {
  font-size:12.5px; color:var(--ink-3);
}
/* A dot between the name and the qualifier, so they read as one line rather
   than two labels that happen to be adjacent. */
.byline span::before { content:"·"; margin-right:7px; color:var(--n600); }

/* First open, nothing asked yet. The panel is otherwise a header, four buttons
   and a paragraph of apology about how long the first answer takes — which is
   the moment the illustration is worth its 13KB, because it is the only place
   in the app with room to actually look at him. */
/* Sized by its content here. The centring lives in the phone rules only: this
   panel is 620px at most and the body is whatever is left of it, so a hero
   claiming min-height:100% overflowed the box it was centring in and cut the
   coach off at the chin. */
.coachhero { text-align:center; padding:14px 6px 6px; }
.coachhero .coachface { margin:0 auto 12px; }
.coachhero .foot { margin:0; }
/* The panel is 70vh, so on a short window the body is small — and the empty
   state is the one thing in here whose height is fixed by an image. At 700px
   tall it did not fit, and what you saw on opening the coach was the wizard cut
   off at the chin with his explanation scrolled out of sight. A HEIGHT query,
   because height is what is actually short: the width is fine. */
@media (max-height: 780px) {
  .coachhero { padding:6px 6px 2px; }
  .coachhero .coachface { width:54px; height:54px; margin-bottom:8px; }
  .coachhero .foot { font-size:12px; line-height:1.5; }
}

/* One question and its answer. The question reads as something the person
   said, so it is set apart rather than styled as a heading. */
.cturn + .cturn { margin-top:14px; padding-top:14px; border-top:1px solid var(--line); }
.casked {
  margin:0 0 8px; font-size:13px; font-weight:600; color:var(--ink);
  padding-left:9px; border-left:2px solid var(--accent);
}
/* THE ANSWER, and the shape it is asked for is the shape this is spaced for: a
   verdict line, two to four bullets, one closing step (see coachSystem). The
   generic .md rhythm is written for a weekly report read on a page — inside a
   420px panel it runs the paragraphs together and greys the list, which is
   backwards here, because in a coach answer the bullets ARE the substance and
   the prose is the frame. */
.cturn .md { font-size:13.5px; max-width:none; }
.cturn .md > :first-child { margin-top:0; }
.cturn .md > :last-child { margin-bottom:0; }
.cturn .md p { margin:0 0 9px; line-height:1.6; }
/* The verdict. It is the one line somebody can stop reading after, so it is
   sized as the answer rather than as the first of four paragraphs. */
.cturn .md > p:first-child { font-size:14.5px; line-height:1.5; color:var(--ink); }
.cturn .md .mdlist { margin:0 0 10px; padding-left:16px; color:var(--ink); }
.cturn .md .mdlist li { margin:6px 0; line-height:1.55; }
/* The marker is punctuation, not content — the eye should land on the bolded
   subject that starts each bullet, not on the dot in front of it. */
.cturn .md .mdlist li::marker { color:var(--ink-3); }
.cturn .md strong { color:var(--ink); font-weight:600; }
.cturn .md code { font:12.5px var(--mono); color:var(--ink);
                  background:var(--n950); padding:1px 5px; border-radius:4px; }
.cturn .state { padding:10px 0 6px; }
.coachbody .asks { margin-top:12px; grid-template-columns:minmax(0, 1fr); }

/* On a phone the panel is the screen, and the launcher gets out of the way of
   the thumb rather than sitting under it. */
/* A phone gets a sheet: full width, off the bottom edge, and tall enough to
   read an answer in. The questions move to the BOTTOM — a conversation's
   composer belongs where the thumb is, and that is the shape every messaging
   app on this device has already taught the reader.

   THE RULE THAT MATTERS: the conversation gets the room, and the questions get
   what is left. The first version had it the other way round and it collapsed.
   It sized the sheet only when `:has(.cturn)` matched, so the panel was
   content-height; the header and the four question cards are both flex:none, so
   the ONLY child that could give way was .coachbody — and a flex item with
   min-height:0 gives way all the way. The answer you had just waited thirty
   seconds for was rendered into a few pixels of scrollable strip.

   So nothing here is conditional on :has() any more. The sheet has a definite
   height whether or not anything has been asked (the empty state fills it with
   the coach's face, which is the reason that used to be a problem and no longer
   is), the body claims a floor of the sheet it cannot be squeezed below, and
   the questions are the part that scrolls when there is not room for all four. */
@media (max-width: 560px) {
  /* The dock still spans the width — that is what lets the SHEET be a sheet. */
  .coachdock { right:0; bottom:0; left:0; align-items:stretch; gap:0; }
  /* The launcher does not. `align-items:stretch` was making it a full-width bar
     across the bottom of every screen: it is one control, sitting over the app
     it is optional to, and it was taking a strip the width of the phone to say
     so. A round button in the corner is what a thing like this is, and the face
     is the label — it is why the illustration is there. */
  .coachbtn {
    align-self:flex-end; margin:0 14px 14px;
    width:56px; height:56px; padding:0; border-radius:999px;
    justify-content:center;
  }
  .coachbtn .coachlabel { display:none; }
  /* Bigger inside the round button than it is in the header: at 56px there is
     room, and the face is now carrying the whole meaning on its own. */
  .coachbtn .coachface { width:42px; height:42px; }
  /* Set by paintCoach rather than derived with :has(), so the launcher cannot
     end up stranded behind the sheet on a browser that does not support it. */
  .coachdock.open .coachbtn { display:none; }
  .coachpanel {
    width:auto; border-radius:14px 14px 0 0; border-bottom:0;
    /* vh first: on a browser without dvh the whole min() would be invalid and
       the height would silently not apply, which is the collapse again. */
    height:min(88vh, 100vh - 36px);
    height:min(88dvh, 100dvh - 36px);
    max-height:none;
  }
  /* The floor. Whatever the questions want, the conversation keeps half the
     sheet — this single line is what makes the answer readable. */
  .coachbody { order:1; flex:1 1 auto; min-height:50%; }
  .coachasks {
    order:2; flex:0 1 auto; max-height:34%; overflow-y:auto;
    overscroll-behavior:contain;
    border-bottom:0; border-top:1px solid var(--line);
    padding-bottom:max(12px, env(safe-area-inset-bottom));
  }
  /* Compact questions, because four of them at desktop size is most of a phone.
     The hint stays — it is what tells you what you are about to spend an answer
     on — but on one line. */
  .coachasks .ask { padding:7px 10px; gap:1px; }
  .coachasks .ask-q { font-size:13px; }
  /* One line, ellipsised. The min-width:0 is what makes that TRUE rather than
     merely requested: a flex child will not shrink below its content without it,
     so the text ran on and took the card with it. */
  .coachasks .ask-h { font-size:11.5px; min-width:0; overflow:hidden;
                      text-overflow:ellipsis; white-space:nowrap; }
  .coachasks .ask-q { min-width:0; overflow-wrap:anywhere; }
  .coachasks .asks { gap:5px; }
  .coachleft { margin-top:7px; }
  /* The header is a fixed cost on a small screen; trim it rather than the
     answer underneath it. */
  .coachhead { padding:10px 12px; }
  /* The sheet is tall whether or not anything has been asked, so the empty
     state is centred in it rather than stranded at the top of a screen of
     nothing. Safe here and only here: this panel has a definite height. */
  .coachhero { padding:6px 4px 2px; min-height:100%;
               display:flex; flex-direction:column; justify-content:center; }
  .coachhero .coachface { width:84px; height:84px; margin-bottom:10px; }
}

/* ── an open sheet owns the screen ────────────────────────────────────────────
   A phone sheet covers the app but does not, by itself, stop it moving. Only
   .coachbody scrolls; a drag anywhere else in the sheet — the header, and on a
   phone the QUESTIONS, which sit at the bottom exactly where the thumb lands —
   went straight through to the page behind. The sheet stayed put while the app
   scrolled away underneath it, which reads as the coach being unusable rather
   than as a scroll landing in the wrong place.

   Scoped to the widths where these actually cover the screen. On a desktop the
   panel is a 420px box in the corner and the page behind it should still
   scroll. */
@media (max-width: 560px) {
  html.coach-open, html.coach-open body { overflow:hidden; overscroll-behavior:none; }
}
/* The drawer had the same hole and the same fix: `menu-open` has been toggled on
   <html> since it was written and nothing has ever styled it. */
@media (max-width: 900px) {
  html.menu-open, html.menu-open body { overflow:hidden; overscroll-behavior:none; }
}

/* ── the coach ───────────────────────────────────────────────────────────────
   The questions are the interface. They are buttons rather than a text box
   because the model behind them answers a KNOWN question over a known payload
   — so each one is a card that says what it will tell you, not a prompt to
   compose. */
.asks { display:grid; gap:8px; grid-template-columns:repeat(auto-fit, minmax(240px, 1fr)); }
.ask {
  display:flex; flex-direction:column; gap:3px; text-align:left; cursor:pointer;
  font:inherit; color:inherit; padding:11px 13px; border-radius:9px;
  border:1px solid var(--line); background:var(--card, var(--raised));
  transition:border-color .12s, background .12s;
  /* A grid item's automatic minimum is its MIN-CONTENT, so without this the card
     is never allowed to be narrower than its longest unbreakable line and the
     whole column grows past the panel. */
  min-width:0;
}
.ask:hover { border-color:var(--accent); background:var(--raised); }
.ask:disabled { opacity:.5; cursor:default; }
.ask-q { font-size:14px; font-weight:600; }
.ask-h { font-size:12.5px; color:var(--ink-3); }

.panel.skel { opacity:.55; }
.panel.skel .panel-h { padding-top:15px; padding-bottom:13px; }
.skel-line {
  display:block; height:11px; margin:9px 0; border-radius:5px;
  background:linear-gradient(90deg, var(--n800) 0%, var(--n700) 40%, var(--n800) 80%);
  background-size:280% 100%; animation:skel 1.5s ease-in-out infinite;
}
.panel-h .skel-line { margin:0; height:13px; }
@keyframes skel { 0% { background-position:120% 0; } 100% { background-position:-40% 0; } }
/* Motion is decoration here; the shape carries the meaning without it. */
@media (prefers-reduced-motion: reduce) {
  .skel-line { animation:none; background:var(--n800); }
}
.state.err { color:#fca5a5; }
.pick { display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:0 24px; }
.pick .row { display:flex; align-items:center; gap:10px; padding:6px 0;
             border-top:1px solid var(--line-soft); }
.pick .row .meta { flex:1 1 auto; color:var(--ink-3); font-size:12px; }
.pick .row .n { flex:none; font-size:13px; color:var(--ink-2); font-variant-numeric:tabular-nums; }
.pick .row .n .unit { color:var(--ink-3); font-size:11px; margin-left:3px; }
.colhead { display:flex; align-items:center; gap:10px; padding-bottom:4px; color:var(--ink-3);
           font-size:11.5px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; }
.colhead .meta { flex:1 1 auto; }
.switcher { display:flex; align-items:center; gap:9px; flex-wrap:wrap; margin:18px 0 4px; }
.switcher .tab { text-decoration:none; }
.switcher .tab[aria-pressed="true"] { color:var(--n950); background:var(--tc,var(--a300));
                                      border-color:var(--tc,var(--a300)); }

/* ── the numbers behind an insight ───────────────────────────────────────────
   A breakdown is the point of the panel, so it opens by default and reads as a
   ranked bar list rather than a sentence about one. */
.ichart { margin-top:12px; }
.ichart .brow { grid-template-columns:minmax(150px,1fr) 2fr 120px; }
.ichart .lbl { color:var(--ink); }
.ichart .val .scoped { margin-left:8px; min-width:38px; display:inline-block; text-align:right; }
.evid { display:flex; flex-wrap:wrap; gap:7px 10px; margin-top:12px; }
.erow { display:inline-flex; align-items:baseline; gap:8px; padding:5px 10px;
        background:var(--raised); border:1px solid var(--line); border-radius:6px; font-size:13px; }
.erow b { color:var(--ink); font-weight:600; font-variant-numeric:tabular-nums; }
.sumtitle { flex:1 1 auto; }
details summary .scoped { margin-left:0; margin-right:2px; white-space:nowrap; }
/* The headline takes the slack so the row count and the chevron sit together at
   the right edge, in that order, whether or not a count is present. */
.sumtitle { flex:1 1 auto; min-width:0; }
details summary .scoped { margin-left:0; margin-right:4px; white-space:nowrap; }
summary::after { margin-left:0; }
/* A chip and the sentence beside it are two things. Whitespace after an
   inline-flex element collapses to almost nothing, so the gap has to be real. */
.standingline { display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
.standingline .chip { flex:none; }
/* Each peer wears the spec they play, so the reader can judge how close the
   comparison is instead of being told. A spec matching yours is lit. */
.peerspec { font-size:11.5px; color:var(--ink-3); margin-left:8px; white-space:nowrap; }
.peerspec.match { color:var(--a300); }
.board .brow .who { min-width:0; }

/* ── peer comparison ─────────────────────────────────────────────────────────
   Five columns, one cell each. The spec used to be a sixth child in a
   five-column grid, which pushed the bar into an auto column and collapsed it to
   a tic-tac. Name and spec are one cell; the track gets the room. */
.peers { display:flex; flex-direction:column; gap:2px; }
.peerhead, .peerrow {
  display:grid; grid-template-columns:30px minmax(190px,1fr) minmax(150px,2.4fr) 84px 56px;
  gap:14px; align-items:center;
}
.peerhead { padding:0 10px 7px; color:var(--ink-3); font-size:11.5px; font-weight:600;
            letter-spacing:.06em; text-transform:uppercase; }
.peerhead .r { text-align:right; }
.peerrow { padding:7px 10px; border-top:1px solid var(--line-soft); }
.peerrow:first-child { border-top:0; }
.peerrow .rank { text-align:right; color:var(--ink-3); font-size:12.5px;
                 font-variant-numeric:tabular-nums; }
/* Centred, not baseline-aligned: .peerspec is 11.5px and the name inherits the
   body size, so a shared baseline hangs the smaller label low against the name it
   belongs to. Their centres are what should line up. */
.peerrow .pname { display:flex; align-items:center; gap:9px; min-width:0; }
.peerrow .pname .who { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.peerrow .peerspec { margin-left:0; flex:none; }
.peerrow .btrack { height:12px; }
.peerrow .pval { text-align:right; font-size:14.5px; font-weight:600; color:var(--ink);
                 font-variant-numeric:tabular-nums; }
.peerrow .praids { text-align:right; font-size:12.5px; color:var(--ink-3);
                   font-variant-numeric:tabular-nums; }
.peerrow.you { background:rgb(219 180 90/.1); border-radius:6px; border-top-color:transparent; }
.peerrow.you .rank, .peerrow.you .pval { color:var(--a300); }
#settings { text-decoration:none; }
.tbl code { font:12.5px var(--mono); color:var(--ink); background:var(--n950);
            padding:2px 6px; border-radius:4px; }

/* A route the setting is actually edited at today. Monospace because it is a
   path, not prose — and muted, because the row's subject is the setting, not
   where it happens to live. */
.where {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .82em; color: var(--ink-3);
  background: var(--raised); border: 1px solid var(--line);
  border-radius: 5px; padding: .12em .42em; white-space: nowrap;
}

/* ── settings controls ──────────────────────────────────────────────────────
   The only writable surface in the app, so the controls have to read as
   controls — the rest of the page is deliberately flat, and an input that
   looks like a table cell invites people to type into a report. */

.btn {
  font: inherit; font-size: .92rem; line-height: 1;
  padding: .58em .95em; border-radius: 7px; cursor: pointer;
  color: var(--ink); background: var(--raised);
  border: 1px solid var(--n700);
}
.btn:hover:not(:disabled) { border-color: var(--n600); background: var(--n800); }
.btn:disabled { opacity: .5; cursor: default; }
/* The one call to action in a group. Same yellow as a selected tab and the
   current nav item — it was --brand-gold, half a shade darker, so a Save button
   and the pressed pill next to it were visibly two different colours saying the
   same thing. */
.btn.primary {
  background: var(--accent); border-color: var(--accent);
  color: var(--n950); font-weight: 600;
}
.btn.primary:hover:not(:disabled) { filter: brightness(1.08); background: var(--accent); }

/* DESTRUCTIVE. Delete permanently, Archive — the two places a click cannot be
   taken back. This class was on both of them and defined nowhere, so the most
   dangerous button in the app rendered as an ordinary one, indistinguishable
   from the Cancel beside it.

   Outlined rather than filled, and that is not timidity: a solid red block is
   louder than the primary action on the same screen, which teaches people that
   red means "the button you press". Red edge and red text says what it is; the
   fill arrives on hover, once the pointer is already committed. */
.btn.danger {
  color: var(--critical); border-color: color-mix(in srgb, var(--critical) 55%, transparent);
  background: transparent;
}
.btn.danger:hover:not(:disabled) {
  color: var(--n950); background: var(--critical); border-color: var(--critical);
}
.btn.sm { font-size: .84rem; padding: .45em .7em; }

/* SIGN IN WITH DISCORD — the one button that is not ours to style.
   It is the only door into the product, and it is somebody else's door: people
   scan for Discord's blurple and its mark, not for a gold button that happens to
   say the word. Wearing --accent made it the same colour as Save, Apply and the
   selected tab, which is the one thing this button is not — it is not "the
   primary action on this screen", it is "log in with the account you already
   have". Brand colours are fixed values on purpose: #5865F2 is Discord's, it does
   not shift with our theme, and it must not be tokenised. */
.btn.discord {
  display: inline-flex; align-items: center; gap: .5em;
  background: #5865F2; border-color: #5865F2; color: #fff; font-weight: 600;
}
.btn.discord:hover:not(:disabled) { background: #4752c4; border-color: #4752c4; }
/* Sized off the button's own type so it tracks .sm and .lg without three rules. */
.btn.discord svg { width: 1.15em; height: 1.15em; fill: currentColor; flex: none; }

/* THE DEMO RAID on the landing — a panel that is also a link, and the only one
   on the page. It wears the accent because it is the one thing here that proves
   the claim rather than making it: a whole analysed night, no account, no
   upload. Everything around it is description. */
.panel.demo {
  display:block; text-decoration:none; cursor:pointer;
  border-color:rgb(230 200 127/.4); background:rgb(230 200 127/.06);
  transition:border-color .15s, background .15s, transform .15s;
}
.panel.demo:hover { border-color:rgb(230 200 127/.65); background:rgb(230 200 127/.1); }
.panel.demo:focus-visible { outline:2px solid var(--ring); outline-offset:3px; }
.panel.demo h2 { font-size:17px; color:var(--accent); margin:0 0 7px; }
.panel.demo p { margin:0; color:var(--ink-2); max-width:68ch; }
/* The arrow is decoration, so it is drawn rather than typed — a literal → in the
   markup would be read out by a screen reader as part of the sentence. */
.panel.demo h2::after { content:"\00a0\2192"; opacity:.65; }
.panel.demo:hover h2::after { opacity:1; }
.panel.demo .foot { margin-top:9px; color:var(--ink-3); font-size:13px; }
@media (prefers-reduced-motion: no-preference) {
  .panel.demo:hover { transform:translateY(-1px); }
}
/* A LINK wearing .btn is still a link: without this it keeps the browser's
   underline and its inline baseline, so it sat lower than the buttons beside it
   and read as text with a box round it. Anchors have carried .btn since the
   Discord card linked out; this is the rule that was missing. */
a.btn { display:inline-flex; align-items:center; justify-content:center;
        text-decoration:none; }
.btn:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

.actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 14px 0 4px; }
.actions.tight { margin: 0; gap: 6px; justify-content: flex-end; }
/* A note sitting BESIDE a button, not under one. .foot exists for a paragraph
   below something and carries a top margin to prove it; inside a flex row that
   margin is still honoured, so the text rides low against the button it labels
   and the row reads misaligned. */
.actions > .foot { margin: 0; }

/* ── the boss plan ─────────────────────────────────────────────────────────
   WHERE a duty happens, as one phrase rather than two columns: "skull, melee"
   reads the way it is called out. Either half can be absent and most are. */
/* dutywhere, not `where`: that name was already taken by the monospace path chip
   above (line ~1057), which carries a background, a border and its own font — so
   a marker and a zone in a table cell came out boxed and monospaced. The second
   generic name I collided with in this file; both were mine. */
.dutywhere { display:inline-flex; align-items:center; gap:6px; }
.dutywhere .wmark { font-size:15px; line-height:1; }
.dutywhere .wzone { font-size:13px; color:var(--ink-2); }

/* Who the log says already does this. Quiet by default — it is an offer, not a
   verdict, and the officer's own typing must stay the loudest thing in the row. */
.psugs { display:flex; align-items:center; flex-wrap:wrap; gap:6px; margin:2px 0 10px; }
.psugs .foot { margin:0; }
.psug {
  font:inherit; font-size:12.5px; cursor:pointer;
  padding:3px 9px; border-radius:999px;
  background:var(--raised); color:var(--ink-2); border:1px solid var(--line);
}
.psug b { color:var(--accent); font-weight:600; margin-left:2px; }
.psug:hover { border-color:var(--accent); color:var(--ink); }
.psug:focus-visible { outline:2px solid var(--ring); outline-offset:2px; }
.actions.addteam { padding-top: 14px; border-top: 1px solid var(--line-soft); }

/* Save feedback. `busy` is not decoration — saving thresholds refolds every
   raid, and without a pending state the button reads as dead. */
.saymsg { font-size: .88rem; color: var(--ink-3); }
.saymsg.ok   { color: var(--good); }
.saymsg.bad  { color: var(--critical); }
.saymsg.busy { color: var(--ink-2); }

.tin {
  font: inherit; font-size: .92rem; color: var(--ink);
  background: var(--ground); border: 1px solid var(--n700);
  border-radius: 6px; padding: .45em .6em; min-width: 0;
}
.tin:focus { outline: none; border-color: var(--brand-emerald); }
.tin[type="color"] {
  width: 34px; height: 32px; padding: 2px; cursor: pointer; flex: 0 0 auto;
}
/* EVERY dropdown, including the ones that carry .tin — a select and a text
   box are the same kind of object to the reader, and the extra right padding
   is the room the arrow needs. */
select {
  font: inherit; font-size: .92rem; color: var(--ink);
  background-color: var(--ground); border: 1px solid var(--n700);
  border-radius: 6px; padding: .45em 1.9em .45em .6em;
  max-width: 100%; cursor: pointer;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237e938f' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .6em center;
}
select:hover { border-color: var(--n600); }
select:focus { outline: none; border-color: var(--brand-emerald); }
select:disabled { opacity: .55; cursor: default; }
/* The open list is drawn by the platform, which does not inherit any of the
   above — without this it falls back to white-on-white on some systems. */
select option { background: var(--surface); color: var(--ink); }
/* A select that also carries .tin: that class sets the background SHORTHAND,
   which resets the arrow to none — and a menu with appearance:none and no
   arrow is a box nobody can tell is a menu. Specificity, not order, is what
   settles it. */
select.tin {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%237e938f' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right .6em center;
  padding-right: 1.9em;
}

/* A number and its unit read as one value, so they share one bordered box —
   otherwise "75" and "%" sit in separate visual objects and the eye pairs the
   % with the next column instead. */
.numin {
  display: inline-flex; align-items: center; gap: .3em;
  background: var(--ground); border: 1px solid var(--n700); border-radius: 6px;
  padding: .3em .5em;
}
.numin:focus-within { border-color: var(--brand-emerald); }
.numin input {
  font: inherit; font-size: .92rem; color: var(--ink);
  background: none; border: 0; padding: 0; width: 3.2em; text-align: right;
  font-variant-numeric: tabular-nums;
  /* No native spinner. The markup says why it is here — the &minus; and + are
     the primary way to move a threshold, and the browser's own arrows are a
     12px target that renders differently everywhere. Two sets of steppers in
     one box, one of them unusable, is worse than either alone.
     The input stays type=number, so the keyboard, arrow keys and a phone's
     numeric pad all still work. */
  appearance: textfield;
  -moz-appearance: textfield;
}
.numin input::-webkit-outer-spin-button,
.numin input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.numin input:focus { outline: none; }
.numin .unit { font-size: .82em; color: var(--ink-3); }

.teamedit { display: flex; align-items: center; gap: 8px; }
.teamedit .tin.name { flex: 1 1 auto; min-width: 8em; }
.teamtbl td { vertical-align: middle; }
.thrtbl td { vertical-align: middle; }

/* Weekday picker. Real checkboxes under the labels — a div-based toggle loses
   keyboard access, and this is the control that decides whose absence counts. */
.nights { display: flex; gap: 4px; flex-wrap: wrap; }
.night { position: relative; cursor: pointer; user-select: none; }
.night input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.night span {
  display: block; font-size: .8rem; padding: .35em .5em; border-radius: 5px;
  border: 1px solid var(--n700); color: var(--ink-3); background: var(--ground);
  min-width: 2.6em; text-align: center;
}
.night input:checked + span {
  color: #17242b; background: var(--brand-emerald); border-color: var(--brand-emerald);
  font-weight: 600;
}
.night input:focus-visible + span { outline: 2px solid var(--ring); outline-offset: 1px; }

/* ── settings sections ──────────────────────────────────────────────────────
   Settings became an area with a subnav, so these are the pieces its screens
   need beyond the controls already defined above. */

/* The team picker on "which raids count" is a link, not a button, so it marks
   itself with aria-current rather than aria-pressed. Same look either way — the
   attribute follows what the element IS. */
.tab[aria-current="page"] {
  color: var(--n950); background: var(--a300); border-color: var(--a300);
  font-weight: 600;
}

/* The instance picker. NOT .zone — that is already a progression card, and a
   checkbox label inheriting its border and padding reads as a broken tile. */
.zpick {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 18px 24px; margin: 14px 0 4px;
}
.zgrp { min-width: 0; }
.zhd {
  font-size: 11.5px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); padding-bottom: 6px; border-bottom: 1px solid var(--line-soft);
  margin-bottom: 6px;
}
/* Excluded instances, as pill toggles — the same control as the raid nights, so
   two settings that are both "tick the ones that apply" do not look like two
   different kinds of thing. The colour is deliberately NOT the nights' green: a
   lit night means "we raid then" while a lit instance means "this one does NOT
   count", and using the affirmative colour for a negative meaning is how a
   whole tier gets excluded by somebody who thought they were selecting it. */
/* Pills that flow, like the raid nights. They were full-width rows carrying a
   run count, which made every one of them the width of the column and stacked
   them edge to edge — a wall rather than a set of choices. */
.zgrp-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.zitem { position: relative; cursor: pointer; user-select: none; }
.zitem input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.zitem > span {
  display: block; font-size: .84rem; padding: .4em .7em; border-radius: 5px;
  border: 1px solid var(--line); color: var(--ink-2); background: var(--ground);
  white-space: nowrap;
}
/* NOT the nights' green. Both controls are "tick the ones that apply", but a lit
   night means "we raid then" and a lit instance means "this one does NOT count" —
   using the affirmative colour for a negative meaning is how a whole tier gets
   excluded by somebody who thought they were selecting it. */
.zitem input:checked ~ span {
  color: var(--ground); background: var(--warning); border-color: var(--warning);
  font-weight: 600;
  /* Struck as well as filled. The colour says "this one is different" and the
     line says WHICH way — an excluded instance reads as crossed off, which is
     the thing a tick alone was never going to convey when ticking means the
     opposite of its usual sense. */
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.zitem input:focus-visible ~ span { outline: 2px solid var(--ring); outline-offset: 1px; }

/* ── the header, and the mark that is the way home ─────────────────────────
   REBUILT 2026-08-20. The mark and the name are ONE link home, not two.

   The logo is drawn as geometry rather than shipped as a raster: public/logo.jpg
   has a dark teal ground baked into it, so it cannot take a theme's accent and it
   blurs at 26px. Every colour below comes from a token, which is what lets one
   drawing be gold-on-dark here and something else elsewhere. */
.home { display:flex; align-items:center; gap:10px; text-decoration:none; color:inherit; flex:none; }
.home:focus-visible { outline:2px solid var(--ring); outline-offset:3px; border-radius:8px; }
/* The crest, as artwork rather than geometry. It is a traced illustration, so it
   is placed as an <img> and given a fixed box — letting it size itself would let
   a 638×527 viewBox decide the header's height. */
.brand { width:34px; height:28px; flex:none; display:block; object-fit:contain; }
.wordmark { font-weight:700; font-size:17px; letter-spacing:-.01em; color:var(--ink); }

/* The shield carries the chart; the axes sit behind it, muted metal so the gold
   shield stays the subject. They are placed to PEEK rather than hide — the blades
   flare past the shoulders and the hafts show below the point, because a weapon
   drawn entirely inside the silhouette is invisible. */
.bk-ring   { fill:none; stroke:var(--n700); stroke-width:1.1; opacity:.55; }
.bk-arms   { opacity:.85; }
.bk-haft   { stroke:var(--n600, #64748b); stroke-width:2.4; stroke-linecap:round; fill:none; }
.bk-blade  { fill:var(--n500, #94a3b8); }
.bk-butt   { fill:var(--n600, #64748b); }
.bk-shield { fill:var(--brand-gold); }
.bk-rim    { fill:none; stroke:#17242b; stroke-width:1.2; opacity:.5; }
.bk-chart  { fill:none; }
.bk-bar    { fill:#17242b; opacity:.55; }
.bk-base   { stroke:#17242b; stroke-width:1.1; opacity:.7; }
.bk-line   { stroke:#17242b; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; fill:none; }
.bk-dot    { fill:#17242b; }

/* Where you are, for when the section tabs are behind the burger. */
/* ── the footer ────────────────────────────────────────────────────────────
   Quiet on purpose. It carries the pages that argue for Ascendry, which a
   signed-in reader has already been argued into — so it sits below the fold of
   every screen, reads as reference, and never competes with the page above it. */
.foot-nav {
  max-width:1140px; margin:0 auto; padding:22px 20px 34px;
  display:flex; flex-wrap:wrap; gap:8px 20px; align-items:center;
  border-top:1px solid var(--line-soft);
}
.foot-nav a { color:var(--ink-3); font-size:13px; }
.foot-nav a:hover { color:var(--ink-2); }
.foot-nav[hidden] { display:none; }

.wherenow { display:none; font-size:14px; color:var(--ink-3); min-width:0;
            overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ── the burger and the drawer ─────────────────────────────────────────────
   Narrow screens get the drawer instead of the section row. Three ways out —
   the X, Escape, and a tap on the drawer's own background — because a menu that
   covers the screen and can only be dismissed by finding one small control is a
   trap. */
.burger { display:none; flex-direction:column; justify-content:center; gap:4px;
          width:38px; height:34px; padding:0 8px; border:1px solid var(--n700);
          border-radius:7px; background:var(--raised); cursor:pointer; flex:none; }
.burger span { display:block; height:2px; border-radius:2px; background:var(--ink-2); }
/* Open is an active state, so it wears the accent every other active control
   wears rather than the logo's bronze. */
.burger[aria-expanded="true"] { border-color:var(--accent); }

.drawer { position:fixed; inset:0; z-index:60; background:rgb(3 7 12/.86);
          backdrop-filter:blur(3px); padding:16px; overflow:auto; }
.drawer[hidden] { display:none; }
.drawer-top { display:flex; align-items:center; justify-content:space-between;
              gap:12px; margin-bottom:14px; }
.drawer-title { font-weight:700; font-size:17px; }
.drawer-x { width:34px; height:34px; border:1px solid var(--n700); border-radius:7px;
            background:var(--raised); color:var(--ink-2); cursor:pointer; font-size:18px;
            line-height:1; }
/* ── what the drawer looks like ────────────────────────────────────────────
   The drawer builds itself from .acc, the same accordion the pages use — and
   with no rules of its own it inherited the IN-PAGE version: hairline rules,
   nine pixels of padding, five sections stacked flush at 43px each. It opened
   correctly and looked broken, which is the same thing as not working.

   Here each section is a card, because on a phone the sections ARE the
   navigation and they should look like something you press. 52px heads and 46px
   links: a thumb is about 44px, and a menu is the one place where being sure
   you hit the right row matters most. */
.drawer .acc {
  border:1px solid var(--line); border-radius:11px; margin-bottom:9px;
  background:var(--card, var(--raised)); overflow:hidden;
}
/* The section you are already inside wears the accent, so opening the menu
   tells you where you are before you read a word of it. */
.drawer .acc:has(a[aria-current="page"]), .drawer a.acc-solo[aria-current="page"] {
  border-color:var(--a400);
}
.drawer .acc-head {
  padding:0 15px; min-height:52px; font-size:15.5px; font-weight:600; color:var(--ink);
}
.drawer .acc-head .acc-caret { margin-left:auto; font-size:12px; }
/* The glyph is already ▾, so it turns over rather than through a right angle —
   the shared rule rotates a ▸ by 90°, which here pointed it at the wall. */
.drawer .acc-head[aria-expanded="true"] .acc-caret { transform:rotate(180deg); }
.drawer .acc-body { padding:0; }
.drawer .acc-body a {
  display:flex; align-items:center; min-height:46px; padding:0 15px 0 27px;
  color:var(--ink-2); border-top:1px solid var(--line-soft); font-size:14.5px;
}
.drawer .acc-body a[aria-current="page"] { color:var(--a300); font-weight:600;
  box-shadow:inset 3px 0 0 var(--a400); }
/* A section with no pages of its own is one row, and has to match the heads it
   sits between rather than read as a link that fell out of a card. */
.drawer a.acc-solo {
  display:flex; align-items:center; min-height:52px; padding:0 15px; margin-bottom:9px;
  border:1px solid var(--line); border-radius:11px; background:var(--card, var(--raised));
  font-size:15.5px; font-weight:600; color:var(--ink);
}
.drawer .pref { margin-top:14px; }
.drawer-theme { display:flex; gap:6px; flex-wrap:wrap; }

@media (max-width: 900px) {
  .burger { display:flex; }
  .wherenow { display:block; }
  .nav { display:none; }
}

/* ── a panel's own controls ───────────────────────────────────────────────
   A sort picker or a tab strip that belongs to ONE panel sits in its header,
   not above the panel where it would read as belonging to the page. */
/* NO margin-left:auto — `.panel-h .note` already carries it. With both, the
   free space splits between them and the note drifts into the middle of the
   header instead of sitting with the controls it belongs beside. */
.panel-ctl { display:flex; align-items:center; gap:7px; flex:none; }
.panel-ctl .sortby { margin-left:0; }
/* A panel header is a flex row; without wrapping, a full-width control block
   simply overhangs the panel — which is where the 60px of page overflow at
   390px came from. */
@media (max-width: 700px) {
  .panel-h { flex-wrap:wrap; }
  .panel-h .note { margin-left:auto; }
  .panel-ctl { width:100%; margin-left:0; }
  .panel-ctl .sortby { margin-left:0; width:100%; }
}

/* Page-level tab strips. Same control as .tabs; the modifier only says the strip
   is the page's own rather than a panel's. */
.pagetabs { margin-bottom:14px; }
.raidtabs, .persontabs { margin-bottom:12px; }

/* ── the card system ───────────────────────────────────────────────────────
   REBUILT 2026-08-20 after a bad edit deleted it. Every list in the app — the
   roster, the nights, the players, the leaderboard, your characters — is these
   classes, which is why losing them collapsed six screens at once.

   Two layouts share one set of parts. The default is a GRID of small cards; add
   `.wide` and the same card becomes one row per person, with the parts laid out
   left to right. The parts keep their names in both so the markup never has to
   know which layout it is in. */
.cards { display:grid; gap:8px; grid-template-columns:repeat(auto-fill, minmax(180px, 1fr)); }
.pcard {
  display:block; padding:9px 11px 10px; border:1px solid var(--line);
  border-radius:9px; background:var(--card, var(--raised)); text-decoration:none;
  color:inherit; transition:border-color .12s, opacity .12s;
}
a.pcard:hover { border-color:var(--n600, var(--ink-3)); }
/* Filtered-out cards dim rather than vanish: the grid keeps its shape, and "who
   did NOT match" is as much of an answer as who did. */
.pcard.dim { opacity:.26; }
.pcard.dim:hover { opacity:1; }
/* Your own. An edge, not a fill — the class colour on the name is what the card
   is read by, and a tinted ground competes with it. */
.pcard.yours, .pcard.youring { border-color:var(--a400, var(--brand-gold));
  box-shadow:inset 3px 0 0 var(--a400, var(--brand-gold)); }
/* A card that belongs to a team wears that team's colour on its edge. */
.pcard.teamed { box-shadow:inset 3px 0 0 var(--tc, var(--line)); }

.pc-top { display:flex; align-items:center; justify-content:space-between; gap:8px; min-width:0; }
.pc-name {
  color:color-mix(in oklab, var(--c-this, var(--ink)) var(--name-lift, 100%), var(--ink));
  font-weight:600; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.pc-top .grade { flex:none; }
/* A long TITLE rather than a name: it takes the room and the chip beside it
   shrinks, which is the opposite of the default. */
.pc-top.wide-name { flex:1 1 auto; min-width:0; gap:12px; }
.pc-top.wide-name .pc-name { flex:1 1 auto; min-width:0; }
.pc-top.wide-name .chip { flex:0 1 auto; min-width:0; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }

.pc-bot { display:flex; align-items:center; gap:6px; margin-top:4px; min-width:0; }
.pc-spec { font-size:12px; color:var(--ink-3); min-width:0; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; }
.pc-flags { margin-left:auto; display:flex; align-items:center; gap:7px; flex:none;
  font-size:11.5px; font-weight:600; font-variant-numeric:tabular-nums; }
.pc-death { color:var(--critical); display:inline-flex; align-items:center; gap:3px; }
.pc-cons { color:var(--warning); display:inline-flex; align-items:center; }
.pc-mark { width:11px; height:11px; flex:none; fill:currentColor; }

/* Shield / cross / blade. WoW's role hues rather than the theme's semantic
   tokens — crit red is reserved for things that went wrong and would misread as
   an error sitting on somebody's card. */
.pc-roles { display:inline-flex; align-items:center; gap:3px; flex:none; }
.rglyph { width:13px; height:13px; flex:none; fill:currentColor; }
.rglyph.tank { color:#7dd3fc; } .rglyph.healer { color:#6ee7b7; } .rglyph.dps { color:#fda4af; }
.rglyph.second { opacity:.4; }

/* The stat strip: a few numbers under a rule, each with its label beneath it. */
.pc-stats { display:flex; gap:16px; margin-top:9px; padding-top:8px;
            border-top:1px solid var(--line); flex-wrap:wrap; }
.pc-stat { display:flex; flex-direction:column; gap:1px; font-variant-numeric:tabular-nums; }
.pc-stat b { font-weight:600; font-size:15px; }
.pc-stat b i { font-style:normal; font-size:11.5px; color:var(--ink-3); font-weight:500; }
.pc-stat > span { font-size:10.5px; letter-spacing:.05em; text-transform:uppercase;
                  color:var(--ink-3); }
.pc-act { display:flex; align-items:center; gap:6px; margin-top:8px; }
.pc-badges { display:flex; flex-wrap:wrap; gap:4px; margin-top:8px; }
/* One character per line. Wrapped inline, a person with three characters and
   two specs each ran together into one paragraph of names where no character's
   specs could be told from the next one's — and a person IS a list of
   characters, so it should read as a list. */
.pc-chars { display:flex; flex-direction:column; gap:5px; margin-top:8px; font-size:12px;
            align-items:flex-start; }
.pc-chars .nm { font-weight:500; }
.pc-chars .nm i { font-style:normal; margin-left:4px; font-size:11px; color:var(--ink-3); }

/* ── one card per ROW ──────────────────────────────────────────────────────
   The card does not change, its axis does. Wide, the pieces sit side by side and
   the stats line up down the page, which is what makes an outlier findable. */
.cards.wide { grid-template-columns:1fr; }
.cards.wide .pcard { display:flex; align-items:center; gap:26px; flex-wrap:wrap; }
/* The name block does NOT shrink, so the chip at its right edge stays in one
   column down the list however much the rest of the row needs. */
.cards.wide .pc-top { flex:0 0 260px; min-width:0; }
/* The spacer, and it has to be an item that GROWS rather than an auto margin
   further right. A flex line is filled and then wrapped — it never shrinks what
   is already on it to fit one more — so a card with enough stats pushed .pc-act
   onto a second line by itself, where it sat at the left edge with no margin to
   move it. Giving .pc-act margin-left:auto fixes that line, but on an unwrapped
   row it would then split the slack with .pc-stats and tear the numbers away
   from the button. Growing .pc-bot leaves no slack for either auto margin to
   take, so the wide row looks exactly as it did while the wrapped one lines up
   at the right. */
.cards.wide .pc-bot { margin-top:0; flex:1 1 auto; }
.cards.wide .pc-chars { margin-top:0; flex:1 1 auto; min-width:0; }
.cards.wide .pc-badges { margin-top:0; margin-left:auto; justify-content:flex-end; flex:none; }
.cards.wide .pc-stats { margin-top:0; padding-top:0; border-top:0; margin-left:auto;
                        flex:none; gap:18px; }
/* Right, on whichever line it lands. Unwrapped there is no slack to take (see
   .pc-bot above) so this is inert; wrapped, it is the whole line and this is the
   only thing that moves it off the left edge. */
.cards.wide .pc-act { margin-top:0; flex:none; margin-left:auto; }
.cards.wide .pcard .pc-top.wide-name { flex:1 1 auto; }
/* Explicit order, so every row agrees where its parts are regardless of the
   sequence the markup happened to emit them in. */
.cards.wide .pc-top { order:1; }
.cards.wide .pc-bot { order:2; }
.cards.wide .pc-chars, .cards.wide .pc-badges { order:3; }
.cards.wide .pc-stats { order:4; }
.cards.wide .pc-act { order:5; }

/* Under 900px there is no room for a row, so the same pieces stack and the list
   becomes the compact grid the cards were designed at. */
@media (max-width: 900px) {
  .cards.wide { grid-template-columns:repeat(auto-fill, minmax(255px, 1fr)); }
  .cards.wide .pcard { display:block; }
  .cards.wide .pc-bot { margin-top:4px; }
  .cards.wide .pc-chars { margin-top:8px; }
  .cards.wide .pc-stats { margin-top:9px; padding-top:8px; border-top:1px solid var(--line);
                          margin-left:0; }
  .cards.wide .pc-badges { margin-left:0; justify-content:flex-start; }
  /* Stacked, the actions are the last thing in the card rather than the last
     column of a row — .cards.wide zeroes their margin for the row layout and it
     was never given back, so Release sat flush against the numbers above it.

     Right-justified for the same reason it is right-justified in the wide row:
     the action is the end of the card. Stacked, .pc-act is a full-width block
     and the button fell back to the left edge, so the same control moved sides
     depending on the width of the screen. `.restcard` has always done this; the
     other three cards that end in a button now agree with it. */
  .cards.wide .pc-act { margin-top:14px; justify-content:flex-end; }
  /* Stacked, a card has no column to keep, so nothing needs to be cut to hold
     one. The ellipsis exists to keep the wide rows aligned; here it only loses
     the end of a raid's name. The name wraps and the chip beside it stays whole,
     rather than both being trimmed to fit a line that no longer matters. */
  .cards.wide .pc-name, .cards.wide .pc-spec,
  .cards.wide .pc-top.wide-name .pc-name {
    overflow:visible; text-overflow:clip; white-space:normal; word-break:break-word;
  }
  .cards.wide .pc-top { align-items:flex-start; }
  .cards.wide .chip, .cards.wide .pc-top.wide-name .chip {
    flex:none; overflow:visible; text-overflow:clip; white-space:nowrap;
  }
}

/* ── phones: the two grids that cannot fit ─────────────────────────────────
   Measured, not guessed. At 390px the page has ~350px of usable width, and
   these two layouts declare more than that in fixed columns alone:

     .peerrow   30 + 190 + 150 + 84 + 56 + 4 gaps = 566px  → +228 of overflow
     .lootrow   240 + 2fr + 22                              → its candidates cut off

   Both stack rather than shrink. Squeezing a five-column row into a phone gives
   five unreadable columns; giving each row two lines keeps every value at its
   full size, which is the point of the numbers being there. */
@media (max-width: 700px) {
  /* Peers: rank and name on the first line, the bar and its numbers under it.
     The head row goes — column headings over stacked rows label nothing. */
  .peerhead { display:none; }
  .peerrow {
    /* minmax(0,…) not 1fr: a grid track's automatic minimum is its content's
       min-content width, so a long spec label refuses to shrink and the row
       overflows by exactly that much. */
    grid-template-columns:26px minmax(0,1fr) auto;
    grid-template-areas:"rank name raids" "bar bar val";
    gap:4px 10px; padding:9px 10px;
  }
  .peerrow .rank   { grid-area:rank; }
  .peerrow .pname  { grid-area:name; }
  .peerrow .btrack { grid-area:bar; align-self:center; }
  .peerrow .pval   { grid-area:val; text-align:right; }
  .peerrow .praids { grid-area:raids; text-align:right; }

  /* Loot: the item over its candidates, and each candidate over its own two
     lines — the rank, name and gain read together, the item they are replacing
     sits under them where a long name has room. */
  .lootrow { grid-template-columns:1fr; gap:10px; }
  .lootrow .cand {
    grid-template-columns:20px 1fr auto;
    grid-template-areas:"pos who gain" ". now now";
    gap:2px 10px;
  }
  .lootrow .cand > .pos  { grid-area:pos; }
  .lootrow .cand > .who, .lootrow .cand > .whoalt { grid-area:who; min-width:0; }
  .lootrow .cand > .gain { grid-area:gain; text-align:right; }
  .lootrow .cand > .item-link, .lootrow .cand > .scoped { grid-area:now; min-width:0;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
}

/* ── the leaderboard card ──────────────────────────────────────────────────
   Four siblings in their own columns, so every row agrees where the rank, the
   name, the points and the badges are. */
.boardcard { display:flex; align-items:center; gap:16px; }
.bc-rank { flex:0 0 2em; text-align:right; font-variant-numeric:tabular-nums;
           color:var(--ink-3); font-weight:600; }
.boardcard.podium .bc-rank { color:var(--a300, var(--brand-gold)); }
.boardcard .pc-name { flex:0 0 auto; }
.boardcard .pts { margin-left:0; font-weight:700; font-variant-numeric:tabular-nums; }
.boardcard .pts .unit { font-size:11px; font-weight:500; color:var(--ink-3); margin-left:3px; }
.cards.wide .boardcard .pc-badges { margin-left:auto; }

/* ── a directory card you can pick ─────────────────────────────────────────
   It is not a link — clicking it fills the form above rather than navigating —
   so it has to LOOK pickable without borrowing the link affordance the name
   inside it already uses. Hence the cursor and the hover, and the button, which
   is the part that says what the click does. */
.restcard { cursor:pointer; }
.restcard:hover { border-color:var(--a400); }
.restcard .pc-top { min-width:0; }
.restcard .pc-act { margin-top:8px; justify-content:flex-end; }
.restcard .pickbtn { opacity:.72; transition:opacity .12s; }
.restcard:hover .pickbtn, .restcard .pickbtn:focus-visible { opacity:1; }

/* The count and the button share a line. .pc-stats stays full width so the rule
   above it still spans the whole card; the button is placed into the same row,
   in the space to the right of a single stat that was empty anyway. */
.restcard { display:grid; grid-template-columns:1fr auto; }
.restcard .pc-top, .restcard .pc-bot { grid-column:1 / -1; }
.restcard .pc-stats { grid-column:1 / -1; grid-row:3; }
.restcard .pc-act { grid-column:2; grid-row:3; align-self:center; margin:9px 0 0; }

/* ── the mark is the way home ──────────────────────────────────────────────
   One anchor around the mark and the name. It is the header's flex item now, so
   it carries the order the brand used to: an un-ordered child among ordered
   siblings defaults to 0 and jumps to the front of the row. */
.home { display:flex; align-items:center; gap:14px; text-decoration:none;
  color:inherit; min-width:0; flex:none; border-radius:6px; }
.home:hover .wordmark { color:var(--a300); }
/* (the .home ring is set once, above — this duplicate said a different colour) */
/* ── the header on a phone ─────────────────────────────────────────────────
   Two rows of two, and the split is deliberate:

     [ crest + Ascendry ]        [ burger ]
     [ where you are   ]     [ workspace ]

   The burger is the way to everything else, so it belongs at the top right and
   must STAY there. It did not: the row was crest / where-you-are / burger, and
   where-you-are is `flex:1 1 auto` growing to fit its text — so on a screen with
   a long label ("Players · By class") it took the width it wanted and pushed the
   burger onto the next line. The control moved depending on which page you were
   reading, which is the one thing a menu button must never do.

   So the first row is fixed content only — a crest and a button, both flex:none —
   and everything that varies in width is below it. `.bar::after` is the line
   break: a zero-height flex item at 100% basis, which is the way to force a wrap
   at a chosen point in a flex row without nesting another element in the markup.

   `.home { order:1 }` alone used to do the opposite of what it looks like: every
   other child defaults to order 0, so ordering the logo pushed it to the END and
   the crest ended up in the top-right corner with the burger on the left. Orders
   are given to ALL of them, so the row cannot be rearranged by adding a sibling
   later — the workspace arrived without one and jumped in front of the crest. */
@media (max-width:900px) {
  .bar { gap:8px 10px; padding:10px 14px; }
  .home     { order:1; flex:none; }
  .burger   { order:2; flex:none; margin-left:auto; }
  /* The break. Everything after it is on the second row. */
  .bar::after { content:""; order:3; flex:1 0 100%; height:0; }
  .wherenow { order:4; flex:1 1 auto; min-width:0; }
  /* Under the burger, right-aligned to it — the account controls stay on the
     same side of the header as the menu they belong with. */
  .wspick   { order:5; flex:none; margin-left:auto; }
  .nav      { order:6; }
  .navout   { order:7; }
  .spacer   { order:8; display:none; }
  .lens     { order:9; flex:1 0 100%; margin-top:2px; }
  .wordmark { font-size:16px; }
  /* Opening leftward from the right edge, because that is the edge it is now
     pinned to; anchored at left:0 the menu would run off the screen. */
  .wsmenu { left:auto; right:0; }
}

/* ── a row that does nothing stops looking like it does ────────────────────
   The editors — accounts, linked characters, tokens, invites, teams, guilds —
   are forms laid out in a grid. Their controls say what is interactive; a tint
   under the whole row only ever promised a click that was not there. */
.memtbl tr:hover td, .chartbl tr:hover td, .toktbl tr:hover td,
.teamtbl tr:hover td, .guildtbl tr:hover td, .thrtbl tr:hover td {
  background:transparent; }

/* ── the spec diamond ──────────────────────────────────────────────────────
   A frame in the class colour with that tree's glyph inside it. The glyphs are
   drawn to survive twelve pixels, and a rotated frame has to be about 1.7× its
   glyph to hold one — which is why the card mark and the inline mark are two
   sizes rather than one scaled twice.

   THE BOX IS THE DRAWN SIZE, NOT THE SQUARE'S SIDE. A rotated square reaches
   ~0.207 of its side past each edge, so a 17px frame occupies 25px and a box
   cut to 17 loses its points to the first ancestor that clips — which is what was
   happening to every card name. The element is sized to the diagonal and the
   diamond is drawn inside it, so there is no overflow to clip. */
/* No frame: the glyph IS the mark. The box keeps the footprint the diamond
   had, so nothing around it shifts — what changes is that the drawing now
   uses all of it rather than a sixth. */
.spmark { display:inline-grid; place-items:center; flex:none;
  width:25px; height:25px;
  /* The class colour lives on the DRAWING now — carrying it was the frame's
     only job, and the frame is gone. */
  color:var(--c-this, var(--ink-2)); }
/* Upright, because the box around it is upright now — there is no rotation left
   to undo. */
.spglyph { width:20px; height:20px; fill:currentColor; }

/* A name is a mark and a piece of text that may still need to ellipsis, so the
   text keeps its own box. Only a MARKED name becomes a flex row — a raid title
   wearing .pc-name is left as the plain span it always was. */
.pc-name.marked { display:inline-flex; align-items:center; gap:7px; }
.pc-name.marked .nm-t { min-width:0; overflow:hidden; text-overflow:ellipsis;
  white-space:nowrap; }

/* Inline in a table the mark is smaller: the row is set in text, not in card
   headings, and a mark that out-measures its own line drags the eye off it. */
.who .spmark { width:22px; height:22px; }
.who .spglyph { width:17px; height:17px; }
/* Measured from the tip now rather than from a flat edge the tip overhung. */
.who { gap:6px; }
/* The dot it replaces. */
.who::before { content:none; }
.who .nm-t { min-width:0; overflow:hidden; text-overflow:ellipsis; }

/* On a card the role glyph sits directly under the spec diamond, so it takes the
   same column and centres in it. Left-aligning two marks of different widths puts
   their edges in a line and their centres six pixels apart, and the eye reads the
   centres. Only on a card: inline in a pill the glyph is a glyph. */
.pcard .rmark { width:25px; justify-content:center; }

/* ── outside attendance ────────────────────────────────────────────────────
   Dashed rather than toned. The colour chips on these screens are judgements —
   good, slipping, broken — and a night that does not count is none of them: a
   team excluded the zone deliberately. The dash reads the way a dashed line
   reads on a chart: not in the total. */
.chip.offcount { background:transparent; color:var(--ink-3);
  border:1px dashed var(--n600); }
/* The same mark in the calendar, where the entry is otherwise a solid team
   colour on a filled ground. */
.cal-r.offcount { background:transparent; color:var(--ink-3);
  border:1px dashed var(--n600); border-left:3px dashed var(--tc, var(--ink-3)); }
.cal-r.offcount:hover { color:var(--ink-2); }
/* And in the key, so the dashes are explained where they are first met. */
.key .sw.dashed { background:transparent; border:1px dashed var(--n600); }

/* The caveat under a night's opening line. Not .because — that is the page's own
   voice and set large; a chip and a footnote inside it argued with the sentence
   that says what the page is for. */
.offnote { display:flex; align-items:flex-start; gap:8px; margin:10px 0 0;
  color:var(--ink-3); font-size:13px; max-width:70ch; }
.offnote .chip { flex:none; }

/* A line per team, stacked. The first keeps the margin the single bar had; the
   rest sit close under it so the group reads as one block, not three bars. */
.zonebar + .zonebar { margin-top:-3px; margin-bottom:3px; }

/* ── the boss planner ──────────────────────────────────────────────────────
   A verdict is never shown without the numbers behind it: a cross on its own is
   an accusation, and the player at zero is the whole point of showing counts. */
.dutytbl td { vertical-align:middle; }
.verdict-in { font-weight:700; margin-right:8px; }
.verdict-in.ok { color:var(--good, #6ee7a8); }
.verdict-in.miss { color:var(--crit, #fca5a5); }
.pcounts { display:inline-flex; flex-wrap:wrap; gap:8px; }
.pcount { color:var(--ink-3); font-size:12px; white-space:nowrap; }
.pcount b { color:var(--ink-2); font-weight:600; }
/* The one the cross is about. */
.pcount.zero { color:var(--crit, #fca5a5); }
.pcount.zero b { color:var(--crit, #fca5a5); }

/* The editor, folded away: most visits read the plan rather than change it. */
.planedit { margin-top:8px; }
.planedit > summary { cursor:pointer; color:var(--ink-3); font-size:12px;
  padding:8px 0; }
.planrows { display:flex; flex-direction:column; gap:8px; margin:8px 0; }
/* Six controls now: duty, who, marker, where, note, remove. The marker is a
   fixed width because its content is a glyph and a short word, and the note
   keeps the largest share because it is the one that runs long. */
.planrow { display:grid; gap:8px; align-items:center;
  grid-template-columns:140px minmax(0,1fr) 130px minmax(0,.8fr) minmax(0,1.4fr) auto; }
@media (max-width:900px) {
  /* Six controls in a row is five too many on a phone. */
  .planrow { grid-template-columns:minmax(0,1fr); }
  .planrow .btn { justify-self:end; }
}

/* The primer's own rows: a label and its list, not a table. */
.prow { display:flex; gap:16px; padding:8px 0; border-top:1px solid var(--line-soft); }
.prow .plabel { flex:none; width:92px; color:var(--ink-3); font-size:12px;
  text-transform:uppercase; letter-spacing:.05em; }
.prow .pval { color:var(--ink-2); min-width:0; }
@media (max-width:900px) { .prow { flex-direction:column; gap:2px; } .prow .plabel { width:auto; } }

/* ── Discord ───────────────────────────────────────────────────────────────
   One row per thing that can post itself. A locked row is dimmed on the CONTROL
   only: the label and the explanation stay at full contrast, because a switch
   you cannot use yet is still something you are meant to read. */
.dhead { display:flex; flex-wrap:wrap; align-items:center; gap:10px; }
.dposts { display:flex; flex-direction:column; margin-top:10px; }
.dpost { display:flex; align-items:flex-start; justify-content:space-between; gap:20px;
  padding:11px 0; border-top:1px solid var(--line-soft); }
.dpost:first-child { border-top:0; }
.dpost-t { display:flex; flex-direction:column; gap:2px; min-width:0; }
.dpost-l { color:var(--ink); font-weight:600; font-size:14px; }
.dpost-n { color:var(--ink-3); font-size:12.5px; max-width:64ch; }
.dpost .chip, .dpost .btn { flex:none; }
.dpost.locked .chip { opacity:.9; }
/* On reads as on: the same accent the rest of the app uses for a live setting.
   Always worn over .btn — the class carries the ON state, not the button. */
.dtoggle.on { border-color:var(--accent); color:var(--accent); }
.chip.pro { background:transparent; border:1px dashed var(--a300); color:var(--a300); }
@media (max-width:900px) {
  /* The switch drops under its label rather than squeezing the sentence. */
  .dpost { flex-direction:column; align-items:stretch; gap:10px; }
  .dpost .btn, .dpost .chip { align-self:flex-start; }
}

/* A character's specs, on the character's own line. Ordered most-played first,
   named rather than counted: the count beside the name is attendance-scoped and
   the spec tallies are workspace-wide, so a number here could not agree with the
   number two centimetres to its left. */
.pc-chars .chspec { font-style:normal; color:var(--ink-3); font-size:11px;
  margin-left:5px; white-space:nowrap; }

/* A character chip carries the diamond now — the class and spec belong to the
   CHARACTER, not to the person the card is about. Inline-flex so the mark aligns
   to the middle of the name rather than sitting on its baseline. */
.pc-chars .nm { display:inline-flex; align-items:center; gap:5px; }
.pc-chars .chspec .spmark { width:18px; height:18px; }
.pc-chars .chspec .spglyph { width:15px; height:15px; }

/* One spec, one diamond, one word — kept together so a wrap never separates a
   mark from the spec it names. */
.chspec { display:inline-flex; align-items:center; flex-wrap:wrap; gap:8px; }
.chspec-one { display:inline-flex; align-items:center; gap:4px; white-space:nowrap; }

/* A name shown BESIDE its own list of specs drops its mark — otherwise the first
   spec appears twice, once as the name's diamond and once in the list. */
.who.nomark .spmark { display:none; }

/* The pulls of one boss — a second rank under the boss pills, quieter, so the
   pair reads as "which fight" then "which attempt". */
.tabs.pulls { margin-top:8px; }
.tabs.pulls .tab { font-size:12px; padding:4px 10px; gap:6px; }
.tabs.pulls .tab .scoped { font-size:11px; }

/* ── this raid, boss by boss ───────────────────────────────────────────────── */
.bosschart { width:100%; height:auto; display:block; overflow:visible; }
.bc-val { fill:var(--ink); font:600 11.5px var(--font); font-variant-numeric:tabular-nums; }
.bc-boss { fill:var(--ink-3); font:10.5px var(--font); }
.bc-axis { fill:var(--ink-3); font:10.5px var(--font); font-variant-numeric:tabular-nums; }
.bc-none { fill:var(--ink-3); font:10.5px var(--font); opacity:.7; }
/* A death is the usual explanation for a short bar, so it reads as a warning
   rather than as another number. */
.bc-death { fill:var(--critical, #fca5a5); font:600 10.5px var(--font); }

/* The night's shape: a band for the spread across earlier raids, a line for the
   average of them, and tonight drawn over both. Same three roles the old chart
   used — the band is context, the average is the benchmark, the heavy line is
   the answer. */
.bc-band { fill:#9b8cff; opacity:.16; }
.bc-band-tick { stroke:#9b8cff; stroke-width:2; opacity:.32; }
.bc-avg { fill:none; stroke:#9b8cff; stroke-width:2; opacity:.75; }
.bc-avg-dot { fill:#9b8cff; opacity:.75; }
.bc-cur { fill:none; stroke:var(--a300); stroke-width:2.5; stroke-linejoin:round; }
.bc-cur-dot { fill:var(--a300); }
.bc-key { display:flex; flex-wrap:wrap; gap:14px; margin-top:6px; color:var(--ink-3);
  font-size:11.5px; }
.bc-key span { display:inline-flex; align-items:center; gap:6px; }
.bc-key i { width:14px; height:3px; border-radius:2px; flex:none; }
.bc-key .k-cur { background:var(--a300); }
.bc-key .k-avg { background:#9b8cff; opacity:.75; }
.bc-key .k-band { background:#9b8cff; opacity:.3; height:9px; }

/* You against your own past. The rate is the number; the volume behind it and
   the kill count it is measured against sit under it, quieter — context that
   should not be mistaken for the reading. */
.selftbl .scoped { display:block; font-size:11px; font-weight:400; }
.selftbl td.num { white-space:nowrap; }
.bc-key .k-death { background:var(--crit, var(--critical, #f87171)); }


/* ── the filter bar ───────────────────────────────────────────────────────
   REBUILT 2026-08-20. Same shape as .tab, which is this theme's segmented
   control: the filter rows and the tab strips are the same kind of thing and
   should not arrive as two design languages. */
.filterbar { display:flex; flex-direction:column; gap:8px; margin-bottom:14px;
             padding-bottom:12px; border-bottom:1px solid var(--line); }
.fline { display:flex; flex-wrap:wrap; align-items:center; gap:6px; }
.flabel { width:92px; flex:none; font-size:11px; font-weight:600; letter-spacing:.08em;
          text-transform:uppercase; color:var(--ink-3); }
.fchip { border:1px solid var(--n700); background:transparent; color:var(--ink-3);
         padding:4px 10px; border-radius:6px; cursor:pointer; font:inherit; font-size:12.5px;
         transition:color .12s, border-color .12s, background .12s; }
.fchip:hover { color:var(--ink); border-color:var(--n600); }
.fchip[aria-pressed="true"] { color:var(--a300); border-color:var(--a400);
                              background:rgb(212 175 55/.12); }
/* Pressed, like every other pressed pill — and through the token, not a literal
   ink colour copied from the primary button it used to match. */
.fchip.star[aria-pressed="true"] { color:var(--n950); background:var(--accent);
                                   border-color:var(--accent); }
.fcount { margin-left:auto; font-size:12px; color:var(--ink-3); }
.fclear { background:none; border:0; color:var(--ink-3); cursor:pointer; font:inherit;
          font-size:12px; padding:2px 4px; }
.fclear:hover { color:var(--ink); }
/* A banner that says the page is filtered — shown only when it IS, because a
   permanent one stops being read. */
.filterbanner { display:flex; align-items:center; gap:8px; flex-wrap:wrap;
                border:1px solid var(--a400); background:rgb(212 175 55/.08);
                border-radius:8px; padding:8px 12px; margin-bottom:12px; font-size:13px; }
.sortby { display:flex; align-items:center; gap:6px; margin-left:auto; font-size:12.5px;
          color:var(--ink-3); }
.dirtoggle { padding:4px 9px; line-height:1; }
.rolepill { display:inline-flex; align-items:center; gap:6px; }
/* This block belongs HERE, immediately after the rules it overrides, not up with
   the other phone rules a thousand lines above — same specificity, and the last
   one in the file wins. Written up there, none of it applied. */
@media (max-width: 900px) {
  .filterbar { gap:14px; }
  /* A 92px label column on a 390px screen leaves 250 for the chips, and the row
     it labels then wraps under it anyway. The label takes its own line and the
     chips get the width. */
  .fline { gap:9px 7px; }
  .flabel { width:100%; }
  .fchip { padding:9px 14px; font-size:13px; }
  /* The count and the sort picker are pushed right by margin-left:auto, which on
     a wrapped row lands them beside whichever chip happened to end it. Their own
     line instead — they belong to the whole bar, not to that chip. */
  .fcount, .filterbar .sortby { margin-left:0; width:100%; }
}

/* ── tables that need their own shape ─────────────────────────────────────── */
.bistbl td, .plantbl td, .geartbl td, .fighttbl td, .dutytbl td { vertical-align:middle; }
.geartbl tr.dim td { opacity:.55; }
.vcol { white-space:nowrap; }
.jobcell { white-space:nowrap; }
.jobcell .pc-roles { vertical-align:middle; }
.jobcell .role { white-space:nowrap; vertical-align:middle; margin-left:6px; }

/* ── gear: what is missing, and what is socketed ──────────────────────────── */
.gaplist { display:flex; flex-direction:column; gap:12px; }
.gapgrp { display:flex; flex-wrap:wrap; align-items:baseline; gap:6px 8px; }
.gaphd { width:100%; font-size:11px; font-weight:600; letter-spacing:.06em;
         text-transform:uppercase; color:var(--ink-3); }
.gapitem { display:inline-flex; align-items:baseline; gap:7px; padding:5px 10px;
           background:var(--raised); border:1px solid var(--line); border-radius:6px;
           font-size:13px; }
.gapitem b { color:var(--ink-2); font-weight:600; }
.gemcell { display:inline-flex; gap:4px; align-items:center; }
.gem { width:8px; height:8px; flex:none; transform:rotate(45deg);
       border:1px solid var(--line); }
.gem.on { background:var(--good); }
/* NOTHING here. .slotrow is a <tr> that ships with the `hidden` attribute, and
   a salvaged `display:flex` was overriding it — `hidden` is only `display:none`
   from the UA stylesheet, so any display in an author rule beats it. That is why
   every drawer on this table was open on arrival despite the markup saying
   otherwise, and it also stopped the row behaving as a table row. Its contents
   are laid out by .gaplist inside the cell; the row itself needs no rule. */

/* ── an accordion ─────────────────────────────────────────────────────────
   Folded by default. The head is a real button so the keyboard reaches it, and
   the caret turns rather than swapping glyph — one element, one state. */
.acc { border-top:1px solid var(--line-soft); }
.acc:first-child { border-top:0; }
.acc-head { display:flex; align-items:center; gap:9px; width:100%; padding:9px 0;
            background:none; border:0; cursor:pointer; font:inherit; color:inherit;
            text-align:left; }
.acc-caret { flex:none; color:var(--ink-3); transition:transform .15s ease; }
.acc-head[aria-expanded="true"] .acc-caret { transform:rotate(90deg); }
.acc-body { padding:2px 0 10px; }
.acc-body[hidden] { display:none; }
/* One item on its own does not need a fold — it opens flat. */
.acc-solo .acc-head { cursor:default; }
.acc-solo .acc-caret { visibility:hidden; }

/* ── the upload drop zone ─────────────────────────────────────────────────── */
.drop { border:1.5px dashed var(--n700); border-radius:10px; padding:26px 18px;
        text-align:center; cursor:pointer; transition:border-color .12s, background .12s; }
.drop:hover, .drop.over { border-color:var(--brand-gold); background:rgb(212 175 55/.05); }
.dropmain { margin:0; font-size:15px; color:var(--ink-2); }
.dropsub { margin:6px 0 0; font-size:12.5px; color:var(--ink-3); }

/* ── odds and ends the screens above rely on ──────────────────────────────── */
.mark { display:inline-block; width:6px; height:6px; border-radius:50%;
        background:var(--ink-3); flex:none; }
.boss.wiping .mark { background:var(--critical); }
.det { font-size:12.5px; color:var(--ink-3); }
.dir { display:inline-flex; align-items:center; }
.dlabel, .glabel { font-size:11px; font-weight:600; letter-spacing:.06em;
                   text-transform:uppercase; color:var(--ink-3); }
/* A written report is the only prose in the app, and it was setting 150
   characters to the line across the full panel. Everything else here is a table
   and wants the width; this wants a measure. */
/* The generated reports fill their panel, like every other panel in the app.
   This used to cap the measure at 74ch, which is the textbook line length for
   running prose — but it applied inside a full-width card, so a third of every
   report was empty and the screen read as broken rather than as typeset. A
   report is part of the app, not a document dropped into it, and matching the
   width of the pages around it is worth more here than the ideal measure. */
.md { max-width:none; }
/* Whatever follows the prose shares its measure — the byline underneath was
   running the full panel width while the report above it stopped at 74
   characters, which read as two different columns. */
.md ~ .foot, .md ~ .actions { max-width:none; }
.mdh { margin:14px 0 6px; font-size:14px; font-weight:600; }
.mdlist { margin:0 0 12px; padding-left:16px; color:var(--ink-2); }
.mdlist li { margin:4px 0; line-height:1.65; }
.tokenbox { background:var(--sunk, var(--ground)); border:1px solid var(--brand-gold);
            border-radius:8px; padding:10px 12px; margin:8px 0 0; }
.tokenbox code { font-family:ui-monospace, monospace; font-size:12px; color:var(--ink);
                 word-break:break-all; user-select:all; line-height:1.55; }
.snip { background:var(--sunk, var(--ground)); border:1px solid var(--line);
        border-radius:8px; margin:8px 0 0; max-height:230px; overflow:auto; }
.invcode { user-select:all; }
.rolesel, .ownersel, .mainin { font:inherit; font-size:13px; color:var(--ink);
            background:var(--sunk, var(--ground)); border:1px solid var(--n700);
            border-radius:6px; padding:5px 8px; }
.teamform { display:block; }
/* A name is a name. Left to flex, these boxes stretched to a thousand pixels
   across a row that has two other things in it. */
.tin[type="text"] { max-width:26em; }
.pwho { font-weight:600; }
.pnote { font-size:12px; color:var(--ink-3); }
.pkind { font-size:11px; letter-spacing:.05em; text-transform:uppercase; color:var(--ink-3); }
.pref { font-size:12px; color:var(--ink-3); }
.prm { font-weight:600; }
/* A two-word label on a narrow column broke as "Which / slots" down the whole
   table. It is a button, not a paragraph. */
.btn.showslots { white-space:nowrap; }
.step { border:1px solid var(--n700); background:var(--raised); color:var(--ink-2);
        width:26px; height:26px; border-radius:6px; cursor:pointer; font:inherit;
        line-height:1; flex:none; }
.step:hover { color:var(--ink); border-color:var(--n600); }
/* ── the grade breakdown ───────────────────────────────────────────────────
   REBUILT. This was a plain flex row, in which a .btrack — a span with a height
   and no width — collapsed to the 3px its fill declares as a minimum. Four
   scores out of a hundred were being shown as four identical slivers.

   A grid, so the bars share one scale down the column and can actually be
   compared, with the explanation on a second row under the bar it explains. */
.gradebars { display:flex; flex-direction:column; gap:10px; }
.gradebar {
  display:grid; grid-template-columns:minmax(140px, 210px) minmax(0,1fr) 3.2em;
  grid-template-areas:"label bar score" ". detail detail";
  align-items:center; gap:3px 12px;
}
.gradebar .glabel  { grid-area:label; }
.gradebar .btrack  { grid-area:bar; }
.gradebar .gscore  { grid-area:score; }
.gradebar .det     { grid-area:detail; margin:0; }
.gscore { font-variant-numeric:tabular-nums; font-weight:600; text-align:right; }
.chain { display:flex; flex-wrap:wrap; align-items:center; gap:6px 8px; }
.chainitem { display:inline-flex; flex-direction:column; gap:3px; padding:6px 10px;
             border:1px solid var(--line); border-radius:6px; background:var(--sunk, var(--ground)); }
/* The current piece. --accent, like every other "this is the one" in the app —
   it was the other yellow. */
.chainitem.now { border-color:var(--accent); }
/* The specs a piece was worn under, under its item line. Small and quiet — it is
   provenance, not a headline. */
.itemspecs { display:inline-flex; gap:4px; flex-wrap:wrap; }
.itemspecs .chip { font-size:11px; padding:1px 6px; }
.specseen { margin-top:6px; }
.chainarrow { color:var(--ink-3); }
/* ── one circle per class ──────────────────────────────────────────────────
   REBUILT. These were left as three placeholder lines when the stylesheet was
   rebuilt, which is why every circle came out the same grey with its label
   jammed into "4Warlock" at the top edge.

   The circle wears its own class colour — that is the entire point of the
   chart, and grey circles sized by population is a worse table. The fill is
   mixed rather than set flat so nine of them overlapping stay readable, and the
   ring is the full colour so the edges still separate where they touch. */
.bubbles { position:relative; }
.bub {
  position:absolute; padding:0; cursor:pointer; font:inherit; color:var(--ink);
  border-radius:50%; border:1.5px solid var(--c-this, var(--ink-3));
  background:color-mix(in oklab, var(--c-this, var(--ink-3)) 20%, transparent);
  transition:background .12s, border-color .12s;
}
.bub:hover { background:color-mix(in oklab, var(--c-this, var(--ink-3)) 34%, transparent); }
.bub.on {
  border-width:2.5px;
  background:color-mix(in oklab, var(--c-this, var(--ink-3)) 46%, transparent);
}
.bub:focus-visible { outline:2px solid var(--ring); outline-offset:3px; }
/* Name over count, centred. column-reverse because the markup emits the number
   first — the number is the measurement, but the class is what you are looking
   for when you scan a cloud of nine. */
.bub-in {
  position:absolute; inset:0; display:flex; flex-direction:column-reverse;
  align-items:center; justify-content:center; gap:2px; line-height:1.2;
  pointer-events:none;
}
.bub-in i { font-style:normal; font-size:12.5px; font-weight:600; letter-spacing:.02em;
            text-transform:capitalize; color:var(--ink-2); }
.bub-in b { font-size:20px; font-weight:700; font-variant-numeric:tabular-nums; }
.bub.on .bub-in i { color:var(--ink); }
/* Same collapse, same cause: this overrode .brow's grid with a flex row and the
   bar went with it. Five columns, and the one carrying the measurement is the
   one allowed to grow. */
.board-row {
  display:grid; grid-template-columns:2em minmax(120px, 1.4fr) minmax(0,3fr) auto auto;
  align-items:center; gap:12px;
}
.board-row .rank { text-align:right; color:var(--ink-3); font-variant-numeric:tabular-nums; }
.board-row .pts  { font-variant-numeric:tabular-nums; font-weight:600; text-align:right; }
.board-row .badges { display:flex; flex-wrap:wrap; gap:5px; justify-content:flex-end; }
@media (max-width: 700px) {
  .board-row {
    grid-template-columns:2em minmax(0,1fr) auto;
    grid-template-areas:"rank who pts" ". bar bar" ". badges badges";
  }
  .board-row .rank { grid-area:rank; } .board-row .who { grid-area:who; }
  .board-row .btrack { grid-area:bar; } .board-row .pts { grid-area:pts; }
  .board-row .badges { grid-area:badges; justify-content:flex-start; }
}

/* ── the badge legend, and badges on a phone ───────────────────────────────
   Deliberately at the END of the file. Every rule below overrides one written
   hundreds of lines above at the same specificity, and the last one wins — the
   same trap that made an earlier phone block silently do nothing. */

/* One row per badge: what it looks like, how you get one, what it is worth. */
.badgelist { display:flex; flex-direction:column; }
.badgerow {
  display:grid; grid-template-columns:minmax(0,10.5em) minmax(0,1fr) auto;
  gap:8px 16px; align-items:center;
  padding:10px 0; border-top:1px solid var(--line-soft);
}
.badgerow:first-child { border-top:0; }
.badgerow .badge { margin:0; justify-self:start; }
.badgehow { color:var(--ink-2); font-size:13.5px; }
.badgepts { font-size:12.5px; color:var(--ink-3); white-space:nowrap; text-align:right;
            font-variant-numeric:tabular-nums; }
.badgepts b { color:var(--ink); font-weight:600; font-size:14.5px; }

@media (max-width: 700px) {
  /* Badge and price on one line, the explanation under both — three columns of
     prose in 350px is three columns of two-word lines. */
  .badgerow {
    grid-template-columns:1fr auto; grid-template-areas:"badge pts" "how how";
    gap:6px 12px; padding:12px 0;
  }
  .badgerow .badge { grid-area:badge; }
  .badgehow { grid-area:how; }
  .badgepts { grid-area:pts; }
}

@media (max-width: 900px) {
  /* Badges were spaced by their own margin — 4px across, 3px down — which on a
     leaderboard card wrapping to three rows read as one striped block rather
     than as separate things you earned. Spacing moves to the containers' gap so
     every group is spaced the same, and the chips themselves grow: 22px is not a
     tap target, and these are the reward the whole page is about. */
  .badge { padding:5px 10px; margin:0; font-size:12px; }
  .pc-badges, .badgekey, .badges, .board .badges, .board-row .badges { gap:7px 6px; }
  .cards.wide .boardcard .pc-badges { margin-top:10px; }
}

/* ── bar rows on a phone ───────────────────────────────────────────────────
   .brow is 180px of label + 1fr + 86px of value. At 390px that is 290px of
   fixed columns plus gaps, leaving the 1fr — the bar, the only part carrying
   the measurement — about forty pixels. "Who raids with whom" was rendering
   everybody's attendance as an identical six-pixel dot.

   The label takes the full width and the bar runs under it with the number at
   its end, so the bar gets the whole row to be long in.

   :not(.board-row) because those rows have five columns and their own phone
   layout a few hundred lines above; without it this would land on top of it. */
@media (max-width: 700px) {
  .brow:not(.board-row) {
    grid-template-columns:minmax(0,1fr) auto;
    grid-template-areas:"lbl lbl" "bar val";
    gap:7px 12px; padding:10px 8px;
  }
  .brow:not(.board-row) .lbl    { grid-area:lbl; }
  .brow:not(.board-row) .btrack { grid-area:bar; align-self:center; }
  .brow:not(.board-row) .val    { grid-area:val; }
  /* Two-line rows need a line between them; 4px of gap that read as a divider
     between one-line rows just reads as a crowd once each row is two. */
  .bars { gap:0; }
  .bars .brow + .brow { border-top:1px solid var(--line-soft); }
}

/* The grade breakdown is the same story one grid over: label / bar / score with
   the explanation spanning the last two columns. At 390px that gave the bar
   about fifty pixels and set the explanation in a 185px trough three words
   wide. Label and score share the first line, the bar gets a whole line of its
   own to be long in, and the explanation gets the full width to read across. */
@media (max-width: 700px) {
  .gradebar {
    grid-template-columns:minmax(0,1fr) auto;
    grid-template-areas:"label score" "bar bar" "detail detail";
    gap:7px 12px; padding:2px 0;
  }
  .gradebars { gap:14px; }
}

/* "17 players" is a count, not a sentence — with the names beside it eating the
   width it was breaking to "17 / players" on every row of the raid's problem
   list. It keeps its line and gives the names the rest. */
.boss .n { white-space:nowrap; }

/* ── a wide table, on a phone ──────────────────────────────────────────────
   RESTORED. labelCells() has been stamping data-label on every cell in the app
   since the rebuild and nothing was reading it — the CSS that turns a table into
   a list of records went with the deleted region. Without it every wide table
   just scrolled sideways, which on Preparation meant a phone showed you names
   and standing and put "missing enchants" — the entire question of the page —
   off the right-hand edge.

   Each row becomes a card, each cell a labelled line. The header is hidden from
   sight but not from screen readers, because the cells still belong to it. */
@media (max-width: 700px) {
  .tbl thead { position:absolute; width:1px; height:1px; margin:-1px;
               overflow:hidden; clip-path:inset(50%); }
  .tbl, .tbl tbody, .tbl tr, .tbl td { display:block; width:auto; }
  /* …but not a row that is meant to be shut. `hidden` is only display:none from
     the user agent, so the line above outranks it and every enchant drawer on
     the page sprang open again — the same trap that had them open in the first
     place, reintroduced one rule later. */
  .tbl tr[hidden] { display:none; }
  .tbl tr {
    border:1px solid var(--line); border-radius:9px; margin-bottom:9px;
    padding:3px 12px 5px; background:var(--card, var(--raised));
  }
  .tbl tr:hover td { background:none; }
  .tbl td {
    display:flex; align-items:baseline; justify-content:space-between;
    /* Wrapping, because a cell's value is not always one thing: the excluded
       instances are three chips in a row, and unwrapped they ran 42px past the
       screen. When a value does not fit beside its label it drops below it. */
    flex-wrap:wrap; gap:6px 14px;
    padding:7px 0; border-top:1px solid var(--line-soft); text-align:left;
  }
  .tbl td::before {
    content:attr(data-label); flex:none;
    font-size:11px; font-weight:600; letter-spacing:.06em;
    text-transform:uppercase; color:var(--ink-3);
  }
  /* The first cell is the record's name, not one of its fields — it reads as a
     heading with the rest listed underneath. Same for any cell whose column had
     no header, which is how the action columns are built. */
  .tbl tbody td:first-child:not([colspan]) {
    border-top:0; padding:8px 0 7px; font-size:15px; font-weight:600;
    display:block;
  }
  .tbl tbody td:first-child::before,
  .tbl td:not([data-label])::before { content:none; }
  .tbl td:not([data-label]) { justify-content:flex-end; }
  /* Right-aligned columns have nothing left to align against. */
  .tbl .r { text-align:left; }
  /* A gauge in a flex cell is a span with a height and no width, so it collapses
     to its fill's 3px minimum — "how close to best in slot" was a sliver on
     every row. Here it is the part of the cell allowed to grow. */
  .tbl td .btrack { flex:1 1 auto; min-width:0; }
  .tbl th:last-child, .tbl td:last-child { padding-right:0; }
  /* A row that spans the table — the enchant drawer — is not a field. */
  .tbl td[colspan] { display:block; border-top:0; padding:4px 0; }
  .tbl td[colspan]::before { content:none; }
  .gapitem { width:100%; flex-wrap:wrap; column-gap:8px; }
  .gapitem .scoped { margin-left:auto; }
  /* Nothing left to scroll sideways, and leaving it on clips the card edges. */
  .scroll { overflow-x:visible; padding-left:0; margin-left:0; }
}

/* ── your characters, on a phone ───────────────────────────────────────────
   These cards carry more per row than any other in the app — a name, a role, up
   to three specs, the workspace, and five numbers — and at 300px wide they were
   all fighting for the same line.

   The stat strip becomes a grid rather than a flex row: a row of five with 16px
   between them squeezes "BEST EVER" against "DEATHS" and lines nothing up
   between one card and the next. Columns wrap into rows, aligned. */
@media (max-width: 700px) {
  .pc-stats {
    display:grid; grid-template-columns:repeat(auto-fit, minmax(5.4em, 1fr));
    gap:13px 10px;
  }
  .pc-stat { min-width:0; }
  /* The specs and "Synergy · since 11 Jun" are two different facts about the
     character, and margin-left:auto was pinning the second to the right edge of
     a line the first had already wrapped onto. It gets its own line. */
  .pc-bot { flex-wrap:wrap; }
  .pc-bot > .chspec { flex:1 1 100%; }
  .pc-bot > .pc-spec { flex:1 1 100%; margin-left:0; }
}
/* The strip's top rule separates it from the card ABOVE it. Used as a panel's
   own opening content there is no card above it — only the panel header, which
   already has a rule — so it drew a second divider twenty pixels under the
   first. Both widths. */
.panel-b > .pc-stats:first-child { border-top:0; margin-top:0; padding-top:0; }

/* ── a boss, opened ────────────────────────────────────────────────────────
   The row is a button now. It keeps the shape it had — mark, name, outcome on
   the right — with a caret in front, so a list that used to be six lines still
   reads as six lines until somebody asks for more. */
/* .boss is a flex ROW — it is also the shape the raid's problem list uses, so it
   keeps that. A boss that can be opened stacks instead: the head is the row, the
   scoreboard goes under it, and side by side they shared the width. */
.boss:has(> .boss-head) { flex-direction:column; align-items:stretch; gap:0; }
.boss-head {
  display:flex; align-items:center; gap:9px; width:100%; padding:5px 0;
  background:none; border:0; cursor:pointer; font:inherit; color:inherit; text-align:left;
}
.boss-head .boss-nm { font-size:13.5px; }
.boss-head .acc-caret { color:var(--ink-3); font-size:11px; transition:transform .15s ease; }
.boss-head[aria-expanded="true"] .acc-caret { transform:rotate(90deg); }
.boss-head:hover .boss-nm { color:var(--ink); }
.boss-head:focus-visible { outline:2px solid var(--ring); outline-offset:3px; border-radius:5px; }
.boss-body { padding:4px 0 12px; }
.boss-body[hidden] { display:none; }

/* A bar behind the number, not beside it: the damage column is read as a
   ranking first and a figure second, and a track wide enough to compare would
   cost the figure its room. */
.bdtbl .minibar {
  display:inline-block; vertical-align:middle; width:64px; height:6px; margin-right:8px;
  background:var(--n800); border-radius:3px; overflow:hidden;
}
.bdtbl .minibar i { display:block; height:100%; background:var(--brand-emerald); border-radius:3px; }
.bdtbl td { white-space:nowrap; }

@media (max-width: 700px) {
  /* Stacked, the bar has a whole line and the label beside it, so it can be as
     wide as the card. */
  .bdtbl .minibar { width:auto; flex:1 1 auto; min-width:40px; margin-right:0; }
  .bdtbl td { white-space:normal; }
}

/* ── the public site ───────────────────────────────────────────────────────
   Signed out, the app is a different product with the same chrome. Nothing here
   introduces a second design language — it is the same panels, the same type
   scale, the same gold — because the whole point of building these as real
   screens rather than mocking them up is to see whether that holds. */
.hero { padding:26px 0 30px; max-width:62ch; }
.hero-h { font-size:38px; line-height:1.15; max-width:18ch; margin:10px 0 12px; }
.hero-p { font-size:17px; line-height:1.6; }
.hero-cta { display:flex; flex-wrap:wrap; gap:10px; margin-top:20px; }
.btn.lg { padding:11px 20px; font-size:14.5px; }

/* Three across where there is room, one where there is not. Each is a claim and
   its justification, so the heading has to be able to stand alone. */
.sells { display:grid; grid-template-columns:repeat(auto-fit, minmax(15rem, 1fr)); gap:20px 26px; }
.sell h3 { font-size:14.5px; margin:0 0 5px; color:var(--ink); }
.sell p { margin:0; color:var(--ink-2); font-size:13.5px; line-height:1.6; }

.steps { margin:0; padding-left:20px; color:var(--ink-2); line-height:1.7; }
.steps li { margin:0 0 9px; }
.steps b { color:var(--ink); }
.steps code, .qa code { font:12.5px var(--mono); background:var(--n950);
                        padding:2px 6px; border-radius:4px; color:var(--ink); }

.price { font-size:30px; font-weight:600; margin:0 0 10px; color:var(--ink); }
.price .per { font-size:14px; font-weight:400; color:var(--ink-3); margin-left:2px; }

.qa { border-top:1px solid var(--line-soft); padding:12px 0; }
.qa:first-child { border-top:0; padding-top:0; }
.qa h3 { font-size:14px; margin:0 0 5px; color:var(--ink); }
.qa p { margin:0; color:var(--ink-2); font-size:13.5px; line-height:1.65; }

/* Sign out is a way OUT, so it does not compete with the sections it sits after.
   No longer nested in .nav — it is a header child now, beside the workspace it
   applies to — so the selector cannot require that ancestor. */
.navout { margin-left:2px; }
/* `hidden` is only display:none from the UA sheet, so any author display rule
   would quietly outrank it — and .navout has one at narrow widths. Stating it
   here means signed-out really means gone, at every width. */
.navout[hidden] { display:none; }
.nav .signin { margin-left:12px; }
.btn.sm, .ghost.sm { padding:5px 11px; font-size:12.5px; }
.drawer .pref { margin-top:16px; display:flex; flex-direction:column; align-items:flex-start; gap:8px; }

@media (max-width: 900px) {
  .hero { padding:14px 0 22px; }
  .hero-h { font-size:29px; max-width:none; }
  .hero-p { font-size:15.5px; }
  .hero-cta .btn { flex:1 1 100%; text-align:center; }
  /* The sections are behind the burger down here, and so is the way out — the
     drawer carries its own Sign out. The WORKSPACE is deliberately not hidden
     with them: it says which account every screen is reading, which is worth a
     row of its own on a phone. */
  .navout { display:none; }
}

/* The drawn boss room. Its parts are named, so the type has to stay legible at
   any width — everything is sized in the SVG's own 0..100 space and scales with
   it, which is why there are no pixel font sizes here. */
.bossroom { margin: 0 0 14px; }
.bossroom svg {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card);
}
.bossroom .rm-floor { fill: color-mix(in oklab, var(--accent) 8%, transparent); stroke: var(--line); stroke-width: .5; }
.bossroom .rf-ground { fill: color-mix(in oklab, var(--accent) 5%, transparent); stroke: none; }
.bossroom .rf-water { fill: color-mix(in oklab, var(--ring) 12%, transparent); }
.bossroom .rf-fire { fill: color-mix(in oklab, var(--critical) 12%, transparent); }
.bossroom .rf-pillar { fill: color-mix(in oklab, var(--ink) 22%, transparent); stroke: var(--line); stroke-width: .4; }
.bossroom .rf-door circle { fill: var(--accent); }
.bossroom .rf-door text { fill: var(--ink); font-size: 3px; text-anchor: middle; opacity: .7; }
.bossroom .rm-boss circle { fill: var(--critical); stroke: var(--card); stroke-width: .8; }
.bossroom .rz circle {
  fill: color-mix(in oklab, var(--accent) 10%, transparent);
  stroke: color-mix(in oklab, var(--accent) 45%, transparent);
  stroke-width: .4; stroke-dasharray: 1.5 1;
}
.bossroom .rz text { fill: var(--ink); font-size: 3.4px; text-anchor: middle; font-weight: 600; }
.bossroom .rz .rz-who { fill: var(--accent); font-size: 3px; font-weight: 500; }
.bossroom .rz.on circle {
  fill: color-mix(in oklab, var(--accent) 20%, transparent);
  stroke: var(--accent); stroke-dasharray: none;
}
