/* MHFU game font (falls back to sans-serif for glyphs it lacks, e.g. ★ — © ) */
@font-face{
  font-family:'MHFU';
  src:url('fonts/mhfu_font.ttf') format('truetype');
  font-display:swap;
}
/* Theme variables are set at runtime from the chosen color (see app.js). */
:root{
  --bg:#3e3e3e; --bg1:#000000; --bg2:#333; --hover:#2f2f2f; --accent:#7a7a7a; --accent-hover:#9a9a9a;
  --text:#f3f3f3; --text-dim:#ffffff; --line:#ffffff1f; --card:#ffffff0d;
  --titlebar-overlay:rgba(0,0,0,0.18);
  --dead:#ff4d4d;
  --fallen-line:#6f6f6f; --fallen-fill:rgba(190,190,190,.12); --fallen-text:#c9c9c9;
}
*{box-sizing:border-box;scrollbar-color:var(--accent) var(--bg2);scrollbar-width:thin}
::-webkit-scrollbar{width:6px;height:6px}
::-webkit-scrollbar-track{background:var(--bg2)}
::-webkit-scrollbar-thumb{background:var(--accent);border-radius:3px}
::-webkit-scrollbar-thumb:hover{background:var(--accent-hover)}
input,select,button,textarea{font-family:inherit}
html,body{margin:0;height:100%;overflow:hidden}
body{
  font-family:'MHFU',"Segoe UI",system-ui,sans-serif;
  background:var(--bg); color:var(--text); font-size:14px;
  user-select:none; -webkit-user-select:none;
}
#app{display:flex;flex-direction:column;height:100vh}
.hidden{display:none !important}

/* Title bar */
.titlebar{display:flex;align-items:center;gap:10px;padding:8px 14px}
.titlebar-header{
  background-image:
    linear-gradient(color-mix(in srgb,var(--accent) 90%,transparent),color-mix(in srgb,var(--accent) 90%,transparent)),
    url('assets/titlebar-background.png');
  background-size:auto 100%;background-position:left center;background-repeat:repeat;
}
.title-icon{width:32px;height:32px;object-fit:contain}
.titlebar h1{font-size:15px;font-weight:600;margin:0;flex:1}
.icon-btn{background:transparent;border:none;color:var(--text);font-size:18px;
  cursor:pointer;padding:6px 9px;border-radius:6px;line-height:1}
.icon-btn:hover{background:#ffffff1a}

/* Layout
   The two rails are FLUID, not fixed. They used to be a hard 300 + 320, which
   meant the board — the one thing you are meant to be able to read at a glance —
   got whatever was left over and then overflowed inside its own column. At 200%
   zoom that left it 63px wide with a horizontal scrollbar under it, while the
   chrome either side stayed full size. The clamp makes the rails give ground
   first: they shrink with the viewport down to a floor that still fits their
   longest control, and the board keeps its ~350px. */
.layout{display:flex;flex:1;min-height:0}
.sidebar{
  width:clamp(220px, 20vw, 300px);
  flex-shrink:0;padding:12px;overflow-y:scroll;scrollbar-gutter:stable;
  display:flex;flex-direction:column;gap:8px;
  background-image:
    linear-gradient(color-mix(in srgb,var(--accent) 90%,transparent),color-mix(in srgb,var(--accent) 90%,transparent)),
    url('assets/sidebar-background-texture1.png');
  background-size:auto 100%;background-position:left center;background-repeat:repeat;
}
.content{flex:1;padding:18px;overflow-y:auto;min-width:0;background-color:var(--bg1)}
/* Everything you act on for the hunt in progress, in its own column. It used to
   sit above the board, where it cost 163px of the height the grid needed — and
   the grid is the thing you are meant to be able to take in at a glance.
   It hides entirely once the run is over: there is nothing to act on, and the
   result screen wants the width back for its three panels. */
.controls{
  width:clamp(235px, 21vw, 320px);
  flex-shrink:0;padding:12px;overflow-y:auto;
  display:flex;flex-direction:column;gap:8px;
  background-image:
    linear-gradient(color-mix(in srgb,var(--accent) 90%,transparent),color-mix(in srgb,var(--accent) 90%,transparent)),
    url('assets/sidebar-background-texture1.png');
  background-size:auto 100%;background-position:left center;background-repeat:repeat;
}
/* Below this there is no width left to divide — 455px of rails at their floor
   against a board that needs ~350 — so stop dividing it. The three columns
   become one scrolling stack, each at full width and its natural height, with
   the board first because that is what you came to look at. html/body stay
   locked; .layout does the scrolling, which is where it was already happening. */
@media (max-width:840px){
  .layout{flex-direction:column;overflow-y:auto}
  .sidebar,.content,.controls{
    width:auto;flex:0 0 auto;overflow:visible;scrollbar-gutter:auto}
  /* Board, then the controls you touch every hunt, then the levers you set once. */
  .content{order:1} .controls{order:2} .sidebar{order:3}
}

/* Buttons */
.btn{background:#ffffff14;color:var(--text);border:1px solid var(--line);
  border-radius:7px;padding:8px 14px;cursor:pointer;font-size:14px}
.btn:hover{background:#ffffff24}
/* Disabled has to be obvious on the accent buttons too — at opacity alone a
   dark accent button barely moves, so it also loses its weight and text shadow. */
.btn:disabled{opacity:.4;cursor:not-allowed}
.btn:disabled:hover{background:#ffffff14}
.btn.accent:disabled{opacity:.45;background:#ffffff10;border-color:var(--line);
  color:var(--text);font-weight:400;text-shadow:none}
.btn.accent:disabled:hover{background:#ffffff10}
.btn.block{width:100%}
.btn.tiny{padding:3px 10px;font-size:12px}
.btn.block,.btn.tiny{
  background-image:
    linear-gradient(color-mix(in srgb,var(--bg2) 80%,transparent),color-mix(in srgb,var(--bg2) 80%,transparent)),
    url('assets/banner-background.png');
  background-position:right;
}
/* Same banner, mirrored to the left edge on full-width buttons — inset the label. */
.btn.block{padding-left:30px}
.btn.accent{background:var(--bg1);border-color:#000;color:#fff;font-weight:600;
  text-shadow:-1px 0 rgba(0,0,0,.55),1px 0 rgba(0,0,0,.55),0 -1px rgba(0,0,0,.55),0 1px rgba(0,0,0,.55)}
.btn.accent:hover{background:var(--bg2)}

/* Collapsible panels */
.panel{border:1px solid var(--line);border-radius:8px;overflow:clip;background:var(--bg2);
  container-type:inline-size}
/* The banner texture sits at the left edge, so the label is inset past it.
   Sized against the PANEL rather than the window: the sidebar is fluid now, and
   at its narrow floor a fixed 22px "Kill Conditions" ran past the edge and was
   silently clipped. cqi means the header shrinks with the rail it lives in,
   which is also what makes it survive the stacked layout unchanged. */
.panel-head{width:100%;text-align:left;background:transparent;border:none;color:var(--text);
  padding:8px clamp(9px,6.1cqi,16px) 8px clamp(12px,11.5cqi,30px);
  font-size:clamp(15px,8.5cqi,22px);font-weight:600;cursor:pointer;
  display:flex;justify-content:space-between;align-items:center;
  background-image:
    linear-gradient(color-mix(in srgb,var(--bg2) 80%,transparent),color-mix(in srgb,var(--bg2) 80%,transparent)),
    url('assets/banner-background.png');
  background-size:auto 100%;background-position:left center;background-repeat:repeat;
}
.panel-head:hover{background-color:#ffffff0d}
/* State on a collapsed panel. A shut panel used to look identical whether the
   thing inside it was on or off, so "collapsed" read as "disabled" — which is
   exactly how someone ends up running with rerolls on by accident. */
.panel-state{margin-left:auto;margin-right:10px;font-size:11px;font-weight:600;
  text-transform:uppercase;letter-spacing:.06em;opacity:.45}
.panel-state.on{opacity:1;color:var(--accent-hover)}
.chev{transition:transform .15s;font-size:20px;font-weight:700;color:transparent}
.panel[data-open="true"] .chev{transform:rotate(180deg)}
.panel-body{display:none;padding:6px 12px 12px;flex-direction:column;gap:4px;
  background-image:
    linear-gradient(color-mix(in srgb,var(--bg1) 60%,transparent),color-mix(in srgb,var(--bg1) 60%,transparent)),
    url('assets/panel-body-bg.png');
  background-size:contain 100%;background-position:left center;background-repeat:repeat;
}
.panel[data-open="true"] .panel-body{display:flex}

/* Checkboxes */
.chk{display:flex;align-items:center;gap:8px;padding:3px 0;cursor:pointer;line-height:1.3}
.chk input{accent-color:var(--accent);width:16px;height:16px;cursor:pointer;flex-shrink:0}
.chk:has(input:disabled){cursor:default}
.chk:has(input:disabled) input{cursor:default}
/* A live run freezes the rules, but they still have to be READABLE — fading the
   whole panel meant you could no longer see what you'd committed to. So instead
   of dimming everything, dim only the options you did NOT take: your own choices
   stay at full strength and the panel becomes a record of the run.
   Radios only. An unticked checkbox is still a decision ("off"), so fading it
   would hide half of what a checkbox is telling you. */
.chk:has(input[type="radio"]:disabled:not(:checked)){opacity:.35}
/* The third state, and the reason the rule above can stay radios-only: an option
   that is not frozen but cannot apply at all. An unticked checkbox is a decision
   and stays readable; this one was never offered, so it dims. */
.chk.na{opacity:.35}
.chk:has(input:disabled):hover{color:inherit}
.chk:has(input:disabled):hover input{accent-color:var(--accent)}
.chk:hover{color:var(--accent-hover)}
.chk:hover input{accent-color:var(--accent-hover,#9a9a9a)}

/* Sub-options that only exist once their master switch is on. The panel body is
   a flex column, so the group has to be one too or the gaps collapse. */
.opt-group{display:flex;flex-direction:column;gap:4px}

.sub-note{margin:2px 0 6px 24px;font-size:11px;opacity:.6;font-style:italic}

/* Difficulty badge on a rule. It lights up exactly when the modifier is being
   applied, so the highlight and the label always agree. */
.chk .w{margin-left:auto;padding-left:8px;font-size:11px;opacity:.6;white-space:nowrap}
/* .on is set by paintBadgeState from cfg, not by :checked. Selection and
   contribution are different things: a revive price stays selected while
   revives are off, and contributes nothing. */
.chk .w.on{opacity:1;color:var(--accent-hover);font-weight:600}

/* The combo count used to be repeated on all six cap options. One readout for
   the chosen cap says the same thing with one chance to be wrong instead of six,
   and it reads as the consequence of the choice rather than part of the label. */
.cap-total{margin-top:12px;padding-top:10px;border-top:1px solid var(--line)}
.cap-total-n{font-size:26px;font-weight:700;line-height:1.15;color:var(--accent-hover)}

.hint{color:var(--text-dim);opacity:.75;font-size:12px;margin:4px 0;line-height:1.45}
.row{display:flex;align-items:center}
.row.gap{gap:8px}.row.end{justify-content:flex-end}

/* Tag lists (fallen loadouts) */
.bl-list{display:flex;flex-direction:column;gap:4px;max-height:320px;overflow-y:auto}
.bl-tag{display:flex;align-items:center;gap:8px;background:var(--card);
  border:1px solid var(--line);border-radius:6px;padding:4px 8px;font-size:12px}
.nuz-tag{border-color:var(--fallen-line);background:var(--fallen-fill)}
.nuz-combo{text-decoration:line-through;opacity:.75;flex:1;white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.nuz-reason{font-size:11px;opacity:.6;white-space:nowrap}

/* ── Status strip ───────────────────────────────────────────────────── */
.status-strip{display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  padding:8px 12px;margin-bottom:12px;border:1px solid var(--line);
  border-radius:8px;background:var(--card);font-size:13px}
.stat b{font-size:15px}
.stat.dead b{color:var(--dead)}

/* ── Hunt bar ───────────────────────────────────────────────────────── */
.hunt-bar{display:flex;flex-direction:column;gap:14px;align-items:stretch;
  padding:12px;border:1px solid var(--line);
  border-radius:8px;background:var(--card)}
.hunt-loadout,.hunt-quest,.hunt-outcome{display:flex;flex-direction:column;gap:6px}
/* Cleared / Carted / Failed sit on one line and overran the rail at its floor.
   They wrap to a second line rather than being clipped — the outcome buttons are
   the last thing that should ever be half off-screen. */
.hunt-outcome .row.gap{flex-wrap:wrap}
.hunt-outcome .row.gap .btn{flex:1 1 auto}
/* Loadout and result stack in one column, quest beside them: the two things you
   act on are the combo and then the outcome for it, so they belong together. */
.hunt-col{display:flex;flex-direction:column;gap:12px}
/* The hint appears and disappears as the state changes. With the outcome now in
   the taller column it drives the whole bar's height, so it holds one line's
   worth of space whether or not it has anything to say. */
#outcomeHint{min-height:17px;margin:2px 0 0}
.hunt-quest{position:relative}
.hl-label{font-size:11px;text-transform:uppercase;letter-spacing:.08em;opacity:.6}
.hl-value{display:flex;align-items:center;gap:8px;font-size:16px;font-weight:600;min-height:26px}
.hl-icon{width:24px;height:24px;object-fit:contain}
/* Stacked, not a row. Three controls side by side came to 301px inside a 271px
   box once the hunt bar moved into a 320px column, and Confirm hung past the
   panel edge. Weapon names are long enough — "Sword & Shield", "Heavy Bowgun" —
   that giving each control the full width is the honest fix rather than
   squeezing them. */
.hl-pick{display:flex;flex-direction:column;gap:6px}
.hl-pick select,.hl-pick button{width:100%}
.hl-pick select{background:var(--bg2);color:var(--text);border:1px solid var(--line);
  border-radius:6px;padding:5px 8px;font-size:13px}

#questSearch{background:var(--bg2);color:var(--text);border:1px solid var(--line);
  border-radius:6px;padding:6px 10px;font-size:13px;width:100%}
.quest-results{position:absolute;top:56px;left:0;right:0;z-index:20;max-height:260px;
  overflow-y:auto;background:var(--bg2);border:1px solid var(--accent);border-radius:6px}
.quest-results button{display:block;width:100%;text-align:left;padding:6px 10px;
  cursor:pointer;font-size:12px;line-height:1.3;background:transparent;border:none;
  color:var(--text)}
.quest-results button:hover,.quest-results button:focus-visible{background:var(--hover);outline:none}
.quest-results button:focus-visible{box-shadow:0 0 0 2px var(--accent-hover) inset}
.quest-results button{display:flex;align-items:baseline;gap:6px}
.quest-results .qr-type{opacity:.55;font-size:11px;flex:1;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.quest-results .qr-worth{font-size:11px;color:var(--accent-hover);white-space:nowrap}
/* Sits under the outcome buttons, so it wants to be smaller than a sidebar rule
   and to stop short of the full row width — it is a note about this hunt, not
   another control competing with Cleared/Carted/Failed. */
.hunt-ins{font-size:12px;gap:6px;padding:6px 0 2px;align-self:flex-start}
.hunt-ins input{width:14px;height:14px}
/* One-faint permits. Deliberately not themed: this is a property of the quest,
   not of the run, and it has to read the same on every palette. */
.quest-results .qr-faint{font-size:10px;font-weight:600;white-space:nowrap;
  color:#ffd9d9;background:#8d1a1a;border-radius:4px;padding:1px 5px}
.quest-results .qr-none{opacity:.55;padding:6px 10px;margin:0;font-size:12px}
/* Sized and bordered up front so nothing moves later: the border and padding are
   always present and merely transparent until the lock engages, and the box
   holds two lines of title whether or not a quest has been named. Between them
   they cover the two things that used to shift the hunt bar — naming a quest,
   and failing one. */
.quest-chosen{font-size:14px;font-weight:600;border:1px solid transparent;
  border-radius:6px;padding:6px 10px;min-height:50px;
  display:flex;align-items:center;flex-wrap:wrap}
.quest-chosen.empty{visibility:hidden}
#questSearch:disabled{opacity:.5;cursor:not-allowed}
.quest-chosen .qc-arena{color:var(--accent-hover);font-size:11px;font-weight:400}
.quest-chosen.locked{border-color:var(--accent);background:#ffffff0d}
/* Training Codex, used wherever the run has something locked */
.lock-icon{width:16px;height:16px;object-fit:contain;vertical-align:-3px;flex-shrink:0}
/* Inline, not block — a block note added a line on activation. Hidden rather
   than removed when the lock is off, so the space stays reserved either way. */
.quest-chosen .qc-note{font-size:11px;font-weight:400;opacity:.85;
  color:var(--accent-hover);margin-left:8px;white-space:nowrap}
.quest-chosen .qc-note.off{visibility:hidden}
/* Pushed to the far end by margin-left:auto so it holds the same spot whatever
   the quest is called, rather than trailing a name of unpredictable length. */
.quest-chosen .qc-clear{margin-left:auto;flex-shrink:0;
  background:transparent;border:1px solid var(--line);border-radius:5px;
  color:var(--text);opacity:.55;cursor:pointer;
  font-size:15px;line-height:1;padding:1px 7px 3px}
.quest-chosen .qc-clear:hover{opacity:1;background:#ffffff1a;border-color:var(--accent)}

/* Difficulty multiplier */
.mult-box{display:flex;flex-direction:column;gap:2px;
  padding:7px 10px;border:1px solid var(--accent);border-radius:7px;
  background:#ffffff0d}
.mult-box.negative{border-color:var(--dead)}
.mult-box.negative .mult-value{color:var(--dead)}
.mult-label{font-size:11px;text-transform:uppercase;letter-spacing:.08em;opacity:.65}
.mult-value{font-size:19px;font-weight:700}
/* The payoff sits under the price, smaller — it's the same fact read the other
   way round, not a second statistic. */
.mult-bonus{font-size:14px;font-weight:600;color:var(--accent-hover)}
.mult-box.locked .mult-value,.mult-box.locked .mult-bonus{opacity:.75}

.stat.earned b{color:var(--accent-hover)}

/* Rules frozen for the duration of a run */
.lock-note{border:1px solid var(--accent);border-radius:6px;padding:6px 8px;
  background:#ffffff0d;opacity:1}
.panel-body:has(input:disabled) .hint{opacity:.5}

.outcome-clear:hover{border-color:#4caf50}
.outcome-cart:hover{border-color:#ffa726}
.outcome-fail:hover{border-color:var(--dead)}

/* ── Board ──────────────────────────────────────────────────────────── */
/* Only shown when both pages have something on them — during a run with a
   previous result to look back at, or after one with the board still worth
   inspecting. */
.view-tabs{display:flex;gap:4px;margin-bottom:12px}
.view-tab{background:#ffffff10;color:var(--text);border:1px solid var(--line);
  border-radius:7px;padding:5px 14px;cursor:pointer;font-size:13px;opacity:.65}
.view-tab:hover{background:#ffffff1c;opacity:.85}
.view-tab.on{opacity:1;font-weight:600;background:var(--bg2);border-color:var(--accent)}

.board{display:flex;flex-direction:column;gap:14px}
/* Each grid is its own container so the headers can size against the columns
   they sit in rather than against the window. Browser zoom shrinks the CSS
   viewport, which narrows these columns — at a fixed 11px the headers bunched
   and clipped rather than shrinking with the space they had.
   The column minimums are low so the board keeps shrinking rather than pushing
   a horizontal scrollbar — at 150% zoom a 40px floor was already overflowing. */
/* The 1px gap, and the cells' square corners, are about the Hermann grid
   illusion — bright patches on a dark field separated by narrow dark corridors
   make illusory grey blobs flicker at every crossing. Three things drove it:
     - rounded corners. At radius 5 with a 3px gap, four cells meeting opened
       the corridor into a ~13px plaza: over 4x the corridor width, and it is
       that widening at the crossings that produces the blob.
     - corridor width. Effect strength falls off sharply as the corridor thins,
       so 3px -> 1px does most of the rest.
     - contrast. Weapon colours ran up to 13.7:1 against the near-black board,
       and the illusion is driven by luminance difference.
   Do not reintroduce a border-radius on .cell — it is what created the plazas.

   The grid deliberately has NO background of its own. A fixed bed was tried, to
   stop translucent states picking up the theme, and it worked but turned the
   board into one flat slab. Theme independence is achieved a better way now:
   nothing in the grid is translucent. Every dimmed state is dimmed with a
   FILTER over opaque pixels rather than with alpha, so there is nothing for the
   theme behind to show through, and the board keeps sitting on the page instead
   of on a panel. Keep it that way — reach for brightness()/grayscale(), never
   opacity, on anything in here. */
.board-grid{display:grid;grid-template-columns:auto repeat(6,minmax(36px,1fr));
  gap:1px;align-items:stretch;container-type:inline-size}

.bh{font-size:clamp(7px, 1.5cqi, 11px);text-transform:uppercase;letter-spacing:.04em;
  opacity:.6;display:flex;align-items:center;justify-content:center;padding:2px;
  text-align:center;overflow-wrap:anywhere}
.bh.corner{opacity:0}
/* Thin dark outline so the weapon colour reads at any lightness — Insect Glaive
   is near white and Switch Axe grey, both of which wash out against the board
   without it. Four-way text-shadow, the same trick .btn.accent already uses. */
.brow-label{display:flex;align-items:center;gap:6px;padding:0 8px 0 2px;
  font-size:12px;font-weight:700;white-space:nowrap;
  text-shadow:-1px 0 rgba(0,0,0,.85),1px 0 rgba(0,0,0,.85),
              0 -1px rgba(0,0,0,.85),0 1px rgba(0,0,0,.85)}
.brow-label img{width:calc(var(--cell-h, 34px) * .6);
  height:calc(var(--cell-h, 34px) * .6);object-fit:contain;max-width:20px;max-height:20px}
/* A row label swaps between the weapon's abbreviation, "Retired" and
   "Selected". "Selected" is the widest of the three — 51px against 47px for the
   longest abbreviation, Hammer — and the label column is auto-sized, so the
   first time anything was selected the column grew and shifted the whole grid
   sideways mid-run.
   The invisible ::after reserves that width. Doing it this way rather than with
   a hardcoded min-width means the gutter is measured in the real MHFU font at
   the real size, so it stays correct if the font, size or wording changes. The
   flex column takes the widest of its items for width while the zero-height
   sizer contributes nothing to the row's height. */
.bl-t{display:inline-flex;flex-direction:column}
.bl-t::after{content:"Selected";height:0;overflow:hidden;visibility:hidden}
/* Row height scales with the window rather than sitting at a fixed 34px: fifteen
   rows plus the hunt bar and status strip overflowed a 720px-tall screen by
   171px, and the board is the thing you are meant to be able to see at a glance.
   Tall screens get the full 34px — reached around 860px, since moving the
   controls into their own column gave the board back the 163px the hunt bar was
   taking. Tuned for the narrower column a live run leaves, where the status
   strip and legend wrap to two and three lines and cost ~50px between them. Everything inside a row is sized off the same variable, so the bone
   and weapon icons come down with it rather than setting a floor. */
.board{--cell-h:clamp(21px, 3.82vh, 34px)}
/* Each row is tinted with its weapon's colour — the same one already used on the
   row label — so a row is identifiable without reading across to the label.
   --wc is set per cell in renderBoard. Kept low: the tint is for orientation,
   and it must not compete with the states drawn on top of it. */
/* A cell IS its weapon's icon rather than a block of its colour. The _tinted
   assets already carry each weapon's own palette (great sword red, hammer
   lavender) with black linework, so they can be used directly instead of being
   masked to a silhouette the way the bone has to be.
   Every state now acts on the ICON rather than on the box, which is why the
   icon lives on ::before with the box left as a plain bed: a filter on .cell
   would take the bed and the border with it, and drop-shadow would trace the
   rectangle instead of the weapon.
   Square corners are load-bearing — see the Hermann grid note on .board-grid. */
.cell{border:1px solid rgba(255,255,255,.045);border-radius:0;
  background:var(--wc, #232a3c);position:relative;
  min-height:var(--cell-h, 34px);display:flex;align-items:center;justify-content:center;
  font-size:11px;transition:background .12s,border-color .12s}
/* The icon shows on cells that are still AVAILABLE, and nowhere else. It is off
   by default and switched on by .alive, so the two states that are out of play
   lose it without needing to say so: fallen carries a bone instead, retired
   carries nothing. What is left on the board is what you can still use. */
.cell::before{content:"";position:absolute;inset:0;display:none;
  background-image:var(--wi);background-repeat:no-repeat;background-position:center;
  background-size:auto calc(var(--cell-h, 34px) * .74);
  transition:filter .12s}
/* Available: the border carries the weapon's own colour, so a row reads as a
   run of coloured frames and the ones you have spent read as dark ones. Mixed
   down toward a fixed dark rather than given an alpha — an opaque border cannot
   pick up the theme behind it, which is the rule for everything in this grid.
   .cell.revived overrides this on purpose: gold outranks the weapon colour,
   because having bought a combo back is the more useful thing to know. */
/* The style, tagged in the corner. The column header already says it, but the
   header is a long way from a cell halfway down a fourteen-row board — this
   answers "which style is this" without tracking back up the column.
   Shown where the icon is, on cells still in play. A fallen or retired cell is
   not something you are choosing between, and the tag would only crowd the bone.
   Dark, because every available cell is a lightened weapon colour: black clears
   7.8:1 against the darkest of them, Great Sword.

   Anchored to the ICON, not to the cell. Pinned to the cell's corner it drifted
   badly: the icons are square and painted at .74 of the row height, so on a
   93px-wide cell the icon's right edge is 34px inside the cell's, and the tag
   sat out in empty colour. 50% is the icon's centre, .37 of the row height is
   half its width, so that offset lands exactly on its edge -- and since the art
   is square the same figure describes the vertical.

   Legibility does NOT come from placement, which was measured and cannot work.
   The icons fill their canvas and are busy in every corner: ink under a
   tag-sized patch runs 0-49% depending on weapon and corner, and no corner is
   under 12% on all fourteen. Six of them have no corner below 20% at all, so
   there is no position -- per row or otherwise -- that lands on empty art
   everywhere, and moving it per weapon would scatter the letters across four
   corners and make the board harder to read rather than easier.
   So the letter carries its own contrast: near-black on a white halo, the same
   four-way text-shadow .brow-label uses to survive a pale weapon colour. Dark
   glyph reads against the light cell, white ring reads against the icon's black
   linework, and it no longer matters what is underneath. */
.stag{position:absolute;line-height:1;pointer-events:none;
  right:calc(50% - var(--cell-h, 34px) * .40);
  bottom:calc(var(--cell-h, 34px) * .09);
  font-size:clamp(7px, calc(var(--cell-h, 34px) * .3), 11px);
  font-weight:700;letter-spacing:-.02em;color:#0a0d14;display:none;
  text-shadow:-1px 0 #fff, 1px 0 #fff, 0 -1px #fff, 0 1px #fff,
              -1px -1px #fff, 1px -1px #fff, -1px 1px #fff, 1px 1px #fff}
.cell.alive .stag{display:block}
.cell.alive::before{display:block}
.cell.alive{cursor:default;
  background:color-mix(in srgb, var(--wc, #ffffff) 82%, #ffffff);
  border-color:color-mix(in srgb, var(--wc, #ffffff) 62%, #0b0f18)}

/* Fallen: the icon is gone and a bone sits in its place. Removing the weapon
   entirely is the point — the slot reads empty-and-marked rather than dimmed,
   so it cannot be confused with retired.
   The border matches retired exactly. It used to be dotted, which made the
   frame do work the contents already do: bone versus dulled icon separates the
   two states on its own, and a second signal only competed with it. Dashed is
   left to mean one thing — this one can be bought back. */
/* Fallen goes to a flat charcoal. Losing the hue is the point: every other
   state is some shade of the weapon's own colour, so the one that is out of the
   run for good is the one with no colour left in it. The bone stays weapon
   tinted, which is what says WHICH combo fell. */
.cell.dead{background:#32363d;border-color:rgba(0,0,0,.5)}
/* The bone takes the weapon's colour without losing its linework.
   The asset is a black-outlined white bone — 1124 black pixels to 677 white —
   so masking it flattened every opaque pixel to one colour and the outlines
   went with them. Instead the icon is multiplied over a solid --wc: white x
   colour is the colour, black x colour is black, so the body tints and the
   lines stay. The mask is still there, but only to clip the colour layer to the
   bone's silhouette rather than leaving an 18px square behind it. */
.cell.dead::after{content:"";position:absolute;left:50%;top:50%;
  width:calc(var(--cell-h, 34px) * .62);height:calc(var(--cell-h, 34px) * .62);
  transform:translate(-50%,-50%);
  background-image:url('assets/ItemIcons/MH4G-Bone_Icon_White.webp'),
                   linear-gradient(var(--wc, #ffffff), var(--wc, #ffffff));
  background-size:contain;background-repeat:no-repeat;background-position:center;
  background-blend-mode:multiply;
  -webkit-mask:url('assets/ItemIcons/MH4G-Bone_Icon_White.webp') center / contain no-repeat;
  mask:url('assets/ItemIcons/MH4G-Bone_Icon_White.webp') center / contain no-repeat;}

/* Retired: still there, out of play. Desaturated and pushed down rather than
   removed, which is what separates it from fallen — one slot is empty and
   marked, the other is full but dulled.
   Grayscale is doing most of the separating, since every live icon is coloured,
   so the dimming only has to be enough to read as out-of-play — pushed too far
   and the slot looks empty, which is the fallen reading. */
.cell.retired{background:color-mix(in srgb, var(--wc, #ffffff) 45%, #000000);
  border-color:rgba(0,0,0,.5)}

/* The crosshair. A weapon lights its row, a style lights its column, and the
   selection is wherever they cross — which is the reading a 14x6 grid can
   actually deliver. Every single-cell marker before this failed for the same
   reason: one cell in eighty-four is hard to find no matter how it is dressed.
   The arms are deliberately quieter than the intersection. They only have to
   say "this row" and "this column"; the crossing says "here", and it is the
   only thing carrying the full yellow outline. Tinted borders rather than a
   wash, so a lit row still shows which combos in it have fallen. */
/* Drawn as RAILS, not as a tint on each cell's outline. Tinting the whole
   border was the first attempt and it barely registered: a 1px muted line
   around each of six cells does not add up to a row, and the column got away
   with it only because fourteen stacked segments happen to look like a line.
   A 2px bar along the top and bottom of the row, and down the left and right of
   the column, reads as one continuous rail instead. Inset shadows, so it costs
   no layout and cannot shift the grid.

   The row is BLUE, the column is RED, and where they cross is PURPLE — the
   crossing looks like the two arms producing it rather than a third thing.

   Red is on the COLUMN deliberately. A row runs the length of ONE weapon, so a
   red row arm sat on red for the whole of Great Sword and Charge Blade; a
   column crosses all fourteen, so red now meets a red cell twice out of
   fourteen instead of for an entire row. Whichever hue an arm takes, the row is
   the one that can collide completely, so it gets the colour that fewest
   weapons are near.

   It is declared, not blended. True mixing would need mix-blend-mode and
   translucent arms, and translucent will not work here: the cells are opaque
   weapon colours, so a see-through bar takes their hue and the arm loses its
   identity entirely.

   Vivid hues rather than the dark ones that would clear 3:1 unaided — a maroon
   does not read as "red", which is the entire point of the colour. They stay
   legible because the crosshair also darkens the cell BORDER, backing every
   rail with a dark line at no extra thickness. Without it, red-on-red measures
   2.1:1 on Great Sword and Charge Blade.

   box-shadow does not merge across rules, so the combinations are spelled out. */
/* The crosshair is its OWN shapes, laid over the grid — three empty divs placed
   as grid items, not styling borrowed from the cells they pass over. That keeps
   the cells free to say what they are (available, fallen, retired) while the
   crosshair says where you are, and it means the shapes span the 1px gaps, so
   each arm is one unbroken rectangle instead of a row of separate outlines.
   pointer-events:none matters — a fallen cell under the arm is still clickable
   to buy back, and an overlay would otherwise swallow that.
   The dark ring outside the border keeps the vivid hues legible against their
   own weapon: red on Great Sword measures 2.1:1 unaided. */
.cross-row,.cross-col,.cross-hit{
  pointer-events:none;border:2px solid;border-radius:1px;
  box-shadow:0 0 0 1px rgba(0,0,0,.55), inset 0 0 0 1px rgba(0,0,0,.55)}
.cross-row{border-color:#1a3ce8;z-index:2}
.cross-col{border-color:#e10000;z-index:2}
/* Above both arms, so the square they share reads as the answer rather than as
   the place two lines happen to overlap. */
.cross-hit{border-color:#a020e0;z-index:3}
/* The headers take the arm's colour too, so each arm is identifiable from the
   edge of the board rather than only where it crosses. */
.bh.col-on{opacity:1;color:#ff5a5a;font-weight:700}
.brow-label.row-on .bl-t{color:#6f8cff}

/* Selected: the crossing, committed. Purple, because that is what the red row
   and the blue column make where they meet — the marker is the arms arriving
   rather than a separate idea.
   The purple is SANDWICHED between two dark lines. Bare, it would repeat the
   failure this replaced: the old glow was drawn in var(--wc) and measured
   1.02:1 against its own cell, and a bare purple runs 2.44:1 on Charge Blade
   for the same reason — a colour cannot separate from a cell of similar
   lightness. The dark does the separating on every hue, the purple does the
   identifying. Same trick .brow-label uses to keep a pale weapon name legible.
   The glow is INSET. A blur radius on an inset shadow lights the cell inward
   from its own edges, which the old outer glow could never do: that one bled
   across the 1px gap onto neighbouring cells and merged into a band down a row.
   Contained, it reads as one lit square. */
.cell.current{z-index:1;
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.9),
             inset 0 0 0 3px #b429f0,
             inset 0 0 0 4px rgba(0,0,0,.9),
             inset 0 0 12px 3px rgba(180,41,240,.85)}

.cell.revived{border-color:#c9a227}
/* A fallen combo you can still buy back */
.cell.dead.revivable{cursor:pointer;border-style:dashed;border-color:#c9a227}
.cell.dead.revivable::after{filter:brightness(.7)}
.cell.dead.revivable:hover::after{filter:none}
.cell.dead.revivable:hover{background:#c9a2272e;box-shadow:0 0 0 1px #c9a227 inset}

/* Revive options */
.revive-options{display:flex;flex-direction:column;gap:8px;margin:10px 0 14px}
.revive-opt{display:flex;flex-direction:column;gap:3px;width:100%;text-align:left;
  padding:10px 12px;border:1px solid var(--line);border-radius:8px;
  background:var(--card);color:var(--text);cursor:pointer;font-size:13px}
.revive-opt:hover,.revive-opt:focus-visible{border-color:#c9a227;background:#c9a22714;outline:none}
.ro-line{display:flex;gap:8px;align-items:baseline}
.ro-qty{color:var(--accent-hover);min-width:26px;font-weight:600}
.ro-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ro-val{opacity:.55;font-size:11px;font-variant-numeric:tabular-nums}
.ro-total{align-self:flex-end;font-weight:700;color:#c9a227;font-variant-numeric:tabular-nums}
/* Paying out of score rather than materials — visually the odd one out */
.revive-opt.pay-zenny{border-style:dashed}
.revive-opt.pay-zenny:hover,.revive-opt.pay-zenny:focus-visible{border-color:var(--dead);background:#ff2a2a14}
.revive-opt.pay-zenny .ro-total{color:var(--dead)}
.ro-note{font-size:11px;opacity:.6}
/* The history here is worth keeping: this marker went through a colour-based
   design first and every version failed on some row. A same-colour glow had
   nothing to separate from, no fixed accent beat all the hues, and a white fill
   sat at 1.09:1 against Insect Glaive. What worked was a SHAPE — it does not
   have to out-contrast anything. That finding is why the board is icons now, so
   the marker no longer has to fight the cell it sits on: it glows in the
   weapon's own colour, which used to be the one thing that could not work. */

/* ── Placeholder / summary ──────────────────────────────────────────── */

/* Queried as containers, not against the viewport: the sidebar takes 300px, so
   these are narrow long before the window is. Two levels, because the columns
   respond to the card while the score responds to its own column. */
.summary{max-width:1400px;margin:20px auto;padding:20px;border:1px solid var(--line);
  border-radius:10px;background:var(--card);container-type:inline-size}
.summary h2{margin:0 0 4px;font-size:26px}
.summary .sub{opacity:.7;margin:0 0 18px;font-size:13px}

/* Rules on the left, the score in the middle, what fell on the right. */
/* Rules are short labels, the centre seats four tiles across, and the quest log
   carries full quest names — so the log gets nearly as much room as the score. */
.sum-cols{display:grid;gap:14px;align-items:start;margin-bottom:16px;
  grid-template-columns:minmax(165px,0.75fr) minmax(0,1.5fr) minmax(230px,1.4fr)}
/* Container queries measure the content box, so this is ~700 + the card's 42px
   of padding and border — a window around 1080px wide. */
@container (max-width:700px){ .sum-cols{grid-template-columns:1fr} }
.sum-panel{padding:14px;border:1px solid var(--line);border-radius:10px;background:#ffffff08}
.sum-center{container-type:inline-size}
.sum-panel h3{margin:0 0 10px;font-size:15px;font-weight:600;opacity:.8;
  text-transform:uppercase;letter-spacing:.06em}
.sum-rules{display:flex;flex-direction:column;gap:9px}
.sr-row{display:flex;flex-direction:column;gap:1px;min-width:0}
.sr-row span{font-size:10px;text-transform:uppercase;letter-spacing:.06em;opacity:.55}
.sr-row b{font-size:12px;font-weight:600;line-height:1.35}
/* The score is the whole point of the screen, so it gets the top and the size. */
.sum-hero{text-align:center;padding:16px 12px;margin-bottom:10px;border:1px solid var(--line);
  border-radius:10px;background:#ffffff0a}
.sum-hero b{display:block;line-height:1.1;font-variant-numeric:tabular-nums;
  color:var(--accent-hover);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Sized off the card rather than stepped, so a long total shrinks to fit instead
   of being clipped. The fit classes only set how fast it gives ground. */
.sum-hero b{font-size:clamp(26px,26cqw,52px)}
.sum-hero b.fit-s{font-size:clamp(22px,16cqw,46px)}
.sum-hero b.fit-xs{font-size:clamp(19px,12cqw,40px)}
.sum-hero b.fit-xxs{font-size:clamp(16px,9cqw,32px)}
.sum-hero span{font-size:12px;opacity:.6;text-transform:uppercase;letter-spacing:.08em}

.sum-stats{display:grid;gap:10px;margin-bottom:10px}
/* Fixed counts rather than auto-fit: the two rows carry different kinds of
   number and reflowing them into one another would lose that split. */
.sum-stats.cols-4{grid-template-columns:repeat(4,1fr)}
.sum-stats.cols-3{grid-template-columns:repeat(3,1fr)}
.sum-stats.last{margin-bottom:0}
/* Against the centre column, so the row folds when that column gets tight. */
@container (max-width:300px){ .sum-stats.cols-4{grid-template-columns:repeat(2,1fr)} }
.sum-stat{text-align:center;padding:10px 6px;border:1px solid var(--line);
  border-radius:8px;background:#ffffff0a}
.sum-stat b{display:block;font-size:22px;line-height:1.2;font-variant-numeric:tabular-nums;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Step the type down as the value gets longer so it stays inside its tile. */
.sum-stat b.fit-s{font-size:18px}
.sum-stat b.fit-xs{font-size:15px}
.sum-stat b.fit-xxs{font-size:13px}
.sum-stat span{font-size:11px;opacity:.6;text-transform:uppercase;letter-spacing:.05em}
.sum-roll{max-height:420px;overflow-y:auto;display:flex;flex-direction:column;gap:4px}
.sum-roll .bl-tag{font-size:11px;flex-wrap:wrap;row-gap:1px}
.sum-roll .sr-n{opacity:.5;min-width:22px}
/* Quest names are long. Give the reason its own line under the combo rather than
   squeezing both onto one and truncating the name that identifies the hunt. */
.sum-roll .nuz-combo{flex:1 0 auto}
.sum-roll .nuz-reason{flex:1 0 100%;margin-left:30px;white-space:normal;line-height:1.35}

/* ── Modals ─────────────────────────────────────────────────────────── */
.modal{position:fixed;inset:0;background:rgba(0,0,0,.6);display:flex;
  align-items:center;justify-content:center;z-index:100;padding:20px}
.modal-card{background:var(--bg2);border:1px solid var(--line);border-radius:10px;
  padding:20px;max-width:560px;width:100%;max-height:82vh;overflow-y:auto}
.modal-card h2{margin:0 0 12px;font-size:22px}
/* Section headings in the help modal — the content outgrew one flat list. */
.modal-card h3{margin:18px 0 6px;font-size:15px;text-transform:uppercase;
  letter-spacing:.06em;opacity:.75}
.modal-card p,.modal-card li{line-height:1.55;font-size:13px}
.modal-card ol,.modal-card ul{padding-left:20px;margin:6px 0}
.modal-card li{margin:3px 0}
.modal-card a{color:color-mix(in srgb, hsl(from var(--accent) calc(h + 180) 75% 60%) 50%, var(--text))}
.modal-card .muted{color:var(--text-dim);font-size:13px;opacity:.75}

/* Other MHGU apps modal */
.app-links{list-style:none;margin:0 0 14px;padding:0;display:flex;flex-direction:column;gap:10px}
.app-links li{border:1px solid var(--line);border-radius:8px;padding:9px 12px;background:#ffffff08}
.app-links li.current{opacity:.72}
.app-links a,.app-name{font-weight:600;font-size:14px}
.app-here{font-weight:400;font-size:11px;opacity:.7;border:1px solid var(--line);border-radius:6px;padding:1px 7px;margin-left:7px}
.app-desc{display:block;margin-top:3px;font-size:12px;opacity:.8;line-height:1.4}

/* Matches the Randomizer's theme picker: square tiles, icon above a label bar. */
.swatches{display:grid;grid-template-columns:repeat(4,1fr);gap:9px;margin-bottom:15px;
  user-select:none;-webkit-user-select:none}
.swatch{aspect-ratio:1;border-radius:8px;border:2px solid transparent;cursor:pointer;
  position:relative;overflow:hidden}
.swatch.sel{border-color:#fff}
.swatch-icon{position:absolute;top:7%;left:50%;transform:translateX(-50%);width:60%;
  pointer-events:none;filter:drop-shadow(0 1px 2px rgba(0,0,0,.45))}
.swatch span{position:absolute;bottom:0;left:0;right:0;text-align:center;font-size:10px;
  line-height:1.5;color:#fff;background:rgba(0,0,0,.45)}
/* Some monsters wear two colours — Bloodbath its navy and its bloodred,
   Boltreaver an Astalos green under its lightning — so those tiles carry a pip
   for each. The pips are painted from the VARIANTS table in JS rather than here,
   so they cannot drift from the colours they select, which also makes the row a
   legend and not just a control. Nothing below is tied to there being two: the
   row centres whatever it is given. Sat above the label bar (15px), not over. */
.pips{position:absolute;left:0;right:0;bottom:18px;display:flex;justify-content:center;gap:5px}
.pip{width:11px;height:11px;padding:0;border-radius:50%;cursor:pointer;
  appearance:none;-webkit-appearance:none;
  border:1px solid rgba(0,0,0,.5);box-shadow:0 1px 2px rgba(0,0,0,.45)}
.pip:hover{border-color:#ffffffb0}
/* White ring for the live state, with a dark halo outside it so the ring reads
   on the pale pips as well as the dark ones. */
.pip.on{border-color:#fff;box-shadow:0 0 0 2px rgba(0,0,0,.45)}
