/* The sidebar width is DERIVED, not chosen: it has to contain a full 5x3
   compendium page at the game's own card size, or the fifth column clips.
     page      5 x 82px card + 4 x 10px gap = 450px
     grid      + 2 x  5px padding           = 460px
     carousel  + 2 x 30px arrow gutters     = 520px
     sidebar   + 2 x 15px padding           = 550px
     scrollbar + ~20px                      = 570px
   Change the card size and this must change with it. */
:root { --sidebar-width: 570px; }

/* =========================================
   1. CORE LAYOUT & TYPOGRAPHY
   ========================================= */
body, html {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    overflow: hidden; 
    background-color: #111;
    color: #eee;
    font-family: 'Georgia', serif;
}

/* =========================================
   2. MAP CONTAINER
   ========================================= */
#map {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    left: var(--sidebar-width); /* Starts exactly where the sidebar ends */
    z-index: 1; 
}

/* Force Leaflet's background color so it's never a black void */
.leaflet-container {
    background-color: #d2b790 !important; 
}

/* =========================================
   3. SIDEBAR MASTER CONTAINER
   ========================================= */
#sidebar {
    position: absolute;
    top: 0;
    left: 0;
    width: var(--sidebar-width); 
    height: 100%;
    
    /* Internal formatting */
    overflow-y: auto; 
    box-sizing: border-box; 
    background-color: #1a1a1a;
    padding: 15px;
    border-right: 2px solid #b71c1c; 
    box-shadow: 2px 0 10px rgba(0,0,0,0.5); 
    z-index: 1000; 
}

/* The playthrough in one line. The number gives the fact, the sliver of gold
   below the title gives the dopamine - it only ever moves right. */
.global-progress-bar {
    height: 3px;
    background: #2a2419;
    border-radius: 2px;
    margin: -6px 0 12px;
    overflow: hidden;
}
#global-progress-fill,
.tx-bar-fill {
    height: 100%;
    width: 0;
    background: #c8a951;
    transition: width 0.4s ease;
}

/* Every section header gets the same sliver the whole playthrough gets, so
   "how far into THE COMPENDIUM am I" reads the same way as "how far into the
   game am I". 2026-08-26: "each section should have a little gold bar." */
.tx-bar {
    height: 2px;
    background: #2a2419;
    overflow: hidden;
}

.global-progress {
    float: right;
    font-size: 12px;
    letter-spacing: 1px;
    color: #8a8270;
    font-variant-numeric: tabular-nums;
    align-self: baseline;
}

.app-title {
    font-size: 24px;
    text-align: center;
    margin: 0 0 15px 0;
    text-transform: uppercase;
    letter-spacing: 2px;
    border-bottom: 1px solid #444;
    padding-bottom: 10px;
}

/* =========================================
   4. GLOBAL CONTEXT HEADER (CHAPTER/TIME)
   ========================================= */
.global-context {
    background: #2a2a2a;
    padding: 10px;
    border-radius: 4px;
    margin-bottom: 15px;
}

.context-row {
    display: flex;
    gap: 5px;
    margin-bottom: 10px;
    width: 100%;
}

.context-row select {
    background: #333;
    color: #fff;
    border: 1px solid #555;
    padding: 5px;
    font-size: 12px;
    min-width: 0; /* Prevents long text from breaking the layout */
    text-overflow: ellipsis; 
    white-space: nowrap;
    overflow: hidden;
}

/* Locked dropdown widths */
#chapter-select { width: 25%; }
#mission-select { width: 50%; }
#time-select { width: 25%; }

.checkbox-row {
    display: flex;
    flex-wrap: wrap; 
    gap: 8px 12px; 
    justify-content: center;
    font-size: 12px;
    color: #aaa;
}

.checkbox-row label {
    display: flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    white-space: nowrap; 
}

.checkbox-row input[type="checkbox"] {
    accent-color: #8b0000;
    cursor: pointer;
}

/* Controls with no data behind them yet */
.control-disabled { opacity: 0.4; cursor: not-allowed !important; }
.checkbox-row label.control-disabled { cursor: not-allowed; }
/* Same class, different reason: while Show All is on, these chips are
   overridden rather than absent - see setupFilterControls. A dimmed
   container alone doesn't stop clicks on the checkboxes inside it. */
.category-chips.control-disabled { pointer-events: none; }

/* Category chips, in labelled groups. The group heading is the hierarchy that
   replaced the separate Quick View bar (2026-08-13) - thirty chips in one
   undifferentiated row is the "insanely overloaded" bar again, just longer. */
.category-chips {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid #3a3a3a;
}

.chip-group + .chip-group { margin-top: 9px; }
.chip-group-label {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: #7d776c;
    margin-bottom: 5px;
}
.chip-group-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.category-chip {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 4px 9px;
    background: #3d3126;
    border: 1px solid #5c4e40;
    border-radius: 12px;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    cursor: pointer;
    user-select: none;
}
.category-chip input { accent-color: #8b0000; cursor: pointer; margin: 0; }
.category-chip b { color: #9a927f; font-weight: normal; font-variant-numeric: tabular-nums; }
.category-chip.off { opacity: 0.45; background: #262626; }

/* Backup / restore */
.save-row { display: flex; gap: 6px; margin-top: 10px; }
.save-row button {
    flex: 1;
    padding: 6px;
    background: #333;
    color: #e0d8c0;
    border: 1px solid #5c4e40;
    font-family: inherit;
    font-size: 11px;
    letter-spacing: 1px;
    cursor: pointer;
}
.save-row button:hover { background: #4a3c2e; }

/* The bulk chip buttons carry no active state any more, and shouldn't: they
   are one-shot operations on the boxes below, and the boxes themselves are
   now the honest readout of what is on. A lit-up "Show All" would go on
   claiming everything was shown after you unticked something. Three buttons
   in a sidebar column need a smaller label than two did. */
.bulk-row button { font-size: 10px; padding-left: 4px; padding-right: 4px; }
.save-status { font-size: 11px; color: #2e8b57; min-height: 14px; margin-top: 4px; text-align: center; }

.icon-credits {
    font-size: 10px;
    color: #6a6250;
    text-align: center;
    margin-top: 15px;
    padding-top: 10px;
    border-top: 1px solid #2a2620;
}
.icon-credits a { color: #8a8168; }
.save-status.save-error { color: #ff6b6b; }

/* Chapter-transition warning. Scoped to the banner element: an unscoped
   .missable-alert once leaked this padding and margin onto compendium cards,
   which share the concept but not the shape. */
#missable-alert {
    background: #4a1010;
    border: 1px solid #b71c1c;
    border-left: 4px solid #b71c1c;
    padding: 10px 12px;
    margin-bottom: 15px;
    font-size: 12px;
    border-radius: 3px;
}
#missable-alert[hidden] { display: none; }
#missable-alert strong { color: #ff9b9b; display: block; margin-bottom: 6px; }
#missable-alert ul { margin: 0 0 6px 0; padding-left: 18px; color: #e0d8c0; }
#missable-alert li { margin-bottom: 2px; }
#missable-alert em { color: #9a927f; display: block; margin-bottom: 6px; }
#missable-alert button {
    background: #b71c1c;
    color: #fff;
    border: none;
    padding: 4px 10px;
    font-family: inherit;
    font-size: 11px;
    cursor: pointer;
}

/* The checklist version - amber, not red, and collapsed by default: this is
   "still gettable" information, not an alarm. See updateChapterMissables. */
.chapter-missable {
    background: #3a2f10;
    border: 1px solid #a3781c;
    border-left: 4px solid #d9a825;
    padding: 6px 12px;
    margin-bottom: 15px;
    font-size: 12px;
    border-radius: 3px;
}
.chapter-missable[hidden] { display: none; }
.chapter-missable summary {
    color: #e8c96b;
    cursor: pointer;
    padding: 4px 0;
}
.chapter-missable ul {
    list-style: none;
    margin: 6px 0 2px 0;
    padding: 0;
    max-height: 220px;
    overflow-y: auto;
}
.chapter-missable li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 3px 0;
    color: #e0d8c0;
    border-top: 1px solid #4a3d1a;
}
.chapter-missable li:first-child { border-top: none; }
.chapter-missable button {
    background: #a3781c;
    color: #fff;
    border: none;
    padding: 2px 8px;
    font-family: inherit;
    font-size: 11px;
    cursor: pointer;
    border-radius: 2px;
    flex-shrink: 0;
}

/* =========================================
   5. PANEL TOGGLES
   =========================================
   Search mode is gone (2026-08-07). It never got past a placeholder, and
   everything it was going to be now exists elsewhere: the taxonomy has its own
   search box, and the table does the finer filtering it was meant to carry. */
/* The five cross-cutting tools, as one block. Before this they were three
   loose buttons here plus two buried inside their own subsections, and only one
   of the five had any styling at all - so TABLE VIEW was a full-width bar and
   WHAT'S NEARBY was a default browser button sitting next to it. */
.toolbox {
    margin-bottom: 15px;
    border: 1px solid #3a3228;
    background: #1c1a17;
}
.toolbox-name {
    padding: 6px 10px;
    color: #6a6250;
    font-size: 10px;
    letter-spacing: 2px;
    text-transform: uppercase;
    border-bottom: 1px solid #2a2620;
}
/* Flex-wrap rather than a grid: five buttons in a three-wide grid leaves an
   empty cell on the second row, which reads as a sixth tool that failed to
   load. Wrapping lets the last row's two stretch to fill instead. */
.toolbox-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 1px;
    background: #2a2620;
}
.toolbox-grid button {
    flex: 1 1 140px;
    padding: 9px 8px;
    background: #262320;
    color: #c0b8a0;
    border: none;
    font-family: inherit;
    font-size: 12px;
    letter-spacing: 1px;
    cursor: pointer;
}
.toolbox-grid button:hover { background: #3d3126; color: #e0d8c0; }

.view-section.hidden { display: none !important; }
.view-section.active { display: block; }

/* =========================================
   6. COMPENDIUM ACCORDION & GRID ENGINE
   ========================================= */
.compendium-category summary {
    background: #2a2a2a;
    padding: 10px;
    font-weight: bold;
    cursor: pointer;
    border-bottom: 2px solid #8b0000;
    margin-bottom: 10px;
}

.grid-carousel-wrapper {
    display: flex;
    align-items: center;
    position: relative;
    padding: 0 30px; 
}

.compendium-grid {
    display: flex; /* Switched to flex to hold the pages horizontally */
    gap: 20px; /* Space between pages */
    width: 100%;
    /* Exactly a 5x3 page's own height (3 x 54px cards + 2 x 10px gap = 182,
       + 2 x 5px grid padding = 192) - not a guessed number. Page height never
       varies with item count, since grid-template-rows is fixed at 3 rows
       regardless of how many cells are filled, so this is precise for every
       grid-layout subsection, not just one. Was 290px, which left ~100px of
       dead space under every grid page - invisible until something sat right
       below it, which is what made the gap above the fish detail panel obvious.
       Row layout already overrides this to 0 below; that override is unaffected. */
    min-height: 192px;
    overflow-x: hidden;
    scroll-behavior: smooth;
    padding: 5px;
    
    /* Prepares the UI for mobile touch scrolling later */
    scroll-snap-type: x mandatory; 
}

/* The individual 5x3 pages inside the track */
.compendium-page {
    display: grid;
    /* 5 across, 3 down - the same shape as the compendium page in game, so the
       player reads across their screen and across this without translating. */
    grid-template-columns: repeat(5, 82px);
    grid-template-rows: repeat(3, 54px);

    gap: 10px;
    flex: 0 0 auto; /* Forces the page to never shrink or stretch */
    scroll-snap-align: start; /* Snaps perfectly to the left edge when swiping */
}

/* Equipment page in game: 4 across, 5 down. */
.compendium-page.page-grid4 {
    grid-template-columns: repeat(4, 82px);
    grid-template-rows: repeat(5, 54px);
}

/* 5 rows instead of 3: (5 x 54px cards + 4 x 10px gap) + 2 x 5px grid padding.
   Width is also capped to one page (4 x 82px cards + 3 x 10px gap + 2 x 5px
   padding = 368px) - the 5-across pages happen to fill the sidebar almost
   exactly so the next page's edge never peeks past overflow-x:hidden, but a
   4-across page is narrower and left uncapped the first column of page 2 sat
   in that leftover space, looking like 5 columns instead of 4. */
.compendium-grid.layout-grid4 { min-height: 300px; width: 368px; max-width: 100%; }

/* Left/Right Nav Arrows */
.nav-arrow {
    position: absolute;
    background: #333;
    color: white;
    border: 1px solid #555;
    height: 40px;
    width: 30px;
    cursor: pointer;
    z-index: 10;
    font-size: 16px;
}
.nav-arrow:hover { background: #555; }
.left-arrow { left: 0; }
.right-arrow { right: 0; }

/* =========================================
   7. ANIMAL CARDS & STATE STYLING
   ========================================= */
.animal-card {
    /* Without this a card is content-box, so its 5px padding and 2px border
       add 14px OUTSIDE the grid column and every card renders --card-w + 14.
       The whole --card-w derivation at the bottom of this file - including
       --sidebar-width - assumes a card is exactly --card-w wide, so the fifth
       card of a row was pushed past the track and clipped while the carousel
       still paged by five. 2026-08-19: "some cards that are meant to display 5
       only show 4 but still scroll 5." */
    box-sizing: border-box;
    /* Fixed: Lightened the brown so it doesn't look completely black */
    background: #3d3126; 
    border: 2px solid #5c4e40;
    border-radius: 4px;
    cursor: pointer;
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
    padding: 5px;
    user-select: none; 
    transition: all 0.1s ease;
}

.card-title {
    font-size: 10px;
    line-height: 1.15;
    /* `word-wrap: break-word` only breaks a word that has nowhere else to go,
       and it measures against the longest WORD - so "BINOCULARS" and
       "CATTLEMAN REVOLVER" still pushed a few pixels past a card that is now
       exactly --card-w rather than --card-w + 14. `anywhere` also lets the
       browser count the break opportunity when sizing, which is the half that
       was missing. */
    word-wrap: break-word;
    overflow-wrap: anywhere;
    overflow: hidden;
    color: #e0d8c0; /* Ensures text is visible */
}

.state-indicator {
    position: absolute;
    bottom: 5px;
    right: 5px;
    font-size: 10px;
    font-weight: bold;
}

/* State 1: None (Default) */
.animal-card[data-state="none"] {
    opacity: 0.6;
}

/* State 2: Tracking (Yellow Border) */
.animal-card[data-state="tracking"] {
    border-color: #f1c40f;
    background: #4a3c2e; /* Lightens up slightly when active */
    opacity: 1;
    box-shadow: 0 0 5px rgba(241, 196, 15, 0.5);
}
.animal-card[data-state="tracking"] .state-indicator::after {
    content: '🎯';
}

/* State 3: Completed (Faded, Green Check) */
.animal-card[data-state="completed"] {
    border-color: #2e8b57;
    opacity: 0.4;
}
.animal-card[data-state="completed"] .state-indicator::after {
    content: '✓';
    color: #2e8b57;
}

/* State 4: Both (Yellow Border AND Green Check) */
.animal-card[data-state="both"] {
    border-color: #f1c40f;
    opacity: 1;
}
.animal-card[data-state="both"] .state-indicator::after {
    content: '✓🎯';
}

/* Card variants */
.animal-card { font-family: inherit; }
.animal-card:focus-visible { outline: 2px solid #f1c40f; outline-offset: 2px; }

.animal-card.card-missable {
    border-color: #b71c1c;
    background: #4a2a26;
}

.warning-badge {
    position: absolute;
    top: 0; left: 0; right: 0;
    background: #b71c1c;
    color: #fff;
    font-size: 6px;
    letter-spacing: 0.5px;
    padding: 1px 0;
}
.animal-card.card-missable .card-title { margin-top: 8px; }

/* 2026-08-12: "The card should also display the reward not just the item
   your fetching" - a second, quieter line under the name. Same size class as
   card-count, which already proves a second line fits a 54px card without
   the title getting crowded out. */
.card-reward {
    margin-top: 2px;
    color: #9a927f;
    font-size: 8px;
    line-height: 1.1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
}

/* Unique reward outranks plain missable as the headline fact - gold instead
   of the red "you can lose this" badge, same corner. */
.warning-badge.badge-unique { background: #c8a951; color: #241d15; }

/* Nothing to draw on the map for this one - don't let it look trackable. */
.animal-card.no-map-data {
    border-style: dashed;
    border-color: #6b6b6b;
}
.animal-card.no-map-data .card-title { color: #9a927f; font-style: italic; }

/* Crafted items (trapper clothing, satchels, talismans, pamphlets...) never
   have a location - that's a fact about the item, not a gap, so it does not
   get the dashed "nobody has filled this in yet" treatment. 2026-08-12:
   "remove the dotted outline... I don't know maybe it's nice to visually
   convey they don't have a location" - a small corner tick reads as "made",
   distinct from the no-map-data dashed border, without borrowing its meaning. */
.animal-card.craft-only {
    position: relative;
}
.animal-card.craft-only::before {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    border-style: solid;
    border-width: 0 10px 10px 0;
    border-color: transparent #4a4335 transparent transparent;
}

/* Occupies its slot but says nothing else - the point is the position, not
   the card. 2026-08-12: "it needs to show a blank card with a lock... because
   the ordering is important for keeping track of what's what." */
.animal-card.card-locked {
    background: #2a2420;
    border-style: dashed;
    border-color: #4a4335;
    cursor: default;
    opacity: 0.55;
}
.card-lock-icon { font-size: 16px; opacity: 0.7; }

.grid-empty { color: #ffab00; padding: 10px; font-size: 12px; }
.cat-count { float: right; color: #9a927f; font-weight: normal; font-size: 12px; }

#load-status { color: #9a927f; text-align: center; font-size: 13px; padding: 20px 0; }
#load-status.load-error { color: #ff6b6b; }

/* =========================================
   8. MAP POPUPS
   ========================================= */
.popup-warning {
    margin-top: 6px;
    padding: 6px;
    background: #b71c1c;
    color: #fff;
    font-size: 11px;
    border-radius: 3px;
}
.popup-note {
    margin-top: 6px;
    font-size: 11px;
    color: #444;
    max-width: 220px;
}
.popup-track-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 8px;
}
.popup-track-status { font-size: 11px; color: #666; }
.popup-track-btn { margin: 0; padding: 5px 10px; }
.popup-chest-contents {
    margin-top: 8px;
    padding-top: 6px;
    border-top: 1px solid #ccc;
    font-size: 11px;
    color: #444;
}
.popup-chest-contents .popup-track-row { margin-top: 4px; }

/* =========================================
   9. TABLE VIEW (the spreadsheet)
   ========================================= */
/* The table's button, panel shell and header are the SHARED ones now - see
   .toolbox and the #horse-compare/#hunt-list/#table-panel rule.
   Only `.hidden` differs, because this panel predates `hidden` and toggles a
   class instead. */
#table-panel.hidden { display: none; }

.table-rollup {
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
    font-size: 12px;
    color: #9a927f;
}
.table-rollup b { color: #e0d8c0; font-size: 14px; }
.table-rollup .stat-warn b { color: #ff6b6b; }
.table-rollup .stat-gap b { color: #f1c40f; }
.table-rollup .rollup-shown { font-style: italic; }

.table-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    padding: 10px 16px;
    background: #1f1f1f;
    border-bottom: 1px solid #333;
}
.table-filters input,
.table-filters select,
.table-filters button {
    background: #2f2f2f;
    color: #eee;
    border: 1px solid #555;
    padding: 5px 8px;
    font-family: inherit;
    font-size: 12px;
}
.table-filters input { min-width: 240px; }
.table-filters button { cursor: pointer; }
.table-filters button:hover { background: #8b0000; }

.table-scroll { flex: 1; overflow: auto; }

#compendium-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
}
#compendium-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: #2a2a2a;
    text-align: left;
    padding: 8px 10px;
    border-bottom: 2px solid #8b0000;
    white-space: nowrap;
    font-size: 12px;
    letter-spacing: 1px;
    text-transform: uppercase;
}
#compendium-table th.sortable { cursor: pointer; user-select: none; }
#compendium-table th.sortable:hover { background: #3a3a3a; }
#compendium-table th.sorted { color: #f1c40f; }
#compendium-table th[data-dir="asc"]::after  { content: ' ▲'; font-size: 9px; }
#compendium-table th[data-dir="desc"]::after { content: ' ▼'; font-size: 9px; }

#compendium-table td {
    padding: 6px 10px;
    border-bottom: 1px solid #262626;
    vertical-align: top;
}
#compendium-table tbody tr:nth-child(even) { background: #1a1a1a; }
#compendium-table tbody tr:hover { background: #2b2419; }

#compendium-table tbody tr.row-missable td.col-name { color: #ff9b9b; }

.col-check { width: 52px; text-align: center; }
.col-check input { accent-color: #8b0000; cursor: pointer; width: 15px; height: 15px; }
.col-name { min-width: 200px; }
.col-cat { color: #9a927f; width: 90px; }
.col-chapter { width: 100px; white-space: nowrap; color: #c9c0a8; }
.col-missable { width: 90px; }
.col-pins { width: 70px; text-align: right; font-variant-numeric: tabular-nums; }
.col-pins.pins-zero { color: #f1c40f; }
.col-note { color: #9a927f; font-size: 12px; line-height: 1.4; min-width: 280px; }

/* Deliberately muted next to .missable-tag: a mission lock is a "not yet",
   not a "lost forever", and must not compete with the red. */
.lock-tag {
    background: #5d4037;
    color: #e0d6c8;
    font-size: 9px;
    padding: 1px 4px;
    border-radius: 2px;
    letter-spacing: 0.5px;
    margin-left: 4px;
}

.missable-tag {
    background: #b71c1c;
    color: #fff;
    font-size: 10px;
    padding: 2px 5px;
    border-radius: 2px;
    letter-spacing: 0.5px;
}
/* ---- What's Nearby ----------------------------------------------------- */
/* Docked beside the map rather than over it, unlike the table: you need to see
   the point you dropped and the list at the same time. */

/* The deliberate exception to the full-screen rule. Every other tool answers a
   question about a LIST; this one answers "what is around this point", and
   covering the map would hide the very thing it is describing. So it stays a
   rail - but shares the header, the button and the z-index of the rest.
   2026-08-10: "hunt list and what's nearby ... have different sizing." */
#nearby-panel {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 340px;
    z-index: 1200;
    display: flex;
    flex-direction: column;
    background: #141414;
    border-left: 2px solid #8b0000;
}
#nearby-panel.hidden { display: none; }


.nearby-controls {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    border-bottom: 1px solid #333;
    font-size: 12px;
}
.nearby-controls input[type="range"] { flex: 1; min-width: 0; }
#nearby-radius-value { min-width: 20px; text-align: right; color: #c8a951; }

.nearby-summary {
    margin: 0;
    padding: 8px 14px;
    font-size: 12px;
    color: #9a9a9a;
}

.nearby-results {
    flex: 1;
    overflow-y: auto;
    margin: 0;
    padding: 0;
    list-style: none;
}
.nearby-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 14px;
    border-bottom: 1px solid #242424;
    font-size: 12px;
    cursor: pointer;
}
.nearby-row:hover { background: #1e1e1e; }

.nearby-done {
    background: none;
    border: 1px solid #4a4a4a;
    border-radius: 2px;
    color: #6a6a6a;
    width: 20px;
    height: 20px;
    flex: none;
    cursor: pointer;
    line-height: 1;
}
.nearby-done:hover { border-color: #4caf50; color: #4caf50; }

/* Distance is the whole point of this list, so it gets the accent and a fixed
   column - the eye scans down it to decide what is worth the detour. */
.nearby-dist {
    color: #c8a951;
    min-width: 32px;
    text-align: right;
    font-variant-numeric: tabular-nums;
    flex: none;
}
.nearby-name { flex: 1; }
.nearby-cat { color: #777; font-size: 10px; text-transform: uppercase; }
.nearby-empty { padding: 16px 14px; color: #777; font-size: 12px; line-height: 1.5; }

/* A dense category can't draw every pin, so the chip says so inline rather
   than leaving the map looking broken. */
.category-chip.chip-dense span i {
    font-style: normal;
    color: #9a927f;
    font-size: 9px;
    opacity: 0.8;
}

/* ---- Taxonomy navigation (Track view) ---------------------------------- */

#taxonomy-search {
    width: 100%;
    box-sizing: border-box;
    padding: 7px 9px;
    margin-bottom: 10px;
    background: #262626;
    border: 1px solid #4a4a4a;
    color: #e0d8c0;
    font-family: inherit;
    font-size: 12px;
}

/* Search results - one row per matching ITEM, above the section accordion.
   See setupSearch in js/trackView.js for why the box does both jobs. */
.taxonomy-results {
    list-style: none;
    margin: 0 0 10px 0;
    padding: 0;
    /* Long result sets scroll inside the list rather than pushing the whole
       accordion off screen - on a phone the sidebar IS the screen. */
    max-height: 45vh;
    overflow-y: auto;
    border: 1px solid #4a4a4a;
    background: #202020;
}
.taxonomy-results[hidden] { display: none; }

.ts-hit {
    display: flex;
    align-items: stretch;
    gap: 6px;
    border-bottom: 1px solid #333;
}
.ts-hit:last-child { border-bottom: none; }

.ts-go {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 2px;
    align-items: flex-start;
    padding: 7px 9px;
    background: none;
    border: none;
    /* The row is the tap target, so it must not be narrower than the text. */
    text-align: left;
    color: inherit;
    font-family: inherit;
    cursor: pointer;
}
.ts-go:hover { background: #2c2c2c; }

.ts-name { font-size: 12px; color: #e0d8c0; }
.ts-meta { font-size: 9px; opacity: 0.65; text-transform: uppercase; letter-spacing: 0.5px; }

/* Same red the map and the cards use for a missable - see MISSABLE_COLOUR. */
.ts-missable .ts-name { color: #ff8a8a; }

.ts-track {
    align-self: center;
    margin: 0 8px 0 0;
    padding: 5px 10px;
    white-space: nowrap;
}

/* Already tracked / done, so the button reads as a state rather than an offer. */
.ts-hit[data-state="tracking"] .ts-track { border-color: #c8a951; color: #c8a951; }
.ts-hit[data-state="completed"] .ts-track { border-color: #4a7c4a; color: #7cc47c; }

.ts-empty, .ts-more {
    padding: 8px 9px;
    font-size: 10px;
    opacity: 0.6;
    font-style: italic;
}

/* Brief highlight on the card a search result jumped to, so the eye can find
   it in a 15-card page without the row having to scroll to the very top. */
@keyframes card-found-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(200, 169, 81, 0); }
    30%      { box-shadow: 0 0 0 3px rgba(200, 169, 81, 0.85); }
}
.animal-card.card-found { animation: card-found-pulse 1.6s ease-in-out; }

.tx-section { margin-bottom: 6px; }
.tx-section[hidden], .tx-group[hidden], .tx-subsection[hidden] { display: none; }

.tx-section > summary {
    display: flex;
    align-items: center;
    gap: 10px;
    background: #2a2a2a;
    padding: 9px 10px;
    cursor: pointer;
    border-bottom: 2px solid #8b0000;
    font-weight: bold;
    text-transform: uppercase;
    letter-spacing: 1px;
    font-size: 12px;
}
.tx-section-name { flex: 1; }

.tx-group { padding: 6px 0 2px 0; }
.tx-group-name {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: #8a7f6d;
    padding: 6px 4px 4px;
}
.tx-group-bulk-track { display: flex; gap: 4px; }
.tx-group-bulk-track .tx-action { margin: 0; padding: 3px 8px; }

.tx-subsection { margin: 0 0 2px 6px; }
.tx-subsection > summary {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    cursor: pointer;
    background: #212121;
    border-left: 2px solid #5c4e40;
    font-size: 12px;
}
.tx-subsection > summary:hover { background: #2b2b2b; }
.tx-label { flex: 1; }
.cat-count { color: #9a927f; font-size: 11px; font-variant-numeric: tabular-nums; }

/* Declared in the taxonomy but with no data behind it. Must never look
   tickable - it's a gap in the data, not something the player missed. */
.tx-subsection.is-empty {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    background: #1c1c1c;
    border-left: 2px solid #333;
    font-size: 12px;
    color: #6a6a6a;
}
.tx-pending {
    font-size: 9px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: #6a5f4d;
    border: 1px solid #3a3228;
    border-radius: 2px;
    padding: 1px 5px;
}

.grid-carousel-wrapper.single-page .nav-arrow { display: none; }
.grid-carousel-wrapper.single-page { padding: 0 6px; }

.taxonomy-orphans {
    font-size: 11px;
    color: #c8a951;
    background: #2a2416;
    border-left: 3px solid #c8a951;
    padding: 8px 10px;
    margin-top: 10px;
    line-height: 1.5;
}
.taxonomy-orphans[hidden] { display: none; }

/* Short lists are a single row in game (legendary animals, fish, horses). */
.compendium-page.page-row {
    grid-template-columns: repeat(5, 82px);
    grid-template-rows: 54px;
}
.compendium-grid.layout-row { min-height: 0; }

/* ---- Detail panel (horses) --------------------------------------------- */

.animal-card.is-expandable.is-selected {
    outline: 2px solid #c8a951;
    outline-offset: 1px;
}

.tx-detail {
    margin: 8px 6px 4px;
    padding: 10px 12px;
    background: #1c1c1c;
    border-left: 3px solid #c8a951;
    font-size: 12px;
}
.tx-detail[hidden] { display: none; }

.dp-head { display: flex; align-items: center; margin-bottom: 8px; }
.dp-name {
    flex: 1;
    font-weight: bold;
    letter-spacing: 1px;
    text-transform: uppercase;
    font-size: 12px;
}
.dp-close { background: none; border: none; color: #999; cursor: pointer; font-size: 14px; }

.dp-row { display: flex; align-items: center; gap: 10px; margin-bottom: 7px; }
.dp-check { display: flex; align-items: center; gap: 5px; cursor: pointer; }
.dp-check input { accent-color: #8b0000; cursor: pointer; }
.dp-field-label { color: #8a7f6d; min-width: 52px; font-size: 11px; text-transform: uppercase; letter-spacing: 1px; }

/* Bonding is 0-4 and reads as a filled meter, not a number field - it's a
   level you climb, and the shape should say so at a glance. */
.dp-bond { display: flex; gap: 3px; }
.dp-bond-step {
    width: 24px;
    height: 22px;
    background: #262626;
    border: 1px solid #4a4a4a;
    color: #6a6a6a;
    cursor: pointer;
    font-family: inherit;
    font-size: 11px;
}
.dp-bond-step.is-set { background: #4a3c2e; border-color: #c8a951; color: #e0d8c0; }
.dp-bond-step.is-current { box-shadow: inset 0 0 0 1px #c8a951; }
.dp-bond-note { color: #c8a951; font-size: 10px; text-transform: uppercase; letter-spacing: 1px; }

.dp-pins { color: #8a8a8a; font-size: 11px; }

.dp-coat-select {
    background: #262626;
    color: #e0d8c0;
    border: 1px solid #4a4a4a;
    padding: 3px 6px;
    font-family: inherit;
    font-size: 12px;
}
.dp-acquire { color: #c0b8a0; margin: 4px 0 8px; line-height: 1.5; }
.dp-stats { display: flex; flex-wrap: wrap; gap: 6px; }
.dp-stat {
    display: flex;
    gap: 6px;
    background: #262626;
    border: 1px solid #3a3a3a;
    padding: 3px 8px;
}
.dp-stat-name { color: #8a7f6d; text-transform: capitalize; font-size: 11px; }
.dp-stat-value { color: #e0d8c0; font-variant-numeric: tabular-nums; }

/* Says why it's empty. An empty dropdown would read as "this horse has no
   coats" rather than "nobody has typed them in yet". */
.dp-pending {
    color: #8a7f6d;
    background: #221f18;
    border: 1px dashed #3a3228;
    padding: 8px 10px;
    line-height: 1.5;
    font-size: 11px;
}

/* A real fact about the recipe (bought consumables it also needs, where the
   pamphlet is obtained), not a gap - solid border, not dashed, so it doesn't
   read as the same "nobody filled this in" message as .dp-pending. */
.dp-note {
    color: #9a927f;
    background: #1c1a17;
    border: 1px solid #3a3228;
    padding: 7px 9px;
    margin-top: 6px;
    line-height: 1.5;
    font-size: 11px;
}

/* One pip per compendium task. The game shows an entry as half-filled when you
   have done some but not all of them; done/not-done alone loses that. */
.card-tasks { display: flex; gap: 3px; margin-top: 3px; }
.task-dot {
    width: 7px;
    height: 7px;
    border: 1px solid #6a6250;
    background: transparent;
    border-radius: 50%;
}
.task-dot.is-done { background: #c8a951; border-color: #c8a951; }

/* Same fact as the pips, for entries with too many units to draw them. */
.task-tally {
    color: #6a6250;
    font-size: 9px;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.5px;
}
.task-tally.is-done { color: #c8a951; }

.dp-progress {
    color: #8a7f6d;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    margin-right: 8px;
}
.dp-progress.is-complete { color: #c8a951; }
.dp-tasks { display: flex; flex-direction: column; gap: 2px; margin-bottom: 9px; }

/* One line per task, its boxes ranged right so every line's boxes start from
   the same edge and the column reads as a tally rather than a form. A task done
   N times is one line with N boxes, not N labelled rows. */
.dp-task {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 3px 0;
    border-bottom: 1px solid #262320;
}
.dp-task:last-child { border-bottom: none; }
.dp-task-name { flex: 1; color: #c0b8a0; font-size: 11px; }
.dp-task-tally {
    color: #6a6250;
    font-size: 10px;
    margin-left: 6px;
    font-variant-numeric: tabular-nums;
}
.dp-task-boxes { display: flex; gap: 3px; }
.dp-task-box {
    width: 14px;
    height: 14px;
    padding: 0;
    background: #262626;
    border: 1px solid #4a4a4a;
    cursor: pointer;
}
.dp-task-box:hover { border-color: #8a7f6d; }
.dp-task-box.is-done { background: #c8a951; border-color: #c8a951; }

/* One row per material a recipe needs. Same visual language as .dp-task above -
   a tally you tick off, not a settings dialog - because it is the same gesture.
   The box is deliberately identical: ticking a pelt and ticking a compendium
   task are the same kind of act. */
.dp-mats { margin: 6px 0; }
.dp-mat {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 0;
    border-bottom: 1px solid #262320;
}
.dp-mat:last-child { border-bottom: none; }
.dp-part-box {
    width: 14px;
    height: 14px;
    padding: 0;
    flex: none;
    background: #262626;
    border: 1px solid #4a4a4a;
    cursor: pointer;
}
.dp-part-box:hover { border-color: #8a7f6d; }
.dp-part-box.is-done { background: #c8a951; border-color: #c8a951; }
.dp-part-boxes { display: flex; gap: 3px; flex: none; }
.dp-mat-name { flex: 1; color: #c0b8a0; font-size: 11px; }
.dp-mat-part { flex: 1; color: #6a6250; font-size: 10px; text-transform: uppercase; letter-spacing: 1px; }
.dp-mat-tag {
    flex: none;
    color: #8a7f6d;
    border: 1px solid #3a3228;
    padding: 1px 4px;
    font-size: 9px;
    letter-spacing: 1px;
}
.dp-mat-tag.is-missable { color: #d08a70; border-color: #5a3020; }
.dp-mat-tag.badge-unique { color: #241d15; background: #c8a951; border-color: #c8a951; }
/* The highest-value thing this panel can say: the item you want needs an animal
   you can no longer get. It gets the loudest treatment on the row. */
.dp-mat-tag.is-lost { color: #e08070; border-color: #7a2020; background: #3a1010; }
.dp-mat-map {
    flex: none;
    background: #262626;
    border: 1px solid #4a4a4a;
    color: #c0b8a0;
    padding: 2px 7px;
    font-family: inherit;
    font-size: 10px;
    cursor: pointer;
}
.dp-mat-map:hover { background: #4a3c2e; border-color: #c8a951; color: #e0d8c0; }

/* One block per requirement: what it wants and how many, then one .dp-mat line
   per animal that can supply it. Single-candidate requirements use the same
   shape, so "how many do I need" is always in the same place. */
.dp-mat-group { margin: 4px 0; }
.dp-mat-group.is-met .dp-mat-group-label { color: #7a8a5a; }
.dp-mat-group-label {
    display: flex;
    align-items: baseline;
    gap: 8px;
    color: #6a6250;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 1px;
    padding: 4px 0 2px;
}
.dp-mat-tally { font-variant-numeric: tabular-nums; color: #8a7f6d; }
.dp-mat-any { color: #4a453c; letter-spacing: 0.5px; }
.dp-mat-group .dp-mat { padding-left: 10px; }

/* The MATERIAL's own row - "Snake Skin", not twelve snakes. This is the line
   the recipe is actually asking for, so it reads at full strength and the
   species underneath are dimmed and folded away. */
.dp-mat-head { padding-left: 0; }
.dp-mat-head .dp-mat-name {
    color: #d8cfb4;
    font-size: 12px;
    letter-spacing: 0.3px;
}
.dp-mat-group.is-met .dp-mat-head .dp-mat-name { color: #8fa06a; }

/* Folded by default. `summary` is the only affordance that says the species
   are still there, so it must not look like a heading you cannot click. */
.dp-mat-sources > summary {
    cursor: pointer;
    padding: 2px 0 2px 10px;
    color: #5c5648;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 1px;
    list-style: none;
}
.dp-mat-sources > summary::-webkit-details-marker { display: none; }
.dp-mat-sources > summary::before {
    content: '▸ ';
    display: inline-block;
    width: 10px;
    transition: transform 0.12s ease;
}
.dp-mat-sources[open] > summary::before { transform: rotate(90deg); }
.dp-mat-sources > summary:hover { color: #a89b7e; }
.dp-mat-sources .dp-mat { padding-left: 20px; }
.dp-mat-sources .dp-mat-name { color: #8a8270; }

/* The lit ingredient. Only ever one at a time, so this can be loud. */
.dp-mat-map.is-lit {
    background: #c8a951;
    border-color: #c8a951;
    color: #241d15;
}
.dp-mat-map.is-lit:hover { background: #d8b961; color: #241d15; }

/* The favourite star. Off it is nearly invisible - it is an opt-in highlight,
   not a control competing with the item's name for attention. */
.dp-fav {
    flex: none;
    background: none;
    border: none;
    color: #3a3630;
    font-size: 13px;
    line-height: 1;
    padding: 0 2px;
    cursor: pointer;
}
.dp-fav:hover { color: #8a7f6d; }
.dp-fav.is-on { color: #c8a951; }

/* Having every material is not owning the coat - you still have to walk to the
   trapper and pay. So this announces the fact and offers the tick, rather than
   silently flipping "crafted" and dropping the item out of the hunt list before
   you have been. 2026-08-10: "when you've checked all the boxes for
   materials it doesn't mark as crafted." */
.dp-ready {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin: 6px 0;
    padding: 6px 8px;
    background: #1e2416;
    border: 1px solid #4a5a30;
    color: #a8c070;
    font-size: 11px;
}
.dp-show-range {
    background: #262626;
    border: 1px solid #4a4a4a;
    color: #c0b8a0;
    padding: 4px 9px;
    font-family: inherit;
    font-size: 11px;
    cursor: pointer;
    margin-top: 8px;
}
.dp-show-range:hover { background: #4a3c2e; border-color: #c8a951; color: #e0d8c0; }

/* Range controls, now inside the fish's own detail panel rather than a separate
   sidebar disclosure - design note: "avoid clutter and put things in menus". */
.dp-range { border-top: 1px solid #3a3228; margin-top: 9px; padding-top: 8px; }
.dp-range .dp-field-label { display: block; margin-bottom: 5px; }
.dp-range .dp-check { font-size: 11px; margin-bottom: 3px; }
.dp-range input[type="radio"] { accent-color: #8b0000; cursor: pointer; }
.dp-rivals { color: #8a7f6d; font-size: 10px; margin: 6px 0 0; line-height: 1.4; }

.fc-range {
    background: #262626;
    border: 1px solid #4a4a4a;
    color: #c0b8a0;
    padding: 2px 8px;
    font-family: inherit;
    font-size: 11px;
    cursor: pointer;
}
.fc-range:hover { background: #4a3c2e; border-color: #c8a951; color: #e0d8c0; }

/* A counter is a number field, not a meter you climb - 144 steps would be a
   silly shape. Buttons flank a typeable value so a shop run's worth of cards
   goes in with two clicks and a mid-playthrough total goes in with one entry. */
.dp-count-row { flex-wrap: wrap; gap: 4px; }
.dp-count-row .dp-field-label { flex-basis: 100%; margin-bottom: 2px; }
.dp-count-step {
    min-width: 30px;
    height: 24px;
    background: #262626;
    border: 1px solid #4a4a4a;
    color: #c0b8a0;
    cursor: pointer;
    font-family: inherit;
    font-size: 11px;
}
.dp-count-step:hover { background: #4a3c2e; border-color: #c8a951; color: #e0d8c0; }
.dp-count-input {
    width: 56px;
    height: 24px;
    background: #1a1a1a;
    border: 1px solid #4a4a4a;
    color: #e0d8c0;
    font-family: inherit;
    font-size: 12px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.dp-count-total { color: #8a7f6d; font-size: 11px; }

.dp-count-bar { height: 5px; background: #262626; border: 1px solid #3a3a3a; margin: 2px 0 9px; }
.dp-count-bar span { display: block; height: 100%; background: #c8a951; }

/* Same treatment as .dp-pending: this is the reason the entry isn't a list of
   144, and it should read as an explanation rather than a stray tip. */
.dp-count-note {
    color: #8a7f6d;
    background: #221f18;
    border: 1px dashed #3a3228;
    padding: 8px 10px;
    line-height: 1.5;
    font-size: 11px;
}

/* The card's face for a counter: the tick can't say 37, so the number does. */
.card-count {
    margin-top: 3px;
    color: #c8a951;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.5px;
}

/* Row-layout cards carry a longer name (breed names, legendary animals), so
   they get the width a 5-across grid cell can't give them. */
.compendium-page.page-row .animal-card { width: 100%; }

/* Coat detail within the horse panel */
.dp-coat-tally { color: #c8a951; font-size: 11px; font-variant-numeric: tabular-nums; }
.dp-owned-row { margin: 8px 0 6px; }

.dp-facts { display: grid; grid-template-columns: auto 1fr; gap: 3px 10px; margin: 6px 0 8px; }
.dp-fact { display: contents; }
.dp-fact-key {
    color: #8a7f6d;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 1px;
    align-self: center;
}
.dp-fact-value { color: #c0b8a0; line-height: 1.4; }

.dp-missable {
    background: #4a1010;
    border-left: 3px solid #b71c1c;
    color: #ff9b9b;
    padding: 5px 8px;
    margin: 6px 0;
    font-size: 11px;
}
.dp-npc {
    color: #8a7f6d;
    font-style: italic;
    border-left: 2px solid #3a3228;
    padding: 4px 8px;
    margin: 6px 0;
    font-size: 11px;
    line-height: 1.5;
}

/* ---- Compare Horses ----------------------------------------------------- */

.tx-action {
    display: block;
    margin: 6px 6px 4px;
    padding: 5px 10px;
    background: #3d3126;
    color: #e0d8c0;
    border: 1px solid #5c4e40;
    font-family: inherit;
    font-size: 11px;
    letter-spacing: 1px;
    cursor: pointer;
}
.tx-action:hover { background: #4a3c2e; }
.tx-bulk-track { display: flex; }
.tx-bulk-track .tx-action { flex: 1; }

/* ONE rule for every full-screen tool. They used to be three separate rules at
   three different z-indexes (1100/1200/1300) with three different header
   styles, which is why they read as three unrelated kinds of thing. Any new
   overlay joins this list - #hunt-list was missing from it, which is the whole
   reason it rendered `position: static` behind the map and looked dead.
   What's Nearby is deliberately NOT here; see its own rule. */
#horse-compare, #hunt-list, #table-panel, #challenge-panel {
    position: absolute;
    inset: 0;
    z-index: 1200;
    display: flex;
    flex-direction: column;
    background: #141414;
}
#horse-compare[hidden], #hunt-list[hidden],
#challenge-panel[hidden] { display: none; }

/* Every tool's title bar, full-screen or not. One size, one border, one close
   button - three rules that disagreed on all three used to be what made these
   panels feel like different apps. */
.overlay-header {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 12px 16px;
    background: #1a1a1a;
    border-bottom: 2px solid #8b0000;
}
.overlay-header h2 {
    margin: 0;
    flex: none;
    font-size: 16px;
    text-transform: uppercase;
    letter-spacing: 2px;
    white-space: nowrap;
}
.overlay-header > button[aria-label^="Close"] {
    margin-left: auto;
    flex: none;
    background: none;
    border: none;
    color: #999;
    font-size: 16px;
    cursor: pointer;
}
.overlay-header > button[aria-label^="Close"]:hover { color: #e0d8c0; }
.hc-progress { display: flex; gap: 18px; flex: 1; font-size: 12px; color: #9a927f; }
.hc-progress b { color: #e0d8c0; font-variant-numeric: tabular-nums; }
.hc-progress .hc-done b { color: #4caf50; }
.hc-toggle { display: flex; align-items: center; gap: 5px; font-size: 11px; color: #9a927f; cursor: pointer; }
.hc-toggle input { accent-color: #8b0000; cursor: pointer; }

.hc-scroll { flex: 1; overflow: auto; }
.hc-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.hc-table th, .hc-table td {
    padding: 5px 9px;
    text-align: left;
    border-bottom: 1px solid #242424;
    white-space: nowrap;
}
.hc-table thead th {
    position: sticky;
    top: 0;
    background: #1f1f1f;
    color: #c0b8a0;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 1px;
    z-index: 1;
}
.hc-table th.sortable { cursor: pointer; user-select: none; }
.hc-table th.sortable:hover { color: #fff; }
.hc-table th.sorted { color: #c8a951; }
.hc-table th.sorted[data-dir="asc"]::after  { content: ' ▲'; font-size: 8px; }
.hc-table th.sorted[data-dir="desc"]::after { content: ' ▼'; font-size: 8px; }
.hc-table tbody tr:hover { background: #1e1e1e; }

.hc-check { width: 28px; }
.hc-check input { accent-color: #2e8b57; cursor: pointer; }
.hc-cost { font-variant-numeric: tabular-nums; color: #c0b8a0; }
.hc-stat { text-align: center; font-variant-numeric: tabular-nums; }
.hc-total { color: #c0b8a0; font-weight: bold; }

/* The best value in each stat column - the whole reason this view exists is
   answering "which is best" without reading every row. */
.hc-stat.is-best { color: #c8a951; background: rgba(200, 169, 81, 0.10); }

/* Gang horses are reference only, so they recede rather than compete. */
.hc-npc td { color: #6a6a6a; font-style: italic; }

/* ---- Hunt list ---------------------------------------------------------- */
/* Two panes: the goals on the left, narrow because a goal is one line, and the
   materials on the right in cards, because each one carries a tally, a list of
   what wants it, and several places to get it. */
.hl-body { flex: 1; display: flex; min-height: 0; }
/* min-width:0 on both panes: without it a flex child refuses to shrink below
   its content and the whole overlay grows a horizontal scrollbar instead of the
   cards reflowing. */
.hl-pane { overflow: auto; padding: 12px 16px; min-width: 0; }
.hl-pane h3 {
    margin: 0 0 8px;
    font-size: 10px;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: #6a6250;
}
.hl-pane-recipes { width: 300px; flex: none; border-right: 1px solid #2a2a2a; background: #171717; }
.hl-pane-materials { flex: 1; }

.hl-recipe {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 0;
    border-bottom: 1px solid #232323;
    font-size: 12px;
}
.hl-recipe-name { flex: 1; color: #c0b8a0; }
.hl-recipe.is-fav .hl-recipe-name { color: #e0d8c0; }
.hl-recipe-count { color: #6a6250; font-size: 10px; font-variant-numeric: tabular-nums; }
.hl-recipe.is-ready .hl-recipe-count { color: #a8c070; }
.hl-craft {
    background: #2a3418;
    border: 1px solid #4a5a30;
    color: #a8c070;
    font-family: inherit;
    font-size: 9px;
    letter-spacing: 1px;
    padding: 2px 6px;
    cursor: pointer;
}
.hl-craft:hover { background: #3a4a20; color: #d0e0a0; }

/* Drops the recipe off the hunt list. Deliberately quiet - it undoes a tick
   you made on purpose, and it sits next to CRAFT, which must stay the loud
   one. Touch-sized because the hunt list is a phone screen first. */
.hl-drop {
    background: none;
    border: 1px solid transparent;
    color: #6a6250;
    font-family: inherit;
    font-size: 14px;
    line-height: 1;
    min-width: 24px;
    min-height: 24px;
    cursor: pointer;
}
.hl-drop:hover { border-color: #5a3030; color: #c08080; }

/* Cards rather than table rows: a requirement has a tally, a "for" list and
   several sources, and a five-column table made all three fight for one line. */
.hl-materials-grid { display: block; }
#hunt-materials {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
    gap: 10px;
    align-items: start;
}
.hl-mat {
    border: 1px solid #2a2a2a;
    background: #1a1a1a;
    padding: 8px 10px;
}
.hl-mat.is-met { opacity: 0.55; border-color: #3a4a30; }
.hl-mat.is-fav { border-color: #6a5a28; }
/* A sub-recipe with nothing recorded. Says what is missing rather than showing
   a blank where six satchels should be. */
.hl-mat.is-gap { border-color: #4a3a20; background: #1e1a14; }
.hl-mat.is-gap .hl-mat-tally { color: #b08a50; }
.hl-gap-note { color: #6a6250; font-size: 10px; line-height: 1.5; }
.hl-mat-any { color: #4a453c; font-size: 9px; letter-spacing: 1px; text-transform: uppercase; }
/* A goal you didn't tick but can't skip - one of Legend of the East's six. */
.hl-prereq { display: block; color: #6a6250; font-size: 9px; letter-spacing: 0.5px; }
/* A prerequisite's box states a fact rather than offering a tick - you mark it
   crafted on its own card, not from the thing that needs it. */
.dp-part-box.is-static { cursor: default; }
.dp-prereqs { margin: 6px 0; }
.hl-mat-head { display: flex; align-items: baseline; gap: 8px; }
.hl-mat-label {
    flex: 1;
    color: #e0d8c0;
    font-size: 12px;
    text-transform: capitalize;
}
.hl-mat-tally { color: #8a7f6d; font-size: 12px; font-variant-numeric: tabular-nums; }
.hl-mat-tally.is-met { color: #a8c070; }
.hl-mat-for { color: #6a6250; font-size: 10px; margin: 2px 0 6px; }
.hl-mat-for .is-fav { color: #c8a951; }

.hl-sources { display: flex; flex-direction: column; gap: 3px; }
.hl-source {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
}
.hl-source-name { flex: 1; min-width: 0; color: #c0b8a0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hl-spawns { color: #4a453c; font-size: 9px; white-space: nowrap; }

/* The +/- pair. 2026-08-10: "there should be a subtract button for
   whenever you actually find the thing." */
.hl-step { display: flex; align-items: center; gap: 4px; flex: none; }
.hl-step button {
    width: 18px;
    height: 18px;
    padding: 0;
    background: #262626;
    border: 1px solid #4a4a4a;
    color: #c0b8a0;
    font-family: inherit;
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
}
.hl-step button:hover:not(:disabled) { background: #4a3c2e; border-color: #c8a951; }
.hl-step button:disabled { opacity: 0.3; cursor: default; }
.hl-step b { min-width: 12px; text-align: center; color: #e0d8c0; font-variant-numeric: tabular-nums; }

.hl-empty { color: #6a6250; font-size: 11px; line-height: 1.6; grid-column: 1 / -1; }

/* Clustering is gone: the dense categories draw to a canvas
   (densePinLayer) and the sparse ones are viewport-culled teardrops
   (mapRenderer), so nothing produces a count bubble any more - and
   leaflet.markercluster, which drew them, is no longer loaded at all.
   2026-08-13: "figure out how to optimize this website for lower end
   devices." */
.pin-icon { background: none; border: none; }

/* The dense-category canvas. Pointer events stay off: it sits over the tiles
   and would otherwise swallow every click meant for an icon pin below it. Its
   own clicks come from a map-level handler instead - see densePinLayer. */
.dense-pin-layer {
    position: absolute;
    top: 0;
    left: 0;
    pointer-events: none;
}

/* THE TEARDROP
   2026-08-13: "instead of circular pins we should have teardrop pins so that
   players will have extremely precise map locations." The tip is the
   coordinate - see PIN_W/iconAnchor in mapRenderer - and everything here is
   laid out around keeping that tip exactly at the bottom centre of the box.

   The head and the glyph are siblings, not nested: the head is rotated 45deg
   to make its one square corner point downward, and a glyph inside it would
   inherit that rotation and sit on its ear. */
.pin-drop {
    position: relative;
    display: block;
    width: 100%;
    height: 100%;
}

.pin-drop-head {
    box-sizing: border-box;
    position: absolute;
    top: 0;
    left: 0;
    /* A square as wide as the icon box. Its own height is its width, NOT the
       box's height - the extra height below it is where the rotated corner
       hangs down to reach the tip. */
    width: 100%;
    aspect-ratio: 1;
    /* Three round corners and one square one, turned so the square one is at
       the bottom. */
    border-radius: 50% 50% 50% 0;
    transform: rotate(-45deg);
    /* Same dark ring as the cluster bubble, for the same reason: a pale
       outline disappears over desert tiles. */
    border: 3px solid rgba(0, 0, 0, 0.7);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}

/* Centred on the round part of the head, which is the middle of the top square
   - not the middle of the taller icon box, or every glyph would sit low and
   drift into the tail.

   An <i> with a background-image, not an inline <svg>. Each icon is emitted
   once as a class (installIconStyles in categoryIcons.js) and shared by every
   pin that uses it - inlining the path into each marker cost 19.9 MB of
   duplicated path data with the plants chip on. The browser decodes one image
   per icon and reuses it, which image caching is already very good at. */
.pin-drop-glyph {
    position: absolute;
    top: 50%;
    left: 50%;
    /* Square, and square against the WIDTH - the icon box is taller than it is
       wide because of the tail, so a 52% height would not match a 52% width and
       the glyph would sit in a letterboxed slot rather than centred on the head. */
    width: 52%;
    aspect-ratio: 1;
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    /* top:50% is half the BOX height (0.6036W), but the head's centre is at
       half its own width (0.5W). A margin-top percentage resolves against the
       container's width, so the correction is exactly that gap: -0.1036W. */
    margin-top: -10.36%;
    transform: translate(-50%, -50%);
    display: block;
    /* Clicks must land on the outer span. This mattered more when the glyph was
       an inline <svg>: an SVGElement's `className` is an SVGAnimatedString
       rather than a string, and Leaflet's class helpers read `.baseVal` off it,
       so a click resolving to the <svg> or its <path> could eat the event. The
       glyph is a plain <i> now, but the rule still earns its place - a click
       belongs to the pin, not to the picture on it. */
    pointer-events: none;
}

/* The glyph's near-black fill is baked into the image - see GLYPH_FILL in
   categoryIcons.js. Near-black rather than pure, and never white: the category
   colours run from pale bone to mid red, and only a dark glyph is legible on
   all of them. */

/* 2026-08-12: "when tracked it puts a dot on the store" - a tracked outfit has
   no coords of its own (see build_shops.py), so the pin that changes is its
   shop's. A small white dot on the shop's own pin, not a second colour for the
   whole marker, because the shop is still a shop first - this says "and
   something here is wanted" without hiding what it already told you. */
.pin-drop.is-wanted::after {
    content: '';
    position: absolute;
    top: -3px;
    right: -3px;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #fff;
    border: 3px solid rgba(0, 0, 0, 0.7);
}


/* =========================================
   INVENTORY PAGE (hunt list, second tab)
   ========================================= */
.hl-tabs { display: flex; gap: 4px; }
.hl-tabs button {
    background: #1e1e1e;
    border: 1px solid #2f2f2f;
    color: #8a8270;
    font-family: inherit;
    font-size: 10px;
    letter-spacing: 1px;
    text-transform: uppercase;
    padding: 4px 10px;
    cursor: pointer;
}
.hl-tabs button.is-on { background: #2e2418; border-color: #6a5a28; color: #e0d8c0; }

.hl-inv-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}
.hl-inv-bar input[type="search"] {
    flex: 1 1 220px;
    min-width: 0;
    background: #111;
    border: 1px solid #333;
    color: #ddd;
    font-family: inherit;
    padding: 6px 8px;
}
.hl-inv-count { color: #6a6250; font-size: 10px; letter-spacing: 1px; }

/* A dense two-column-ish list rather than cards: the inventory is something you
   scan for one name and type a number into, not something you read. */
#hunt-inv-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
    gap: 2px 16px;
}
.hl-inv {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 6px;
    border-bottom: 1px solid #202020;
    font-size: 12px;
    color: #7a7466;
}
.hl-inv.is-held { color: #c0b8a0; background: #1b1a16; }
.hl-inv-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hl-inv-part { color: #5a5448; font-size: 10px; letter-spacing: 0.5px; }
.hl-inv-input {
    width: 3.5em;
    background: #111;
    border: 1px solid #333;
    color: #ddd;
    font-family: inherit;
    font-size: 12px;
    text-align: center;
    padding: 2px;
}

/* =========================================
   RESPONSIVE
   =========================================
   Everything above was authored at one size. The card grid is the constraint:
   the sidebar width is DERIVED from a 5-across page (see the top of this file),
   so making the app scale means scaling the card, not overriding the widths one
   at a time. --card-w/--card-h are those numbers; the derivation is unchanged,
   just written in terms of them. 2026-08-10: "the fixed pixel sizes make
   it impossible to scale this around." */
:root {
    --card-w: clamp(56px, 5.6vw, 82px);
    --card-h: clamp(37px, 3.7vw, 54px);
    --sidebar-width: calc(5 * var(--card-w) + 4 * 10px + 10px + 60px + 30px + 20px);
}
.compendium-page { grid-template-columns: repeat(5, var(--card-w)); grid-template-rows: repeat(3, var(--card-h)); }
.compendium-page.page-grid4 { grid-template-columns: repeat(4, var(--card-w)); grid-template-rows: repeat(5, var(--card-h)); }
/* page-row was left behind when the fixed 82px sizes moved to --card-w, so it
   kept `repeat(5, 82px)` while the sidebar around it shrank. Five cards no
   longer fit, the fifth was clipped, and the carousel still paged by five -
   2026-08-19: "some cards that are meant to display 5 only show 4 but still
   scroll 5. These are Legendary animals, legendary fish, gangs, horses, and
   camp request", which is every layout:'row' subsection. */
.compendium-page.page-row { grid-template-columns: repeat(5, var(--card-w)); grid-template-rows: var(--card-h); }
.compendium-grid { min-height: calc(3 * var(--card-h) + 2 * 10px + 10px); }
.compendium-grid.layout-grid4 {
    min-height: calc(5 * var(--card-h) + 4 * 10px + 10px);
    width: calc(4 * var(--card-w) + 3 * 10px + 10px);
}

/* Below this the two-pane hunt list is two half-panes of nothing. */
@media (max-width: 900px) {
    .hl-body { flex-direction: column; }
    .hl-pane-recipes { width: auto; flex: 0 0 auto; border-right: none; border-bottom: 1px solid #2a2a2a; }
    .hl-pane-materials { flex: 1 1 auto; }
}

/* Phone / second screen. One bottom tab bar, four panes that are the sidebar's
   EXISTING children - no markup moved, desktop still gets the single stacked
   rail. Compendium is the landing tab: 2026-08-26, "it's cluttered and I have
   to click too much to get around... the main user is also probably on their
   phone", and the common user came for the compendium, not the chip bar that
   used to stand between the title and the search box. */
#mobile-nav { display: none; }

@media (max-width: 780px) {
    :root { --card-w: clamp(52px, 17vw, 82px); --card-h: clamp(34px, 11vw, 54px); }

    #map { left: 0; }
    #sidebar {
        width: 100%;
        border-right: none;
        padding: 10px;
        /* Room for the tab bar, which is fixed over the bottom edge. */
        padding-bottom: 64px;
    }
    body[data-mtab="map"] #sidebar { display: none; }

    /* One group of sidebar children per tab. Everything is opt-in per tab so a
       new sidebar section fails visible (shows everywhere) rather than
       vanishing. The missable banners belong with the compendium, not the
       filters - they are "do this now" reading, not configuration. */
    /* Do Now rides with Filters, not with the compendium: on the desktop page
       it sits directly under the chapter row, and the landing tab is the one
       place a missable warning is worth putting. */
    body[data-mtab="list"] .global-context,
    body[data-mtab="list"] .toolbox,
    body[data-mtab="list"] .icon-credits,
    body[data-mtab="list"] #missable-alert,
    body[data-mtab="list"] #chapter-missable,
    body[data-mtab="filters"] .toolbox,
    body[data-mtab="filters"] #content-area,
    body[data-mtab="filters"] .icon-credits,
    body[data-mtab="tools"] .global-context,
    body[data-mtab="tools"] #content-area,
    body[data-mtab="tools"] #missable-alert,
    body[data-mtab="tools"] #chapter-missable { display: none; }

    /* The chapter dropdown is the one filter the compendium tab cannot lose -
       everything on screen is gated by it - so it is repeated nowhere and the
       Filters tab is one tap away. */

    #nearby-panel { width: 100%; left: 0; right: 0; }

    /* 2026-08-26: "'What's nearby' just doesn't work like this" - it needs the
       map visible beside the results to mean anything, and on a phone the two
       take turns. Hidden rather than reworked; the desktop rail is untouched. */
    #nearby-toggle-btn { display: none; }

    #mobile-nav {
        display: flex;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        /* Above the sidebar (1000), below the tool overlays (1200): switching
           tabs is meaningless while the hunt list covers everything. */
        z-index: 1100;
        background: #171310;
        border-top: 1px solid #3a2f1e;
        padding-bottom: env(safe-area-inset-bottom, 0);
    }
    #mobile-nav button {
        flex: 1;
        background: none;
        border: none;
        border-top: 2px solid transparent;
        color: #8a8270;
        font-family: inherit;
        font-size: 10px;
        letter-spacing: 1.5px;
        text-transform: uppercase;
        padding: 12px 2px 10px;
        cursor: pointer;
    }
    #mobile-nav button.is-on {
        color: #e0d8c0;
        border-top-color: #c8a951;
    }

    .overlay-header { flex-wrap: wrap; gap: 6px 10px; }
    .toolbox-grid button { flex: 1 1 45%; padding: 14px 6px; }
    /* Thumbs, not mouse pointers. */
    .hl-step button, .dp-part-box, .hl-tabs button { min-width: 30px; min-height: 30px; }
    .table-filters input { min-width: 0; width: 100%; }
}

/* `display: flex` on .hl-body outranks the [hidden] attribute's UA rule, so the
   two hunt-list pages rendered on top of each other. Same trap as any flex/grid
   container that also toggles `hidden`. */
.hl-body[hidden] { display: none; }
/* Header only - the inventory page has its own .hc-toggle ("Only what I have")
   inside .hl-inv-bar, which an unscoped selector would hide too. */
#hunt-list.is-inventory > .overlay-header .hc-toggle,
#hunt-list.is-inventory > .overlay-header #hunt-list-progress { display: none; }
/* .hl-pane carries no flex-grow (the hunt view's two panes size themselves),
   so the inventory's single pane shrank to its content and left most of a
   1600px overlay empty. */
.hl-inv-body .hl-pane { flex: 1; }

/* CHALLENGES
   Nine cards on an auto-fitting grid rather than a fixed column count: the
   card has a natural minimum (a rank name plus its tip is ~2 lines at 320px)
   and the screen decides how many fit. Per the project's rule, no px width is
   authored here beyond that one minimum. */
.ch-body {
    flex: 1;
    overflow-y: auto;
    padding: 16px;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 12px;
    align-content: start;
}

.ch-card {
    background: #1c1c1c;
    border: 1px solid #333;
    border-radius: 4px;
    padding: 12px 14px;
}
.ch-card.is-finished { opacity: 0.6; }

.ch-card-head { display: flex; align-items: baseline; gap: 10px; }
.ch-card-head h3 {
    margin: 0;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: #e0d8c0;
}
.ch-count { font-size: 12px; color: #9a927f; font-variant-numeric: tabular-nums; }
.ch-expand {
    margin-left: auto;
    background: none;
    border: 1px solid #4a4a4a;
    border-radius: 3px;
    color: #9a927f;
    font-family: inherit;
    font-size: 10px;
    padding: 2px 7px;
    cursor: pointer;
}
.ch-expand:hover { color: #e0d8c0; border-color: #6a6a6a; }

.ch-bar { height: 3px; background: #2b2b2b; border-radius: 2px; margin: 8px 0 10px; }
.ch-bar i { display: block; height: 100%; background: #8b0000; border-radius: 2px; }

.ch-rank { display: flex; gap: 9px; padding: 7px 0; border-top: 1px solid #2a2a2a; }
.ch-rank:first-of-type { border-top: none; }
.ch-rank.is-done { opacity: 0.45; }
/* The rank you're on, when the whole ladder is showing. Without it, an
   expanded card is ten equal rows and the answer to "what now" is gone. */
.ch-rank.is-current { background: #201c14; margin: 0 -14px; padding: 7px 14px; }
.ch-rank.is-locked .ch-rank-name { color: #8a8378; }

.ch-tick {
    flex: none;
    width: 18px;
    height: 18px;
    margin-top: 1px;
    background: #262626;
    border: 1px solid #555;
    border-radius: 3px;
    color: #4caf50;
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
}
.ch-tick:hover { border-color: #8b0000; }

.ch-rank-main { min-width: 0; }
.ch-rank-name { font-size: 12px; color: #d8d0c0; }
.ch-rank-name b { color: #9a927f; font-weight: normal; }
.ch-tip { margin: 4px 0 0; font-size: 11px; line-height: 1.45; color: #9a927f; }

.ch-places { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.ch-place {
    background: #2b2b2b;
    border: 1px solid #4a4a4a;
    border-radius: 3px;
    color: #c9a24a;
    font-family: inherit;
    font-size: 10px;
    padding: 2px 6px;
    cursor: pointer;
}
.ch-place:hover { background: #3a3a3a; }
/* A place we hold no coordinates for. Same shape, deliberately not a button -
   there is nowhere to fly to, and a dead control is worse than a label. */
.ch-place-flat { color: #7d776c; cursor: default; border-style: dashed; }

.ch-locked { margin: 5px 0 0; font-size: 10px; color: #d9a825; }
.ch-finished { margin: 4px 0 0; font-size: 11px; color: #4caf50; }
.ch-empty { color: #9a927f; }

/* Coordinate picker, ?pick only - see js/coordPicker.js. A data-authoring
   tool, so it deliberately sits over the map rather than earning sidebar
   space no player would ever use. */
.coord-picker {
    position: absolute; top: 12px; right: 12px; z-index: 1200;
    width: 230px; max-height: 60vh; overflow-y: auto;
    background: #14110d; border: 1px solid #4a3f2d; border-radius: 3px;
    padding: 8px; font-size: 11px; color: #d8cfba;
    font-family: ui-monospace, Menlo, Consolas, monospace;
}
.coord-picker .cp-head {
    font-family: Georgia, serif; color: #c8a951; margin-bottom: 6px;
}
.coord-picker .cp-list div { padding: 1px 0; white-space: nowrap; }
.coord-picker .cp-list b { color: #c8a951; margin-right: 4px; }
.coord-picker .cp-row { margin-top: 8px; display: flex; gap: 4px; }
.coord-picker button { flex: 1; font-size: 10px; }
