/* Everything Pico has an opinion about is Pico's: colour, typography, forms,
   buttons, tables, cards, nav, dropdowns. This file adds only what a CSS
   framework cannot know about -- item quality colours, a price chart, the
   grids this app lays content out in.

   If a rule here is fighting Pico rather than extending it, the rule is wrong.
   Every value below is derived from a `--pico-*` token so the two layers stay
   in step through theme changes. */

/* --- density -------------------------------------------------------------
   Pico scales its root type with the viewport, 100% up to 131% at 1536px.
   That is right for prose and wrong for a dashboard: at 125% a node grid and
   a price table stop fitting on a screen. Pinning the root size opts out of
   the scaling without overriding a single one of Pico's own rules. */
:root { --pico-font-size: 100%; }

/* --- nav brand -------------------------------------------------------------
   Pico sizes the brand link like any other nav item, which reads as one more
   link rather than the app's name. */
.brand { font-size: 1.5rem; }

/* --- domain colour -------------------------------------------------------
   Item quality is the game's own palette. Players read these hues faster than
   any label, so they are the one place this app overrides Pico's colours
   outright rather than deriving from them. Light and dark need different
   values only because several are unreadable on one background or the other. */
:root {
  --q-poor: #6f6f6f;
  --q-common: #2a2f3c;
  --q-uncommon: #128a12;
  --q-rare: #1268c9;
  --q-epic: #7a2ec4;
  --q-legendary: #b85c00;
  --q-artifact: #8a6f28;
  --q-heirloom: #007f9f;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --q-poor: #9d9d9d;
    --q-common: #e4e6ec;
    --q-uncommon: #1eff00;
    --q-rare: #4a9dff;
    --q-epic: #b463f0;
    --q-legendary: #ff8000;
    --q-artifact: #e6cc80;
    --q-heirloom: #00ccff;
  }
}

[data-theme="dark"] {
  --q-poor: #9d9d9d;
  --q-common: #e4e6ec;
  --q-uncommon: #1eff00;
  --q-rare: #4a9dff;
  --q-epic: #b463f0;
  --q-legendary: #ff8000;
  --q-artifact: #e6cc80;
  --q-heirloom: #00ccff;
}

.quality-poor { color: var(--q-poor); }
.quality-common { color: var(--q-common); }
.quality-uncommon { color: var(--q-uncommon); }
.quality-rare { color: var(--q-rare); }
.quality-epic { color: var(--q-epic); }
.quality-legendary { color: var(--q-legendary); }
.quality-artifact { color: var(--q-artifact); }
.quality-heirloom { color: var(--q-heirloom); }

/* --- text helpers -------------------------------------------------------- */
.muted, .hint, .sub, .load-text, .rank-label { color: var(--pico-muted-color); }
.hint, .sub, .load-text, .rank-label { font-size: .85em; }
.good { color: var(--pico-ins-color); }
.bad { color: var(--pico-del-color); }
.now { font-size: 1.25rem; font-weight: 600; }

/* --- layout grids --------------------------------------------------------
   Pico's `.grid` is equal columns that collapse to one at every breakpoint.
   These wrap by content width instead, which is what a wall of item cards or
   node cards actually wants. */
.card-grid, .node-grid {
  display: grid;
  gap: var(--pico-block-spacing-horizontal);
  grid-template-columns: repeat(auto-fill, minmax(21rem, 1fr));
}
.stat-grid {
  display: grid;
  gap: var(--pico-block-spacing-horizontal);
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  margin-bottom: var(--pico-block-spacing-vertical);
}
/* Quality ranks sit side by side inside an item card, so the minimum has to
   be small enough that two fit within one card's width -- at 11rem they
   silently stacked, which reads as two separate items. */
.rank-cols {
  display: grid;
  gap: .75rem;
  grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
}
.two-col {
  display: grid;
  gap: var(--pico-block-spacing-horizontal);
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}

/* A table wider than its column scrolls itself rather than the page. */
.scroll-x { overflow-x: auto; }

/* --- small compositions --------------------------------------------------
   `.stat` and `.rank-col` are label-over-value pairs. Pico has no such
   component, and a <dl> puts them side by side rather than stacked. */
/* Each figure in its own tile. A row of bare label/value pairs reads as one
   run-on sentence; boxing them makes the count of things being reported
   obvious before any of them is read. */
.stat {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .15rem;
  padding: .55rem .7rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  background: var(--pico-card-background-color);
}
/* An icon beside the label/value pair rather than above it -- `.has-icon`
   switches the tile to a row instead of forking `.stat` itself, so every
   other tile that never asked for an icon (panels, gear cells, admin
   figures) keeps the plain stacked layout. */
.stat.has-icon { flex-direction: row; align-items: center; gap: .55rem; }
.stat.has-icon .stat-text { display: flex; flex-direction: column; gap: .15rem; }
.stat .icon { flex-shrink: 0; color: var(--pico-primary); }
/* `align-items: start` matters: a flex column stretches its children, which
   pulled the "vs usual" pill out to the full width of the column. An
   inline-block does not opt out of that -- the alignment has to. */
.rank-col { display: flex; flex-direction: column; align-items: flex-start; gap: .1rem; }
/* The rank label and its "vs usual" figure share one line. The figure is on
   some ranks and not others, and a line that appears only sometimes is what
   knocked the columns of a card out of alignment with each other. */
.rank-head { display: flex; align-items: baseline; gap: .4rem; }
.delta {
  font-size: .75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.delta.down { color: var(--pico-ins-color); }
.delta.up { color: var(--q-legendary); }
.stat .label, .rank-label {
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--pico-muted-color);
}
.stat .value { font-size: 1.15rem; font-weight: 600; line-height: 1.2; }

/* --- chips ---------------------------------------------------------------
   Role toggles: six per node card, several cards per row. Pico's buttons are
   sized for page actions, and at that size a node grid stops fitting on a
   screen, so these are deliberately smaller than anything Pico ships. */
.chip {
  /* Buttons inherit their size from the document, not from `--pico-font-size`
     (that variable is the root scale), so this has to be set directly. */
  font-size: .75rem;
  margin: 0;
  padding: .2rem .55rem;
  width: auto;
  border-radius: var(--pico-border-radius);
  line-height: 1.4;
}
.roles, .controls { display: flex; flex-wrap: wrap; gap: .3rem; }
.controls { margin-top: var(--pico-block-spacing-vertical); }
.controls button { font-size: .75rem; margin: 0; width: auto; padding: .2rem .55rem; }

/* --- form toolbars -------------------------------------------------------
   A row of labelled controls ending in a button: an expansion picker, a job
   submission. Pico has no component for it -- `role="group"` joins bare
   controls and has nowhere to put a label, and `.grid` gives every child an
   equal column, which turns "Apply" into the largest thing on the page. */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: .6rem .75rem;
  margin-bottom: var(--pico-block-spacing-vertical);
}
.toolbar label {
  display: flex;
  flex-direction: column;
  gap: .2rem;
  margin: 0;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--pico-muted-color);
}
.toolbar input,
.toolbar select,
.toolbar button { margin: 0; width: auto; }
/* A <select> sizes itself to its widest option, and the realm picker's widest
   is "EU · Ravencrest, Zul'jin, …". Left alone it pushed the whole page into
   a horizontal scroll, which is the one thing a page body must never do. The
   label is the flex item, so it is what has to be told it may shrink. */
.toolbar label { min-width: 0; }
.toolbar select { max-width: min(100%, 28rem); }
.toolbar input, .toolbar select { min-width: 9rem; }
.toolbar button { padding: var(--pico-form-element-spacing-vertical) 1.25rem; }
.toolbar small { display: block; margin-top: .15rem; }
/* The hint sits under the row rather than in it. */
.toolbar-note { display: block; margin: -.35rem 0 var(--pico-block-spacing-vertical); }
/* An `.alert` already carries its own padding and border, so the negative
   pull that closes the gap for a bare `<small>` would just overlap it here. */
.alert.toolbar-note { margin-top: 0; }
/* What every figure below was measured from: the snapshot it came out of and
   the window it is compared against. Once per page, above the cards. */
.page-meta { display: block; margin: -.25rem 0 var(--pico-block-spacing-vertical); }

/* --- groups --------------------------------------------------------------
   A heading over a grid of cards. Deliberately not an <article>: the cards
   inside are already boxes, and a box of boxes adds a border and a background
   without adding meaning. */
.group { margin-bottom: calc(var(--pico-block-spacing-vertical) * 1.5); }
.group > h2 { margin-bottom: .25rem; }

/* --- tags and badges -----------------------------------------------------
   Inline status words next to a heading. `mark` would be Pico's answer but it
   is a highlighter, and these need to carry a state colour. */
.tag, .badge {
  display: inline-block;
  padding: .1rem .45rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: 1rem;
  font-size: .7rem;
  line-height: 1.6;
  color: var(--pico-muted-color);
  white-space: nowrap;
}
.tag.ok, .badge.ok { color: var(--pico-ins-color); border-color: currentColor; }
.tag.warn, .badge.warn { color: var(--q-legendary); border-color: currentColor; }
.tag.bad, .badge.bad { color: var(--pico-del-color); border-color: currentColor; }
.sim { font-style: normal; }

/* --- alerts --------------------------------------------------------------
   An <article> with a colour down one edge: enough to separate a warning from
   the page without inventing a second card style. */
.alert {
  border-left: 4px solid var(--pico-muted-border-color);
  padding: var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal);
  margin-bottom: var(--pico-block-spacing-vertical);
  background: var(--pico-card-background-color);
  border-radius: var(--pico-border-radius);
}
.alert-archived { border-left-color: var(--q-legendary); }
.alert-error { border-left-color: var(--pico-del-color); }
/* A note rather than a warning: same tile, tinted with the site's own accent
   instead of a new colour, and the icon sits beside the text rather than
   above it -- the box is read left to right, not top to bottom. */
.alert-info {
  border-left-color: var(--pico-primary);
  display: flex;
  align-items: flex-start;
  gap: .6rem;
}
.alert-info .icon { flex-shrink: 0; margin-top: .1rem; color: var(--pico-primary); }
.alert-info .hint { margin: 0; }

.credit { display: block; font-size: .75em; }

/* --- inline icons ----------------------------------------------------------
   Heroicons 24x24 outline, `stroke="currentColor"` -- they take whatever
   colour their container sets. Sized down from the 24px source: at 1.35rem
   they sit level with a stat's value line instead of dwarfing it. */
.icon { width: 1.35rem; height: 1.35rem; }

/* --- the language menu ---------------------------------------------------
   Pico gives a nav dropdown `display: inline`, which leaves its menu hanging
   off a line box rather than off the button: at narrow widths it opened
   *beside* the trigger instead of under it. An inline-block gives the
   absolutely-positioned menu a box to anchor to, and `right: 0` pins it to
   the button's own edge -- it is the last control in the bar, so it has to
   open leftwards.

   This replaces a `dir="rtl"` on the menu, which achieved the same opening
   direction by making every label right-aligned and ragged. */
nav details.dropdown { display: inline-block; position: relative; }
/* The label is a language name and a chevron; wrapping it turns the control
   into two ragged lines the moment the bar gets crowded. */
nav details.dropdown > summary { white-space: nowrap; }
nav details.dropdown > ul {
  left: auto;
  right: 0;
  min-width: max-content;
}

/* A link that changes the page is styled as a button, so that "goes somewhere"
   looks the same everywhere it appears. Breadcrumbs and the nav bar are the
   exception: they are navigation furniture, and a row of buttons there reads
   as a toolbar. In a table cell the button carries no top margin. */
td > .rank-link { margin: 0; }

/* --- item cards ----------------------------------------------------------
   The icon and name of a tracked item, above its per-rank prices.

   Cards sit in a grid, so the head has to be the same height on every one of
   them: a name that wraps to two lines would otherwise start this card's
   prices a line lower than its neighbours', and a row of cards whose figures
   do not line up cannot be read across. Hence a fixed height, a name clamped
   to two lines, and the whole thing centred against the 40px icon. */
.item-head { display: flex; align-items: center; gap: .6rem; }
.item-card > header {
  display: flex;
  align-items: center;
  gap: .7rem;
}
/* Two lines of name plus one of category, whether or not the name needs them.
   The height lives here rather than on the header because the header's own
   padding is Pico's and would otherwise have to be subtracted from it. */
.item-head-text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
  min-height: 3.9rem;
}
.item-card > header h3 {
  margin: 0;
  font-size: 1.05rem;
  line-height: 1.25;
  /* Two lines, then an ellipsis. `title` on the element carries the rest. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
.item-icon {
  width: 40px;
  height: 40px;
  border-radius: var(--pico-border-radius);
  flex: none;
}
/* The Auction House index's category cards have no item icon to show --
   there is no one item behind "Consumables" -- so this fills the same slot
   in the header with a line icon instead, at the same visual weight as the
   40px raster ones beside it. */
.category-icon { flex: none; color: var(--pico-primary); }
.category-icon .icon { width: 2rem; height: 2rem; }
/* The last line of a card before its button: an expansion's link to its
   prices, a patch's list of the raid tiers it opened.

   Height held, and clamped to two lines. Cards sit in a grid and their rows
   have to line up (§7): a patch that opened two tiers next to one that opened
   none put their two Open buttons at different heights, which is the same bug
   as a `.verdict` that renders on one card and not the next. */
.card-foot {
  color: var(--pico-muted-color);
  font-size: .8em;
  min-height: 2.6rem;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
/* Pico sizes `role="button"` for page actions; this one sits at the foot of a
   price column, two to a card. */
.rank-link {
  display: inline-block;
  width: auto;
  margin: .35rem 0 0;
  padding: .25rem .6rem;
  font-size: .75rem;
}
.rank-empty { color: var(--pico-muted-color); }

/* --- valuation band and sparkline ----------------------------------------
   Where a price sits in its own market's history, and the shape it got there
   by. Both replaced the Avg/Low/High rows a card used to print, so both live
   inside the same column and inherit its left alignment. */
/* The band's line is always in the flow, even when there is no band to put on
   it. Cards sit in a grid and the columns of one card sit in a grid inside
   that: a line present on one market and absent on the next is the same bug
   §7 records against the realm picker, one level down. */
.verdict {
  display: flex;
  align-items: baseline;
  gap: .35rem;
  min-height: 1.15rem;
  font-size: .75rem;
}
.band {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .03em;
  white-space: nowrap;
}
/* The same two colours the "vs usual" figure already uses, and deliberately:
   a card that called a price cheap in green on one line and dear in orange on
   the next would be teaching two colour vocabularies for one idea. The middle
   band is muted rather than coloured -- "Typical" is the absence of news. */
.band-very-cheap, .band-cheap { color: var(--pico-ins-color); }
.band-typical { color: var(--pico-muted-color); }
.band-expensive, .band-very-expensive { color: var(--q-legendary); }
/* The tails are the ones worth a second look, so they carry the weight the
   inner bands do not. */
.band-very-cheap, .band-very-expensive { text-decoration: underline .1em; text-underline-offset: .2em; }
.band-none { font-style: italic; }

/* A market that did not refresh when the rest of the page did. Its figures are
   about a different moment from the ones on the card beside it, which is the
   only reason a per-card age is worth printing at all. */
.rank-stats .stale { color: var(--q-legendary); }

/* The card's sparkline. `preserveAspectRatio="none"` here and nowhere else:
   the line has no axis and no glyphs in it, so stretching it to the column's
   width distorts nothing a reader is reading -- which is exactly the argument
   that does not hold for the analysis page's chart, where it was tried and
   reverted. */
.spark {
  display: block;
  width: 100%;
  height: 1.5rem;
  margin: .15rem 0 .35rem;
  overflow: visible;
}
.spark-line {
  fill: none;
  /* The first slot of `chart::SERIES_COLOURS`, kept in step by
     `the_stylesheet_paints_the_sparkline_the_series_colour`. */
  stroke: #3b82f6;
  stroke-width: 1.2;
  stroke-linejoin: round;
  stroke-linecap: round;
  /* The line is stretched horizontally by the viewBox; without this the
     stroke is stretched with it and comes out thicker than it is tall. */
  vector-effect: non-scaling-stroke;
}
.spark-dot, .spark-now { fill: #3b82f6; stroke: none; }

/* --- gear cards ----------------------------------------------------------
   A BoE card is denser than a commodity one: a block per region, and an
   upgrade ladder inside each. Wider than the other cards because two regions
   of tiers do not fit in 21rem. */
/* Wide enough that two regions genuinely sit side by side inside one card:
   the whole point of the cross-realm view is comparing them at a glance, and
   a EU block stacked above a US block is two screens of scrolling apart. */
.gear-grid { grid-template-columns: repeat(auto-fill, minmax(34rem, 1fr)); }
.gear-regions {
  display: grid;
  gap: var(--pico-block-spacing-horizontal);
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}
/* The ladder of item levels at the top of a statistics page: one chip per
   market, the current one filled in and the rest outlined. Written here
   rather than with Pico's `.outline` because these are chips, which already
   override the button sizing -- two systems fighting over one element is how
   the legend swatches lost their colour once already. */
.level-links { margin-bottom: var(--pico-block-spacing-vertical); }
.level-links .chip {
  background: transparent;
  border: 1px solid var(--pico-primary);
  color: var(--pico-primary);
}
.level-links .chip.on {
  background: var(--pico-primary);
  color: var(--pico-primary-inverse);
}

/* A card is a stack of item levels; each level is a row of cells, one per
   region. The column count comes from the markup rather than `auto-fit`,
   because auto-fit would collapse two regions into one column at a width
   where they still fit -- and a card whose regions sometimes stack and
   sometimes do not is exactly the unevenness this layout exists to remove.

   The cells themselves are `.rank-col`, the same component the consumable and
   reagent cards use, so a gear card reads as the same kind of object rather
   than as a second design that happens to live next door. */
.gear-cells {
  display: grid;
  gap: var(--pico-block-spacing-horizontal);
  grid-template-columns: repeat(var(--scopes, 1), minmax(0, 1fr));
}
.gear-scopes { margin-bottom: .35rem; }
.gear-level + .gear-level {
  margin-top: .6rem;
  padding-top: .5rem;
  border-top: 1px solid var(--pico-muted-border-color);
}
.gear-level-name { display: block; margin-bottom: .1rem; }
/* One line, whatever it holds: the full list is on the element's title. */
.gear-extras { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Two regions of figures need the room; below that they stack, which is
   honest at a width where side by side would be unreadable. */
@media (max-width: 40rem) {
  .gear-cells { grid-template-columns: 1fr; }
  .gear-scopes { display: none; }
}

/* --- the collection page ------------------------------------------------
   A hundred and eighty-four switches. What makes that readable is not the
   switches, it is the headings between them: a region, then a language, then
   a run of realms short enough to scan. */
.admin-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .75rem;
}
.admin-head h2 { margin: 0; }
.admin-language + .admin-language {
  margin-top: .9rem;
  padding-top: .7rem;
  border-top: 1px solid var(--pico-muted-border-color);
}
.admin-language .rank-label { display: block; margin-bottom: .4rem; }
/* Each switch is its own form. `display: contents` drops the form's box so the
   chips are flex items of the row rather than a column of forms -- and then
   Pico's `button[type=submit] { width: 100% }` wins over `.chip`'s `auto` and
   stretches every one of them across the page. Two selectors beat it back. */
.switch { display: contents; }
.switch .chip { width: auto; }

/* One box per auction house, in an even grid: every market is the same kind
   of object whether it holds one realm or ten, and a grid row makes them the
   same height. Realms inside a box are lines, not chips, because they are a
   list of names rather than a row of controls. */
/* Columns rather than a grid: a market holds one realm or ten, and a grid row
   stretches every box to the tallest in it -- a two-name box drawn as tall as
   a ten-name one, mostly empty. Columns let each box be its own size and pack
   the next one under it. */
.market-grid {
  columns: 14rem auto;
  column-gap: .5rem;
}
.market {
  break-inside: avoid;
  margin-bottom: .5rem;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  padding: .15rem;
  border: 1px solid var(--pico-primary);
  border-radius: var(--pico-border-radius);
  background: color-mix(in srgb, var(--pico-primary) 12%, transparent);
}
/* Off is an outline: the market is still there, it is simply not collected. */
.market.off {
  border-color: var(--pico-muted-border-color);
  background: transparent;
}
.market button {
  width: 100%;
  margin: 0;
  padding: .15rem .4rem;
  border: none;
  border-radius: calc(var(--pico-border-radius) - 1px);
  background: transparent;
  color: var(--pico-primary);
  font-size: .78rem;
  line-height: 1.5;
  text-align: left;
}
.market.off button { color: var(--pico-muted-color); }
.market button:hover {
  background: color-mix(in srgb, var(--pico-primary) 25%, transparent);
}

/* --- definition lists ----------------------------------------------------
   Pico stacks <dt> above <dd>, which is right for a glossary. These are
   label/value pairs in a card, so they read as two columns. */
.caps, .rank-stats {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .15rem .75rem;
  margin: 0 0 .4rem;
}
.caps dt, .rank-stats dt { color: var(--pico-muted-color); font-size: .8em; }
.caps dd, .rank-stats dd { margin: 0; font-size: .85em; }
/* The date under a low or a high goes on its own line, always. Left inline it
   fits a three-digit price and wraps a four-digit one, which makes the two
   ranks of a card -- and every card in the row -- end at different heights. */
.rank-stats dd small { display: block; line-height: 1.3; }

/* --- load bars -----------------------------------------------------------
   `<progress>` is Pico's, and used directly; this only keeps a bar inline
   with the text beside it. */
progress.bar { margin: .35rem 0 .1rem; }

/* --- event log ----------------------------------------------------------- */
.event-log { list-style: none; margin: 0; padding: 0; }
.event-log li {
  display: flex;
  gap: .6rem;
  padding: .3rem 0;
  border-bottom: 1px solid var(--pico-muted-border-color);
  font-size: .85em;
}
.event-log time { color: var(--pico-muted-color); font-variant-numeric: tabular-nums; }
.event-log li:last-child { border-bottom: none; }

/* --- the price chart -----------------------------------------------------
   Inline SVG, so it inherits the theme through currentColor rather than
   carrying a palette of its own. */
.viz-root { width: 100%; height: auto; }

/* --- analysis panels -----------------------------------------------------
   A panel is a question with an answer under it. Phase 6's exit gate is that
   each one names its question, window, units, coverage and freshness, so the
   terms line is a fixed part of the component rather than prose somebody
   remembers to write. */
.panel { margin-bottom: var(--pico-block-spacing-vertical); }
.panel-head { margin-bottom: .75rem; }
.panel-head h2, .panel-head h3 { margin-bottom: .25rem; }
/* The terms wrap rather than scroll: there are up to five and they are short,
   and a horizontally scrolling header is a header a reader will not read. */
.panel-terms {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem .9rem;
  margin: 0;
  font-size: .78rem;
  color: var(--pico-muted-color);
}
.panel-terms .term { white-space: nowrap; }
.panel-terms .term-label {
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: .9em;
  opacity: .75;
  margin-right: .3rem;
}
/* The row of figures under a panel head. `.stat` is the shared tile the
   dashboard and the category cards already use -- §7's rule about a new page
   reusing the existing components, applied to the page that tempted hardest
   to invent its own. */
.verdict-row {
  display: grid;
  gap: .6rem;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  margin-bottom: .8rem;
}
.verdict-row .when { font-size: .75rem; line-height: 1.3; }
/* The band reads at tile size here, where on a card it is a caption. Same
   colours, because it is the same claim (§7). */
.verdict-row .band { font-size: 1rem; }
/* An anomaly is not a valuation, and is not coloured like one: it is a flag
   about the shape of the distribution, so only the two that mean something
   are marked at all. */
.anomaly-ordinary { color: var(--pico-muted-color); }
.anomaly-mild { color: var(--q-legendary); }
.anomaly-extreme { color: var(--q-legendary); font-weight: 600; }
.chart-empty { color: var(--pico-muted-color); }
.legend { display: flex; flex-wrap: wrap; gap: .75rem; font-size: .8em; color: var(--pico-muted-color); }
.legend .key { display: inline-flex; align-items: center; gap: .35rem; }
/* The colour itself is set inline, from the chart's palette. */
.legend .swatch { width: .7rem; height: .7rem; border-radius: 2px; flex: none; }

/* --- item tooltips -------------------------------------------------------
   A hover card fetched on demand. It is `position: absolute` so that the
   placeholder inside it -- which exists only until the fetch lands -- never
   takes part in the card's layout. Without that, every item in a grid renders
   the word "Loading…" beside its icon at rest. */
.tip { position: relative; display: inline-flex; }
.tip-panel {
  position: absolute;
  z-index: 20;
  left: 0;
  top: calc(100% + .35rem);
  display: none;
  min-width: 16rem;
  padding: var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal);
  background: var(--pico-card-background-color);
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  box-shadow: var(--pico-card-box-shadow);
}
.tip:hover .tip-panel,
.tip:focus-within .tip-panel { display: block; }

/* --- item tooltips -------------------------------------------------------
   Drawn in the order the game draws one. */
.tip-card { max-width: 22rem; }
.tip-name { font-weight: 600; }
.tip-line, .tip-rank, .tip-note { font-size: .85em; }
/* What an item *does*, in green: the "Use:" line on a consumable and the stat
   block on a piece of gear. They are the same thing to a reader -- the reason
   to buy this rather than the one beside it -- and reading as one thing across
   both is the point (§7). */
.tip-effect, .tip-stat { color: var(--q-uncommon); font-size: .85em; }
.tip-flavor { font-style: italic; color: var(--pico-muted-color); font-size: .85em; }
.tip-note, .tip-loading { color: var(--pico-muted-color); font-size: .8em; }

/* --- htmx ----------------------------------------------------------------
   The one piece of state HTMX exposes to CSS: an in-flight request. */
.htmx-indicator { opacity: 0; transition: opacity .2s; }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { opacity: 1; }

/* Fade a fragment out and in as it is swapped, so a live update reads as a
   change rather than a flicker. */
.htmx-swapping { opacity: .5; transition: opacity .1s; }

/* Today's alerts, on the auction house index and on the alerts page.
   A <details> because it is a thing you glance at and then dismiss, not
   something to scroll past on every visit -- and because a <details> needs no
   JavaScript to be one. */
.alerts { margin-block: 1rem; }
.alerts > summary {
  cursor: pointer;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: .5rem;
}
.alerts > summary::marker { color: var(--pico-muted-color); }
.alerts table { margin-block: .75rem 0; }

/* The follow control sits at the end of the item header, pushed away from the
   name so the two never look like one control. */
.item-head .follow { margin: 0 0 0 auto; }
.item-head .follow button { margin: 0; white-space: nowrap; }

/* The watchlist: one row per followed item, its name doing the linking and
   the unfollow button at the end of the line. */
.watchlist { list-style: none; padding: 0; margin: 0; }
.watchlist li {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding-block: .4rem;
  border-bottom: 1px solid var(--pico-muted-border-color);
}
.watchlist li:last-child { border-bottom: 0; }
.watchlist form { margin: 0 0 0 auto; }
.watchlist button { margin: 0; padding: .2rem .6rem; font-size: .85em; }

/* A realm's name under a price, kept to one line.
   Cards sit next to each other and their rows have to line up (§7). One
   auction house can be eight realms, and the joined name wrapped to three
   lines while its neighbour took one -- which put every row below it out of
   step. One name, clamped, with the rest on hover. */
.realm-name {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The realm picker: our own dropdown, because a <datalist>'s popup is the
   browser's -- it draws it wherever it likes, which on a wide window is a
   column floating off to the right of the box it belongs to, and no styling
   of ours reaches it. This one is part of the page. */
/* No margin. Pico gives a `<details>` a block of margin below it -- room for
   the content it drops open -- and in a toolbar of bottom-aligned controls
   that lifted this one sixteen pixels above the search box and the button
   beside it. The panel here is positioned absolutely, so there is nothing
   under the summary that needs the room. It is the `<details>` that carries
   the margin, not the `<summary>`; targeting the summary changes nothing. */
.realm-picker { position: relative; min-width: 14rem; }
/* Beats Pico's `label > details.dropdown`, which nudges a dropdown down a
   quarter-space below its caption. The toolbar's labels already space
   themselves, so all that nudge does here is lift the control off the line the
   search box and the button sit on. Written to out-specify that selector
   rather than to tie with it: a tie survives only while the file order does. */
.toolbar label > .realm-picker { margin: 0; }
.realm-picker > summary { white-space: nowrap; }

/* The toolbar shouts its labels in small caps. The picker's own contents are
   realm names and a search box, and neither is a label. */
.realm-picker,
.realm-picker * {
  text-transform: none;
  letter-spacing: normal;
  font-size: 1rem;
  color: var(--pico-color);
}

/* Floated over the page, not wedged into it: a panel that pushes the cards
   down every time it opens is a page that moves under the pointer. */
.realm-panel {
  position: absolute;
  z-index: 20;
  top: calc(100% + .25rem);
  left: 0;
  /* Match the closed picker exactly. A larger minimum here made the open
     panel jut out to the right even though both states are one control. */
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: .4rem;
  padding: .5rem;
  background: var(--pico-card-background-color);
  border: 1px solid var(--pico-muted-border-color);
  border-radius: var(--pico-border-radius);
  box-shadow: var(--pico-card-box-shadow);
}
.realm-panel input { margin: 0; }
/* Capped and scrolling, so the panel is the same size whatever is typed.
   A block, *not* a flex column: a flex container with a max-height squashes
   its items to fit rather than scrolling, and sixty realms in sixteen rems
   came out ten pixels tall each -- a stack of coloured slivers with no
   readable text in them. Scrolling is what was wanted, and a block does it. */
.realm-options {
  min-height: 4rem;
  max-height: 16rem;
  overflow-y: auto;
}
.realm-options a {
  display: block;
  padding: .3rem .4rem;
  border-radius: var(--pico-border-radius);
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.realm-options a:hover {
  background: var(--pico-primary-background);
  color: var(--pico-primary-inverse);
}
