/* ============================================================
   Horse Nation – Game Stylesheet
   Nature-themed, earthy greens and warm tones
   ============================================================ */

:root {
    --hn-green-dark:  #2d5016;
    --hn-green:       #4a7c2f;
    --hn-green-light: #6aad3d;
    --hn-brown:       #7b4f2e;
    --hn-brown-light: #c49a6c;
    --hn-gold:        #e8b624;
    --hn-cream:       #f5f0e8;
    --hn-text:        #2c2416;
    --hn-border:      #c8b99a;
    --hn-sidebar-bg:  #3a6421;
    --hn-sidebar-w:   160px;
    --hn-banner-h:    161px;
    --hn-header-h:    44px;
    color-scheme: light;
}

/* ── Base ─────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { margin: 0; padding: 0; background: #ddd8c8; }

body {
    margin: 0 auto;
    padding: 0;
    background-color: #e8e0d0;
    color: var(--hn-text);
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    font-size: 0.88rem;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    max-width: 1400px;
}
@media (min-width: 1432px) {
    body { box-shadow: 0 0 24px rgba(0,0,0,.15); }
}

a { color: var(--hn-green); text-decoration: none; }
a:hover { color: var(--hn-green-dark); text-decoration: underline; }

/* ── Flex utilities (no Bootstrap loaded) ─────────────────── */
.d-flex             { display: flex; }
.d-inline-flex      { display: inline-flex; }
.flex-wrap          { flex-wrap: wrap; }
.align-items-center { align-items: center; }
.align-items-start  { align-items: flex-start; }
.align-items-end    { align-items: flex-end; }
.align-self-center  { align-self: center; }
.justify-between    { justify-content: space-between; }
.justify-center     { justify-content: center; }
.ms-auto            { margin-left: auto; }
.ms-2               { margin-left: 0.5rem; }
.gap-1              { gap: 0.25rem; }
.gap-2              { gap: 0.5rem; }
.gap-3              { gap: 0.75rem; }

/* ── Header / Top Bar ─────────────────────────────────────── */
#hn-header {
    background: linear-gradient(135deg, var(--hn-green-dark) 0%, var(--hn-green) 60%, var(--hn-green-light) 100%);
    color: #fff;
    padding: 0 1rem 0 0;
    position: sticky;
    top: var(--hn-banner-h);
    z-index: 1030;
    height: var(--hn-header-h);
    display: flex;
    align-items: center;
    box-shadow: 0 2px 6px rgba(0,0,0,.4);
}

/* Mirrors the sidebar width so the flash pill starts at the same x as #hn-content */
.hn-header-sidebar-zone {
    width: var(--hn-sidebar-w);
    min-width: var(--hn-sidebar-w);
    flex-shrink: 0;
    display: flex;
    align-items: center;
    padding-left: 1rem;
}


.hn-topbar-stat {
    background: rgba(0,0,0,.25);
    border-radius: 4px;
    padding: 4px 12px;
    font-size: 0.85rem;
    white-space: nowrap;
}
.hn-topbar-stat strong { color: var(--hn-gold); }
a.hn-topbar-link { text-decoration: none; color: inherit; }
a.hn-topbar-link:hover { opacity: .85; }

/* Live HH:mm:ss clock in the navbar, gold and tabular-nums so the
   digits never jitter horizontally as seconds tick. Lives inside
   .hn-header-sidebar-zone so it sits in the far-left column of the
   navbar - vertically above the main sidebar on desktop. On phones
   (<=767px) it hides and the mobile twin .hn-banner-clock takes
   over in the top-right corner of #hn-banner, freeing horizontal
   space in the topbar for the quick-action row. The ticker in
   game.js drives both via [data-clock]; only one is visible at any
   breakpoint, but both stay in sync across a viewport resize.
   Always Europe/Amsterdam; never the user's local time.
   Opt-in: only rendered when the user enables show_navbar_clock
   under /account, so the rules below are dormant for everyone else. */
.hn-topbar-clock {
    color: var(--hn-gold);
    font-weight: 700;
    font-size: 0.95rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.5px;
    white-space: nowrap;
    line-height: 1;
    margin-left: 0.5rem;
}

/* Mobile twin of .hn-topbar-clock, anchored to the top-right corner
   of #hn-banner over the blue sky background. Default hidden on
   every viewport; the @media (max-width: 767px) block further down
   this file flips the visibility so only this twin paints on
   phones. Brown text with a soft cream halo - the warm leather
   brown reads cleanly against both the light sky and the white
   clouds, and the halo keeps it legible if it overlaps a bright
   cloud edge as the banner shrinks at the 640px/400px breakpoints.
   pointer-events:none so the area stays a no-op for taps (no
   accidental selection or text-cursor flicker when scrolling
   the page on touch devices). The same horizontal anchor as
   .hn-banner-beta-badge, but that badge is positioned RELATIVE
   to the centered logo so it stays near upper-center, well clear
   of the clock's corner. */
.hn-banner-clock {
    display: none;
    position: absolute;
    top: 6px;
    right: 10px;
    z-index: 2;
    color: var(--hn-brown);
    font-weight: 700;
    font-size: 0.9rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.5px;
    white-space: nowrap;
    line-height: 1;
    text-shadow: 0 0 4px rgba(245, 240, 232, 0.85);
    pointer-events: none;
}

/* The XP topbar chip's visible text is resolved server-side from the
   user's xp_display_numbers preference in User::xpChipText():
     - 0 (default) -> .hn-topbar-xp-display contains a single text node
                      with the percentage (e.g. "47%"), same on mobile
                      and desktop.
     - 1           -> .hn-topbar-xp-display contains two children:
                      .hn-topbar-xp-display-compact ("1.2K / 5.7K") and
                      .hn-topbar-xp-display-full ("1.234 / 5.678"). The
                      media-query block below shows compact on mobile
                      (<=767px) so the abbreviation fits the narrow
                      chip, and shows full on desktop (>=768px) so
                      precise numbers are visible when there's room.
   The 15s status poll in game.js and the training-submit handler in
   views/training/index.php both detect the twin spans and write the
   matching server fields (xp_compact / xp_full) into them, so neither
   viewport ever flashes the other one's format. The whole chip is an
   <a> to "/" because the home dashboard renders the full xp_bar
   partial with the same number, so tapping the chip lands you on the
   page that shows the rest. */
.hn-topbar-xp-display-compact { display: none; }
.hn-topbar-xp-display-full    { display: inline; }
@media (max-width: 767px) {
    .hn-topbar-xp-display-compact { display: inline; }
    .hn-topbar-xp-display-full    { display: none; }
}

/* Left-aligned quick action icons in the topbar (Klusjes / Training /
   Postvak). Pure inline-SVG artwork - no chip background by default.
   The icon inherits color via `currentColor`, so the two highlight
   states are just colour swaps:
     .active     - subtle white-translucent square, white icon stroke
                   (user is on that page; matches the left-sidebar
                   active link so the whole header reads as one
                   navigation surface)
     .has-action - icon stroke turns gold (something needs attention)
   When both classes are present, the active square stays AND the
   icon keeps its gold stroke, so being on /training while you still
   have a trainable horse left looks like a gold icon inside a
   translucent square. The 15s status poll in game.js removes the
   .has-action class as soon as the underlying state clears (timer
   expired, last horse trained, last unread read), so the gold tint
   self-cancels without needing per-page glue code. */
.hn-topbar-quick {
    padding-left: 0.75rem;
    gap: 0.25rem;
}
.hn-topbar-quick-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 4px;
    color: rgba(255, 255, 255, .8);
    border-radius: 6px;
    text-decoration: none;
    transition: color .15s, background .15s, transform .15s;
}
.hn-topbar-quick-btn:hover {
    color: #fff;
    text-decoration: none;
    transform: scale(1.08);
}
.hn-topbar-quick-btn svg {
    width: 24px;
    height: 24px;
    display: block;
}
.hn-topbar-quick-btn.has-action {
    color: var(--hn-gold);
}
.hn-topbar-quick-btn.has-action:hover {
    color: var(--hn-gold);
}
.hn-topbar-quick-btn.active {
    background: rgba(255, 255, 255, .12);
    color: #fff;
}
.hn-topbar-quick-btn.active:hover {
    background: rgba(255, 255, 255, .12);
    color: #fff;
}
/* On the active page AND something still pending: keep the active
   square AND the gold icon stroke. Drops back to plain .active the
   moment the next 15s poll observes that the action has cleared. */
.hn-topbar-quick-btn.active.has-action,
.hn-topbar-quick-btn.active.has-action:hover {
    color: var(--hn-gold);
}
@media (max-width: 400px) {
    .hn-topbar-quick { padding-left: 0.4rem; gap: 0.1rem; }
    .hn-topbar-quick-btn { padding: 3px; }
    .hn-topbar-quick-btn svg { width: 20px; height: 20px; }
}

/* ── Banner ───────────────────────────────────────────────── */
/* Seven-layer artwork (bottom -> top):
     - sky.png             (gradient sky, 1601x161, CSS background)
     - clouds.png          (cloud overlay, 1401x161, CSS background
                            on top of the sky)
     - horse-left.png      (275x161, transparent, pinned to the LEFT
                            edge, sits in front of the clouds)
     - horse-right.png     (274x161, transparent, pinned to the
                            RIGHT edge, sits in front of the clouds)
     - flowers-left.png    (701x161, left half of the original
                            flowers strip, pinned to the LEFT edge)
     - flowers-right.png   (701x161, right half, pinned to the
                            RIGHT edge)
     - logo.png            (555x373, centered, clickable, scales
                            with banner height; rendered last so it
                            always sits on top of every other layer)

   Sky + clouds use `cover; bottom center` so both crop in lockstep
   at every viewport width and the horizon stays pinned.

   Horses are real <img> elements (not backgrounds) so they preserve
   aspect ratio while scaling vertically with --hn-banner-h, and stay
   glued to their viewport edge as the screen narrows. The shrink
   ladder lower in this stylesheet (75% / 65% / 55%) keeps them from
   crowding the logo at narrow widths.

   Flowers were originally a single 1401x161 strip with detailed
   elements (fences, tulips) at specific horizontal positions. Width-
   fit shrunk those details to invisibility on phones; cover-fit
   cropped the side fences off entirely. Splitting the strip down
   the middle and pinning each half to its viewport edge at NATURAL
   pixel size gives us the best of both: the two outer fences stay
   anchored to the screen edges at their original size on every
   viewport, and as the screen narrows the two halves slide inward
   over each other in the (mostly-transparent) middle of the image.
   The 1-pixel overlap at the cut point hides any seam at exactly
   1401px viewports. */
#hn-banner {
    position: sticky;
    top: 0;
    z-index: 1031;
    width: 100%;
    height: var(--hn-banner-h);
    overflow: hidden;
    background:
        url('../images/banner/clouds.png') bottom center / cover no-repeat,
        url('../images/banner/sky.png')    bottom center / cover no-repeat;
}
.hn-banner-horse {
    position: absolute;
    bottom: 0;
    height: 100%;
    width: auto;
    display: block;
    pointer-events: none;
}
.hn-banner-horse-left  { left:  0; }
.hn-banner-horse-right { right: 0; }
.hn-banner-flowers {
    /* Each half is pinned to its viewport edge and scaled with
       --hn-banner-h (height: 100% = banner height). Source halves
       are 701x161 each, so on the default 161px banner they render
       at native pixel size. The shrink ladder lower down then mirrors
       the horse-shrink ladder, keeping fences and tulips proportional
       to the horse silhouettes at every breakpoint. */
    position: absolute;
    bottom: 0;
    height: 100%;
    width: auto;
    display: block;
    pointer-events: none;
}
.hn-banner-flowers-left  { left:  0; }
.hn-banner-flowers-right { right: 0; }
.hn-banner-logo-link {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    height: 75%;
    line-height: 0;
    display: block;
}
.hn-banner-logo {
    height: 100%;
    width: auto;
    display: block;
}
/* Beta-only stamp overlaid on the top-right corner of the logo. Rendered
   by the three site layouts (main, auth, public) inside .hn-banner-logo-link
   when APP_ENV === 'beta'. Sits inside the link so it scales/positions
   relative to the centered logo, not the banner. pointer-events:none keeps
   the parent <a> fully clickable underneath the badge. */
.hn-banner-beta-badge {
    position: absolute;
    top: -0.4rem;
    right: -1.2rem;
    background: #c0392b;
    color: #fff;
    font-weight: 700;
    font-size: 0.85rem;
    line-height: 1;
    letter-spacing: 1px;
    padding: 4px 10px;
    border-radius: 4px;
    border: 1px solid #fff;
    transform: rotate(12deg);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35);
    text-transform: uppercase;
    pointer-events: none;
    user-select: none;
}
#hn-banner a { line-height: 0; }

/* ── Layout Wrapper ───────────────────────────────────────── */
#hn-body {
    display: flex;
    flex: 1;
    position: relative;
    background:
        linear-gradient(to right, var(--hn-sidebar-bg) var(--hn-sidebar-w), transparent var(--hn-sidebar-w)),
        linear-gradient(to left, #f0e8d6 150px, transparent 150px);
}
#hn-body::after {
    content: '';
    position: absolute;
    right: 150px;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--hn-border);
}

/* ── Left Sidebar ─────────────────────────────────────────── */
#hn-sidebar-left {
    width: var(--hn-sidebar-w);
    min-width: var(--hn-sidebar-w);
    background: var(--hn-sidebar-bg);
    color: #dde;
    padding: 0.5rem 0;
    flex-shrink: 0;
    position: sticky;
    top: calc(var(--hn-banner-h) + var(--hn-header-h));
    align-self: flex-start;
}

.hn-nav-section {
    padding: 0.4rem 0.75rem 0.2rem;
    font-size: 0.74rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--hn-gold);
    border-top: 1px solid rgba(255,255,255,.1);
    margin-top: 0.4rem;
    display: flex;
    align-items: center;
}
.hn-nav-section:first-child { border-top: none; margin-top: 0; }
.hn-sidebar-mobile-only .hn-nav-section:first-child {
    border-top: 1px solid rgba(255,255,255,.1);
    margin-top: 0.4rem;
}
.hn-nav-section[data-section] {
    /* The heading itself is no longer the click target; only the chevron
       button (`.hn-section-toggle`) at the right edge collapses the
       section. This avoids accidental collapses when tapping near a
       section heading on mobile. */
}

/* Nameless spacer used to detach the bottom Uitloggen link from
   whichever section sits above it. Keeps the existing top-border
   separator but drops the heading padding so it reads as a thin
   horizontal rule, not as an empty heading. */
.hn-nav-section-spacer {
    padding: 0;
    min-height: 0;
}

#hn-sidebar-left a {
    display: block;
    color: #cde;
    padding: 3px 0.75rem;
    font-size: 0.82rem;
    transition: background .15s, color .15s;
}
#hn-sidebar-left a.flex-between {
    display: flex;
}
#hn-sidebar-left a:hover,
#hn-sidebar-left a.active {
    background: rgba(255,255,255,.12);
    color: #fff;
    text-decoration: none;
}

/* ── Center Content ───────────────────────────────────────── */
#hn-content {
    flex: 1;
    padding: 1rem;
    min-width: 0;
    background: var(--hn-cream);
}

/* ── Right Sidebar ────────────────────────────────────────── */
#hn-sidebar-right {
    width: 150px;
    min-width: 150px;
    background: #f0e8d6;
    padding: 0.5rem 0;
    flex-shrink: 0;
    position: sticky;
    top: calc(var(--hn-banner-h) + var(--hn-header-h));
    align-self: flex-start;
}

.hn-action-section {
    padding: 0.4rem 0.75rem 0.2rem;
    font-size: 0.74rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--hn-brown);
    border-top: 1px solid var(--hn-border);
    margin-top: 0.4rem;
    display: flex;
    align-items: center;
}
.hn-action-section:first-child { border-top: none; margin-top: 0; }
.hn-action-section[data-section] {
    /* See `.hn-nav-section[data-section]` above: only the chevron button
       collapses the section, not the whole heading. */
}

.hn-section-hidden { display: none !important; }

.hn-section-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.hn-section-badge {
    margin-left: 0.4rem;
    letter-spacing: 0;
    text-transform: none;
    font-size: 0.65rem;
    padding: 0 6px;
}

/* ── Sidebar section collapse toggle ──────────────────────────
   The chevron at the right edge of every collapsible sidebar
   heading is a real <button>, so only this small region (not the
   whole heading) collapses the section. Negative margins extend
   the touch target to the heading's outer edges (full row height
   + reach to the right edge of the sidebar) without changing the
   chevron's visual position. */
.hn-section-toggle {
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: 0;
    color: inherit;
    font: inherit;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    margin: -0.4rem -0.75rem -0.2rem 0.4rem;
    padding: 0.4rem 0.75rem 0.2rem;
    border-radius: 0;
}
.hn-nav-section .hn-section-toggle:hover,
.hn-nav-section .hn-section-toggle:focus-visible {
    background: rgba(255,255,255,.08);
    color: #ffe6a8;
    outline: none;
}
.hn-action-section .hn-section-toggle:hover,
.hn-action-section .hn-section-toggle:focus-visible {
    background: rgba(122,79,40,.10);
    outline: none;
}
.hn-section-chevron {
    display: inline-block;
    line-height: 1;
    letter-spacing: 0;
    transition: transform .15s ease;
}
.hn-section-chevron::before { content: '\25BE'; }
.hn-nav-section .hn-section-chevron { font-size: 1.05em; }
.hn-action-section .hn-section-chevron { font-size: 0.85em; }
.hn-nav-section[data-section].collapsed .hn-section-chevron,
.hn-action-section[data-section].collapsed .hn-section-chevron {
    transform: rotate(-90deg);
}

/* ── Card-header collapse toggle ──────────────────────────────
   Same chevron pattern as the sidebar, but tuned for the
   white-on-green `.hn-card-header`. Currently used on /tasks
   for the Klusjes / Opdrachten panels: when the parent card has
   `.is-collapsed`, every `.task-row-collapsible` form inside
   the body is hidden. The toggle button is the ONLY click
   target so users do not collapse the panel by accidentally
   tapping the header label. */
.hn-card-collapse-toggle {
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: 0;
    color: inherit;
    font: inherit;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    margin: -0.4rem -0.75rem -0.4rem 0.4rem;
    padding: 0.4rem 0.75rem;
    border-radius: 0;
}
.hn-card-collapse-toggle:hover,
.hn-card-collapse-toggle:focus-visible {
    background: rgba(255,255,255,.15);
    outline: none;
}
.hn-card-collapse-chevron {
    display: inline-block;
    line-height: 1;
    font-size: 1.05em;
    transition: transform .15s ease;
}
.hn-card-collapse-chevron::before { content: '\25BE'; }
.hn-card.is-collapsed .hn-card-collapse-chevron { transform: rotate(-90deg); }
.hn-card.is-collapsed .task-row-collapsible { display: none; }

#hn-sidebar-right a {
    display: block;
    color: var(--hn-green-dark);
    padding: 3px 0.75rem;
    font-size: 0.82rem;
    transition: background .15s;
}
#hn-sidebar-right a.flex-between {
    display: flex;
}
#hn-sidebar-right a:hover {
    background: rgba(74,124,47,.12);
    text-decoration: none;
}
#hn-sidebar-right a.active {
    background: rgba(74,124,47,.18);
    color: var(--hn-green-dark);
    font-weight: 600;
    text-decoration: none;
}

/* Profession status dot (sidebar toggle) */
.prof-status-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    margin-right: .3rem;
    flex-shrink: 0;
    cursor: pointer;
    transition: background .2s, box-shadow .2s;
}
.prof-status-on {
    background: #27ae60;
    box-shadow: 0 0 4px rgba(39,174,96,.5);
}
.prof-status-off {
    background: #c0392b;
    box-shadow: 0 0 4px rgba(192,57,43,.4);
}
.prof-status-dot:hover {
    transform: scale(1.25);
}

/* Presence indicator used in hoefsmid/vet picker rows */
.presence-name {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}
.presence-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    background: #888;
}
.presence-online  { background: #27ae60; box-shadow: 0 0 4px rgba(39,174,96,.5); }
.presence-recent  { background: #b7b824; }
.presence-stale   { background: #e67e22; }
.presence-offline { background: #888; }
.presence-label {
    margin-top: .1rem;
}

/* ── Country flag inline image ──────────────────────────────
   Used by CountryFlag::imgHtml() to render a small rectangular
   flag SVG (loaded from flagcdn.com) next to a username. The
   1px contrast outline keeps mostly-white flags (e.g. Japan)
   from disappearing into the card background. */
.country-flag {
    display: inline-block;
    vertical-align: -2px;
    border-radius: 1px;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .15);
    object-fit: cover;
}

/* Disabled "link-style" elements (stat maxed, cooldown active, or a
   button-shaped <span> used as a status pill, e.g. an incoming friend
   request on a profile). No `a.` prefix on purpose: the same look works
   for spans, which we use when there is no actionable URL. */
.disabled-link {
    pointer-events: none;
    opacity: .4;
    cursor: not-allowed;
}

/* ── Page Cards ───────────────────────────────────────────── */
.hn-card {
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    margin-bottom: 1rem;
    overflow: hidden;
}
.hn-card-header {
    display: flex;
    align-items: center;
    background: #5a9a35;
    color: #fff;
    padding: 0.4rem 0.75rem;
    font-weight: 700;
    font-size: 0.9rem;
}
.hn-card-header .icon-box {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    align-self: stretch;
    background: rgba(255,255,255,0.7);
    font-size: 0.95rem;
    flex-shrink: 0;
    margin: -0.4rem 0.5rem -0.4rem -0.75rem;
}
.hn-card-body { padding: 0.75rem; }

/* Danger-styled card (e.g. account deletion) */
.hn-card-danger { border-color: #e57373; }
.hn-card-danger .hn-card-header { background: #b71c1c; }

/* Soft-deleted user placeholder ("Verwijderde gebruiker"). Rendered by
   User::displayLink() / user_link() when the underlying account has been
   anonymised, so the name is never wrapped in a clickable <a>. */
.hn-deleted-user {
    color: #888;
    font-style: italic;
    cursor: default;
}
.stalnaam-deleted {
    color: inherit;
    font-style: italic;
    cursor: default;
}

/* Big 6-digit code entry (verify e-mail / delete account) */
.hn-code-input {
    padding: 10px;
    font-size: 1.8rem;
    font-family: "Courier New", Courier, monospace;
    letter-spacing: .35em;
    box-sizing: border-box;
}

/* In-card "are you sure?" warning panel */
.hn-danger-panel {
    padding: .9rem 1rem;
    background: #fdf0ef;
    border: 1px solid #e57373;
    border-radius: 6px;
    max-width: 480px;
}
.hn-danger-panel-title {
    color: #b71c1c;
    font-weight: 600;
    margin: 0 0 .3rem;
}
.hn-delete-bullets {
    margin: .2rem 0 .9rem 1.1rem;
    padding: 0;
    line-height: 1.55;
}

/* Sub-section heading inside a card body */
.hn-card-section {
    color: var(--hn-green-dark);
    font-size: 0.88rem;
    font-weight: 700;
    margin: 0 0 0.4rem;
}
.hn-card-section + * { margin-top: 0; }

/* ── Tables ───────────────────────────────────────────────── */
.hn-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Tall, viewport-bounded scroll container. Add as a second class on a
   .hn-table-wrap when the underlying table can grow to many rows and
   we want the bottom horizontal scrollbar to stay reachable without
   the user scrolling the whole page first. Used on /leaderboard
   ("Alle spelers", "Paarden"), /breed ("Beschikbare hengsten"),
   and /market ("Paardenmarkt", "Veilinggeschiedenis").
   The wrap is capped to roughly the visible viewport minus the sticky
   banner + header + the card header / filter row above the table, so
   both scrollbars stay in view and the user scrolls inside the table
   instead of the whole page. The thead becomes sticky so column labels
   stay visible while scrolling vertically. The dvh unit is used (with
   vh as a fallback for ancient browsers) so the cap follows the
   dynamic viewport on mobile when the URL bar shows/hides instead of
   leaving an awkward gap or letting the table grow off-screen.

   The min-height floor (16rem) is a landscape-phone safety net: the
   381px deduction (banner 161 + header 44 + 11rem 176) goes <= 0 on
   every phone in landscape (viewport heights 320-430px), which would
   otherwise clamp the body to zero and leave only the sticky thead
   visible. The floor is scoped to short viewports via the media
   query below, NOT applied unconditionally. The reason: min-height
   does not only beat max-height when they conflict, it also floors
   the box's natural CONTENT height. Applied globally, a short table
   (few or no rows) on a tall desktop viewport gets forced up to 16rem
   regardless of how little content it holds, reserving an awkward
   strip of empty whitespace below the last row (symptom: /messages
   postvak and friend requests on desktop). The max-height calc only
   drops below the 16rem (256px) floor when the viewport height is
   under ~637px (381 + 256), so the floor only ever had a real effect
   under that threshold anyway; gating it on `max-height: 640px` keeps
   the landscape-phone protection intact while letting taller
   viewports size the table to its content.
   Symptom this protects against: paardenmarkt + veilinggeschiedenis
   on /market showing "only headers, no rows" on rotated phones. */
.hn-tall-table {
    max-height: calc(100vh  - var(--hn-banner-h) - var(--hn-header-h) - 11rem);
    max-height: calc(100dvh - var(--hn-banner-h) - var(--hn-header-h) - 11rem);
    overflow-y: auto;
}
@media (max-height: 640px) {
    .hn-tall-table { min-height: 16rem; }
}
.hn-tall-table .hn-table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
}

.hn-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.85rem;
}
.hn-table th {
    background: #f0e8d6;
    color: var(--hn-brown);
    padding: 0.4rem 0.6rem;
    text-align: left;
    border-bottom: 2px solid var(--hn-border);
    font-weight: 600;
    white-space: nowrap;
}
/* The .text-center utility (specificity 0,1,0) loses to `.hn-table th`
   (0,1,1), so headers stay left-aligned even when marked .text-center.
   Bump specificity here so .text-center works on header cells too. */
.hn-table th.text-center { text-align: center; }
.hn-table th.text-right { text-align: right; }
.hn-table td {
    padding: 0.35rem 0.6rem;
    border-bottom: 1px solid #e8dfcf;
    vertical-align: middle;
}
.hn-table tr:last-child td { border-bottom: none; }
.hn-table tr:hover td { background: #f9f4ec; }
.hn-table tr[data-href] { cursor: pointer; }
.hn-table-no-row-hover tr:hover td { background: transparent; }
.hn-table-no-row-hover tbody:hover td { background: #f9f4ec; }
.hn-table-no-row-hover tbody { border-top: 2px solid #d4c9b5; }

/* Group separator rows used by the shared horses_table partial when its
   $groupedHorses payload is set (currently /stable). Reads as a thin
   divider band carrying the group's name; not hoverable, not clickable.
   Slightly tinted darker than the column header so it stands apart from
   both the table header and the body rows. */
.hn-table tr.hn-table-group-sep th {
    background: #ebe1c8;
    color: var(--hn-brown);
    font-weight: 600;
    font-size: .82rem;
    padding: .35rem .6rem;
    border-top: 1px solid #d4c9b5;
    border-bottom: 1px solid #d4c9b5;
    letter-spacing: .02em;
}

/* ── Sticky-left column for hn-table ─────────────────────────
   Opt-in per cell via `.hn-sticky-col` on the <th>/<td> the table
   wants frozen at the wrap's left edge during horizontal scroll.
   Used by the /stable, /horses/{id} (offspring), /user profile
   (paarden tab), /caretaker, /breed (Beschikbare hengsten) and
   /market (active auctions + history) horse tables to keep the
   row identifier visible while the wide breed/SDG/age/levels/
   health/status (and price/owner/end-date) columns scroll out of
   view on narrow screens. The cell needs an explicit background
   or scrolling content would show through; we hard-code the same
   colors the non-sticky cells already have so the sticky cell
   looks identical to a non-sticky one when no horizontal scroll
   is needed. On `.hn-tall-table` the thead is also `position:
   sticky; top: 0`, so the corner cell ends up locked at top-left
   in both axes - that's intentional. */
.hn-table th.hn-sticky-col,
.hn-table td.hn-sticky-col {
    position: sticky;
    left: 0;
    z-index: 1;
    background: #fff;
}
.hn-table thead th.hn-sticky-col {
    z-index: 3;
    background: #f0e8d6;
}
.hn-table tr:hover td.hn-sticky-col {
    background: #f9f4ec;
}
/* The right-edge divider + drop-shadow live on a pseudo-element
   because `box-shadow` on <td>/<th> is silently dropped by every
   major browser when the table uses `border-collapse: collapse`
   (which .hn-table does): the cell's collapsed-border model eats
   the shadow. Rendering it on an absolutely-positioned ::after
   sidesteps the bug entirely - the pseudo is a normal positioned
   box, not a table cell, so its shadow / gradient render correctly.
   `position: sticky` on the parent <td>/<th> establishes the
   containing block, so `right: -9px` is anchored to the cell's own
   right edge and travels with it during horizontal scroll. */
.hn-table th.hn-sticky-col::after,
.hn-table td.hn-sticky-col::after {
    content: '';
    position: absolute;
    top: 0;
    right: -6px;
    bottom: 0;
    width: 6px;
    pointer-events: none;
    background: linear-gradient(
        to right,
        rgba(60, 40, 20, .07),
        rgba(60, 40, 20, 0) 60%
    );
    border-left: 1px solid var(--hn-border);
    box-sizing: border-box;
}

/* ── Sortable table headers ───────────────────────────────── */
.hn-table th.sortable {
    cursor: pointer;
    user-select: none;
}
.hn-table th.sortable:hover { background: #e7dfca; }
.hn-table th.sortable .sort-indicator {
    display: inline-block;
    margin-left: 0.25em;
    color: var(--hn-brown);
    opacity: 0.25;
    font-size: 0.78em;
}
.hn-table th.sort-active .sort-indicator { opacity: 1; }

/* ── Detail / label-value table (no header, no hover rows) ── */
.hn-detail-table {
    font-size: 0.85rem;
    width: 100%;
    border-collapse: collapse;
}
.hn-detail-table td { padding: 2px 0; vertical-align: top; }
.hn-detail-table td:first-child {
    color: #888;
    padding-right: 0.6rem;
    white-space: nowrap;
}

/* ── Buttons ──────────────────────────────────────────────── */
.btn-hn {
    display: inline-block;
    background: var(--hn-green);
    color: #fff;
    border: none;
    border-radius: 3px;
    padding: 4px 12px;
    font-size: 0.82rem;
    font-family: inherit;
    cursor: pointer;
    line-height: 1.4;
    vertical-align: middle;
    box-sizing: border-box;
    cursor: pointer;
    transition: background .15s;
    /* Anchor variants (<a class="btn-hn …">) would otherwise inherit the
       global link underline. Buttons are buttons, never underlined. */
    text-decoration: none;
}
.btn-hn:hover:not(:disabled):not(.disabled-link) {
    background: var(--hn-green-dark);
    color: #fff;
    /* The base .btn-hn already declares text-decoration: none, but the
       global a:hover rule (text-decoration: underline) has equal or
       higher specificity for anchor variants and would re-add the
       underline on hover. Re-assert it here so <a class="btn-hn …">
       buttons stay underline-free in every state. */
    text-decoration: none;
}
.btn-hn:disabled { opacity: .45; cursor: not-allowed; }
.btn-hn-gold {
    background: var(--hn-gold);
    color: #fff;
}
.btn-hn.btn-hn-gold:hover:not(:disabled) { background: #b8870f; color: #fff; }
.btn-hn-danger {
    background: #c0392b;
    color: #fff;
    border: 1px solid #a93226;
}
.btn-hn.btn-hn-danger:hover:not(:disabled) { background: #a93226; color: #fff; }
.btn-hn-success {
    background: #16a34a;
    color: #fff;
    border-color: #16a34a;
}
.btn-hn.btn-hn-success:hover:not(:disabled) {
    background: #15803d;
    color: #fff;
}
.btn-hn-sm { padding: 2px 8px; font-size: 0.78rem; }

/* Save button that flips to a warning-orange when its parent form has
   unsaved changes. Paired with data-unsaved-warning on the form (set by
   forum-editor.js); the form's data-dirty="true" attribute drives the
   swap. Default (data-dirty="false") falls through to whatever btn-hn
   colour variant the button already has, e.g. the stock green .btn-hn.
   Used in the Kladblok scratchpad save button. */
form[data-unsaved-warning][data-dirty="true"] .btn-hn.btn-hn-dirty {
    background: #e67e22;
    border-color: #d97706;
    color: #fff;
}
form[data-unsaved-warning][data-dirty="true"] .btn-hn.btn-hn-dirty:hover:not(:disabled) {
    background: #d97706;
    color: #fff;
}

/* Button sized to vertically match a standard form <input>/<select>.
   Use whenever a button sits on the same row as a form input
   (typically in a flex row with align-items:flex-end). Compose with
   .btn-hn or any color variant:

       <button class="btn-hn btn-hn-input">Filteren</button>

   The fixed `height` matches .hn-filter-input so all controls in a
   form row line up to the pixel. The transparent 1px border keeps
   the rendered height identical when combined with bordered
   variants like .btn-hn-danger. */
.btn-hn-input {
    height: 32px;
    padding: 0 14px;
    font-size: .85rem;
    line-height: 1.4;
    border: 1px solid transparent;
}

/* Action-buttons columns (pending-request tables) */
.action-btn-stack { display: flex; gap: 5px; white-space: nowrap; }
.action-btn-stack form { margin: 0; }
.action-btn-stack .btn-hn { border: 1px solid transparent; }
.action-btn-stack .btn-hn.btn-hn-danger { border-color: #a93226; }
.action-col-desktop { width: 1%; white-space: nowrap; }
.action-col-desktop .btn-hn { padding: 4px 10px; font-size: 0.82rem; }
.action-col-mobile .btn-hn { padding: 6px 12px; font-size: 1rem; line-height: 1; min-width: 36px; text-align: center; }
/* Respect btn-hn-sm sizing inside action-col-mobile so callers that explicitly
   pick a small button (e.g. dense /races tables) don't get bumped up to the
   default 1rem/6px-12px touch target intended for plain btn-hn. Mirrors the
   stock .btn-hn-sm values from above + resets the line-height / min-width set
   by the rule on the previous line. */
.action-col-mobile .btn-hn-sm { padding: 2px 8px; font-size: 0.78rem; line-height: normal; min-width: 0; }
.action-col-mobile { width: 1%; white-space: nowrap; }
@media (max-width: 600px) { .action-col-desktop { display: none; } }
@media (min-width: 601px)  { .action-col-mobile  { display: none; } }
.btn-hn-muted {
    background: transparent;
    color: #555;
    border: 1px solid #bbb;
}
.btn-hn-muted:hover:not(:disabled) { background: #f0f0f0; color: #333; }
.btn-hn-outline {
    background: transparent;
    color: var(--hn-green);
    border: 1px solid var(--hn-green);
}
.btn-hn-outline:hover { background: var(--hn-green); color: #fff; }

/* Action button that sits inside a green .hn-card-header (or any
   variant of it). The default .btn-hn variants either use the same
   green as the header (invisible) or a transparent fill with green
   text+border (also invisible). This variant uses the canonical
   "translucent black chip on green" treatment that the messages
   header has been using as .msg-view-header-btn. */
.btn-hn-on-header {
    background: rgba(0, 0, 0, .18);
    color: #fff;
    border: 1px solid transparent;
}
.btn-hn-on-header:hover:not(:disabled) {
    background: rgba(0, 0, 0, .28);
    color: #fff;
}
.zelf-trainen-btn {
    background: #e8f0f8;
    color: #1a6abf;
    border: 1px solid #b3cde0;
}
.zelf-trainen-btn:hover:not(:disabled):not(.disabled-link) {
    background: #d2e4f4;
    color: #145a9e;
}
.zelf-trainen-btn:disabled,
.zelf-trainen-btn.disabled-link {
    opacity: .5;
    cursor: not-allowed;
    background: #e8f0f8;
    color: #1a6abf;
    border-color: #b3cde0;
}
.laten-trainen-btn {
    background: #eef3eb;
    color: var(--hn-green-dark);
    border: 1px solid #c5d9bc;
}
.laten-trainen-btn:hover:not(.disabled-link) {
    background: #ddebd5;
    color: var(--hn-green-dark);
}

/* ── Stat Bars ────────────────────────────────────────────── */
.stat-bar-wrap { min-width: 80px; }
.stat-bar {
    height: 8px;
    border-radius: 4px;
    background: #e0d8c8;
    overflow: hidden;
    display: inline-block;
    width: 100%;
}
.stat-bar-fill {
    height: 100%;
    border-radius: 4px;
    transition: width .3s;
    min-width: 2px;
}
.stat-bar > .stat-bar-fill { display: block; }
.stat-bar-fill.good    { background: #4a7c2f; }
.stat-bar-fill.ok      { background: #d4a017; }
.stat-bar-fill.bad     { background: #c0392b; }
.stat-bar-sm           { width: 50px; flex-shrink: 0; }

/* ── Hoeven progress bar ──────────────────────────────────── */
.hoeven-bar-block {
    margin-top: .5rem;
}
.hoeven-bar-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: .75rem;
    margin-bottom: 2px;
}
.hoeven-kreupel-note {
    margin-top: .35rem;
    font-size: .78rem;
    color: #856404;
}
.uitleg-ol {
    margin: 0 0 .75rem;
    padding-left: 1.2rem;
}
.uitleg-ul {
    margin: 0 0 .75rem;
    padding-left: 1.2rem;
}
.uitleg-tier-table {
    margin: 0 0 .75rem;
}
.uitleg-energy-fresh { border-left: 3px solid #4a7c2f; padding-left: .6rem; }
.uitleg-energy-spent { border-left: 3px solid #6c1919; padding-left: .6rem; }

/* ── Flash Messages ───────────────────────────────────────── */
.hn-flash {
    padding: 0.55rem 0.9rem;
    border-radius: 5px;
    font-size: 0.88rem;
    line-height: 1.5;
    margin-bottom: 0.75rem;
}
.hn-flash-success { background: rgba(30, 130, 60, 0.85);  color: #fff; }
.hn-flash-error   { background: rgba(200, 40, 50, 0.85);  color: #fff; }
.hn-flash-info    { background: rgba(15, 130, 155, 0.85); color: #fff; }

/* Floating flash bar – fixed near the top of the viewport on every
   breakpoint. Overlays the banner so showing/hiding it never shifts
   page content, and the message can never sit on top of the header's
   money/XP stats row. Auto-dismisses via initFlashMessages(). z-index
   sits above the banner (1031) so it reads as a notification. */
#hn-flash-bar {
    position: fixed;
    top: 0.5rem;
    left: 50%;
    transform: translateX(-50%);
    width: calc(100% - 1.5rem);
    max-width: 520px;
    z-index: 1050;
    margin: 0;
    text-align: center;
    font-weight: 600;
    box-shadow: 0 2px 8px rgba(0,0,0,.45);
}
#hn-flash-bar.hn-flash-success { background: #2e8a3f; color: #fff; }
#hn-flash-bar.hn-flash-error   { background: #c8202e; color: #fff; }
#hn-flash-bar.hn-flash-info    { background: #0c8aa3; color: #fff; }

/* ── Stale push subscription banner ────────────────────────────
   Injected by push.js when the device had push enabled at some
   point (hn-push-ever-enabled in localStorage) but the browser no
   longer has a PushSubscription object - typically after FCM /
   Apple revoked the endpoint AND the browser dropped its side too
   (site data cleared, OS-level permission reset, etc.). Fixed at
   the bottom-center so it never collides with the top flash bar
   and doesn't shift the page layout. Sits just below the flash
   bar's z-index so a real flash still wins. */
.hn-push-stale-banner {
    position: fixed;
    left: 50%;
    bottom: 0.75rem;
    transform: translateX(-50%);
    width: calc(100% - 1.5rem);
    max-width: 520px;
    z-index: 1045;
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.55rem 0.75rem;
    background: var(--hn-brown);
    color: #fff;
    border: 1px solid var(--hn-gold);
    border-radius: 6px;
    box-shadow: 0 2px 8px rgba(0,0,0,.45);
    font-size: 0.88rem;
    line-height: 1.4;
}
.hn-push-stale-text   { flex: 1 1 auto; }
.hn-push-stale-action {
    flex: 0 0 auto;
    background: var(--hn-gold);
    color: #2a1f08;
    border: 0;
    padding: 0.3rem 0.7rem;
    border-radius: 4px;
    font-weight: 600;
    font-size: 0.85rem;
    cursor: pointer;
}
.hn-push-stale-action:hover:not(:disabled) { filter: brightness(1.05); }
.hn-push-stale-action:disabled             { opacity: 0.6; cursor: default; }
.hn-push-stale-close {
    flex: 0 0 auto;
    background: transparent;
    color: #fff;
    border: 0;
    padding: 0 0.4rem;
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.8;
}
.hn-push-stale-close:hover { opacity: 1; }

/* ── Badge ────────────────────────────────────────────────── */
.badge-exclusive  {
    background: var(--hn-gold);
    color: #fff;
    font-size: 0.65rem;
    padding: 1px 5px;
    border-radius: 3px;
    vertical-align: middle;
}

/* ── Gender icons ─────────────────────────────────────────── */
.gender-male   { color: #5b8dd9; }
.gender-female { color: #c0396b; }

.gender-sym {
    font-size: 1.3em;
    line-height: 1;
    vertical-align: middle;
    -webkit-text-stroke: 0.6px currentColor;
    display: inline-block;
}

/* Keep gender labels white when shown on the green card header */
.hn-card-header .gender-male,
.hn-card-header .gender-female { color: #fff; opacity: .9; }

/* "Totaal verdiend" pill in card headers (e.g. trainer dashboard, race
   results). The amount is highlighted in gold. */
.hn-card-header-earned {
    color: #fff;
    font-size: .8rem;
    font-weight: 400;
}
.hn-card-header-earned-amount {
    color: #ffd700;
    font-weight: 700;
}

/* ── Progress XP bar ──────────────────────────────────────── */
.xp-bar {
    height: 12px;
    border-radius: 6px;
    background: #e0d8c8;
    overflow: hidden;
    position: relative;
}
.xp-bar-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--hn-green-light), var(--hn-green));
    border-radius: 6px;
    transition: width .4s;
}

/* ── Energy bar (task tiredness) ──────────────────────────── */
.energy-card-body {
    padding: .6rem .75rem;
}
.energy-card-label {
    color: var(--hn-brown);
    font-size: .8rem;
    font-weight: 600;
    white-space: nowrap;
}
.energy-card-value {
    color: #666;
    font-size: .8rem;
    white-space: nowrap;
}
.energy-bar {
    flex: 1;
    height: 12px;
    border-radius: 6px;
    background: #e0d8c8;
    overflow: hidden;
    position: relative;
}
.energy-bar-fill {
    height: 100%;
    border-radius: 6px;
    transition: width .4s;
}
.energy-bar-fill.tier-0 { background: linear-gradient(90deg, #6ab04c, #4a7c2f); }
.energy-bar-fill.tier-1 { background: linear-gradient(90deg, #f6e58d, #d4a017); }
.energy-bar-fill.tier-2 { background: linear-gradient(90deg, #e17055, #c0392b); }
.energy-bar-fill.tier-3 { background: linear-gradient(90deg, #b33939, #6c1919); }
.energy-regen {
    font-size: .75rem;
    color: #666;
}
/* Row under the energy bar: optional regen-timer on the left, lifetime
   "Totaal verdiend" on the right. Mirrors the header earned style used
   on /races and the profession dashboards, but tinted for a body
   background instead of the dark brown card header. */
.energy-card-meta {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-top: .25rem;
    min-height: 1rem;
}
.energy-card-earned {
    color: var(--hn-brown);
    font-size: .75rem;
    font-weight: 600;
    white-space: nowrap;
}
.energy-card-earned-amount {
    color: var(--hn-green-dark);
    font-weight: 700;
}

/* ── Cooldown timer ───────────────────────────────────────── */
.cooldown-badge {
    background: #ffeaa7;
    color: #856404;
    font-size: 0.75rem;
    padding: 1px 6px;
    border-radius: 10px;
    border: 1px solid #ffc107;
}

/* ── Horse action countdown banner (in-card, above buttons) ── */
.horse-cd-banner {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.75rem;
    background: #fff8e1;
    border-bottom: 1px solid #ffe082;
    font-size: 0.8rem;
    color: #6d4c00;
}
.horse-cd-banner--trainer {
    border-left-color: #7b4f2e;
}
.horse-cd-banner--trainer .horse-cd-banner-body {
    flex: 1;
}
.horse-cd-banner--trainer .horse-cd-banner-title {
    color: #7b4f2e;
}
.horse-cd-banner--trainer .horse-cd-banner-detail {
    font-size: .8rem;
    color: #666;
}
.horse-cd-time {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: #c0392b;
}

/* ── Horse care/training action buttons ───────────────────── */
.btn-horse-action {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
    padding: 7px 10px;
    background: #faf7f2;
    color: #3d2b14;
    border: 1px solid #d8cbb5;
    border-left: 3px solid var(--hn-green-light);
    border-radius: 5px;
    font-size: 0.84rem;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
    transition: background 0.15s, box-shadow 0.15s;
}
.btn-horse-action:hover:not(:disabled) {
    background: #f0e8d8;
    box-shadow: 0 1px 5px rgba(0,0,0,.1);
}
.btn-horse-action:disabled {
    cursor: not-allowed;
}
/* ── Game icon utility sizes ─────────────────────────────────────────── */
/* xs  - tight table cells, compact inline (inbox, level badges, stat cols) */
.hn-icon-xs   { height: .9rem;  width: auto; vertical-align: middle; }
/* sm  - table headers, discipline labels, trainer/shop stats             */
.hn-icon-sm   { height: 1.1rem; width: auto; vertical-align: middle; }
/* lg  - care buttons, stat bars, banners, card headers                   */
.hn-icon-lg   { height: 1.4rem; width: auto; vertical-align: middle; flex-shrink: 0; }
/* Race detail header: discipline icon + responsive info grid side by side */
.race-info-row {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
}
.race-info-icon-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}
.race-info-grid {
    flex: 1 1 0;
    min-width: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
}
.race-discipline-icon {
    height: 2.8rem;
    width: auto;
    flex-shrink: 0;
}
/* prof - profession cards (square crop)                                  */
.hn-icon-prof { height: 2.6rem; width: 2.6rem; object-fit: contain; border-radius: 6px; flex-shrink: 0; }

/* Training buttons: taller with larger icon, horizontal layout (icon left, label right) */
.train-action-btn {
    border-left-color: #5b8dd9;
    min-height: 82px;
}
/* Scale up the icon inside a training button */
.train-action-btn .bha-icon .hn-icon-lg {
    height: 3rem;
    width: auto;
}

/* Button inner layout helpers */
.bha-label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    min-width: 0;
    overflow: hidden;
}
.bha-icon {
    font-size: 1rem;
    line-height: 1;
    flex-shrink: 0;
}
/* Label text truncates with ellipsis when the button is too narrow */
.bha-label-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.bha-cost {
    display: block;
    color: #7b4f2e;
    font-size: 0.76rem;
    margin-top: 1px;
}

/* Effect tags (e.g. "+15 Geluk", "+1–3") */
.bha-tags {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    flex-shrink: 0;
}
.bha-tag {
    display: inline-block;
    font-size: 0.69rem;
    border-radius: 10px;
    padding: 1px 7px;
    white-space: nowrap;
    font-weight: 600;
    letter-spacing: 0.01em;
}
.bha-tag--happy {
    background: #fff0c2;
    color: #7a5300;
    border: 1px solid #f5d76e;
}
.bha-tag--health {
    background: #e8f5e0;
    color: #2d5016;
    border: 1px solid #a8d880;
}
.bha-tag--train {
    background: #e8f0fb;
    color: #1a4a9e;
    border: 1px solid #a8c0f0;
}
.bha-tag--max {
    background: #2e7d32;
    color: #fff;
    border: 1px solid #1b5e20;
}

/* ── Form inputs & selects ────────────────────────────────── */
select,
input[type="text"],
input[type="number"],
input[type="email"],
input[type="password"] {
    background: #fff;
    color: var(--hn-text);
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    font-family: inherit;
    font-size: 0.85rem;
}
/* The popup that opens from a native <select> is rendered by the OS
   window manager (DWM on Windows), not by our CSS. On Chrome+Windows
   when the user runs the system in dark mode, that popup window
   briefly paints in the OS theme (BLACK) before Chromium repaints it
   in the page's color scheme. Setting `color-scheme: light` on `:root`
   is supposed to suppress this, but Chrome often only honors the value
   when it's set directly on the <select>/<option> elements themselves
   when deciding the popup's theme. Belt and braces: declare it on the
   form controls explicitly. See Chromium / Microsoft DWM "white flash
   on dark mode" fix history for context. */
select, option { color-scheme: light; }
select { appearance: auto; }

/* iOS Safari auto-zooms any focused form control (input, select,
   textarea, or contenteditable like .ql-editor) whose computed
   font-size is < 16px. The auto-zoom is jarring on mobile because the
   layout jumps and the user has to manually zoom back out after
   typing. Force >= 16px on touch-only devices so the iPhone keyboard
   pops up WITHOUT the zoom. Desktop styling stays untouched because
   the media query is gated on (hover: none) + (pointer: coarse) -
   only fingers, no mouse, no stylus. Pinch-zoom remains intact (we
   deliberately do NOT set maximum-scale=1 on the viewport meta).

   The `!important` is load-bearing: dozens of feature-specific
   classes in this file (.forum-input, .hn-contact-input,
   .helpdesk-assign-select, .credit-tile-select, .horse-rename-input,
   ...) set font-size with class-level specificity (0,1,0), which
   beats a bare `select` type-selector (0,0,1). Without !important the
   override only catches `input[type=...]` rules (where the attribute
   selector bumps specificity to 0,1,1) and silently misses every
   class-targeted <select>. */
@media (hover: none) and (pointer: coarse) {
    select,
    input[type="text"],
    input[type="number"],
    input[type="email"],
    input[type="password"],
    input[type="search"],
    input[type="tel"],
    input[type="url"],
    input[type="date"],
    input[type="time"],
    input[type="datetime-local"],
    textarea,
    .ql-editor {
        font-size: 16px !important;
    }
}

/* ── Footer ───────────────────────────────────────────────── */
#hn-footer {
    background: var(--hn-green-dark);
    color: rgba(255,255,255,.6);
    text-align: center;
    font-size: 0.75rem;
    padding: 0.5rem;
}
#hn-footer a { color: rgba(255,255,255,.7); }

/* ── Auth pages ───────────────────────────────────────────── */
.auth-bg {
    flex: 1;
    background: linear-gradient(160deg, var(--hn-green-dark) 0%, var(--hn-green) 50%, var(--hn-green-light) 100%);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 2rem 1rem;
}
.auth-card {
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 8px 32px rgba(0,0,0,.3);
    padding: 2rem;
    width: 100%;
    max-width: 380px;
}
.auth-card:has(.starter-picker) { max-width: 520px; }

/* ── Register form checkboxes (newsletter / single-account / terms) ─── */
.register-checkbox {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    cursor: pointer;
    font-size: .85rem;
    margin-bottom: .75rem;
}
.register-checkbox input[type="checkbox"] {
    margin-top: 2px;
    flex-shrink: 0;
}
.register-checkbox--last { margin-bottom: 1rem; }

/* ── Starter horse picker ─────────────────────────────────── */
.starter-picker-lore {
    background: #fdf6e3;
    border: 1px solid #e0c97a;
    border-radius: 6px;
    padding: .7rem .9rem;
    margin-bottom: 1.2rem;
    font-size: .82rem;
    color: #5c4a1e;
    line-height: 1.45;
}
.starter-picker-lore strong {
    display: block;
    margin-bottom: .3rem;
    color: #7b4f2e;
    font-size: .9rem;
}
.starter-picker-lore p { margin: 0 0 .4rem; }
.starter-picker-lore p:last-child { margin-bottom: 0; }
.starter-picker-note {
    font-style: italic;
    color: #8a7550;
    font-size: .78rem;
}
.starter-picker-controls { margin-bottom: 1rem; }
.starter-picker-field { margin-bottom: .75rem; }
.starter-picker-colors {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
}
.sp-color-swatch {
    border: 2px solid #d5cfc3;
    border-radius: 6px;
    padding: 5px 12px;
    font-size: .8rem;
    cursor: pointer;
    background: #faf8f4;
    color: #4a3f2f;
    transition: border-color .15s, background .15s, box-shadow .15s;
}
.sp-color-swatch:hover {
    border-color: #a08c6a;
    background: #f5f0e5;
}
.sp-color-swatch.active {
    border-color: var(--hn-green);
    background: #e8f5e0;
    box-shadow: 0 0 0 2px rgba(74,124,47,.25);
    font-weight: 600;
}
.starter-picker-preview {
    text-align: center;
    padding: 1rem 0 .5rem;
}
.starter-picker-img {
    max-width: 220px;
    height: auto;
    border-radius: 8px;
    background: #fff;
    padding: 8px;
    border: 1px solid #e0d8cc;
    box-shadow: 0 2px 8px rgba(0,0,0,.08);
}
.starter-picker-summary {
    margin-top: .5rem;
    font-size: .85rem;
    font-weight: 600;
    color: #5c4a2a;
}

/* ── Buy-horse picker (credits-bought variant) ─────────────
   Reuses .starter-picker-* and .sp-color-swatch from above.
   These extras supply the gender fieldset, the cancel link
   and the conditional show/hide control, all of which differ
   from the registration starter picker. */
.buy-horse-hidden { display: none; }
.buy-horse-label  { margin-bottom: 3px; }
.buy-horse-legend {
    margin-bottom: 4px;
    padding: 0;
}
.buy-horse-select {
    padding: 7px 10px;
    font-size: .88rem;
    box-sizing: border-box;
    background: #fff;
}
.buy-horse-gender { padding: 0; border: 0; }
.buy-horse-submit { padding: 8px; }
.buy-horse-cancel {
    color: #6a5430;
    text-decoration: underline;
}
.buy-horse-cancel:hover { color: #4a3f2f; }

/* ── Mobile / Responsive ──────────────────────────────────── */
.hn-sidebar-mobile-only { display: none; }
.age-abbr { display: none; }

/* Right sidebar (Beroepen / Acties / In het dorp / Spellen) only
   appears once the viewport has enough room for both sidebars AND
   a comfortable main column. Used to pop in at 992px which left
   roughly 680px for the main content card; that crammed the user
   profile (avatar | stats | Acties+Foto's) and horse profile
   (image | Eigenschappen | Stats) layouts. From 1180px upwards
   the page has ~870px+ of content room which the 3-col layouts
   need. Below that the right-sidebar's links are still reachable
   through the `.hn-sidebar-mobile-only` block hoisted into the
   left sidebar. */
@media (max-width: 1179px) {
    #hn-sidebar-right { display: none; }
    #hn-body {
        background:
            linear-gradient(to right, var(--hn-sidebar-bg) var(--hn-sidebar-w), transparent var(--hn-sidebar-w));
    }
    #hn-body::after { display: none; }
    .hn-sidebar-mobile-only { display: block; }
    .hn-nav-section,
    .hn-action-section {
        font-size: 0.78rem;
        padding: 0.4rem 0.75rem 0.2rem;
    }
    #hn-sidebar-left a {
        font-size: 0.86rem;
        padding: 3px 0.75rem;
    }
}

/* Drop the "Rang: ..." chip earlier than the rest of the topbar stats:
   on tablets and small laptops (<= 899px) the four-pill row (Geld /
   Bank / Rang / XP) starts pushing into the quick-action icons, and
   the rank string ("Stalknecht", "Fokker met passie", ...) is the
   widest of the four. Money / Bank / XP carry actionable numbers; the
   rank is decorative status, so it's the cheapest to drop first.
   Re-appears once the viewport widens past 899px. */
@media (max-width: 899px) {
    .hn-topbar-rank { display: none; }
}

@media (max-width: 767px) {
    #hn-sidebar-left {
        position: fixed;
        left: -170px;
        top: calc(var(--hn-banner-h) + var(--hn-header-h));
        bottom: 0;
        z-index: 1040;
        transition: left .25s ease;
        overflow-y: auto;
        width: 170px;
        align-self: stretch;
        max-height: none;
    }
    #hn-sidebar-left.open { left: 0; }
    #hn-body { flex-direction: column; background: none; }
    #hn-content { padding: 0.75rem; }

    .hn-nav-section,
    .hn-action-section {
        font-size: 0.78rem;
        letter-spacing: 0.5px;
        padding: 0.4rem 0.6rem 0.2rem;
    }
    #hn-sidebar-left a {
        font-size: 0.88rem;
        padding: 3px 0.75rem;
    }
    #hn-sidebar-right a {
        font-size: 0.88rem;
        padding: 3px 0.75rem;
    }
    /* Sidebar zone collapses to just the button width on mobile */
    .hn-header-sidebar-zone {
        width: auto;
        min-width: auto;
        padding-left: 0.75rem;
    }
    /* Restore header's own left padding on mobile; clip overflow so
       stats can never render outside the green header background. */
    #hn-header { padding-left: 0; overflow: hidden; }
    /* Show money and XP on mobile */
    .hn-topbar-stat { display: none; }
    .hn-topbar-money,
    .hn-topbar-xp { display: inline-block; }
    .hn-topbar-stats { flex-wrap: nowrap; overflow: hidden; }

    /* On phones the topbar twin is too cramped: the sidebar zone
       collapses to just the hamburger button, and any extra inline
       element here pushes the quick-action row sideways into the
       stats. Hide the topbar twin and reveal the banner twin in the
       far-top-right corner of #hn-banner instead, where there's
       breathing room over the sky background. */
    .hn-topbar-clock { display: none; }
    .hn-banner-clock { display: inline-block; }
}

/* Shrink the banner on narrow viewports so it doesn't eat half the
   screen. The 640px cutoff is intentionally above the smartphone
   breakpoint - it covers narrow tablets and phones in landscape too,
   which would otherwise still show the full 161px banner with a
   too-large centered logo. Logo.png inside is sized as `height: 100%`
   so it scales with --hn-banner-h - no per-breakpoint logo override
   needed. */
@media (max-width: 640px) {
    :root { --hn-banner-h: 120px; }
}
@media (max-width: 400px) {
    :root { --hn-banner-h: 96px; }
    .hn-topbar-stat { font-size: 0.75rem; padding: 3px 8px; }
    /* Tighten the banner-twin clock to match the shrunken banner
       (96px tall vs 120px at 640px and 161px on desktop). At this
       point the topbar twin is already display:none from the 767px
       block above, so the size override only applies to the banner
       copy that's actually visible. */
    .hn-banner-clock { font-size: 0.8rem; letter-spacing: 0; top: 4px; right: 8px; }
}

/* Step the framing horses AND the flowers strip down as the viewport
   narrows. The banner-height breakpoints above (640 -> 120px, 400 ->
   96px) shrink the raw size along with the banner, but in the
   641-900px zone the banner is still 161px and full-size horses
   would reach ~275px wide each - enough to graze the logo. Flowers
   ride the same cascade so the fence and tulips stay visually
   proportional to the horses at every breakpoint. Both stay glued
   to the bottom edge so hooves and fence posts still sit on ground
   level. */
@media (max-width: 900px) {
    .hn-banner-horse,
    .hn-banner-flowers { height: 75%; }
}
@media (max-width: 640px) {
    .hn-banner-horse,
    .hn-banner-flowers { height: 65%; }
}
@media (max-width: 400px) {
    .hn-banner-horse,
    .hn-banner-flowers { height: 55%; }
}

/* BETA stamp shrinks alongside the banner so it doesn't dwarf the logo
   on phones (banner: 161px desktop -> 120px @640 -> 96px @400). */
@media (max-width: 640px) {
    .hn-banner-beta-badge {
        font-size: 0.7rem;
        padding: 3px 7px;
        top: -0.3rem;
        right: -0.9rem;
    }
}
@media (max-width: 400px) {
    .hn-banner-beta-badge {
        font-size: 0.6rem;
        padding: 2px 6px;
        top: -0.25rem;
        right: -0.7rem;
    }
}

/* ── Hamburger toggle ─────────────────────────────────────── */
#hn-menu-toggle {
    display: none;
    background: none;
    border: none;
    color: #fff;
    font-size: 1.1rem;
    padding: 0 0.4rem;
    cursor: pointer;
}
#hn-menu-toggle {
    position: relative;
}
.hn-menu-badge {
    position: absolute;
    top: -4px;
    right: -6px;
    background: #e74c3c;
    color: #fff;
    border-radius: 10px;
    padding: 0 5px;
    font-size: .6rem;
    font-weight: 700;
    line-height: 1.5;
    min-width: 16px;
    text-align: center;
    pointer-events: none;
}
.hn-badge {
    background: #e74c3c;
    color: #fff;
    border-radius: 10px;
    padding: 1px 7px;
    font-size: .7rem;
    font-weight: 700;
    line-height: 1.4;
}
.hn-badge-muted {
    background: #9ca3af;
}
.admin-badge {
    color: #d4a017;
    font-size: .75em;
    vertical-align: baseline;
    position: relative;
    top: -.05em;
    margin-left: 1px;
}
/* Staff usernames are coloured AND bolded across the whole site (chat,
   forum posts, profile pages, leaderboards, messages, etc.) so a
   moderator, admin, or helpdesk member speaking up is visually distinct
   from a regular player. Bold also rescues .hn-helpdesk-name on light
   backgrounds where the navy blue alone is hard to read.

   Each class sits inside whatever wrapper the surrounding view uses
   (`<a class="chat-user">`, `.forum-author`, `<strong>`, plain `<span>`, …)
   so its own color + weight always wins over the parent's. Plain user
   names should NOT be bolded - the contexts that historically made all
   names bold (e.g. .chat-user) have been switched to font-weight:
   normal so only staff stand out.

   Precedence at the call site (User::displayName): admin > mod > helpdesk.
   Admin pairs with the gold .admin-badge star; mod and helpdesk are
   colour+weight only (no badge) so the star stays unique to admin. */
.hn-admin-name,
.hn-mod-name,
.hn-helpdesk-name {
    font-weight: 700;
}
.hn-admin-name {
    color: #8b0000;
}
.hn-mod-name {
    color: #8b0000;
}
.hn-helpdesk-name {
    color: #1e3a8a;
}
.admin-details-summary {
    cursor: pointer;
    user-select: none;
    list-style: none;
    justify-content: space-between;
    border: 1px solid var(--hn-border);
    border-radius: 8px;
    margin-bottom: .75rem;
}
.admin-details-summary::-webkit-details-marker { display: none; }
.admin-details-summary .hn-collapse-arrow { transition: transform .2s; }
details[open] > .admin-details-summary .hn-collapse-arrow { transform: rotate(180deg); }
.admin-handelaar-create {
    display: grid;
    gap: .75rem;
    margin-bottom: .75rem;
}
.admin-handelaar-form {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: .6rem .75rem;
    align-items: end;
}
.admin-handelaar-form label {
    display: grid;
    gap: .25rem;
    margin: 0;
    font-size: .82rem;
    font-weight: 600;
    color: var(--hn-brown);
}
.admin-handelaar-form select,
.admin-handelaar-form input[type="number"],
.admin-handelaar-form input[type="text"] {
    width: 100%;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    padding: 4px 8px;
    font-size: .85rem;
    background: #fff;
}
.admin-handelaar-check {
    grid-template-columns: auto 1fr;
    align-items: center;
    align-self: center;
}
.admin-handelaar-note {
    font-size: .82rem;
    color: #888;
}
.admin-prize-level-hidden {
    display: none !important;
}
.admin-handelaar-breed-table td:not(:first-child),
.admin-handelaar-breed-table th:not(:first-child) {
    text-align: right;
}
.admin-handelaar-stock-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    margin-bottom: .75rem;
}
.admin-handelaar-toggle {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    margin: 0;
    font-size: .82rem;
    font-weight: 600;
    color: var(--hn-brown);
}
.admin-handelaar-stock-scroll {
    max-height: 58rem;
    overflow-y: auto;
}
.admin-handelaar-stock:not(.show-retired) .admin-handelaar-retired-row {
    display: none;
}
.admin-handelaar-stock-table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: #f7f2e8;
}
.admin-handelaar-action-form {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin: 0;
    flex-wrap: nowrap;
}
.admin-handelaar-price-input {
    width: 7rem;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    padding: 4px 8px;
    font-size: .85rem;
    background: #fff;
}
.admin-stats-details {
    margin-bottom: 1rem;
}

/* Period selector + summary toolbar on the admin dashboard header card. */
.admin-dashboard-toolbar {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
}
.admin-dashboard-period-form {
    display: flex;
    align-items: center;
    gap: .5rem;
}
.admin-dashboard-period-label {
    font-size: .85rem;
    font-weight: 600;
}
.admin-dashboard-period-select {
    padding: 4px 8px;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    font-size: .85rem;
}
.admin-dashboard-summary {
    font-size: .82rem;
    color: #888;
}

/* A single collapsible card inside an admin tab. The <summary> reuses
   the hn-card-header look so visually it lines up with non-collapsed
   cards, and `display:block` keeps the chevron + label on the same
   horizontal row across browsers. */
.admin-collapsible-card {
    display: block;
    margin-bottom: .75rem;
    border: 1px solid var(--hn-border);
    border-radius: 8px;
    overflow: hidden;
}
.admin-collapsible-card > summary.admin-details-summary {
    border: none;
    border-radius: 0;
    margin-bottom: 0;
}
.admin-collapsible-card[open] > summary.admin-details-summary {
    border-bottom: 1px solid var(--hn-border);
}
.admin-collapsible-card > .hn-card {
    border: none;
    border-radius: 0;
    margin: 0;
}
.admin-graphs-note {
    font-size: .82rem;
    color: #888;
    margin: .75rem .25rem;
}
/* ── Profile badges (awarded user badges, see hn_badges) ─── */
.profile-badge-list {
    display: inline-flex;
    flex-wrap: wrap;
    gap: .35rem;
    align-items: center;
}
.hn-badge-pill {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    background: #e5e0d2;
    color: #3a2f1a;
    font-size: .72rem;
    font-weight: 600;
    letter-spacing: .02em;
    padding: 2px 8px;
    border-radius: 10px;
    line-height: 1.4;
    vertical-align: middle;
}
.hn-badge-pill__icon {
    display: inline-block;
    font-weight: 700;
    line-height: 1;
}
.hn-badge-pill__label {
    display: inline-block;
}
/* Per-badge skin: applies on top of .hn-badge-pill via hn_badges.css_class. */
.beta-badge {
    background: linear-gradient(135deg, #6f42c1, #4c2a93);
    color: #fff;
}
.tester-badge {
    background: linear-gradient(135deg, #1f8a70, #145c4a);
    color: #fff;
}
@media (max-width: 767px) { #hn-menu-toggle { display: block; } }
@media (max-width: 767px) {
    .age-full { display: none; }
    .age-abbr { display: inline; }
}

/* ── Roulette wheel visual ────────────────────────────────── */
.roulette-choice label {
    cursor: pointer;
    border: 2px solid transparent;
    border-radius: 6px;
    padding: 0.4rem 0.75rem;
    display: block;
    text-align: center;
    transition: border .15s, transform .1s;
}
.roulette-choice input:checked + label {
    border-color: var(--hn-green);
    transform: scale(1.05);
    font-weight: 700;
}
.choice-red   label { background: #fee2e2; color: #991b1b; }
.choice-black label { background: #1f2937; color: #fff; }
.choice-green label { background: #d1fae5; color: #065f46; }

/* ── Daily-limit reached banner (lottery, roulette, hoger lager) ── */
.hn-limit-warning {
    background: #fff3cd;
    border: 1px solid #e0c36a;
    border-radius: 6px;
    padding: .85rem 1rem;
    color: #856404;
    font-size: .88rem;
}

/* ── Food sub-button quantity badge ──────────────────────── */
.bha-food-qty {
    flex-shrink: 0;
    margin-left: auto;
    padding-left: .2rem;
}

/* ── Horse action buttons: keep row layout on mobile, allow text wrap ── */
@media (max-width: 767px) {
    .btn-horse-action {
        align-items: center;
    }
    .bha-label {
        flex: 1;
        min-width: 0;
    }
    .bha-label-text {
        white-space: normal;
        overflow: visible;
        text-overflow: clip;
    }
}

/* (food sub-buttons are always stacked - see .care-food-row below) */

/* ── Care / Train responsive grid ────────────────────────── */
.care-train-grid {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: .75rem;
}
/* Caretaker view has no equipment panel - let the care card span full width. */
.care-train-grid--full {
    grid-template-columns: 1fr;
}
@media (max-width: 600px) {
    .care-train-grid {
        grid-template-columns: 1fr;
    }
}

/* ── Feed/Water buttons: column layout ───────────────────── */
.care-progress-btn {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
}

/* Top row inside a progress button: label left, pct right on the cost line */
.bha-prog-row {
    display: flex;
    align-items: flex-end;
    gap: .4rem;
    width: 100%;
}
.bha-prog-row .bha-label {
    flex: 1;
    min-width: 0;
}
.bha-prog-pct {
    font-size: .69rem;
    color: #777;
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
    line-height: 1.4;
    padding-bottom: 1px;
}


/* ── In-button food/water progress bar ───────────────────── */
.bha-progress-wrap {
    width: 100%;
    height: 6px;
    background: rgba(0,0,0,.1);
    border-radius: 3px;
    margin-top: .4rem;
    overflow: hidden;
    position: relative;
}
.bha-progress-bar {
    position: absolute;
    inset: 0 auto 0 0;
    height: 100%;
    min-width: 6px;
    background: #6ab04c;
    border-radius: 3px;
    transition: width .3s ease;
}
.bha-progress-bar--water {
    background: #3d9be9;
}
.bha-progress-bar--danger {
    background: #c0392b !important;
}

/* ── Per-button cooldown badge (top-right corner) ─────────── */
.bha-cd-badge {
    position: absolute;
    top: .3rem;
    right: .4rem;
    background: rgba(0,0,0,.55);
    color: #fff;
    font-size: .68rem;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    padding: 2px 6px;
    border-radius: 8px;
    pointer-events: none;
    z-index: 1;
}
/* Food sub-buttons (Hooi/Appels/Biks) are short and horizontal: the +5%/+10%/+15%
   gain text would clash with an absolute badge, so render the cooldown inline as
   a regular flex item, slotted via `order` between the label and the `1×` quantity
   (vertical centering is inherited from .btn-horse-action's align-items: center). */
.care-food-btn .bha-cd-badge {
    position: static;
    top: auto;
    left: auto;
    right: auto;
    transform: none;
    order: 1;
    margin-right: .25rem;
    flex-shrink: 0;
}
.care-food-btn .care-food-qty {
    order: 2;
}


/* ── Pedigree / Stamboom ──────────────────────────────────── */
.ped-card {
    border-radius: 6px;
    padding: .5rem .6rem;
    text-align: center;
    width: 100%;
    background: #fff;
    border: 1px solid var(--hn-border);
    transition: box-shadow .15s;
}
.ped-card:hover {
    box-shadow: 0 2px 8px rgba(0,0,0,.07);
}
.ped-card--subject {
    border: 2px solid var(--hn-brown);
    background: #fdf6ed;
}
.ped-role {
    display: inline-block;
    font-size: .68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--hn-brown);
    background: #f0e8d6;
    padding: 1px 8px;
    border-radius: 10px;
    margin-bottom: .3rem;
}
.ped-name {
    font-weight: 700;
    font-size: .88rem;
    line-height: 1.3;
}
.ped-name a {
    color: var(--hn-text);
    text-decoration: none;
}
.ped-name a:hover {
    color: var(--hn-green);
    text-decoration: underline;
}
.ped-breed {
    display: inline-block;
    font-size: .75rem;
    color: #555;
    background: var(--hn-cream);
    padding: 1px 7px;
    border-radius: 8px;
    margin-top: .2rem;
}
.ped-sdg {
    font-size: .7rem;
    font-weight: 600;
    color: #6a5430;
    letter-spacing: .04em;
    font-variant-numeric: tabular-nums;
    margin-top: .2rem;
}
.ped-owner {
    font-size: .72rem;
    color: #777;
    margin-top: .2rem;
}
.ped-owner--self {
    color: var(--hn-green);
    font-weight: 600;
}
.ped-unknown {
    font-size: .82rem;
    color: #999;
    padding: .15rem 0;
    font-style: italic;
}
.ped-connector {
    height: 14px;
}
.ped-connector--r {
    border-right: 1px solid var(--hn-border);
    border-bottom: 1px solid var(--hn-border);
}
.ped-connector--l {
    border-left: 1px solid var(--hn-border);
    border-bottom: 1px solid var(--hn-border);
}
@media (max-width: 500px) {
    .ped-card { padding: .3rem .25rem; }
    .ped-role {
        font-size: .58rem;
        padding: 1px 5px;
        letter-spacing: 0;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        max-width: 100%;
    }
    .ped-name { font-size: .76rem; }
    .ped-breed { font-size: .65rem; padding: 1px 4px; }
    .ped-sdg { font-size: .6rem; }
    .ped-unknown { font-size: .74rem; }
    .ped-connector { height: 10px; }
}

/* ── Horse profile card ───────────────────────────────────── */
/* Three breakpoints for the horse-card body, all driven by a
   single CSS grid on `.horse-profile-wrap`. `.horse-profile-grid`
   uses display:contents at every breakpoint so its two children
   (Eigenschappen + Stats) sit directly in the wrap's grid:

     <=500px           501-899px              >=900px
     +-----------+     +------+---------+     +------+------+------+
     |   image   |     |image | eigens. |     |image |eigens| stats|
     +-----------+     +------+---------+     +------+------+------+
     | eigenschap|     |     stats      |
     +-----------+     +----------------+
     |   stats   |
     +-----------+

   The <=500 layout matches the user-profile avatar at the same
   breakpoint: image centered alone on row 1 so the photo gets
   pride of place when there's no horizontal room. At 501-899 the
   image sits left of Eigenschappen and Stats spans the full width
   on row 2 (the same spillover we used to do below 600). At >=900
   we promote to the classic 3-col layout where Stats has its own
   column with a left border. The 3-col was originally turned on
   at 600px viewport; below ~900px the ~190px stats column made
   "Inenting Rhinopneumonie" and right-aligned dates spill out of
   the card, which is why we keep the 2-row layout up to 899. */
.horse-profile-wrap {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    padding: .5rem;
    justify-items: center;
}
.horse-profile-img {
    width: 200px;
    height: auto;
    object-fit: contain;
    padding: 8px;
    background: #fff;
    border-radius: 8px;
    border: 2px solid var(--hn-border);
}
.horse-profile-grid {
    display: contents;
}
.horse-profile-grid > div:first-child,
.horse-profile-stats {
    width: 100%;
    min-width: 0;
    justify-self: start;
}
.horse-profile-stats {
    border-top: 1px solid var(--hn-border);
    padding-top: .75rem;
}
.horse-profile-grid .hn-detail-table td:not(:first-child),
.horse-profile-stats .hn-detail-table td:not(:first-child) {
    text-align: right;
}
.horse-stat-row {
    margin-bottom: .6rem;
}
.horse-stat-header {
    display: flex;
    justify-content: space-between;
    font-size: .85rem;
    margin-bottom: 2px;
}
.horse-stat-label {
    color: #888;
}
.horse-detail-val-right td:last-child {
    text-align: right;
}
@media (min-width: 501px) {
    .horse-profile-wrap {
        grid-template-columns: auto 1fr;
        gap: 1rem 1.5rem;
        justify-items: stretch;
    }
    .horse-profile-img {
        align-self: start;
    }
    .horse-profile-stats {
        grid-column: 1 / -1;
    }
}
@media (min-width: 900px) {
    .horse-profile-wrap {
        grid-template-columns: auto 1fr 1fr;
        gap: 1.5rem;
    }
    .horse-profile-stats {
        grid-column: auto;
        width: auto;
        border-top: none;
        padding-top: 0;
        border-left: 1px solid var(--hn-border);
        padding-left: 1.5rem;
    }
}
@media (max-width: 600px) {
    .horse-profile-wrap {
        gap: .75rem 1rem;
        padding: .25rem;
    }
    .horse-profile-img {
        width: 130px;
        padding: 5px;
    }
}
@media (max-width: 500px) {
    /* No .horse-profile-img width override here: we deliberately keep
       the 130px from the <=600px block at this breakpoint too, so the
       photo stays a reasonable size on phones. The pre-launch 100px
       override felt too small once we put cost-tied UI next to it. */
    .horse-profile-grid .hn-detail-table td:first-child {
        white-space: normal;
    }
    .horse-profile-grid .hn-detail-table,
    .horse-profile-stats .hn-detail-table {
        font-size: .8rem;
    }
}

/* ── Lifetime stats block (horse detail page, under "Stats" header) ──
   A small key/value table that lists race counters and previous owners.
   Sits between the .hn-card-section heading and the health stat-bar,
   so we add a touch of bottom margin to separate it from the bars
   below. Podium counters render as plain emoji + Nx with a small gap,
   no pill backgrounds (the medal emoji already carries the color). */
.horse-lifetime-stats {
    margin-bottom: .75rem;
}
.horse-podium-counts {
    display: inline-flex;
    flex-wrap: wrap;
    gap: .6rem;
    justify-content: flex-end;
}
.horse-podium-count {
    white-space: nowrap;
}
.horse-previous-owners {
    display: inline-block;
    line-height: 1.4;
    word-break: break-word;
}

/* ── Custom horse photo (avatar) upload ──────────────────────────
   Sits inside .hn-card-body on the horse detail page. Trigger
   buttons live in TWO positions:

     - .horse-photo-actions--desktop: inside .horse-profile-img-col
       (right under the avatar) on >=501px viewports.
     - .horse-photo-actions--mobile : after .horse-profile-wrap (at
       the bottom of the card) on <=500px viewports.

   This split is intentional so the "Foto wijzigen" call-to-action
   stays visually attached to the avatar on desktop but doesn't
   crowd the small mobile layout where the avatar already sits
   alone at the top. The cropper PANEL renders only once, after the
   wrap, and is targeted by both triggers via querySelectorAll.

   The standalone "Verwijderen" form duplicates between the two
   action groups too. That's the cheaper alternative to teleporting
   a single DOM node across the grid; the form is stateless and
   the buttons do the same POST.

   The custom JPEG is stored at 442x352 (2x scale of the 221x176
   breed sprites). The 5:4 aspect ratio matches both, so
   .horse-profile-img keeps the same width/height: auto rule. The
   --custom modifier is a hook for future visual differentiation
   that should only apply to user-uploaded photos. */
.horse-profile-img--custom {
    /* Empty: hook present for future visual differentiation.
       Inherits everything from .horse-profile-img. */
}
.horse-profile-img-link {
    /* Wrapper anchor on custom uploaded photos. Progressive
       enhancement: with JS off the link opens the full image in a
       new tab; with JS on screenshots-lightbox.js intercepts the
       click and shows a fullscreen overlay. The line-height: 0
       collapses the inline-block whitespace gap below the image. */
    display: inline-block;
    line-height: 0;
    cursor: zoom-in;
    border-radius: 8px;
    text-decoration: none;
    color: inherit;
    transition: box-shadow .12s;
}
.horse-profile-img-link:hover,
.horse-profile-img-link:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--hn-gold);
}
.horse-profile-img-col {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .75rem;
}
.horse-photo-actions {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    width: 100%;
}
.horse-photo-actions--desktop {
    /* Hidden by default; the @media (min-width: 501px) block below
       flips it to flex. Keeps the avatar uncluttered on phones. */
    display: none;
}
.horse-photo-actions--mobile {
    /* Visible by default (mobile-first). Sits at the bottom of the
       card so the avatar at the top stays unobstructed. */
    display: flex;
    margin-top: 1rem;
}
@media (min-width: 501px) {
    .horse-photo-actions--desktop {
        display: flex;
    }
    .horse-photo-actions--mobile {
        display: none;
    }
}
.horse-photo-edit-panel {
    width: 100%;
    max-width: 28rem;
    margin: .75rem auto 0;
    padding: .75rem;
    border: 1px solid var(--hn-border);
    border-radius: 8px;
    background: #fff;
}
.horse-photo-free-banner {
    color: var(--hn-success, #2e7d32);
    font-weight: 600;
}
.horse-photo-delete-form {
    margin: 0;
}
[id^="hn-horse-crop-preview-"] {
    max-height: 320px;
    margin-bottom: .5rem;
    overflow: hidden;
    background: #222;
    border-radius: 6px;
}
[id^="hn-horse-crop-img-"] {
    display: block;
    max-width: 100%;
    max-height: 320px;
}
.train-row {
    display: flex;
    align-items: stretch;
    gap: .4rem;
}
.train-form {
    display: flex;
    flex-direction: column;
    gap: .5rem;
}
.train-row .btn-horse-action {
    flex: 1;
}
.laten-trainen-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    line-height: 1.2;
    text-align: center;
    padding-top: .25rem;
    padding-bottom: .25rem;
}
.caretaker-banner {
    margin-bottom: .75rem;
    border-left: 4px solid #7b4f2e;
}
.caretaker-banner .hn-card-body {
    padding: .65rem .85rem;
    font-size: .9rem;
}

/* Small "(verzorger)" pill in the /hoefsmid and /vet pending tables that
   marks rows which were placed by a caretaker on a care+races contract
   instead of by the owner. Owner of the horse sees the same row tagged
   "via verzorger" with a tooltip explaining their wallet is still the
   payer. Reuses .hn-badge for shape but overrides the red background so
   the row doesn't read like an error. */
.hn-pending-caretaker-tag {
    background: #c8e0c2;
    color: #2d5e1f;
    margin-left: .35rem;
    vertical-align: middle;
}

/* Caretaker contract-type badge (list + pending/active tables, caretaker banner). */
.caretaker-type-badge {
    display: inline-block;
    padding: .15rem .5rem;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 600;
    background: #eee5d3;
    color: #6a5430;
    border: 1px solid #d6c7a3;
    line-height: 1.25;
    white-space: nowrap;
}
.caretaker-type-badge--races {
    background: #fff3d0;
    color: #8a6300;
    border-color: #e9c874;
}

/* Non-binding duration picker on /caretakers/list. */
.caretaker-duration-select {
    gap: .5rem;
}
.caretaker-duration-option {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .3rem .6rem;
    border: 1px solid var(--hn-border);
    border-radius: 999px;
    background: #fff;
    cursor: pointer;
    font-size: .82rem;
}
.caretaker-duration-option input[type="radio"] {
    accent-color: #7b4f2e;
}

/* Container for the Verzorgen / Verzorg+Wedstrijden action buttons on /caretakers/list. */
.caretaker-request-actions {
    gap: .4rem;
}

/* Owner's care grid visually locked while a care contract is active. */
.care-progress-btn--locked {
    opacity: .55;
    filter: saturate(.6);
}

/* Retired/confiscated horse caretaker view. */
.care-grid-locked {
    opacity: .4;
    pointer-events: none;
}
.custody-banner {
    margin-bottom: .75rem;
    border-left: 4px solid #c0392b;
    background: #fdecea;
}
.custody-banner .hn-card-header {
    background: #c0392b;
}
.pending-training-body {
    padding: .65rem .75rem;
}
.pending-training-hint {
    font-size: .83rem;
    color: #666;
    margin: 0 0 .6rem;
}
.horse-pregnancy-due {
    color: #c0396b;
}
.horse-pregnancy-covered {
    color: #a86a4f;
}
.horse-profile-grid .hn-detail-table td.horse-birth-blocked {
    color: #b91c1c;
    font-weight: 600;
    padding-top: 4px;
    white-space: normal;
}

.horse-discipline-details {
    margin-top: .6rem;
    border-top: 1px dashed #d4c8a8;
    padding-top: .5rem;
}
.horse-discipline-summary {
    cursor: pointer;
    color: var(--hn-green-dark);
    font-weight: 700;
    font-size: 0.88rem;
    list-style: none;
}
.horse-discipline-summary::-webkit-details-marker { display: none; }
.horse-discipline-summary::before {
    content: '▸';
    display: inline-block;
    width: 1em;
    transition: transform .15s ease;
}
.horse-discipline-details[open] > .horse-discipline-summary::before {
    transform: rotate(90deg);
}
.horse-discipline-table {
    margin-top: .4rem;
}
.horse-discipline-table td {
    vertical-align: middle;
}
.horse-discipline-value {
    white-space: nowrap;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: .55rem;
}
.horse-discipline-value > .horse-discipline-level {
    flex: 0 0 auto;
}
.horse-discipline-value > .stat-bar {
    flex: 0 0 50px;
}
.horse-discipline-value > .stat-bar > .stat-bar-fill {
    display: block;
}
.horse-discipline-value > .horse-discipline-pct {
    flex: 0 0 auto;
    min-width: 2.8em;
    text-align: right;
    font-variant-numeric: tabular-nums;
}
.horse-discipline-promotion-row td {
    padding-top: 0;
}
.horse-discipline-promotion-form {
    display: flex;
    justify-content: flex-end;
}

/* ── Care food sub-buttons ────────────────────────────────── */
.care-food-row {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    margin-top: .4rem;
}
.care-food-row .btn-horse-action {
    flex-direction: row;
}
@media (min-width: 1200px) {
    .care-food-row {
        flex-direction: row;
    }
}
.care-food-btn {
    flex: 1 1 0;
    min-width: 0;
    justify-content: flex-start;
    padding: .25rem .4rem;
    gap: .35rem;
    position: relative;
}
.care-food-btn .bha-label {
    flex: 1 1 auto;
    gap: .35rem;
    min-width: 0;
}
.care-food-img {
    height: 2.25rem;
    width: 2.25rem;
    object-fit: contain;
    flex-shrink: 0;
}
.care-food-name {
    font-size: .76rem;
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.care-food-qty {
    font-size: .72rem;
    font-weight: 700;
}
.care-food-gain {
    margin-left: .3rem;
    font-size: .68rem;
    font-weight: 600;
    color: #7b4f2e;
    opacity: .8;
}
.care-food-qty--empty {
    color: #c0392b;
}
.care-food-qty--stocked {
    color: #7b4f2e;
}
.horse-breadcrumb {
    font-size: .82rem;
}

/* ── Generic filter form (reusable across pages) ──────────── */
.hn-filter-form {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: flex-end;
}
.hn-filter-label {
    display: block;
    font-size: .75rem;
    color: #666;
    margin-bottom: 3px;
}
/* Standard form input/select inside an .hn-filter-form (or any
   row that wants identical-height controls). The explicit `height`
   plus `box-sizing: border-box` is what makes <input> and <select>
   render at exactly the same pixel height; without it browsers
   give <select> extra intrinsic vertical space for the chevron and
   it ends up taller than a plain <input>. Pair with .btn-hn-input
   (same height) for a button on the same row. */
.hn-filter-input {
    height: 32px;
    box-sizing: border-box;
    padding: 0 8px;
    font-size: .85rem;
    line-height: 1.4;
    font-family: inherit;
    border: 1px solid #ccc;
    border-radius: 4px;
    background: #fff;
}
.hn-input-w-sm { width: 110px; }
.hn-input-w-md { width: 130px; }
.hn-input-w-lg { width: 160px; }
.hn-filter-checkbox {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .84rem;
    cursor: pointer;
    padding: 6px 0;
}
.hn-accent-brown { accent-color: var(--hn-brown); }

/* ── Generic card-header count/pill ───────────────────────── */
.hn-header-count {
    background: rgba(255,255,255,.25);
    color: #fff;
    border-radius: 3px;
    padding: 1px 8px;
    font-size: .75rem;
    font-weight: 600;
    margin-left: .5rem;
    vertical-align: middle;
}

/* ── Small inline separator (pair with .text-muted) ───────── */
.hn-sep { margin: 0 .15em; }

/* ── Small muted tag (e.g. "privé" chip) ──────────────────── */
.hn-tag-muted {
    font-size: .68rem;
    background: #eee;
    color: #888;
    border-radius: 3px;
    padding: 1px 5px;
    margin-left: 4px;
    vertical-align: middle;
}

/* ── SDG value cell in tables ─────────────────────────────── */
.hn-sdg-cell {
    font-size: .82rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    letter-spacing: .02em;
    color: #6a5430;
}

/* ── Button variants ──────────────────────────────────────── */
.btn-hn-grey { background: #888; }
.btn-hn-grey:hover:not(:disabled):not(.disabled-link) { background: #6f6f6f; color: #fff; }
.btn-hn-disabled { opacity: .5; cursor: not-allowed; }

/* ── Breed page ───────────────────────────────────────────── */
.breed-row-unaffordable { opacity: .55; }

.training-horse-btn.breed-mare-ineligible {
    color: #c0392b;
    border-color: #e6a19a;
    background: #fdecea;
}
.training-horse-btn.breed-mare-ineligible:hover {
    background: #fbdedb;
    border-color: #d88a8a;
}
/* Selected state for blocked mares: keep the red palette instead of
   inheriting the generic green .active background. The .training-horse-btn.active
   rule further down in this file would otherwise win for `background`,
   painting selected red buttons green. */
.training-horse-btn.breed-mare-ineligible.active,
.training-horse-btn.breed-mare-ineligible.active:hover {
    background: #fce4e4;
    border-color: #a93226;
    color: #943126;
}

.breed-mare-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .75rem;
    margin-bottom: .75rem;
}
@media (max-width: 700px) {
    .breed-mare-stats { grid-template-columns: 1fr; }
}
.breed-stat-box {
    border: 1px solid #e0d7c5;
    border-radius: 8px;
    padding: .75rem 1rem;
    background: #faf6ef;
}
.breed-stat-label {
    font-size: .78rem;
    font-weight: 600;
    color: #6a5430;
    text-transform: uppercase;
    letter-spacing: .04em;
    margin-bottom: .35rem;
}
.breed-stat-value {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--hn-brown);
    font-variant-numeric: tabular-nums;
}
.breed-stat-sub {
    font-size: .85rem;
    color: #666;
    margin-top: .1rem;
}
.breed-coef-good { color: #2d7a2d; }
.breed-coef-ok   { color: #c77520; }
.breed-coef-bad  { color: #c0392b; }

/* ── Crossbreed breed-mix toggle (horse profile) ─────────── */
/* The "Kruising" cell on /horses/{id} is wrapped in a button so the
 * user can click the breed label or the chevron to expand the per-breed
 * percentage breakdown below. Native button-reset to inherit the table
 * cell's font/color so it looks like the surrounding label until you
 * spot the chevron. */
.breed-mix-toggle {
    appearance: none;
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    line-height: inherit;
    text-align: left;
}
.breed-mix-toggle:focus-visible {
    outline: 2px solid var(--hn-green-dark);
    outline-offset: 2px;
    border-radius: 3px;
}
/* Reuse the .horse-discipline-summary chevron pattern: a right-pointing
 * triangle that rotates 90° to point down when expanded. The character
 * sits naturally on the text baseline so no font-metric babysitting is
 * needed. */
.breed-mix-toggle::after {
    content: '▸';
    display: inline-block;
    width: 1em;
    margin-left: .15rem;
    transition: transform .15s ease;
}
.breed-mix-toggle[aria-expanded="true"]::after {
    transform: rotate(90deg);
}
.breed-mix-row td {
    padding-top: 1px;
    padding-bottom: 1px;
    font-size: .88rem;
    color: #6a5430;
}
.breed-mix-row .breed-mix-label {
    padding-left: 1.1rem;
}
.breed-mix-row .breed-mix-pct {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.breed-block-msg {
    background: #fef3e2;
    border: 1px solid #f0c36d;
    color: #856404;
    border-radius: 6px;
    padding: .6rem .85rem;
    font-size: .88rem;
    font-weight: 600;
    margin-bottom: .75rem;
}

/* ── Profile avatar ───────────────────────────────────────── */
.hn-avatar {
    width: 128px;
    height: 128px;
    border-radius: 8px;
    object-fit: cover;
    border: 2px solid var(--hn-border);
    flex-shrink: 0;
}
.hn-avatar-frame {
    position: relative;
    display: inline-flex;
    flex-shrink: 0;
    line-height: 0;
    overflow: hidden;
    border-radius: 8px;
}
.hn-avatar-frame img {
    display: block;
}
.hn-avatar-frame--sm {
    border-radius: 5px;
}
.hn-avatar-frame--forum {
    border-radius: 6px;
}
.hn-avatar-frame--jailed::before,
.hn-avatar-frame--jailed::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 1;
}
.hn-avatar-frame--jailed::before {
    background: repeating-linear-gradient(
        90deg,
        transparent 0 12%,
        rgba(28, 28, 28, .78) 12% 20%,
        transparent 20% 35%,
        rgba(28, 28, 28, .78) 35% 43%,
        transparent 43% 58%,
        rgba(28, 28, 28, .78) 58% 66%,
        transparent 66% 81%,
        rgba(28, 28, 28, .78) 81% 89%,
        transparent 89% 100%
    );
}
.hn-avatar-frame--jailed::after {
    background: linear-gradient(90deg, rgba(255,255,255,.22), transparent 40%, rgba(0,0,0,.18));
}
/* Hoofd tab card: avatar + stats + side (Acties/Foto's) laid out
   with grid-template-areas so we can shuffle the side column between
   three breakpoints without touching the DOM:

     <=500px      501-999px           >=1000px
     +--------+   +-----+--------+    +-----+--------+--------+
     | avatar |   |avtr | stats  |    |avtr | stats  | side   |
     +--------+   +-----+--------+    +-----+--------+--------+
     | stats  |   |     side     |
     +--------+   +--------------+
     |  side  |
     +--------+

   The 501-999px row pulls "side" out from under stats so Acties/Foto's
   can span the full card width. Bumped the 3-col threshold from 701
   to 1000 because at 700-999 the card was ~600-820px wide and the
   3-col split (auto / 1fr / 1fr) gave each side column ~180-260px,
   which made the 256px Foto's tile and "Profielfoto wijzigen" button
   overlap the rightmost stats values. From 1000px onwards there's
   ~840px of card-internal room which the 3-col layout actually fits.
   The right sidebar (#hn-sidebar-right) doesn't show until 1180px,
   so the 1000-1179 range gets the full main column for this 3-col. */
.hn-avatar-wrap {
    display: grid;
    grid-template-columns: 1fr;
    grid-template-areas:
        "avatar"
        "stats"
        "side";
    gap: .75rem 1.25rem;
    align-items: start;
}
.hn-avatar-wrap > .hn-avatar-frame { grid-area: avatar; justify-self: center; }
.hn-avatar-wrap > .hn-profile-info { grid-area: stats; min-width: 0; }
.hn-avatar-wrap > .profile-side-right { grid-area: side; min-width: 0; }

/* ── Admin-only "Admin details" disclosure on user profile ────────
   Sits under the "Naam" row, visible only to admins. The summary is
   a small subtle link; opening it reveals private user data (email,
   IPs, credit ledger). */
.profile-admin-details {
    display: block;
    margin-top: .25rem;
}
.profile-admin-details > summary {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    cursor: pointer;
    user-select: none;
    list-style: none;
    font-size: .78rem;
    color: #6a5430;
    font-weight: 600;
    padding: 2px 0;
}
.profile-admin-details > summary::-webkit-details-marker { display: none; }
.profile-admin-details > summary .hn-collapse-arrow { transition: transform .2s; }
.profile-admin-details[open] > summary .hn-collapse-arrow { transform: rotate(180deg); }
.profile-admin-table {
    margin-top: .35rem;
    background: #fffdf6;
    border: 1px dashed #d4b87a;
    border-radius: 6px;
    padding: .5rem .65rem;
    font-size: .8rem;
}
.profile-admin-table td { padding: 1px 0; }
.profile-admin-ip {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .78rem;
    word-break: break-all;
}
@media (min-width: 501px) {
    .hn-avatar-wrap {
        grid-template-columns: auto 1fr;
        grid-template-areas:
            "avatar stats"
            "side   side";
    }
    .hn-avatar-wrap > .hn-avatar-frame { justify-self: start; }
}
/* 3-col "avatar | stats | side" needs ~700px of card-internal room
   to read cleanly (the stats table has long labels like
   "Western Pleasure winstpunten" and the side column hosts a 256px
   photo + a "Profielfoto wijzigen" button). Below 1000px viewport
   the main column shrinks to ~600-800px and 3-col made the stats
   value column and side column overlap; keep the 2-col layout
   (avatar+stats top, Acties+Foto's row 2) up to 999px and only
   promote to 3-col from 1000px onwards. */
@media (min-width: 1000px) {
    .hn-avatar-wrap {
        grid-template-columns: auto 1fr 1fr;
        grid-template-areas: "avatar stats side";
        gap: .75rem 2rem;
    }
}
/* Cropper.js container */
#hn-crop-preview {
    height: 340px;
    background: #222;
    border-radius: 6px;
    margin-bottom: .75rem;
}
#hn-crop-preview img {
    display: block;
    max-width: 100%;
    max-height: 100%;
}
@media (max-width: 500px) {
    .hn-avatar {
        width: 96px;
        height: 96px;
    }
}

/* ── User profile photos (Foto's tab) ─────────────────────── */
/* Visitor + owner: 1:1 swipeable carousel.
   Slides stack absolutely so the frame keeps its aspect ratio while we
   cross-fade with the .is-active toggle. */
.user-photo-carousel {
    max-width: 512px;
    margin: 0 auto;
}

/* Hoofd tab right column: holds Acties + the optional Foto's tile that
   links to the photos tab. DOM order is [Acties, Foto's]:
   - Wide (viewport >=1000px): flex-column. The `.hn-avatar-wrap`
     grid puts this block in a narrow 3rd column next to stats, so
     Acties stacks on top of the 256px photo.
   - Mid (viewport 501-999px): flex-row. The `.hn-avatar-wrap` grid
     gives this block a full-width row under avatar+stats; spread
     Acties on the left and the 128px photo on the right so the
     wide row doesn't read as empty space.
   - Small (viewport <=500px): the wrap is a single column, so
     flex-row would push the photo off-screen. Switch back to
     flex-column so Acties stacks above the photo. */
.profile-side-right {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: 1rem;
}
.profile-side-actions { flex: 1 1 auto; min-width: 0; }
.profile-side-photos { flex: 0 0 auto; }
/* Avatar upload/crop panel. Lives inside .profile-side-actions, hidden by
   default and toggled open under the "Profielfoto wijzigen" button so the
   crop UI appears right where the user clicked instead of in the stats
   column. */
.hn-avatar-photo-panel {
    margin-top: .75rem;
}
@media (max-width: 500px) {
    .profile-side-right {
        flex-direction: column;
        gap: 0;
    }
    .profile-side-photos { margin-top: 1rem; }
}
@media (min-width: 1000px) {
    .profile-side-right {
        flex-direction: column;
        gap: 0;
    }
    .profile-side-photos { margin-top: 1rem; }
}

/* Hoofd tab photo tile: square thumbnail (matches .hn-avatar so the
   visual rhythm reads "another portrait of this player") that links to
   ?tab=fotos. The optional +N badge bottom-right tells the visitor
   more photos are waiting in the gallery. */
.user-photo-primary {
    position: relative;
    display: inline-block;
    width: 128px;
    height: 128px;
    border-radius: 8px;
    overflow: hidden;
    border: 2px solid var(--hn-border);
    line-height: 0;
    text-decoration: none;
    transition: box-shadow .15s ease, transform .15s ease;
}
.user-photo-primary:hover {
    box-shadow: 0 4px 14px rgba(0, 0, 0, .18);
    transform: translateY(-1px);
}
.user-photo-primary img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.user-photo-primary-badge {
    position: absolute;
    bottom: 4px;
    right: 4px;
    background: rgba(0, 0, 0, .6);
    color: #fff;
    font-size: .72rem;
    font-weight: 600;
    line-height: 1;
    padding: 3px 6px;
    border-radius: 10px;
    pointer-events: none;
}

/* Desktop variant: bigger (caps at 256px), fluid down to the right
   column's actual width so it never overflows on tablets where
   .hn-profile-grid keeps 2 cols at minmax(160px, 1fr). aspect-ratio
   keeps it square once height: auto kicks in. On mobile we shrink
   back to the same 128x128 the avatar uses. */
.user-photo-primary--lg {
    width: 100%;
    max-width: 256px;
    height: auto;
    aspect-ratio: 1 / 1;
}
/* Mirror the .profile-side-right breakpoint: while the side row
   uses the flex-row "Acties left, Foto's right" layout (viewport
   <=999px), lock the photo to a 128x128 thumbnail so it matches
   the avatar's footprint and leaves room for the buttons. At
   >=1000px the side column gets its own grid column and the
   photo can scale up to the full 256px tile again. */
@media (max-width: 999px) {
    .user-photo-primary--lg {
        width: 128px;
        max-width: 128px;
        height: 128px;
    }
}
.upc-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
    background: #1a1a1a;
    border-radius: 8px;
    overflow: hidden;
    user-select: none;
}
.upc-slide {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity .25s ease;
    pointer-events: none;
}
.upc-slide.is-active {
    opacity: 1;
    pointer-events: auto;
}
.upc-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, .45);
    color: #fff;
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    z-index: 2;
    font-size: 1rem;
    line-height: 1;
    transition: background .15s ease;
}
.upc-arrow:hover { background: rgba(0, 0, 0, .65); }
.upc-arrow-prev  { left: .5rem; }
.upc-arrow-next  { right: .5rem; }
@media (hover: none) {
    .upc-arrow { display: none; }
}
.upc-dots {
    display: flex;
    justify-content: center;
    gap: .4rem;
    margin-top: .55rem;
}
.upc-dot {
    width: 9px;
    height: 9px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: #c8b993;
    cursor: pointer;
    opacity: .65;
    transition: background .15s ease, opacity .15s ease, transform .15s ease;
}
.upc-dot:hover { opacity: 1; }
.upc-dot.is-active {
    background: var(--hn-brown);
    opacity: 1;
    transform: scale(1.15);
}

/* Owner: thumbnail strip with drag-reorder + per-tile delete + cropper. */
.user-photos-manage {
    margin-top: 1rem;
}
.user-photo-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}
.user-photo-thumb {
    position: relative;
    width: 80px;
    height: 80px;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid var(--hn-border);
    background-color: #f5efde;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    cursor: grab;
    /* NOTE: do NOT add `-webkit-user-drag: none` here. SortableJS uses
       the native HTML5 drag-and-drop API on desktop, and that property
       blocks dragstart, killing reorder on desktop. The OS-level
       long-press "Save Image" callout is already prevented by rendering
       the thumb as a background-image <div> (no real <img> = no callout). */
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
}
.user-photo-thumb:active { cursor: grabbing; }
.upt-drag-handle {
    position: absolute;
    top: 4px;
    left: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 24px;
    min-height: 24px;
    background: rgba(255, 255, 255, .9);
    color: #6a5430;
    font-size: 1rem;
    line-height: 1;
    padding: 2px 5px;
    border-radius: 4px;
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
    pointer-events: none;
    z-index: 2;
}
.upt-delete-form {
    position: absolute;
    top: 2px;
    right: 2px;
    margin: 0;
    z-index: 2;
}
.upt-delete-btn {
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(180, 50, 50, .85);
    color: #fff;
    font-size: .9rem;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.upt-delete-btn:hover { background: rgb(180, 50, 50); }
/* SortableJS state classes (mirror .groups-row-* naming). */
.upt-ghost  { opacity: .35; background: #f5efde; }
.upt-chosen { box-shadow: 0 0 0 2px rgba(125, 99, 50, .25); }
.upt-drag {
    box-shadow: 0 8px 22px rgba(0, 0, 0, .2);
    border-radius: 6px;
}

/* Cropper.js container for the Add-photo panel. Mirrors #hn-crop-preview
   above (same fixed-height letterbox) so the in-tab cropper feels identical
   to the avatar editor. */
.hn-photos-panel {
    margin-top: .85rem;
    border-top: 1px solid var(--hn-border);
    padding-top: .85rem;
}
.hn-photos-hint-meta { opacity: .7; }
#hn-photos-crop-preview {
    height: 340px;
    background: #222;
    border-radius: 6px;
    margin-bottom: .75rem;
}
#hn-photos-crop-preview img {
    display: block;
    max-width: 100%;
    max-height: 100%;
}

/* ── Jail ─────────────────────────────────────────────────── */
.jail-admin-card {
    margin-bottom: 1rem;
}
.jail-admin-form {
    display: grid;
    grid-template-columns: minmax(150px, 1fr) minmax(220px, 2fr) minmax(150px, 1fr) auto;
    gap: .75rem;
    align-items: end;
}
.jail-form-field {
    display: flex;
    flex-direction: column;
    gap: .25rem;
}
.jail-form-field label {
    font-size: .85rem;
    font-weight: 600;
    color: var(--hn-brown);
}
.jail-form-actions {
    display: flex;
    align-items: end;
}
.jail-player-cell {
    display: flex;
    align-items: center;
    gap: .6rem;
    min-width: 170px;
}
.jail-table td {
    vertical-align: middle;
}
.jail-locked {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .9rem;
    text-align: center;
}
.jail-locked h1 {
    margin: .25rem 0 0;
    color: var(--hn-brown);
}
.jail-locked-avatar {
    margin-bottom: .25rem;
}
.jail-locked-meta {
    color: #5f4a2a;
    font-weight: 600;
    line-height: 1.5;
}
.jail-locked-reason {
    width: 100%;
    padding: .85rem 1rem;
    border: 1px solid var(--hn-border);
    border-radius: 8px;
    background: #f7f0df;
    color: #4d3a1f;
    line-height: 1.5;
}
.jail-locked-contact {
    width: 100%;
    margin: 0;
    font-size: .85rem;
    color: #6b5a3a;
    line-height: 1.5;
}
@media (max-width: 767px) {
    .jail-admin-form {
        grid-template-columns: 1fr;
    }
    .jail-form-actions {
        justify-content: flex-start;
    }
}

/* ── Overlay for mobile sidebar ───────────────────────────── */
#hn-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.4);
    /* Below header (1030) so the hamburger button stays accessible,
       but above all regular page content */
    z-index: 1025;
}
@media (max-width: 767px) {
    #hn-overlay.active { display: block; }
}

/* Horse fast-switcher bar */
.horse-switcher {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .35rem;
    padding: .45rem .6rem;
    margin-bottom: .75rem;
    background: #f0e8d6;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
}
@media (max-width: 767px) {
    .horse-switcher {
        flex-wrap: nowrap;
        justify-content: flex-start;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
}
.horse-switcher-item {
    display: inline-flex;
    align-items: center;
    padding: .2rem .6rem;
    border-radius: 4px;
    font-size: .78rem;
    font-weight: 600;
    white-space: nowrap;
    text-decoration: none;
    color: var(--hn-green);
    background: #fff;
    border: 1px solid var(--hn-border);
    transition: background .15s, color .15s, border-color .15s;
}
.horse-switcher-item:hover {
    background: var(--hn-cream);
    color: var(--hn-green-dark);
    border-color: var(--hn-green-light);
    text-decoration: none;
}
.horse-switcher-item--active {
    background: var(--hn-cream);
    color: var(--hn-brown-light);
    border-color: transparent;
    cursor: default;
    pointer-events: none;
}

/* ── Toggle switch ───────────────────────────────────────── */
.hn-toggle {
    position: relative;
    display: inline-block;
    flex-shrink: 0;
    cursor: pointer;
}
.hn-toggle input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}
.hn-toggle-track {
    display: block;
    width: 40px;
    height: 22px;
    background: #ccc;
    border-radius: 11px;
    transition: background .2s;
    position: relative;
}
.hn-toggle-track::after {
    content: '';
    position: absolute;
    top: 3px;
    left: 3px;
    width: 16px;
    height: 16px;
    background: #fff;
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(0,0,0,.2);
    transition: transform .2s;
}
.hn-toggle input:checked + .hn-toggle-track {
    background: var(--hn-green);
}
.hn-toggle input:checked + .hn-toggle-track::after {
    transform: translateX(18px);
}

/* Row pairing a toggle with its label + description (used in /account settings) */
.hn-setting-row {
    display: flex;
    align-items: flex-start;
    gap: .6rem;
    cursor: pointer;
    margin-bottom: .85rem;
}
.hn-setting-row .hn-toggle {
    margin-top: 1px;
}
.hn-setting-label {
    font-weight: 600;
    font-size: .88rem;
    color: var(--hn-text);
}
.hn-setting-desc {
    font-size: .78rem;
    color: #888;
    margin-top: .1rem;
    max-width: 460px;
}

/* ── Profession toggle row (trainer / hoefsmid dashboard) ──
   Wrapper is intentionally a <div>, NOT a <label>. Only the
   inner .hn-toggle switch reacts to taps; the description text
   beside it is decorative. Nesting a <label> inside another
   <label> (both implicitly bound to the same checkbox) makes
   Samsung Browser / mobile Chromium fire two synthetic clicks
   per tap, which cancel each other and suppress the `change`
   event the JS toggle handler listens for. Keeping the outer
   wrapper as a plain <div> is the fix. */
.profession-toggle-row {
    display: flex;
    align-items: center;
    gap: .6rem;
}
.profession-toggle-label {
    font-weight: 600;
    font-size: .88rem;
    color: var(--hn-text);
}

/* Quick-slot picker rows on /account (Topbar shortcuts card). Mirrors
   the .hn-setting-row layout but uses a select instead of a toggle. */
.hn-quick-slot-row {
    display: flex;
    align-items: center;
    gap: .6rem;
    margin-bottom: .55rem;
    max-width: 320px;
}
.hn-quick-slot-label {
    font-weight: 600;
    font-size: .85rem;
    color: var(--hn-text);
    flex: 0 0 4rem;
}
.hn-quick-slot-select {
    flex: 1 1 auto;
    padding: 4px 6px;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    font-size: .9rem;
    background: #fff;
}
.stud-fee-box {
    background: #f9f4ec;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    padding: .6rem .75rem;
    margin-bottom: .75rem;
}
.stud-fee-label {
    display: block;
    font-size: .84rem;
    font-weight: 600;
    color: var(--hn-brown);
    margin-bottom: .35rem;
}
.stud-fee-input {
    width: 120px;
}

/* ── Browser push settings (admin /account card) ─────────────── */
.push-prefs-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: .4rem .9rem;
    border-top: 1px solid var(--hn-border);
    padding-top: .75rem;
}
.push-pref-row {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .85rem;
    color: var(--hn-text);
    cursor: pointer;
}
.push-pref-row input[type="checkbox"] {
    accent-color: var(--hn-green);
    flex-shrink: 0;
}
.push-pref-row input[type="checkbox"]:disabled + span {
    color: #aaa;
}
.push-pref-row input[type="checkbox"]:disabled {
    cursor: not-allowed;
}
/* Inline "Opgeslagen" flash that appears next to the category heading
   right after a checkbox is toggled. Hidden until JS adds .is-visible,
   then fades in/out without shifting the layout around it. */
.push-saved-flash {
    font-size: .75rem;
    color: var(--hn-green);
    opacity: 0;
    transition: opacity .2s ease;
    min-width: 5rem;
}
.push-saved-flash.is-visible {
    opacity: 1;
}
.push-saved-flash.is-error {
    color: #b71c1c;
}

/* ── Forum ───────────────────────────────────────────────────── */
.forum-page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1rem;
    flex-wrap: wrap;
    gap: .5rem;
}
.forum-page-header h2 { margin: 0; }
.forum-search-form { margin-bottom: 1rem; }
.forum-search-bar {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
}
.forum-search-input {
    flex: 1 1 200px;
    min-width: 0;
    padding: .4rem .6rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: .9rem;
    background: #fff;
    color: #333;
}
.forum-search-input:focus { outline: none; border-color: var(--hn-green); }
.forum-search-title-only {
    display: flex;
    align-items: center;
    gap: .3rem;
    font-size: .85rem;
    color: #555;
    white-space: nowrap;
    cursor: pointer;
}
.forum-search-snippet {
    font-size: .8rem;
    color: #777;
    margin-top: .2rem;
    line-height: 1.4;
    word-break: break-word;
}
.forum-search-snippet mark {
    background: #fff3cd;
    color: #333;
    padding: 0 1px;
    border-radius: 2px;
}
.forum-search-clear-wrap { padding: .6rem 1rem; }
.forum-search-clear { font-size: .85rem; color: #555; text-decoration: none; }
.forum-search-clear:hover { color: var(--hn-green); text-decoration: none; }
.forum-back { font-size: .85rem; color: #555; text-decoration: none; display: inline-block; margin-bottom: 1rem; }
.forum-back:hover { color: var(--hn-green); text-decoration: none; }
.forum-header-count {
    background: rgba(255,255,255,.25);
    color: #fff;
    border-radius: 3px;
    padding: 1px 8px;
    font-size: .75rem;
    margin-left: .5rem;
}
.forum-badge {
    border-radius: 3px;
    padding: 1px 6px;
    font-size: .68rem;
    margin-left: .4rem;
    vertical-align: middle;
    color: #fff;
    font-weight: 600;
}
.forum-badge--pending  { background: #f59e0b; }
.forum-badge--rejected { background: #dc2626; }
.forum-badge--header-pending  { background: rgba(255,255,255,.35); }
.forum-badge--header-rejected { background: rgba(220,38,38,.7); }
.forum-badge--header-locked   { background: rgba(255,255,255,.25); border: 1px solid rgba(255,255,255,.4); }
.forum-badge--header-pinned   { background: rgba(232,182,36,.85); color: #2a1f00; }

.forum-lock-icon,
.forum-pin-icon {
    display: inline-block;
    margin-right: 4px;
    color: #6b7280;
    font-size: .9em;
    vertical-align: middle;
}
.forum-pin-icon { color: var(--hn-gold); }

.forum-locked-banner {
    display: flex;
    align-items: center;
    gap: .5rem;
    background: #f3f4f6;
    border: 1px solid #d1d5db;
    border-left: 4px solid #6b7280;
    color: #374151;
    padding: .6rem .85rem;
    border-radius: 4px;
    margin-bottom: 1rem;
    font-size: .88rem;
}
.forum-locked-banner__icon { font-size: 1.1rem; }

.btn-hn.btn-forum-lock       { background: #9ca3af; }
.btn-hn.btn-forum-lock:hover:not(:disabled) { background: #6b7280; }
.btn-hn.btn-forum-unlock     { background: #6366f1; }
.btn-hn.btn-forum-unlock:hover:not(:disabled) { background: #4f46e5; }
.btn-hn.btn-forum-pin        { background: var(--hn-gold); color: #2a1f00; }
.btn-hn.btn-forum-pin:hover:not(:disabled) { background: #b8870f; color: #fff; }
.btn-hn.btn-forum-unpin      { background: #6b7280; }
.btn-hn.btn-forum-unpin:hover:not(:disabled) { background: #4b5563; }
.btn-hn.btn-forum-pin-order  { background: var(--hn-gold); color: #2a1f00; }
.btn-hn.btn-forum-pin-order:hover:not(:disabled) { background: #b8870f; color: #fff; }

/* ── Pinned-topic ordering (staff only) ───────────────────────── */
/* Wrapped in <details> so the input is collapsed by default. This
   prevents mobile browsers (iOS Safari especially) from auto-focusing
   a number input near the top of the page on load, which triggers the
   page-zoom-on-focus behaviour that surprised mods who only opened the
   topic to read it. The input is also rendered at 16px so even when
   intentionally expanded it doesn't trigger iOS's zoom-on-focus rule. */
.forum-pin-order-details {
    margin: 0;
}
.forum-pin-order-summary {
    list-style: none;
    cursor: pointer;
}
.forum-pin-order-summary::-webkit-details-marker { display: none; }
.forum-pin-order-summary::marker               { content: ''; }
.forum-pin-order-details[open] .forum-pin-order-summary {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}
.forum-pin-order-form {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem;
    background: rgba(232, 182, 36, .12);
    border: 1px solid rgba(232, 182, 36, .55);
    border-top: none;
    border-radius: 0 0 4px 4px;
    padding: 4px 6px;
    margin-top: -1px;
}
.forum-pin-order-label {
    font-size: .78rem;
    color: #6a5430;
    font-weight: 600;
    margin: 0;
}
.forum-pin-order-input {
    width: 3.6rem;
    padding: 2px 4px;
    border: 1px solid #d1d5db;
    border-radius: 3px;
    font-size: 16px; /* keep at 16px so iOS Safari does NOT auto-zoom on focus */
    text-align: center;
    background: #fff;
}
.forum-pin-order-badge {
    display: inline-block;
    margin-right: 4px;
    padding: 1px 6px;
    background: rgba(232, 182, 36, .25);
    border: 1px solid rgba(232, 182, 36, .55);
    border-radius: 8px;
    font-size: .7rem;
    font-weight: 600;
    color: #6a5430;
    vertical-align: middle;
}
.btn-hn.btn-forum-mod-on     { background: #6366f1; }
.btn-hn.btn-forum-mod-on:hover:not(:disabled) { background: #4f46e5; }
.btn-hn.btn-forum-mod-off    { background: #9ca3af; }
.btn-hn.btn-forum-mod-off:hover:not(:disabled) { background: #6b7280; }
.btn-hn.btn-forum-follow     { background: #2d5016; }
.btn-hn.btn-forum-follow:hover:not(:disabled) { background: #3a6a1f; }
.btn-hn.btn-forum-following  { background: #6b7280; }
.btn-hn.btn-forum-following:hover:not(:disabled) { background: #4b5563; }
.btn-hn.btn-feedback-follow     { background: #2d5016; }
.btn-hn.btn-feedback-follow:hover:not(:disabled) { background: #3a6a1f; }
.btn-hn.btn-feedback-following  { background: #6b7280; }
.btn-hn.btn-feedback-following:hover:not(:disabled) { background: #4b5563; }
.forum-reply-count,
.forum-view-count {
    background: #ddd;
    border-radius: 10px;
    padding: 1px 7px;
    font-size: .72rem;
    color: #555;
}
.forum-topic-link {
    color: inherit;
    text-decoration: none;
    font-weight: 400;
}
.forum-topic-link:hover { color: var(--hn-green); text-decoration: underline; }
.forum-topic-unread .forum-topic-link { font-weight: 600; }
.forum-unread-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--hn-green);
    margin-right: 6px;
    vertical-align: middle;
}
.forum-date    { font-size: .78rem; color: #888; }
.forum-date-sm { font-size: .75rem; color: #999; text-decoration: none; }
a.forum-date-sm:hover { color: #666; text-decoration: underline; }
.forum-edit-meta {
    font-size: .72rem;
    color: #999;
    margin-left: .35rem;
    font-style: italic;
}

/* Trailing edge of .forum-post-meta. Groups the date (or the edit
   marker once a post has been edited) so the meta row's
   space-between flex puts the author block on the left and this
   trail on the right - never a date stranded in the middle. */
.forum-meta-trail {
    display: inline-flex;
    align-items: baseline;
    gap: .25rem;
}
.forum-meta-trail > .forum-edit-meta {
    margin-left: 0;
}
.forum-last-activity {
    display: flex;
    flex-direction: column;
    gap: 1px;
    text-decoration: none;
    color: inherit;
}
.forum-last-activity:hover { color: var(--hn-green); }
.forum-last-activity:hover .forum-last-replier { text-decoration: underline; }
.forum-last-replier { font-size: .8rem; font-weight: 500; }
.forum-last-date    { font-size: .72rem; color: #999; }
.forum-empty   { padding: .75rem 1rem; color: #888; }

/* Post layout: avatar + content */
.forum-post {
    display: flex;
    gap: .85rem;
    align-items: flex-start;
}
.forum-avatar {
    width: 96px;
    height: 96px;
    border-radius: 6px;
    object-fit: cover;
    border: 1px solid var(--hn-border);
    flex-shrink: 0;
}
.forum-avatar-sm {
    width: 80px;
    height: 80px;
    border-radius: 5px;
    object-fit: cover;
    border: 1px solid var(--hn-border);
    flex-shrink: 0;
}
.forum-post-content { flex: 1; min-width: 0; }
.forum-post-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: .4rem;
    flex-wrap: wrap;
    gap: .3rem;
}
/* No font-weight here on purpose. Plain users render at the document
   default weight; staff overrides land via the inner span produced by
   User::displayName() (.hn-admin-name / .hn-mod-name / .hn-helpdesk-name)
   which sets font-weight: 700 on top of this. */
.forum-author     { }
.forum-author-sm  { font-size: .85rem; }
.forum-post-body {
    word-break: break-word;
    line-height: 1.6;
    font-size: .9rem;
}
.forum-post-body p { margin: 0; }
.forum-post-body p:empty,
.forum-post-body p:has(> br:only-child) { line-height: .5; }
.forum-post-body h1, .forum-post-body h2, .forum-post-body h3 { margin: .3em 0 .1em; }
.forum-post-body ol,
.forum-post-body ul { margin: 0; padding-left: 1.5em; }
.forum-post-body img {
    max-width: 100%;
    max-height: 400px;
    display: block;
    border-radius: 4px;
    margin: .4em 0;
}
.forum-post-body a.forum-image-link {
    display: inline-block;
    line-height: 0;
}
.forum-post-body a.forum-image-link img { cursor: zoom-in; }
.forum-post-body blockquote {
    border-left: 3px solid var(--hn-border);
    margin: .4em 0;
    padding: .3em .8em;
    color: #555;
}
.forum-post-body-sm {
    word-break: break-word;
    line-height: 1.5;
    font-size: .88rem;
}
.forum-post-body-sm p { margin: 0; }
.forum-post-body-sm p:empty,
.forum-post-body-sm p:has(> br:only-child) { line-height: .5; }
.forum-post-body-sm h1, .forum-post-body-sm h2, .forum-post-body-sm h3 { margin: .3em 0 .1em; }
.forum-post-body-sm ol,
.forum-post-body-sm ul { margin: 0; padding-left: 1.5em; }
.forum-post-body-sm img {
    max-width: 100%;
    max-height: 400px;
    display: block;
    border-radius: 4px;
    margin: .4em 0;
}
.forum-post-body-sm a.forum-image-link {
    display: inline-block;
    line-height: 0;
}
.forum-post-body-sm a.forum-image-link img { cursor: zoom-in; }
.forum-post-body-sm blockquote {
    border-left: 3px solid var(--hn-border);
    margin: .4em 0;
    padding: .3em .8em;
    color: #555;
}
.msg-body p { margin: 0; }
.msg-body p:empty,
.msg-body p:has(> br:only-child) { line-height: .5; }
.msg-body h1, .msg-body h2, .msg-body h3 { margin: .3em 0 .1em; }
.msg-body ol,
.msg-body ul { margin: 0; padding-left: 1.5em; }
.msg-body img {
    max-width: 240px;
    max-height: 180px;
    height: auto;
    display: block;
    border-radius: 4px;
    margin: .4em 0;
}
.msg-body a.hn-image-link {
    display: inline-block;
    line-height: 0;
}
.msg-body a.hn-image-link img { cursor: zoom-in; }
.msg-body blockquote {
    border-left: 3px solid var(--hn-border);
    margin: .4em 0;
    padding: .3em .8em;
    color: #555;
}
/* ── PM thread bubbles ──────────────────────────────────────
   The conversation view at /messages/{id} stacks one .msg-bubble per
   message, with a colour modifier per author/state. Layout (gap, padding,
   flex direction) lives on .msg-thread-body so a bubble injected by the
   live-replies poll lands in the same column without inheriting any
   sibling-only spacing. */
.msg-thread-body {
    display: flex;
    flex-direction: column;
    gap: .75rem;
    padding: .75rem 1rem;
}
.msg-thread-collapsed {
    flex-direction: column;
    gap: .75rem;
}
.msg-thread-tail {
    display: none;
}
.msg-collapse-toggle {
    background: rgba(0, 0, 0, .06);
    border: 1px solid #ddd;
    border-radius: 6px;
    padding: 6px 16px;
    cursor: pointer;
}
.msg-bubble {
    border: 1px solid;
    border-radius: 8px;
    padding: .75rem 1rem;
}
.msg-bubble--theirs   { background: #f8f6f0; border-color: #e0d8c8; }
.msg-bubble--mine     { background: #eef6ee; border-color: #c5ddc5; }
.msg-bubble--system   { background: #f0f4ff; border-color: #c7d2fe; }
.msg-bubble--pending  { background: #fffbeb; border-color: #fde68a; }
.msg-bubble--rejected { background: #fef2f2; border-color: #fecaca; }
.msg-bubble-meta { margin-bottom: .45rem; }
.msg-bubble-author--mine  { color: #2d6b2d; }
.msg-bubble-author--other { color: #555; }
.msg-bubble-author--system { color: #4338ca; }
.msg-bubble-mod-pill {
    font-weight: 700;
    border-radius: 4px;
    color: #fff;
    padding: 1px 6px;
    font-size: .68rem;
}
.msg-bubble-mod-pill--pending  { background: #f59e0b; }
.msg-bubble-mod-pill--rejected { background: #dc2626; }
.msg-bubble-time { color: #999; }
.msg-bubble-del-btn {
    background: none;
    border: none;
    cursor: pointer;
    color: #bbb;
    padding: 0 2px;
    line-height: 1;
}
.msg-bubble-del-btn:hover { color: #c0392b; }
.msg-body { font-size: .92rem; line-height: 1.65; word-break: break-word; }
.msg-body-link {
    color: #2d5016;
    text-decoration: underline;
}
.msg-reply-card { margin-top: .75rem; }
.msg-reply-actions { margin-top: .6rem; }

/* "Nieuwe berichten ontvangen" banner: docked above the reply form on
   /messages/{id} when the 15s poll detects a reply that arrived after
   the page rendered. Stays full-width like the surrounding cards so it
   reads as part of the conversation column, never as a floating tooltip. */
.msg-new-banner {
    display: flex;
    align-items: center;
    gap: .55rem;
    width: 100%;
    margin-top: .75rem;
    padding: .65rem 1rem;
    background: #fffbe5;
    border: 1px solid #f0c419;
    border-radius: 8px;
    color: #6a5430;
    font: inherit;
    font-size: .9rem;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
}
.msg-new-banner:hover { background: #fff5c2; border-color: #d4a90f; }
.msg-new-banner[hidden] { display: none; }
.msg-new-banner-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.4rem;
    height: 1.4rem;
    background: #f0c419;
    color: #fff;
    border-radius: 50%;
    font-size: .9rem;
    line-height: 1;
    flex-shrink: 0;
}
.msg-new-banner-text { flex: 1; }

.msg-view-header {
    display: flex;
    align-items: center;
    min-width: 0;
    gap: .5rem;
}
.msg-view-header a {
    color: #fff;
    text-decoration: underline;
}
.msg-view-header a:hover {
    color: #fff;
    opacity: .85;
}
.msg-view-header .hn-deleted-user {
    color: #f0f0f0;
}
.msg-view-subject {
    flex: 1;
    min-width: 0;
    white-space: normal;
    overflow-wrap: anywhere;
}
/* Participant sub-bar: lighter strip directly under the green header
   that lists the two thread participants. Moved out of the green
   header so the dark-red admin name (.hn-admin-name) reads cleanly,
   and so a long pair of usernames can wrap to a second line on
   mobile without crowding the action buttons. */
.msg-view-subheader {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .35rem .55rem;
    background: #faf6ec;
    border-bottom: 1px solid var(--hn-border);
    padding: .35rem .75rem;
    font-size: .82rem;
    line-height: 1.4;
}
.msg-view-subheader-label {
    color: #6a5430;
    font-weight: 600;
}
.msg-view-subheader-names {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .35rem;
    min-width: 0;
}
.msg-view-subheader-sep {
    color: #999;
}
.msg-view-actions {
    flex-shrink: 0;
}
.msg-view-header-btn {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    background: rgba(0, 0, 0, .15);
    border: none;
    color: #fff;
    padding: 4px 10px;
    min-height: 1.85rem;
    line-height: 1.2;
    cursor: pointer;
}
.msg-view-header-btn:hover {
    background: rgba(0, 0, 0, .25);
}
/* Icon-only variant used by the prev/next thread arrows in the header.
   No label, square-ish footprint so the two arrows sit flush next to
   each other on the right side of the green bar. The text-decoration
   reset overrides the .msg-view-header a underline so the chevrons
   render as clean glyphs instead of underlined characters. */
.msg-view-header-btn--icon {
    gap: 0;
    padding: 4px 9px;
    min-width: 1.85rem;
    justify-content: center;
    font-size: .95rem;
    text-decoration: none;
}
.msg-view-header a.msg-view-header-btn--icon,
.msg-view-header a.msg-view-header-btn--icon:hover {
    text-decoration: none;
}
.msg-view-header-btn[disabled],
.msg-view-header-btn--icon[disabled] {
    opacity: .35;
    cursor: not-allowed;
}
.msg-view-header-btn[disabled]:hover,
.msg-view-header-btn--icon[disabled]:hover {
    background: rgba(0, 0, 0, .15);
}
/* Action group docked to the right of the participants subrow (the
   "Tussen" strip). Pushes itself to the far right via auto margin so
   long usernames in the middle still wrap cleanly under it on mobile. */
.msg-view-subheader-actions {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    flex-shrink: 0;
}
.inbox-row-save-btn,
.inbox-row-delete-btn {
    background: none;
    border: none;
    cursor: pointer;
    padding: 2px 5px;
    line-height: 1;
    font-size: 1.25rem;
}
.inbox-row-save-btn         { color: #f5c518; }
.inbox-row-save-btn:hover   { color: #e0a800; }
.inbox-row-delete-btn       { color: #bbb; }
.inbox-row-delete-btn:hover { color: #c0392b; }
.msg-save-icon {
    color: #f5c518;
    font-size: 1.05rem;
    vertical-align: -1px;
}
.forum-post-actions {
    margin-top: .5rem;
    display: flex;
    gap: .5rem;
    align-items: center;
}
.forum-edit-link {
    font-size: .78rem;
    color: #888;
    text-decoration: none;
}
.forum-edit-link:hover { color: var(--hn-green); text-decoration: underline; }
.forum-delete-form { display: inline; }
.forum-delete-link {
    font-size: .78rem;
    color: #c0392b;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    font-family: inherit;
}
.forum-delete-link:hover { color: #e74c3c; text-decoration: underline; }

/* Reactions */
.forum-reactions {
    display: flex;
    gap: .3rem;
    margin-top: .4rem;
    justify-content: flex-end;
    flex-wrap: wrap;
}
.forum-reaction-btn {
    display: inline-flex;
    align-items: center;
    gap: .2rem;
    padding: 1px 6px;
    border: none;
    border-radius: 8px;
    background: transparent;
    cursor: pointer;
    font-size: .75rem;
    font-family: inherit;
    line-height: 1.4;
    transition: background .15s, opacity .15s;
    color: #aaa;
    opacity: .6;
}
.forum-reaction-btn:hover {
    background: rgba(0,0,0,.04);
    opacity: 1;
}
.forum-reaction-btn.forum-reaction--active {
    background: transparent;
    color: var(--hn-green);
    opacity: 1;
}
.forum-reaction-emoji {
    font-size: .78rem;
    line-height: 1;
}
.forum-reaction-count {
    font-size: .68rem;
    font-weight: 600;
    min-width: .7em;
    text-align: center;
}

/* Staff-only "bugfixed" reaction (admins + moderators).
   Distinct color so it doesn't blend in with the normal user reactions
   and so its active state visually reads as "marked as fixed". */
.forum-reaction-btn--bugfixed {
    color: #8a5f2e;
}
.forum-reaction-btn--bugfixed:hover {
    background: rgba(140, 95, 46, .08);
}
.forum-reaction-btn--bugfixed.forum-reaction--active {
    background: #d4edda;
    color: #1e6b34;
    opacity: 1;
}

/* ── Site nieuws ───────────────────────────────────────────── */
/* The timestamp pill sits in the dark .hn-card-header bar, so it
   uses a slightly faded white instead of pure white to recede next
   to the bold title. */
.news-date {
    color: rgba(255, 255, 255, .75);
    font-weight: 400;
}
/* The news body renders the same forum-flavoured HTML as forum posts
   and private messages: headings, lists, alignment, indents, inline
   images and blockquotes. Per-element rules mirror .forum-post-body /
   .msg-body so a Quill-authored paragraph looks the same across the
   site. The color override keeps news links from inheriting the green
   used elsewhere in the card. */
.news-body {
    color: #444;
    line-height: 1.6;
    word-break: break-word;
}
.news-body p { margin: 0; }
.news-body p:empty,
.news-body p:has(> br:only-child) { line-height: .5; }
.news-body h1, .news-body h2, .news-body h3 { margin: .3em 0 .1em; }
.news-body ol,
.news-body ul { margin: 0; padding-left: 1.5em; }
.news-body img {
    max-width: 100%;
    max-height: 400px;
    display: block;
    border-radius: 4px;
    margin: .4em 0;
}
.news-body a.hn-image-link {
    display: inline-block;
    line-height: 0;
}
.news-body a.hn-image-link img { cursor: zoom-in; }
.news-body blockquote {
    border-left: 3px solid var(--hn-border);
    margin: .4em 0;
    padding: .3em .8em;
    color: #555;
}

/* Reply row */
.forum-reply { padding: .65rem .85rem; }
.forum-reply + .forum-reply { border-top: 1px solid var(--hn-border); }
.forum-reply--pending { background: #fffde7; }
.forum-reply--deleted { background: #f3f4f6; opacity: .85; }
.forum-post-body-deleted {
    font-style: italic;
    color: #6b7280;
}

/* Universal anchor-jump offset: account for sticky banner + header so #foo
   targets (forum replies, uitleg sections, anything with an id) don't
   land underneath the fixed top bars. */
html {
    scroll-padding-top: calc(var(--hn-banner-h) + var(--hn-header-h) + .5rem);
}

/* Pagination */
.forum-pagination {
    display: flex;
    justify-content: center;
    gap: .5rem;
    margin-top: .75rem;
    margin-bottom: .75rem;
}
.forum-page-link {
    padding: 4px 10px;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    font-size: .82rem;
    text-decoration: none;
    color: #555;
}
.forum-page-link:hover { text-decoration: none; background: #f0e8d6; }
.forum-page-link--active {
    background: var(--hn-green);
    color: #fff;
    border-color: var(--hn-green);
}
.forum-page-link--active:hover { background: var(--hn-green-dark); color: #fff; }
.forum-page-ellipsis { padding: 4px 4px; font-size: .82rem; color: #999; user-select: none; }
.forum-page-link--jump { font-weight: 700; letter-spacing: -1px; color: #888; }
.forum-page-link--jump:hover { background: var(--hn-green); color: #fff; border-color: var(--hn-green); }

.forum-post-number {
    font-size: .75rem;
    color: #999;
    margin-right: .35rem;
}

/* Create / Edit form */
.forum-rules {
    background: #f0ebe1;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    padding: .6rem .85rem;
    margin-bottom: 1rem;
    font-size: .82rem;
    color: #555;
}
.forum-form-group { margin-bottom: .75rem; }
.forum-form-label {
    font-weight: 600;
    font-size: .88rem;
    display: block;
    margin-bottom: .3rem;
}
.forum-input,
.forum-textarea {
    width: 100%;
    padding: .45rem .6rem;
    border: 1px solid var(--hn-border);
    border-radius: 5px;
    font-size: .88rem;
    box-sizing: border-box;
    font-family: inherit;
}
.forum-textarea { resize: vertical; }

.forum-editor-wrap { margin-bottom: .3rem; }
.hn-card:has(.forum-editor-wrap),
.hn-card:has([data-user-autocomplete]) { overflow: visible; }
.hn-card:has(.forum-editor-wrap) > .hn-card-header { border-radius: 5px 5px 0 0; }
.forum-editor-wrap .ql-tooltip {
    left: 0 !important;
    z-index: 10;
}
.forum-editor-wrap .ql-toolbar {
    border-radius: 5px 5px 0 0;
    border-color: var(--hn-border);
    background: #faf9f5;
}
.forum-editor-wrap .ql-container {
    border-radius: 0 0 5px 5px;
    border-color: var(--hn-border);
    font-family: inherit;
    font-size: .88rem;
}
.forum-editor-wrap .ql-editor {
    min-height: 120px;
    max-height: 400px;
    overflow-y: auto;
}
.forum-editor-wrap .ql-editor img {
    max-width: 100%;
    max-height: 300px;
}
.forum-img-status {
    padding: .35rem .6rem;
    font-size: .82rem;
    border: 1px solid var(--hn-border);
    border-top: 0;
    background: #faf9f5;
}
.forum-img-uploading { color: #666; }
.forum-img-error {
    color: #c0392b;
    background: #fdf0ef;
    border-color: #e6b0aa;
}
.forum-img-progress {
    margin-top: .35rem;
    height: 3px;
    background: rgba(0, 0, 0, .08);
    border-radius: 2px;
    overflow: hidden;
}
.forum-img-progress-bar {
    height: 100%;
    width: 0%;
    background: var(--hn-green);
    transition: width .15s ease-out;
}

/* @mention autocomplete dropdown */
.ql-mention-dropdown {
    position: absolute;
    z-index: 100;
    min-width: 160px;
    max-height: 200px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 5px;
    box-shadow: 0 4px 12px rgba(0,0,0,.12);
    padding: .2rem 0;
}
.ql-mention-item {
    padding: .4rem .7rem;
    cursor: pointer;
    font-size: .88rem;
    white-space: nowrap;
}
.ql-mention-item.ql-mention-active {
    background: var(--hn-green, #27ae60);
    color: #fff;
}

/* user-autocomplete dropdown (compose, etc.) */
[data-autocomplete-dropdown] {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-top: none;
    border-radius: 0 0 6px 6px;
    box-shadow: 0 4px 12px rgba(0,0,0,.1);
    z-index: 50;
    max-height: 200px;
    overflow-y: auto;
}
.hn-ac-item {
    padding: 8px 12px;
    cursor: pointer;
    font-size: .88rem;
    border-bottom: 1px solid #f0ebe3;
}
.hn-ac-item:last-child { border-bottom: none; }
.hn-ac-item:hover, .hn-ac-item.active { background: #f0ebe3; }
.hn-ac-empty {
    padding: 8px 12px;
    color: #999;
    font-size: .85rem;
}

.ql-emoji-picker {
    position: absolute;
    z-index: 100;
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    padding: .4rem;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 5px;
    box-shadow: 0 4px 12px rgba(0,0,0,.12);
    max-width: 320px;
}
.ql-emoji-pick {
    border: none;
    background: none;
    font-size: 1.3rem;
    width: 32px;
    height: 32px;
    cursor: pointer;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    line-height: 1;
}
.ql-emoji-pick:hover {
    background: #f0f0f0;
}

/* character-limit counter */
.ql-char-counter {
    text-align: right;
    font-size: .78rem;
    color: #999;
    padding: 4px 8px 0;
    transition: color .2s, font-weight .2s;
}
.ql-char-counter.ql-char-warn {
    color: #b45309;
    font-weight: 600;
}
.ql-char-counter.ql-char-over {
    color: #dc2626;
    font-weight: 700;
}
@keyframes ql-char-shake {
    0%, 100% { transform: translateX(0); }
    20%, 60% { transform: translateX(-4px); }
    40%, 80% { transform: translateX(4px); }
}
.ql-char-shake {
    animation: ql-char-shake .4s ease;
}

@media (max-width: 500px) {
    .stable-info-grid { grid-template-columns: 1fr !important; }
}

/* stable description preview */
.stable-desc-preview .ql-editor-content {
    line-height: 1.5;
    font-size: .88rem;
    word-wrap: break-word;
}
.stable-desc-preview .ql-editor-content p { margin: 0; }
.stable-desc-preview .ql-editor-content p:empty,
.stable-desc-preview .ql-editor-content p:has(> br:only-child) { line-height: .5; }
.stable-desc-preview .ql-editor-content h1, .stable-desc-preview .ql-editor-content h2, .stable-desc-preview .ql-editor-content h3 { margin: .3em 0 .1em; }
.stable-desc-preview .ql-editor-content ol,
.stable-desc-preview .ql-editor-content ul { margin: 0; padding-left: 1.5em; }
.stable-desc-preview .ql-editor-content blockquote {
    border-left: 3px solid var(--hn-border);
    margin: .4em 0;
    padding: .3em .8em;
    color: #555;
}
.stable-desc-preview .ql-editor-content img {
    max-width: 100%;
    max-height: 300px;
    border-radius: 4px;
}
.stable-desc-preview .ql-editor-content iframe,
.forum-editor-wrap .ql-editor iframe,
.ql-editor-content iframe {
    max-width: 100%;
    width: 480px;
    height: 270px;
    border: none;
    border-radius: 4px;
    margin: .5rem 0;
}

@media (max-width: 500px) {
    .forum-avatar    { width: 56px; height: 56px; }
    .forum-avatar-sm { width: 48px; height: 48px; }
    .forum-post { gap: .5rem; }
}

/* ── Quill formatting classes (extended toolbar) ──────────────────────────
   The Quill editor emits ql-align-*, ql-indent-* and ql-font-* classes on
   block elements when those features are enabled. The bundled
   quill.snow.css scopes its rules to `.ql-editor`, so when we render the
   saved HTML elsewhere (stable description preview, profile bio, …) we
   need to repeat the same visual rules here. Strikethrough uses the
   semantic <s> tag which already renders correctly in browsers. */
.forum-post-body .ql-align-center,
.forum-post-body-sm .ql-align-center,
.msg-body .ql-align-center,
.news-body .ql-align-center,
.stable-desc-preview .ql-editor-content .ql-align-center,
.ql-editor-content .ql-align-center { text-align: center; }

.forum-post-body .ql-align-right,
.forum-post-body-sm .ql-align-right,
.msg-body .ql-align-right,
.news-body .ql-align-right,
.stable-desc-preview .ql-editor-content .ql-align-right,
.ql-editor-content .ql-align-right { text-align: right; }

/* Centered / right-aligned list items: pull the bullet (or number) into
   the inline flow so text-align actually moves it. Default browser
   rendering puts the marker `outside`, sitting in the parent <ul>/<ol>
   padding-left, which leaves it pinned to the left while the text moves.
   In the live editor Quill draws the marker via a ::before pseudo on the
   <li> (scoped to .ql-editor), so the issue only shows up in the saved
   HTML rendered through .forum-post-body and friends. */
.forum-post-body   ul:has(> li.ql-align-center),
.forum-post-body   ol:has(> li.ql-align-center),
.forum-post-body-sm ul:has(> li.ql-align-center),
.forum-post-body-sm ol:has(> li.ql-align-center),
.msg-body          ul:has(> li.ql-align-center),
.msg-body          ol:has(> li.ql-align-center),
.news-body         ul:has(> li.ql-align-center),
.news-body         ol:has(> li.ql-align-center),
.stable-desc-preview .ql-editor-content ul:has(> li.ql-align-center),
.stable-desc-preview .ql-editor-content ol:has(> li.ql-align-center),
.ql-editor-content ul:has(> li.ql-align-center),
.ql-editor-content ol:has(> li.ql-align-center),
.forum-post-body   ul:has(> li.ql-align-right),
.forum-post-body   ol:has(> li.ql-align-right),
.forum-post-body-sm ul:has(> li.ql-align-right),
.forum-post-body-sm ol:has(> li.ql-align-right),
.msg-body          ul:has(> li.ql-align-right),
.msg-body          ol:has(> li.ql-align-right),
.news-body         ul:has(> li.ql-align-right),
.news-body         ol:has(> li.ql-align-right),
.stable-desc-preview .ql-editor-content ul:has(> li.ql-align-right),
.stable-desc-preview .ql-editor-content ol:has(> li.ql-align-right),
.ql-editor-content ul:has(> li.ql-align-right),
.ql-editor-content ol:has(> li.ql-align-right) { padding-left: 0; }

.forum-post-body   li.ql-align-center,
.forum-post-body-sm li.ql-align-center,
.msg-body          li.ql-align-center,
.news-body         li.ql-align-center,
.stable-desc-preview .ql-editor-content li.ql-align-center,
.ql-editor-content li.ql-align-center,
.forum-post-body   li.ql-align-right,
.forum-post-body-sm li.ql-align-right,
.msg-body          li.ql-align-right,
.news-body         li.ql-align-right,
.stable-desc-preview .ql-editor-content li.ql-align-right,
.ql-editor-content li.ql-align-right { list-style-position: inside; }

.forum-post-body .ql-align-justify,
.forum-post-body-sm .ql-align-justify,
.msg-body .ql-align-justify,
.news-body .ql-align-justify,
.stable-desc-preview .ql-editor-content .ql-align-justify,
.ql-editor-content .ql-align-justify { text-align: justify; }

/* Images inserted via Quill end up as <p class="ql-align-*"><img></p>.
   In forum / message / profile-bio contexts the <img> is forced to
   display:block (so a tall image pushes following text below it
   instead of overlapping line-height). text-align on the parent
   paragraph does not center a block-level child, so we re-center the
   image itself with auto side-margins. Vertical margin from the base
   <container> img rule stays untouched. */
.forum-post-body .ql-align-center img,
.forum-post-body-sm .ql-align-center img,
.msg-body .ql-align-center img,
.news-body .ql-align-center img,
.stable-desc-preview .ql-editor-content .ql-align-center img,
.ql-editor-content .ql-align-center img {
    margin-left: auto;
    margin-right: auto;
}
.forum-post-body .ql-align-right img,
.forum-post-body-sm .ql-align-right img,
.msg-body .ql-align-right img,
.news-body .ql-align-right img,
.stable-desc-preview .ql-editor-content .ql-align-right img,
.ql-editor-content .ql-align-right img {
    margin-left: auto;
    margin-right: 0;
}

.forum-post-body .ql-indent-1,
.forum-post-body-sm .ql-indent-1,
.msg-body .ql-indent-1,
.news-body .ql-indent-1,
.stable-desc-preview .ql-editor-content .ql-indent-1,
.ql-editor-content .ql-indent-1 { padding-left: 3em; }
.forum-post-body .ql-indent-2,
.forum-post-body-sm .ql-indent-2,
.msg-body .ql-indent-2,
.news-body .ql-indent-2,
.stable-desc-preview .ql-editor-content .ql-indent-2,
.ql-editor-content .ql-indent-2 { padding-left: 6em; }
.forum-post-body .ql-indent-3,
.forum-post-body-sm .ql-indent-3,
.msg-body .ql-indent-3,
.news-body .ql-indent-3,
.stable-desc-preview .ql-editor-content .ql-indent-3,
.ql-editor-content .ql-indent-3 { padding-left: 9em; }
.forum-post-body .ql-indent-4,
.forum-post-body-sm .ql-indent-4,
.msg-body .ql-indent-4,
.news-body .ql-indent-4,
.stable-desc-preview .ql-editor-content .ql-indent-4,
.ql-editor-content .ql-indent-4 { padding-left: 12em; }
.forum-post-body .ql-indent-5,
.forum-post-body-sm .ql-indent-5,
.msg-body .ql-indent-5,
.news-body .ql-indent-5,
.stable-desc-preview .ql-editor-content .ql-indent-5,
.ql-editor-content .ql-indent-5 { padding-left: 15em; }
.forum-post-body .ql-indent-6,
.forum-post-body-sm .ql-indent-6,
.msg-body .ql-indent-6,
.news-body .ql-indent-6,
.stable-desc-preview .ql-editor-content .ql-indent-6,
.ql-editor-content .ql-indent-6 { padding-left: 18em; }
.forum-post-body .ql-indent-7,
.forum-post-body-sm .ql-indent-7,
.msg-body .ql-indent-7,
.news-body .ql-indent-7,
.stable-desc-preview .ql-editor-content .ql-indent-7,
.ql-editor-content .ql-indent-7 { padding-left: 21em; }
.forum-post-body .ql-indent-8,
.forum-post-body-sm .ql-indent-8,
.msg-body .ql-indent-8,
.news-body .ql-indent-8,
.stable-desc-preview .ql-editor-content .ql-indent-8,
.ql-editor-content .ql-indent-8 { padding-left: 24em; }

/* Font picker entries: keep the same families used by the editor toolbar
   below so the saved HTML matches what the user saw while typing. */
.ql-font-sans-serif { font-family: Helvetica, Arial, sans-serif; }
.ql-font-serif      { font-family: Georgia, "Times New Roman", serif; }
.ql-font-monospace  { font-family: "Courier New", Courier, monospace; }
.ql-font-georgia    { font-family: Georgia, "Times New Roman", serif; }
.ql-font-comic      { font-family: "Comic Sans MS", "Comic Sans", cursive; }

/* Toolbar dropdown labels for the font picker. Quill renders the picker
   as a <span class="ql-picker-label" data-value="..."> and shows the
   data-value via a CSS ::before. We need a label per whitelist entry
   plus a matching font-family so the preview in the dropdown looks right. */
.ql-toolbar .ql-font .ql-picker-label[data-value="sans-serif"]::before,
.ql-toolbar .ql-font .ql-picker-item[data-value="sans-serif"]::before { content: "Standaard"; font-family: Helvetica, Arial, sans-serif; }
.ql-toolbar .ql-font .ql-picker-label[data-value="serif"]::before,
.ql-toolbar .ql-font .ql-picker-item[data-value="serif"]::before { content: "Serif"; font-family: Georgia, "Times New Roman", serif; }
.ql-toolbar .ql-font .ql-picker-label[data-value="monospace"]::before,
.ql-toolbar .ql-font .ql-picker-item[data-value="monospace"]::before { content: "Monospace"; font-family: "Courier New", Courier, monospace; }
.ql-toolbar .ql-font .ql-picker-label[data-value="georgia"]::before,
.ql-toolbar .ql-font .ql-picker-item[data-value="georgia"]::before { content: "Georgia"; font-family: Georgia, "Times New Roman", serif; }
.ql-toolbar .ql-font .ql-picker-label[data-value="comic"]::before,
.ql-toolbar .ql-font .ql-picker-item[data-value="comic"]::before { content: "Comic Sans"; font-family: "Comic Sans MS", "Comic Sans", cursive; }

/* Same idea for the editor itself: when ql-font-* is applied to a span,
   render it with the matching family regardless of whether the editor
   was loaded with the snow theme stylesheet first. */
.ql-editor .ql-font-sans-serif { font-family: Helvetica, Arial, sans-serif; }
.ql-editor .ql-font-serif      { font-family: Georgia, "Times New Roman", serif; }
.ql-editor .ql-font-monospace  { font-family: "Courier New", Courier, monospace; }
.ql-editor .ql-font-georgia    { font-family: Georgia, "Times New Roman", serif; }
.ql-editor .ql-font-comic      { font-family: "Comic Sans MS", "Comic Sans", cursive; }

/* ── Races ─────────────────────────────────────────────────────────────── */

.race-period-cell {
    background: #f8f6f1;
    padding: 6px 12px;
    font-weight: 700;
    font-size: .82rem;
}
.race-period-cell[data-period="ochtend"] { color: #d4920a; border-bottom: 2px solid #d4920a; }
.race-period-cell[data-period="middag"]  { color: #2d7d46; border-bottom: 2px solid #2d7d46; }
.race-period-cell[data-period="avond"]   { color: #5c3d99; border-bottom: 2px solid #5c3d99; }
.race-period-cell[data-period="nacht"]   { color: #2a5a8c; border-bottom: 2px solid #2a5a8c; }

.race-period-toggle  { cursor: pointer; user-select: none; }
.race-period-chevron { display: inline-block; transition: transform .15s; margin-right: 4px; }
.race-period-count   { font-weight: 400; color: #999; font-size: .78rem; margin-left: 6px; }

.race-day-separator-tbody { border-top: 2px solid #d4c9b5; }
.race-day-separator-cell {
    background: #ece4d2;
    color: #6a5430;
    font-weight: 700;
    font-size: .85rem;
    letter-spacing: .05em;
    text-transform: uppercase;
    text-align: left;
    padding: 8px 12px;
    border-bottom: 1px solid #d4c9b5;
}

.race-picker-wrap  { margin-bottom: 1rem; }
.race-picker-label { font-size: .85rem; color: #555; display: block; margin-bottom: .35rem; }
.race-picker-select {
    padding: 6px 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    font-size: .9rem;
    max-width: 400px;
    width: 100%;
}

.race-cell-level   { font-size: .82rem; white-space: nowrap; }
.race-cell-time    { font-size: .82rem; color: #888; white-space: nowrap; }
.race-cell-time-sm { font-size: .78rem; color: #888; white-space: nowrap; vertical-align: middle; }
.race-cell-result  { font-size: .85rem; }
.race-cell-center  { text-align: center; }
.race-cell-mid     { vertical-align: middle; }

.race-number-tag { font-size: .73rem; color: #7b6b3a; font-weight: 600; }
.race-disc-taken { color: #888; }
.race-no-races   { padding: .5rem 0; color: #888; font-size: .88rem; }
.race-pick-hint  { color: #aaa; font-size: .85rem; }
.race-muted      { color: #888; font-size: .88rem; }
.race-my-horse   { color: #5a3e1b; font-weight: 600; }
.race-dash       { color: #ccc; }
.race-dash-light { color: #bbb; }

.race-pos-gold   { color: #d4a017; font-weight: 700; }
.race-pos-silver { color: #7b4f2e; font-weight: 700; }
.race-pos-bronze { color: #888;    font-weight: 700; }
.race-pos-dns    { color: #ccc;    font-weight: 700; }
.race-pos-other  { color: #333;    font-weight: 700; }

.race-score        { font-weight: 600; }
.race-score-faults { color: #c0392b; font-size: .78rem; }
.race-score-clean  { color: #2d5016; font-size: .78rem; }
.race-points-pos   { color: #2d5016; font-weight: 600; }

/* ── Blocked-horses diagnostic panel (race picker) ─────────────── */
.race-blocked {
    margin-top: .75rem;
    padding: .35rem .75rem;
    background: #fbf6ec;
    border: 1px solid #d8c79a;
    border-radius: 6px;
}
.race-blocked[open] {
    padding-bottom: .6rem;
}
.race-blocked-summary {
    cursor: pointer;
    font-size: .85rem;
    font-weight: 600;
    color: #5a3e1b;
    padding: .2rem 0;
    list-style: none;
}
.race-blocked-summary::-webkit-details-marker {
    display: none;
}
.race-blocked-summary::before {
    content: '▸';
    display: inline-block;
    width: .9rem;
    color: #a08756;
    transition: transform .15s ease;
}
.race-blocked[open] > .race-blocked-summary::before {
    transform: rotate(90deg);
}
.race-blocked-list {
    list-style: none;
    margin: .35rem 0 0;
    padding: 0;
}
.race-blocked-item {
    padding: .25rem 0;
    border-top: 1px dashed #e0d0a8;
}
.race-blocked-item:first-child {
    border-top: 0;
}
.race-blocked-horse {
    font-size: .88rem;
}
.race-blocked-reasons {
    margin: .15rem 0 .15rem 1.1rem;
    padding: 0;
    list-style: disc;
    font-size: .8rem;
    color: #6a5430;
}
.race-blocked-reasons li {
    margin: 0;
}
.race-blocked-group .race-blocked-horse {
    font-style: italic;
    color: #6a5430;
}

.race-money-free  { color: #2d5016; font-weight: 600; }
.race-money-prize { color: #2d5016; }
.race-money-pos   { color: #2d5016; }
.race-money-none  { color: #888; }
.race-winner-gold { color: #d4a017; }

/* Race-results group sections -------------------------------------------- */
.subrace-section + .subrace-section { margin-top: 1rem; }
.subrace-dns-section .hn-card-header { background: #888; }
.subrace-size-badge {
    background: rgba(0,0,0,.2);
    padding: 1px 8px;
    border-radius: 3px;
}
.subrace-row-winner { background: #fff9e6; }
.subrace-row-dns    { background: #f9f9f9; opacity: .7; }
.subrace-pos        { font-weight: 700; color: #333; }
.subrace-pos-1      { color: #d4a017; }
.subrace-pos-2      { color: #7b4f2e; }
.subrace-pos-3      { color: #888; }
.subrace-pos-dns    { color: #ccc; }
.subrace-dns-reason {
    display: inline-block;
    margin-left: .25rem;
    font-size: .75rem;
    font-weight: 500;
    color: #888;
    white-space: nowrap;
}
.subrace-empty      { color: #bbb; }
.subrace-faults-clean { color: #2d5016; font-weight: 600; }
.subrace-faults-bad   { color: #c0392b; }
.subrace-prize-paid { color: #2d5016; }
.subrace-prize-none { color: #888; }

.btn-hn-danger { background: #c0392b; border-color: #a93226; }

/* ── Training Page ───────────────────────────────────────────── */
.hn-card:has(.training-horse-bar) { overflow: visible; }
.hn-card:has(.training-horse-bar) > .hn-card-header { border-radius: 5px 5px 0 0; }
.training-horse-bar {
    display: flex;
    flex-wrap: nowrap;
    gap: .5rem;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 4px;
    margin-bottom: .75rem;
}
@media (max-width: 767px) {
    .training-horse-bar {
        flex-wrap: wrap;
    }
}
.hn-card-body > .training-horse-bar:only-child {
    padding: 6px 0;
    margin-bottom: 0;
}

/* Vertical orientation: opt-in via $pickerOrientation = 'vertical' on
   horse_picker_bar.php. Switches the bar to a column of full-width
   buttons that scrolls vertically when it overflows. Useful as a
   sidebar picker on wide layouts (the /inventory tack-loadout pane
   uses this to put the picker on the left of the per-horse card on
   desktop). The bounded height comes from --thb-vertical-max-height
   so callers can override per-context (set on a wrapping element).
   Auto-reverts to horizontal at <768px - vertical mode would otherwise
   eat the entire mobile viewport before the user reached the actual
   content below it. Callers that need strict vertical-always can
   override by re-setting flex-direction in their own scope. */
.training-horse-bar--vertical {
    --thb-vertical-max-height: 360px;  /* ~8 typical buttons before scroll */
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    overflow-x: hidden;
    overflow-y: auto;
    max-height: var(--thb-vertical-max-height);
    padding-bottom: 0;
    padding-right: 4px;       /* room for the scrollbar */
    margin-bottom: 0;          /* parent layout handles spacing */
}
/* Buttons in vertical mode: keep their pill shape but justify content
   to the start so a 30-horse list reads as a clean left-aligned list
   instead of having every name centred and feeling decorative. Allow
   long horse names ("Praying For Rain N Shade") to wrap onto a second
   line inside the button instead of being clipped against the narrow
   column - row heights become slightly uneven, which the vertical
   scroll handles cleanly. */
.training-horse-bar--vertical .training-horse-btn {
    justify-content: flex-start;
    white-space: normal;
    text-align: left;
    overflow-wrap: anywhere;
}
/* Section separator in vertical mode: flip from a vertical dashed left
   border to a horizontal dashed top border so it actually reads as a
   between-row divider when buttons are stacked. */
.training-horse-bar--vertical .training-horse-bar-sep {
    flex-basis: auto;
    width: auto;
    height: 0;
    border-left: 0;
    border-top: 1px dashed #c8b89a;
    margin: .25rem 0;
}
@media (max-width: 767px) {
    /* On phones, a vertical column eats the entire viewport before the
       user reaches the content below. Revert to a single-row horizontally
       scrolling strip - more compact than the default bar's wrap-to-N-rows
       behaviour, and closer in spirit to the desktop vertical scroll
       (one scrollable list of all horses, just rotated 90°). The
       justify-content + separator overrides are undone here so the row
       reads like the standard desktop picker. */
    .training-horse-bar--vertical {
        flex-direction: row;
        flex-wrap: nowrap;
        align-items: stretch;
        overflow-x: auto;
        overflow-y: hidden;
        max-height: none;
        padding-bottom: 4px;
        padding-right: 0;
        margin-bottom: .75rem;
    }
    .training-horse-bar--vertical .training-horse-btn {
        justify-content: center;
    }
    /* Restore the default desktop separator (vertical dashed left rule)
       since we are back to a single horizontally-scrolling row. */
    .training-horse-bar--vertical .training-horse-bar-sep {
        flex-basis: auto;
        width: 0;
        height: auto;
        border-top: 0;
        border-left: 1px dashed #c8b89a;
        margin: 0 .35rem;
    }
}
/* Group separator inside the horse picker bar. Used by /horses/{id} to
   visually segment buttons by hn_horse_groups (and a final "Verzorging"
   bucket for caring_for horses) so the bar mirrors the /stable table
   ordering. Currently renders label-less: just a thin dashed vertical
   rule between sections (desktop) or a dashed top rule across the row
   (mobile, where the picker wraps). The section label is still emitted
   onto data-section-label by the partial so we can flip it back on
   later via a single .training-horse-bar-sep::before content rule.
   Skipped entirely when only one section exists - see
   horse_picker_bar.php's $_pickerShowLabels guard - so the legacy
   single-group bar looks identical to the pre-section render. */
.training-horse-bar-sep {
    display: inline-block;
    flex: 0 0 auto;
    align-self: stretch;
    width: 0;
    padding: 0;
    margin: 0 .35rem;
    border-left: 1px dashed #c8b89a;
}
.training-horse-bar-sep:first-child {
    display: none;
}
@media (max-width: 767px) {
    /* When the bar wraps to multiple rows the dashed left rule between a
       wrapped-around button and the next section reads as a stray vertical
       line. Force the separator onto its own row and turn the rule
       horizontal so each group's buttons sit visually below the divider
       instead of next to a stray vertical sliver. */
    .training-horse-bar-sep {
        flex-basis: 100%;
        width: auto;
        height: 0;
        border-left: 0;
        border-top: 1px dashed #c8b89a;
        margin: .25rem 0;
    }
    .training-horse-bar-sep:first-child {
        display: none;
    }
}
.auto-switch-toggle { margin-left: auto; }
.auto-switch-label {
    font-size: .75rem;
    font-weight: 400;
    opacity: .85;
    margin-left: .4rem;
}

/* ========================================================================
   Horse picker button
   ------------------------------------------------------------------------
   Category is carried purely by the border colour:
     • green  - own horse (default)
     • slate  - own horse under an external caretaker  (.has-caretaker)
     • amber  - horse the user is caretaker of         (.is-caring-for)
   States layered on top:
     • .active            soft tint of the category colour as the
                          "selected" marker. The thicker category-coloured
                          border keeps telling you what kind of horse it is.
     • .thb-cd-badge      cooldown pill, painted in the button's category
                          accent colour.
     • .on-cooldown /
       .is-fully-trained  muted text; category border stays visible.
     • .has-caretaker-dim extra greying for pages where the owner cannot
                          act on the horse (e.g. /training).
     • .in-pasture        leading fence thumbnail + dashed border on
                          horses currently in the user's weiland. The
                          dashed stroke reads as pasture rails. Border
                          *colour* stays in its category (pasture is a
                          state, not a category) so a pastured caretaker
                          horse still reads as caretaker.
     • .in-pasture-dim    pair with .in-pasture on pages where pastured
                          horses can't be acted on. Same dimming idiom
                          as .has-caretaker-dim. Currently no view turns
                          this on (vet/hoefsmid don't actually block
                          pastured horses); kept for future use.
     • .is-current-page   "you are already here" - non-clickable, no grey.
     • .thb-tack-badge    /inventory only: tiny corner pill summarising
                          tack-loadout state. Three variants paired with
                          matching button-level classes:
                            --complete     + .is-tack-complete     (green ✓)
                            --race-ready   + .is-tack-race-ready   (green X/4,
                                                                   informative)
                            --race-blocked + .is-tack-race-blocked (red X/4,
                                                                   warm border
                                                                   tint - zadel
                                                                   or hoofdstel
                                                                   missing)
======================================================================== */
.training-horse-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    gap: .35rem;
    padding: 5px 13px;
    font-size: .85rem;
    font-weight: 600;
    background: #faf7f0;
    border: 2px solid #9ebd8f;
    border-radius: 6px;
    color: #333;
    text-decoration: none;
    cursor: pointer;
    transition: background .1s, box-shadow .1s, border-color .1s,
                color .1s, opacity .1s;
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
}
.training-horse-btn:hover {
    background: #f4efe4;
    border-color: var(--hn-green);
    color: #333;
    text-decoration: none;
}

/* Category borders (override the default green for the two variants).
   The blue is a muted slate so it sits next to the warm green and amber
   without shouting. */
.training-horse-btn.has-caretaker        { border-color: #7a94a8; }
.training-horse-btn.has-caretaker:hover  { border-color: #5a7a92; }
.training-horse-btn.is-caring-for        { border-color: #d99a40; }
.training-horse-btn.is-caring-for:hover  { border-color: #c27a1e; }

/* Busy/maxed: keep the category border, just mute the text. */
.training-horse-btn.on-cooldown,
.training-horse-btn.is-fully-trained { color: #999; }

/* Blocked: horse can't be trained from /training without a vet
   (race injury), hoefsmid (kreupel) or general care (sick) visit
   first. Same muted-text convention as is-fully-trained, with a soft
   warm tint so it reads as a "needs attention" state rather than a
   "you're done" state. The training submit handler already rejects
   these horses; sinking them to tier 2 keeps the auto-switch from
   cycling back to them every time a sibling finishes its cooldown. */
.training-horse-btn.is-blocked {
    color: #b07a4a;
    background: #fbf3e8;
}

/* Selected: soft tint in the category colour + a 6px left rail painted
   as a ::before overlay. We draw the rail as an absolutely-positioned
   pseudo (instead of inset box-shadow or border-left:4px) for two
   reasons: (1) no layout shift when toggling between horses, and
   (2) the rail completely covers the (sometimes dashed, for .in-pasture)
   left border, so we never get the browser's mitered "wedge" artifact
   where a solid edge meets a dashed edge at the corner. The pseudo
   matches the button's outer dimensions on the left side and rounds its
   top-left / bottom-left corners to the same 6px radius as the button,
   so the rail flows smoothly into the rounded corners. */
.training-horse-btn.active {
    background: #eef5e6;
    border-color: var(--hn-green);
}
.training-horse-btn.has-caretaker.active,
.training-horse-btn.has-caretaker.active:hover {
    background: #eef1f4;
    border-color: #5a7a92;
}
.training-horse-btn.is-caring-for.active,
.training-horse-btn.is-caring-for.active:hover {
    background: #fdf0e0;
    border-color: #a86a1a;
}
.training-horse-btn.active::before {
    content: '';
    position: absolute;
    top: -2px;
    bottom: -2px;
    left: -2px;
    width: 6px;
    background: var(--hn-green);
    border-top-left-radius: 6px;
    border-bottom-left-radius: 6px;
    pointer-events: none;
}
.training-horse-btn.has-caretaker.active::before { background: #5a7a92; }
.training-horse-btn.is-caring-for.active::before { background: #a86a1a; }
.training-horse-btn.breed-mare-ineligible.active::before { background: #a93226; }

/* Extra dim layer for /training: the owner can't train a horse under a
   care contract, so pair this with .has-caretaker to visually signal that. */
.training-horse-btn.has-caretaker-dim        { color: #888; opacity: .7; }
.training-horse-btn.has-caretaker-dim:hover  { opacity: .9; }
.training-horse-btn.has-caretaker-dim.active { opacity: 1; }

/* In pasture: orthogonal state, not a category. Switch the border to
   dashed so it reads as pasture rails while the *colour* of the border
   keeps signalling category (green/slate/amber). When the button is
   also selected, the .active rail pseudo above already covers the
   dashed left border with a solid stripe — no extra style overrides
   needed here. */
.training-horse-btn.in-pasture { border-style: dashed; }

/* Same dim convention as has-caretaker-dim, scoped to pasture. */
.training-horse-btn.in-pasture-dim        { color: #888; opacity: .7; }
.training-horse-btn.in-pasture-dim:hover  { opacity: .9; }
.training-horse-btn.in-pasture-dim.active { opacity: 1; }

/* Brown-fence thumbnail rendered inside the pasture button. The PNG
   ships at 72x40; we display it at the height of a button glyph so it
   sits on the baseline next to the name. */
.thb-pasture-icon {
    display: inline-block;
    width: 22px;
    height: 12px;
    object-fit: contain;
    flex-shrink: 0;
    -webkit-user-drag: none;
    user-select: none;
}
.training-horse-btn.in-pasture-dim .thb-pasture-icon { opacity: .85; }

/* Non-clickable "you are already on this page" state. */
.training-horse-btn.is-current-page {
    cursor: default;
    pointer-events: none;
}

/* "+N meer" / "Toon minder" pill at the end of the picker bar. Same height
   and rhythm as a horse button but visually distinct (dashed neutral border,
   no fill) so it never reads as a horse. */
.training-horse-btn-more {
    flex-shrink: 0;
    padding: 5px 13px;
    font-size: .85rem;
    font-weight: 600;
    background: transparent;
    border: 2px dashed #c4b89c;
    border-radius: 6px;
    color: var(--hn-brown);
    cursor: pointer;
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
    transition: background .1s, border-color .1s, color .1s;
}
.training-horse-btn-more:hover {
    background: #f4efe4;
    border-color: var(--hn-brown);
}
.training-horse-btn-more.is-expanded {
    border-style: solid;
    border-color: #cfc6b0;
    color: #777;
}

.caring-for-banner .hn-card-body { background: #fdf0e0; border-left: 3px solid #e67e22; }

/* Cooldown pill. Defaults to the green accent (own horse); the category
   rules below repaint it for caretaker/caring-for buttons. */
.thb-cd-badge {
    display: inline-block;
    background: var(--hn-green);
    color: #fff;
    font-size: .7rem;
    font-weight: 700;
    padding: 1px 6px;
    border-radius: 10px;
    min-width: 36px;
    text-align: center;
    transition: opacity .25s ease, width .3s ease .15s,
                min-width .3s ease .15s, padding .3s ease .15s,
                margin .3s ease .15s, background .1s;
    overflow: hidden;
    box-sizing: border-box;
}
.training-horse-btn.has-caretaker .thb-cd-badge { background: #5a7a92; }
.training-horse-btn.is-caring-for .thb-cd-badge { background: #a86a1a; }
.thb-cd-badge.thb-cd-done {
    opacity: 0;
    min-width: 0;
    width: 0;
    padding-left: 0;
    padding-right: 0;
    /* Collapse the parent's flex `gap` (.35rem) to the right of the
       badge during fade-out / fade-in. Without this the picker button
       would stay ~5-6px wider than its no-badge baseline whenever the
       badge is in its 0-width state — and would visibly stay that way
       in the brief window before the DOM-removal lands (or forever, if
       transitionend ever drops on the floor). margin-right is added to
       the same transition list below so it eases in/out alongside
       width/padding rather than jumping. */
    margin-right: -.35rem;
}

/* ── Tack-loadout badge (/inventory picker bar) ───────────────────
   Sits in the same corner slot as .thb-cd-badge but conveys equipment
   state instead of cooldown. Three variants, mapping to the in-game
   race-entry rule (Item::hasRequiredTack: zadel + hoofdstel both
   required):
     • --complete     green ✓     (all 4 tack slots filled)
     • --race-ready   green X/4   (zadel + hoofdstel ok, optional missing
                                   - informative, not race-blocking)
     • --race-blocked red   X/4   (zadel OR hoofdstel missing - cannot
                                   enter wedstrijden until equipped)
   Pastured horses and veulens get NO badge at all (suppressed in the
   partial), since their tack state is moot. */
.thb-tack-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 28px;
    height: 18px;
    padding: 0 6px;
    border-radius: 10px;
    font-size: .68rem;
    font-weight: 700;
    line-height: 1;
    color: #fff;
    font-variant-numeric: tabular-nums;
    box-sizing: border-box;
}
.thb-tack-badge--complete {
    background: var(--hn-green);
    min-width: 18px;
    padding: 0 5px;
}
.thb-tack-badge--race-ready {
    /* Same hn-green family as --complete so "you're race-ready" reads
       at a glance, just with the X/4 digits to communicate "not 100%
       kitted yet, but won't block you." */
    background: var(--hn-green);
}
.thb-tack-badge--race-blocked {
    /* Warm red so it reads as "needs attention" - the only state that
       actually stops the player from doing something (entering races).
       Distinct enough from the green that a colour-blind eye can still
       pick out the digit / glyph difference (X/4 in red vs X/4 in green
       vs ✓ in green). */
    background: #c0392b;
}

/* Subtle border tint on race-blocked buttons so the state reads even
   from a quick scan, before the eye finds the corner pill. The
   race-ready variant deliberately gets NO border tint - it's
   informative, not actionable, and tinting every "X/4" green button
   would drown out the actual red ones. Active/selected buttons keep
   their green/slate/amber rail via the existing .active rules - this
   only touches the resting border colour. */
.training-horse-btn.is-tack-race-blocked:not(.active) {
    border-color: #d88a8a;
    background: #fdf2f0;
}
.training-horse-btn.is-tack-race-blocked:not(.active):hover {
    background: #fbe6e2;
    border-color: #c0392b;
}

@keyframes hn-auto-select {
    0%   { outline-color: rgba(120,180,90,.6); outline-offset: 0; }
    40%  { outline-color: rgba(120,180,90,.35); outline-offset: 3px; }
    100% { outline-color: rgba(120,180,90,0); outline-offset: 5px; }
}
.training-horse-btn.auto-selected {
    outline: 2.5px solid rgba(120,180,90,.6);
    animation: hn-auto-select .6s ease-out forwards;
}
#training-content { transition: opacity .15s ease; }
.training-level-badge {
    margin-left: auto;
    font-size: .75rem;
    font-weight: 700;
    background: #f0e8d6;
    color: var(--hn-brown);
    padding: 1px 8px;
    border-radius: 10px;
}
.training-level-badge + .training-level-badge {
    margin-left: .35rem;
}
.training-collapse-chevron { display: none; }
@media (max-width: 700px) {
    .training-collapse-header { user-select: none; cursor: pointer; }
    .training-collapse-chevron {
        display: inline-block;
        margin-left: .5rem;
        width: 0; height: 0;
        border-left: 5px solid transparent;
        border-right: 5px solid transparent;
        border-top: 6px solid var(--hn-brown);
        transition: transform .15s ease;
        vertical-align: middle;
    }
    .training-collapse-chevron.is-collapsed { transform: rotate(-90deg); }
}

.training-tables-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .75rem;
}
.training-tables-grid--western {
    grid-template-columns: 1fr 1fr 1fr;
}
@media (max-width: 1000px) {
    .training-tables-grid--western { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 700px) {
    .training-tables-grid,
    .training-tables-grid--western { grid-template-columns: 1fr; }
}

.training-style-pane[hidden],
.training-style-pane.is-hidden { display: none; }
.training-style-pane.is-disabled {
    opacity: .4;
    pointer-events: none;
}

/* ── Training disciplines header + Engels|Western split-button ──── */
/*
 * Section header that sits between the horse picker and the discipline
 * cards. Carries the "Disciplines" label on the left and a small
 * segmented control on the right (when the active horse's group has
 * both English and Western enabled). Stays at a fixed offset above
 * the cards regardless of how many rows the picker wraps to, so the
 * style switch is never covered by an overflowing horse list.
 *
 * The segment buttons keep the legacy data-training-style attribute +
 * .is-active class so views/training/_horse_content.php's pane-toggle
 * JS continues to work without rewrites. The container also keeps
 * data-allowed-tabs for the readPref() fallback there.
 */
.training-disciplines-header {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin: 0 0 .65rem;
    padding-bottom: .4rem;
    border-bottom: 1px solid var(--hn-border);
}
.training-disciplines-label {
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: var(--hn-brown);
}
.training-style-segment {
    display: inline-flex;
    margin-left: auto;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    background: #fff;
    overflow: hidden;
}
.training-style-segment-btn {
    appearance: none;
    background: transparent;
    border: none;
    border-right: 1px solid var(--hn-border);
    padding: .3rem 1rem;
    font: inherit;
    font-size: .82rem;
    font-weight: 700;
    color: #7b6f4f;
    cursor: pointer;
    min-width: 5rem;
}
.training-style-segment-btn:last-child { border-right: none; }
.training-style-segment-btn:hover { color: #4a3f25; }
.training-style-segment-btn.is-active {
    background: #eef5e6;
    color: var(--hn-green-dark);
}

/* ── Training layout: groups sidebar + main column ─────────────── */
/*
 * Active when the user has more than one horse group. Renders a
 * fixed-width left rail with the group list and a flexible main
 * column for the horse picker + discipline cards. Single-group
 * accounts skip this wrapper entirely (just the flat picker + cards
 * inside .hn-card-body, same as pre-groups).
 *
 * Position relative scopes the mobile drawer's absolute positioning
 * to the layout, so the drawer overlay covers exactly the training
 * options pane without leaking into other cards on the page.
 */
.training-layout {
    display: grid;
    grid-template-columns: 200px minmax(0, 1fr);
    gap: 1rem;
    position: relative;
}
.training-main { min-width: 0; }

.training-sidebar {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: .25rem .35rem;
    background: #faf6ee;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    align-self: start;
}
.training-sidebar-heading {
    padding: .35rem .55rem .25rem;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: #7b6f4f;
}

/*
 * Group row. Mirrors .training-horse-btn.active visual language so the
 * sidebar reads as the same family as the picker - soft cream-green
 * fill with a 4px green left rail when active. Hover stays subtle so
 * the active row doesn't need to compete.
 */
.training-sidebar-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: .35rem;
    width: 100%;
    padding: .4rem .55rem .4rem .8rem;
    background: transparent;
    border: none;
    border-radius: 4px;
    color: var(--hn-text);
    font-size: .82rem;
    font-weight: 500;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}
.training-sidebar-row:hover {
    background: rgba(74, 124, 47, .08);
    text-decoration: none;
    color: var(--hn-text);
}
.training-sidebar-row.is-active {
    background: #eef5e6;
    color: var(--hn-green-dark);
    font-weight: 700;
}
.training-sidebar-row.is-active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 4px;
    bottom: 4px;
    width: 4px;
    background: var(--hn-green);
    border-radius: 2px;
}
.training-sidebar-row .tsr-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.training-sidebar-row .tsr-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.4em;
    padding: 0 .4em;
    border-radius: 999px;
    background: rgba(123, 111, 79, .18);
    color: #4a3f25;
    font-size: .72rem;
    font-weight: 700;
    line-height: 1.4;
}
.training-sidebar-row.is-active .tsr-count {
    background: var(--hn-green);
    color: #fff;
}

/*
 * Per-group "soonest cooldown" chip. Same green pill as .thb-cd-badge
 * so the visual language across picker buttons and sidebar rows reads
 * consistently. Hidden when at least one horse in the group is
 * available right now - the count badge already conveys that.
 *
 * Server-side first paint comes from views/training/_training_sidebar.php's
 * $_soonestForGroup closure; once a second after that, the JS ticker
 * window.updateGroupTabCountdowns() in views/training/index.php
 * re-derives it from the live .thb-cd-badge._endTimeMs values.
 */
.training-sidebar-row .tsr-cd {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.6em;
    padding: 0 .45em;
    border-radius: 999px;
    background: var(--hn-green);
    color: #fff;
    font-size: .68rem;
    font-weight: 700;
    line-height: 1.4;
    font-variant-numeric: tabular-nums;
}
.training-sidebar-row .tsr-cd.is-empty { display: none; }

/*
 * Synthetic Verzorging row (caretaker bucket, group id -1). Repaints the
 * row in the amber/orange verzorgpaarden palette so it reads as a
 * different category from the user's own horse groups, mirroring the
 * .training-horse-btn.is-caring-for picker buttons (amber border) and the
 * .caring-for-banner card (cream-orange wash). The cooldown chip and
 * count badge follow the same theme so a maxed-out Verzorging tab still
 * looks unmistakably amber rather than green.
 */
.training-sidebar-row.is-caretaker-group:hover {
    background: rgba(217, 154, 64, .12);
}
.training-sidebar-row.is-caretaker-group.is-active {
    background: #fdf0e0;
    color: #a86a1a;
}
.training-sidebar-row.is-caretaker-group.is-active::before {
    background: #d99a40;
}
.training-sidebar-row.is-caretaker-group.is-active .tsr-count {
    background: #c27a1e;
    color: #fff;
}
.training-sidebar-row.is-caretaker-group .tsr-cd {
    background: #c27a1e;
}

/* Footer link inside .training-sidebar that takes the user to the full
   /groups management page. Sits below the row list separated by a thin
   top border so it reads as "meta", not as another selectable group. */
.training-sidebar-manage {
    display: block;
    margin: .35rem .25rem 0;
    padding: .55rem .55rem;
    border-top: 1px solid rgba(155, 134, 96, .25);
    font-size: .82rem;
    color: var(--hn-brown, #6a5430);
    text-decoration: none;
    text-align: center;
    border-radius: 4px;
}
.training-sidebar-manage:hover {
    background: rgba(74, 124, 47, .08);
    color: var(--hn-brown, #6a5430);
    text-decoration: none;
}

/* Cross-group horses stay in the DOM (so auto-schakel can fall through
   to them) but are visually hidden under the active group. The click
   handler in views/training/index.php#applyActiveGroup smoothly flips
   the active group via XHR instead of doing a hard navigation. */
.training-horse-btn.is-other-group { display: none; }

/* ── Mobile: collapse the sidebar into a dropdown ─────────────── */
/*
 * Below 768px the sidebar drops out of the grid and becomes a small
 * popover-style dropdown anchored to the top-right of the layout
 * (which sits directly under the trigger button in the card header).
 * The hamburger chip in .hn-card-header (.training-groups-trigger)
 * toggles .is-drawer-open on .training-layout, which flips the
 * dropdown's display + animates it. Dismissal goes through the
 * document-level click handler in views/training/index.php; the old
 * full-overlay backdrop is no longer needed because the dropdown is
 * compact and tap-outside-to-close is handled at document scope.
 */
.training-groups-trigger { display: none; }
.training-sidebar-overlay { display: none; }

@media (max-width: 767px) {
    .training-layout {
        grid-template-columns: minmax(0, 1fr);
    }
    /* Dropdown popover anchored to the top-LEFT of the layout. The
       trigger button in .hn-card-header sits just after the page
       title (with .auto-switch-toggle pushed all the way right via
       its own ms-auto), so the trigger lives on the left side of
       the header. Anchoring the menu's left edge to "left: 0" of
       the layout makes the dropdown drop straight from underneath
       the trigger and reads as "this menu came from this button"
       without crossing the header to the opposite side. Hidden by
       default via display:none so absolutely-positioned space
       isn't reserved when irrelevant; the 1px border + drop-shadow
       give it the standard popover-on-card look used elsewhere. */
    .training-sidebar {
        position: absolute;
        top: 0;
        left: 0;
        bottom: auto;
        right: auto;
        margin: 0;
        padding: .35rem;
        min-width: 14rem;
        max-width: min(20rem, 100%);
        max-height: 70vh;
        overflow-y: auto;
        z-index: 12;
        display: none;
        background: #faf6ee;
        border: 1px solid var(--hn-border);
        border-radius: 6px;
        /* Y-only offset + negative spread so the shadow falls downward
           and tucks tightly against the sides. Without the negative
           spread the upward halo would paint over the green card
           header just above the dropdown's top edge and look muddy. */
        box-shadow: 0 8px 16px -4px rgba(0, 0, 0, .22);
        /* Reset any desktop transform/transition that the rule above
           may set; the dropdown opens via display + a short keyframe. */
        transform: none;
        transition: none;
    }
    .training-layout.is-drawer-open .training-sidebar {
        display: block;
        animation: tgt-dropdown .14s ease-out;
    }
    @keyframes tgt-dropdown {
        from { opacity: 0; transform: translateY(-4px); }
        to   { opacity: 1; transform: translateY(0); }
    }
    /* The sidebar heading reads as "Groepen" on desktop where it sits
       at the top of a permanent column. Inside the compact dropdown it
       repeats the trigger's purpose, so hide it to keep the menu tight. */
    .training-sidebar .training-sidebar-heading { display: none; }

    /* Hamburger chip in .hn-card-header. Picks up the same
       "translucent black on green" treatment as .btn-hn-on-header so
       it sits comfortably on the green header without a custom
       background. Sits between the title and the auto-schakel toggle;
       .auto-switch-toggle's existing margin-left:auto pushes the
       toggle pair to the far right so the trigger naturally lines up
       just after the title. */
    .training-groups-trigger {
        display: inline-flex;
        align-items: center;
        gap: .35rem;
        margin-left: .55rem;
        padding: 2px 8px;
        background: rgba(0, 0, 0, .18);
        color: #fff;
        border: none;
        border-radius: 4px;
        font: inherit;
        font-size: .78rem;
        font-weight: 600;
        cursor: pointer;
        max-width: 12rem;
    }
    .training-groups-trigger .tgt-icon {
        display: inline-flex;
        flex-direction: column;
        gap: 2px;
        width: 12px;
        flex-shrink: 0;
    }
    .training-groups-trigger .tgt-icon::before,
    .training-groups-trigger .tgt-icon::after,
    .training-groups-trigger .tgt-icon span {
        content: '';
        display: block;
        height: 2px;
        background: #fff;
        border-radius: 1px;
    }
    .training-groups-trigger .tgt-active-name {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    /* Verzorging-active variant: tint the chip in the amber palette
       so the at-a-glance "current group" cue matches the sidebar
       row's amber treatment. The trigger sits on the dark green
       card header, so the cream-orange wash reads clearly without
       competing with the header background. */
    .training-groups-trigger.is-caretaker-group {
        background: #d99a40;
        color: #fff;
    }
}

/* ── Shop tabs (English / Western / Universal) ────────────────── */
.shop-tabs {
    display: flex;
    gap: .25rem;
    padding: .5rem .75rem 0;
    border-bottom: 2px solid #d4c8a8;
}
.shop-tab {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    border-bottom: none;
    border-radius: 6px 6px 0 0;
    padding: .45rem 1.1rem;
    font: inherit;
    font-size: .92rem;
    font-weight: 600;
    color: #7b6f4f;
    cursor: pointer;
    margin-bottom: -2px;
}
.shop-tab:hover { color: #4a3f25; }
.shop-tab.is-active {
    background: #fff8ea;
    color: #4a3f25;
    border-color: #d4c8a8;
    border-bottom: 2px solid #fff8ea;
}
.shop-tab-pane.is-hidden { display: none; }
.shop-empty { padding: .75rem; }

.shop-tier-badge {
    display: inline-block;
    padding: .1rem .3rem;
    border-radius: 3px;
    font-size: .62rem;
    font-weight: 600;
    border: 1px solid currentColor;
    margin-left: .2rem;
}
.shop-bonus-cell {
    line-height: 1.6;
}
.shop-bonus-chip {
    display: inline-block;
    padding: .08rem .3rem;
    border-radius: 3px;
    font-size: .62rem;
    font-weight: 700;
    letter-spacing: .02em;
    margin: 0 .15rem .1rem 0;
    border: 1px solid currentColor;
    line-height: 1.25;
}
.shop-bonus-d   { color: #1a6abf; background: #e6f0fa; }
.shop-bonus-j   { color: var(--hn-green-dark); background: #e7f2dd; }
.shop-bonus-r   { color: #a55a1a; background: #f7ead9; }
.shop-bonus-t   { color: #7a5a1a; background: #f1ead2; }
.shop-bonus-wp  { color: #a04a8a; background: #f5e3ed; }
.shop-bonus-u   { color: #555; background: #e5e5e5; border-color: #aaa; }
.shop-bonus-empty { color: #aaa; }

.shop-bonus-legend {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem .85rem;
    padding: .5rem .75rem .65rem;
    font-size: .72rem;
    color: var(--hn-brown, #7b4f2e);
    background: #faf6ec;
    border-top: 1px solid var(--hn-border, #e0d6bf);
}
.shop-bonus-legend-item {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
}
.shop-bonus-legend-label {
    line-height: 1;
}

.inv-group-badge {
    display: inline-block;
    padding: .08rem .35rem;
    border-radius: 3px;
    font-size: .62rem;
    font-weight: 600;
    margin-left: .25rem;
    text-transform: uppercase;
    letter-spacing: .03em;
}
.inv-group-english   { background: #e6f0fa; color: #1a6abf; }
.inv-group-western   { background: #f6e6d4; color: #a55a1a; }
.inv-group-universal { background: #ece6d4; color: #6a5430; }

.shop-tier-basic { color: #888;    border-color: #888;    }
.shop-tier-mid   { color: #1a6abf; border-color: #1a6abf; }
.shop-tier-high  { color: #d4a017; border-color: #d4a017; }

/* ── /horses Equipment block ─────────────────────────────── */
/* Item-name cell wrapper: keeps the name + tier + discipline
   badges aligned and lets them wrap as a unit when the column
   is narrow (the equipment card sits in the 1fr lane of the
   care/equipment 2fr/1fr grid). */
.equip-item-name {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .15rem .35rem;
}
.equip-item-name .shop-tier-badge,
.equip-item-name .inv-group-badge {
    margin-left: 0;
}
.equip-bonus-cell {
    line-height: 1.6;
    white-space: normal;
}
.equip-warn-bar {
    padding: .6rem .75rem;
    background: #fff3cd;
    border-bottom: 1px solid var(--hn-border);
    color: #856404;
    font-size: .78rem;
}
.equip-inventory-heading {
    padding: .4rem .75rem;
    background: #f0e8d6;
    color: #7b4f2e;
    font-size: .7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .5px;
}
.equip-inventory-heading .text-muted {
    margin-left: .35rem;
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
}
/* Tack-ownership badges shown on the equipped-items table when a
   care+races contract is active, so both the owner and the caretaker
   can see at a glance whose zadelkamer each item will return to. */
.tack-owner-owner {
    background: #6a5430;
}
.tack-owner-caretaker {
    background: #2e7d32;
}

/* ── /inventory — per-horse loadout layout ────────────── */

/* Voer-tegelstrip (zelfde uitstraling als de huidige /inventory,
   maar via klassen i.p.v. inline style). */
.inv2-food-strip {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
}
.inv2-food-tile {
    flex: 1 1 90px;
    min-width: 90px;
    background: var(--hn-cream);
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    padding: .75rem .5rem;
    text-align: center;
}
.inv2-food-img {
    display: block;
    height: 52px;
    width: auto;
    object-fit: contain;
    margin: 0 auto;
}
.inv2-food-name {
    margin: .35rem 0 .2rem;
    font-size: .78rem;
    font-weight: 700;
    color: var(--hn-brown);
    text-transform: uppercase;
    letter-spacing: .4px;
}
.inv2-food-qty {
    font-size: 1.3rem;
    font-weight: 700;
}
.inv2-food-qty.is-stocked { color: var(--hn-green-dark); }
.inv2-food-qty.is-empty   { color: #c0392b; }

/* Per-paard loadout-grid: 1 kolom op smal, autofit op breder */
/* Two-column tack layout used by the "Uitrusting per paard" pane: a
   vertical horse picker on the left, the selected horse's loadout
   card on the right. Card body has p-0, so we own the padding here.
   The picker column is fixed at 260px so a horse name like "Praying
   For Rain N Shade" wraps gracefully but still reads as a sidebar.
   The detail column caps its content at 540px so the four tack-slot
   rows don't stretch into pulled taffy on a 1400px monitor. */
.inv2-tack-layout {
    display: grid;
    grid-template-columns: 260px 1fr;
    gap: .75rem;
    padding: .75rem;
    align-items: start;        /* keep the cards top-aligned in their tracks */
}
/* CRITICAL: grid items default to min-width: auto, which sizes the cell
   to its content's intrinsic width. Without this override, the picker
   in horizontal-nowrap mode (mobile fallback) or a tack card row with
   a long item name would push the cell wider than 1fr, blowing out the
   card and causing horizontal page overflow. With min-width: 0 the cell
   respects the column's declared width and the picker's overflow-x:auto
   (or the tack card's natural wrapping) takes over instead. */
.inv2-tack-layout__picker,
.inv2-tack-layout__detail {
    min-width: 0;
}
.inv2-tack-layout__detail > .inv2-horse-card {
    max-width: 540px;
    margin: 0;
}
@media (max-width: 767px) {
    /* Stack on phones: picker on top (auto-reverts to horizontal via
       the .training-horse-bar--vertical mobile override), detail card
       below. Tighter padding because the vertical breathing room is
       expensive on a small screen. */
    .inv2-tack-layout {
        grid-template-columns: 1fr;
        padding: .5rem;
        gap: .5rem;
    }
    .inv2-tack-layout__detail > .inv2-horse-card {
        max-width: none;
    }
}

/* Legacy multi-horse loadout grid - retained for any future caller that
   wants the auto-fill grid (e.g. a "show all horses at once" view).
   The current /inventory pane uses .inv2-tack-layout above. */
.inv2-loadout-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: .75rem;
    padding: .75rem;
}
.inv2-horse-card {
    /* Erft .hn-card padding/border; we voegen alleen een interne layout toe. */
    margin: 0;
}
.inv2-horse-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
}
.inv2-horse-name {
    color: #fff;
    text-decoration: none;
    font-weight: 700;
}
.inv2-horse-name:hover { text-decoration: underline; }
.inv2-horse-meta {
    font-size: .68rem;
    color: rgba(255, 255, 255, .82);
    text-transform: uppercase;
    letter-spacing: .04em;
    font-weight: 600;
}
.inv2-pasture-banner {
    padding: 1rem .75rem;
    text-align: center;
    color: #7b4f2e;
    background: #fbf6e8;
    font-size: .82rem;
}
.inv2-slot-list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.inv2-slot-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .35rem .5rem;
    padding: .5rem .75rem;
    border-bottom: 1px solid var(--hn-border);
    font-size: .85rem;
}
.inv2-slot-row:last-child { border-bottom: none; }
.inv2-slot-label {
    flex: 0 0 80px;
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: #7b4f2e;
    font-weight: 700;
}
.inv2-slot-equipped {
    flex: 1 1 140px;
    min-width: 0;
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .15rem .35rem;
}
.inv2-slot-equipped .shop-tier-badge,
.inv2-slot-equipped .inv-group-badge {
    margin-left: 0;
}
.inv2-slot-item-name {
    font-weight: 600;
    color: #2d2317;
    overflow-wrap: anywhere;
}
.inv2-slot-empty {
    flex: 1 1 140px;
    min-width: 0;
    color: #b0a48a;
    font-style: italic;
    font-size: .82rem;
}
.inv2-slot-empty--blocked {
    color: #b07b1a;
    font-style: normal;
    font-weight: 600;
    font-size: .76rem;
    text-transform: uppercase;
    letter-spacing: .03em;
}
.inv2-slot-action {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .35rem;
}

/* Compact inline equip-form: select + button on één regel,
   wrap netjes onder elkaar als de naam te lang is. */
.inv2-equip-inline {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
}
.inv2-equip-select {
    padding: 3px 6px;
    border: 1px solid var(--hn-border);
    border-radius: 3px;
    font-size: .8rem;
    max-width: 180px;
    background: #fff;
}

/* Voettekst per kaart: gekleurde D/S/R/T/WP-bonuschips
   (totaal over de huidige loadout). Cream-strip onder het laatste
   slot-rij; chips wrappen wanneer er meer dan één discipline-kleur
   meedoet (b.v. universele Top-tack vult alle vijf kolommen). */
.inv2-horse-footer {
    padding: .45rem .75rem;
    background: #faf6ec;
    color: #6a5430;
    font-size: .72rem;
    border-top: 1px solid var(--hn-border);
    line-height: 1.6;
}

/* Zadelkamer-paneel (de spaarstukken-lijst per categorie) */
.inv2-zk-count {
    margin-left: .3rem;
    font-weight: 400;
    font-size: .78rem;
    /* Translucent white reads as a muted secondary against the dark
       green .hn-card-header background. The previous warm-brown was a
       holdover from when this label sat on a cream body and was
       effectively invisible on the header. */
    color: rgba(255, 255, 255, .75);
}
.inv2-spares-cat-header {
    padding: .4rem .75rem;
    background: #f0e8d6;
    color: #7b4f2e;
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .5px;
    border-top: 1px solid var(--hn-border);
}
.inv2-spares-cat-header:first-child {
    border-top: none;
}
.inv2-spares-cat-count {
    margin-left: .3rem;
    font-weight: 400;
    font-size: .68rem;
    color: #a07850;
    text-transform: none;
    letter-spacing: 0;
}
.inv2-spares-list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.inv2-spare-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .4rem .6rem;
    padding: .55rem .75rem;
    border-bottom: 1px solid var(--hn-border);
    font-size: .85rem;
}
.inv2-spare-row:last-child { border-bottom: none; }
.inv2-spare-name {
    flex: 1 1 200px;
    min-width: 0;
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .15rem .35rem;
}
.inv2-spare-name .shop-tier-badge,
.inv2-spare-name .inv-group-badge {
    margin-left: 0;
}
.inv2-spare-item-name {
    font-weight: 600;
    color: #2d2317;
    overflow-wrap: anywhere;
}
/* Bonus chips (D+5, U16, ...) now sit inline with the item name inside
   .inv2-spare-name so they read as "Top Hoofdstel U16" instead of
   floating in a separate centred column between the name and the
   action buttons. The legacy .inv2-spare-bonus rule was removed in
   the same change; the markup that wrapped them is gone too. */
.inv2-spare-name .shop-bonus-chip {
    margin: 0;
}
.inv2-spare-action {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .35rem;
}

.training-exercise-list {
    display: flex;
    flex-direction: column;
}
.training-exercise-row {
    display: block;
    width: 100%;
    padding: .55rem .75rem;
    border: none;
    border-bottom: 1px solid #e8dfcf;
    background: #fff;
    cursor: pointer;
    text-align: left;
    font-family: inherit;
    font-size: .85rem;
    transition: background .1s;
}
.training-exercise-list > .training-exercise-row:last-child,
.training-exercise-list > :last-child .training-exercise-row { border-bottom: none; }
.training-exercise-row:hover:not(:disabled) { background: #f9f4ec; }
.training-exercise-row.selected { background: #eef5e6; box-shadow: inset 3px 0 0 var(--hn-green); }
.training-exercise-row:disabled { opacity: .65 !important; cursor: default; }
.training-exercise-row.is-maxed { opacity: .55; }

.ter-info {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 4px;
}
.ter-name { color: #333; }
.diff-badge {
    display: inline-block;
    font-size: .68rem;
    font-weight: 600;
    padding: 1px 6px;
    border-radius: 8px;
    margin-left: .35rem;
    vertical-align: middle;
    letter-spacing: .2px;
}
.diff-badge--easy   { background: #eef7ec; color: #2d6a1e; }
.diff-badge--medium { background: #fef4e8; color: #c27020; }
.diff-badge--hard   { background: #fdecea; color: #b33a2a; }
.ter-pct  { font-size: .82rem; color: #555; min-width: 34px; text-align: right; }

.ter-bar-wrap {
    height: 6px;
    background: #e8dfcf;
    border-radius: 3px;
    overflow: hidden;
}
.ter-bar {
    height: 100%;
    border-radius: 3px;
    transition: width .3s;
}

.training-panel .tp-subtitle {
    margin: 0 0 .75rem;
    font-size: .88rem;
    color: #666;
}

.training-methods-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: .6rem;
}
@media (max-width: 700px) {
    .training-methods-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 420px) {
    .training-methods-grid { grid-template-columns: 1fr; }
}

.training-method-card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .3rem;
    padding: .75rem .5rem;
    border: 1px solid #e8dfcf;
    border-radius: 6px;
    background: #faf7f0;
    text-align: center;
    cursor: pointer;
    transition: background .1s, box-shadow .1s;
}
.training-method-card:hover {
    background: #f9f4ec;
}
.training-method-card.is-selected {
    background: #eef5e6;
    box-shadow: inset 3px 0 0 var(--hn-green);
}
.tmc-header {
    font-weight: 700;
    font-size: .88rem;
    color: var(--hn-brown);
}
.tmc-detail {
    font-size: .82rem;
    font-weight: 600;
    color: var(--hn-green-dark);
}
.tmc-cost {
    font-size: .78rem;
    color: #888;
    margin-bottom: .25rem;
}
.tmc-cost--free { color: var(--hn-green); font-weight: 600; }
.tmc-xp {
    font-size: .78rem;
    font-weight: 600;
    color: #b8870f;
}
.tmc-xp--muted {
    color: #888;
    font-weight: 500;
    font-style: italic;
}
.tmc-btn { width: 100%; }

/* ── Trainer picker: Bonus + owner-XP stacked cell ────────── */
/* The "Bonus" column packs two values on two lines: the stat-gain
   percentage (top) and the XP the picker earns per session (bottom).
   Owner XP scales inversely to the trainer's niveau, so the XP line
   is color-tiered to make the trade-off visible at a glance:
     +5 XP -> green   (starter trainers, highest owner XP)
     +3 XP -> orange  (experienced trainers, mid owner XP)
     +1 XP -> dark red (master trainers, lowest owner XP)
   The system NPC has owner_xp = 0 and renders a muted dash on the
   XP line so the row still occupies two lines and the column stays
   vertically aligned with player rows. */
.trainer-bonus-pct {
    display: block;
    line-height: 1.2;
}
.trainer-owner-xp {
    display: block;
    font-size: .8rem;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
}
.trainer-owner-xp--high  { color: var(--hn-green); }
.trainer-owner-xp--mid   { color: #d97706; }
.trainer-owner-xp--low   { color: #8b1e1e; }
.trainer-owner-xp--empty {
    color: #999;
    font-weight: 500;
}

/* Dedicated star column on the trainer picker. Lives between the
   mobile-only action cell and the trainer-name cell, so the favorite
   toggle aligns on its own axis instead of crowding the username on
   one row but not the other. No header label (it's just an icon).
   Selectors are qualified with the cell element so we beat the
   `.hn-table th/td` default-padding rule (specificity 0,1,1) - same
   trick as `.hn-table th.text-center` above. */
.hn-table th.trainer-star-cell,
.hn-table td.trainer-star-cell {
    width: 1%;
    white-space: nowrap;
    text-align: center;
    padding-left: .15rem;
    padding-right: .15rem;
}
/* On mobile the picker has to fit Trainen + star + name + 5 data
   columns in ~360px, so collapse the star column's gutter (and shrink
   the star button's own min-width) only here. Scoped to
   .trainer-star-cell so the same star in other contexts keeps its
   comfortable 1.6em touch target. */
@media (max-width: 600px) {
    .hn-table th.trainer-star-cell,
    .hn-table td.trainer-star-cell {
        padding-left: 0;
        padding-right: 0;
    }
    .trainer-star-cell .provider-fav-star,
    .trainer-star-cell .provider-fav-star-spacer {
        min-width: 1.2em;
        width: 1.2em;
        padding-left: .1rem;
        padding-right: .1rem;
    }
}

/* ── Vet action buttons ──────────────────────────────────── */
.vet-actions-bar {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .75rem;
    margin-bottom: .75rem;
}
@media (max-width: 600px) {
    .vet-actions-bar { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 380px) {
    .vet-actions-bar { grid-template-columns: 1fr; }
}

.vet-action-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .35rem;
    padding: 1rem .75rem;
    border: 2px solid var(--hn-green);
    border-radius: 8px;
    background: #fff;
    cursor: pointer;
    transition: background .15s, border-color .15s, box-shadow .15s, transform .1s;
    text-align: center;
    font-family: inherit;
}
.vet-action-btn:hover:not(:disabled) {
    background: #eef5e6;
    border-color: var(--hn-green-dark);
    box-shadow: 0 2px 8px rgba(74,124,47,.18);
    transform: translateY(-1px);
}
.vet-action-btn:active:not(:disabled) {
    transform: translateY(0);
    box-shadow: 0 1px 3px rgba(74,124,47,.12);
}
.vet-action-btn.vet-action-selected {
    background: #eef5e6;
    border-color: var(--hn-green);
    box-shadow: inset 4px 0 0 var(--hn-green), 0 2px 8px rgba(74,124,47,.15);
}
.vet-action-btn.vet-action-selected:hover:not(:disabled) {
    box-shadow: inset 4px 0 0 var(--hn-green), 0 2px 8px rgba(74,124,47,.18);
}

.vet-action-btn.vet-action-disabled {
    border-color: #ddd;
    background: #f9f9f9;
    cursor: not-allowed;
    opacity: .65;
}
.vet-action-btn.vet-action-disabled .vet-action-title { color: #999; }
.vet-action-btn.vet-action-disabled .vet-action-desc { color: #b55; font-style: italic; }

.vet-horse-info {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem 1.25rem;
    padding: .6rem .85rem;
    margin-bottom: .75rem;
    background: #faf7f0;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    font-size: .85rem;
}
.vet-horse-vaccines {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem .9rem;
}
.vet-vaccine-tag {
    display: inline-flex;
    gap: .3rem;
    font-size: .82rem;
}

.vet-action-icon { font-size: 1.6rem; line-height: 1; }
.vet-action-title { font-weight: 700; font-size: .92rem; color: var(--hn-brown); }
.vet-action-desc { font-size: .78rem; color: #888; line-height: 1.3; }
.vet-panel {
    display: none;
    margin-top: .75rem;
}
.vet-panel.is-visible {
    display: block;
}
.vet-status.is-success {
    color: #27ae60;
}
.vet-status.is-warning {
    color: #e67e22;
}
.vet-status.is-danger {
    color: #c0392b;
}
.vet-alert {
    border-radius: 6px;
    padding: .6rem .85rem;
    font-size: .88rem;
    font-weight: 600;
}
.vet-alert-danger {
    background: #fdecea;
    border: 1px solid #e6a19a;
    color: #c0392b;
}
.vet-provider-row {
    cursor: pointer;
}

/* ── Provider favorites star + unavailable rows ───────────────────────────
   Used by the trainer / vet / hoefsmid pickers. The star is a hollow ☆ for
   pool members and a filled ★ for the player's favorites; clicking either
   toggles via POST /favorites/toggle. Rows whose provider is offline /
   paused / lacks the right equipment for the selected treatment / etc.
   stay rendered (favorites only) but get .is-unavailable + a small
   .provider-unavailable-label below the username so the player knows why
   they can't use that row right now. */
.provider-fav-star {
    background: none;
    border: 0;
    padding: .15rem .25rem;
    cursor: pointer;
    /* Big enough to be a comfortable finger target on mobile without
       blowing out the row height. The glyph itself is ~24px at the
       default body font; min-width keeps the hollow ☆ and filled ★
       boxes the same width so toggling doesn't shift the username. */
    font-size: 1.55em;
    line-height: 1;
    min-width: 1.6em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #b8b0a0;
    transition: color .12s ease, transform .12s ease;
}
.provider-fav-star:hover {
    transform: scale(1.15);
}
.provider-fav-star.is-on {
    color: #d4a017;
}
.provider-fav-star:disabled {
    cursor: not-allowed;
    opacity: .5;
}
.provider-fav-star:focus-visible {
    outline: 2px solid #d4a017;
    outline-offset: 2px;
    border-radius: 4px;
}
/* HN system row gets a non-interactive placeholder so its username
   stays vertically aligned with the rows that have a real star. */
.provider-fav-star-spacer {
    display: inline-block;
    width: 1.6em;
    padding: .15rem .25rem;
}
tr.provider-row.is-unavailable {
    opacity: .55;
    cursor: default;
}
tr.provider-row.is-unavailable .btn-hn {
    pointer-events: none;
    opacity: .6;
}
.provider-unavailable-label {
    font-size: .78em;
    color: #b35a4a;
    font-style: italic;
    margin-top: .15rem;
}
/* ── Shared profession stat tiles ──────────────────────────────────────────
   Used by the trainer + hoefsmid dashboards (the vet dashboard uses the
   parallel .vet-stat-tile set below). Same visual treatment, but the
   auto-fit grid is sized for the smaller numeric tiles (140-160 px) those
   two dashboards use. */
.hn-stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: .75rem;
}
.hn-stat-tile {
    background: var(--hn-card-bg, #faf6f0);
    border: 1px solid var(--hn-border, #e6dfd5);
    border-radius: 8px;
    padding: .65rem .85rem;
    text-align: center;
}
.hn-stat-value {
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--hn-brown, #7b4f2e);
}
.hn-stat-value.is-text {
    color: var(--hn-text, #333);
}
.hn-stat-label {
    margin-top: .15rem;
}

.vet-level-grid,
.vet-equipment-grid,
.vet-price-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: .75rem;
}
.vet-price-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (max-width: 991px) {
    .vet-price-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}
@media (max-width: 540px) {
    .vet-price-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}
.vet-stat-tile,
.vet-equipment-card,
.vet-price-field {
    background: var(--hn-card-bg, #faf6f0);
    border: 1px solid var(--hn-border, #e6dfd5);
    border-radius: 8px;
    padding: .75rem;
}
.vet-stat-value {
    color: var(--hn-brown, #7b4f2e);
    font-size: 1.35rem;
    font-weight: 700;
}
.vet-progress-row {
    margin-top: .85rem;
}
.vet-progress-meta {
    margin-bottom: .3rem;
}
.vet-progress-track {
    background: #e6dfd5;
    border-radius: 6px;
    height: 8px;
    overflow: hidden;
}
.vet-progress-fill {
    height: 100%;
    background: var(--hn-brown, #7b4f2e);
    border-radius: 6px;
    transition: width .3s;
}
.vet-progress-fill.is-max {
    background: #27ae60;
}

/* ── Trainer niveau summary block (private trainer dashboard) ───────────
   Mirrors the hoefsmid tier-summary visual pattern. Used in
   views/trainer/dashboard.php to show the trainer's per-profession
   niveau (Trainer / Ervaren trainer / Meestertrainer), the XP awarded
   per training in that niveau, and a progress bar toward the next tier. */
.trainer-niveau-block {
    background: #f9f4ec;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    padding: .65rem .75rem;
    margin-bottom: .85rem;
}
.trainer-niveau-head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .35rem .55rem;
    margin-bottom: .25rem;
}
.trainer-niveau-label {
    color: var(--hn-brown);
    font-weight: 600;
    font-size: .9rem;
}
.trainer-niveau-name {
    color: var(--hn-text);
    font-weight: 700;
    font-size: 1rem;
}
.trainer-niveau-xp {
    margin-left: auto;
    color: var(--hn-green-dark, #27ae60);
    font-weight: 700;
    font-size: .9rem;
}
.trainer-niveau-meta {
    color: var(--hn-text-muted, #666);
    font-size: .82rem;
    margin-bottom: .45rem;
}
.trainer-niveau-progress-meta {
    margin-bottom: .25rem;
    font-size: .8rem;
    color: var(--hn-text-muted, #666);
}
.trainer-niveau-progress-track {
    background: #e6dfd5;
    border-radius: 6px;
    height: 8px;
    overflow: hidden;
}
.trainer-niveau-progress-fill {
    height: 100%;
    background: var(--hn-brown, #7b4f2e);
    border-radius: 6px;
    transition: width .3s;
}
.trainer-niveau-progress-fill.is-max {
    background: #27ae60;
}
.vet-toggle-row {
    display: flex;
    align-items: center;
    gap: .65rem;
    cursor: pointer;
}
.vet-price-field {
    display: flex;
    flex-direction: column;
    gap: .35rem;
}
.vet-price-field input {
    max-width: 150px;
    padding: .35rem .55rem;
}
.vet-price-field.is-locked {
    opacity: .65;
}
.vet-price-title {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    line-height: 1.25;
}
.vet-price-icon {
    flex-shrink: 0;
    font-size: 1rem;
    line-height: 1;
}
.vet-price-locked-flag {
    color: #9a6a19;
    font-weight: 700;
}
.vet-equipment-card.is-owned {
    border-color: #9bc27a;
    background: #f3f8ee;
}
#vet-content.is-loading {
    opacity: .5;
}
.foal-transition-card {
    border: 2px solid #4a7c2f;
}
.training-alert-warning {
    background: #fef3e2;
    border: 1px solid #f0c36d;
    border-radius: 6px;
    color: #856404;
    font-size: .88rem;
    padding: .6rem .85rem;
}

/* Sick / recovering inline banners shown on horse profile, training and vet
   views. Recovering is intentionally a calmer warm-amber tone (vet has been
   paid, just waiting it out) so it reads visually distinct from the harder
   red "Ziek" alert. */
.hn-banner-sick {
    background: #fdecea;
    border: 1px solid #e6a19a;
    border-radius: 6px;
    color: #c0392b;
    font-size: .88rem;
    font-weight: 600;
    padding: .6rem .85rem;
    margin-bottom: .75rem;
}
.hn-banner-recovering {
    background: #fff4dc;
    border: 1px solid #e8c275;
    border-radius: 6px;
    color: #8a6112;
    font-size: .88rem;
    font-weight: 600;
    padding: .6rem .85rem;
    margin-bottom: .75rem;
}

/* ============================================================
   Utility classes
   Place at end of file for highest specificity.
   ============================================================ */

/* ── Display ─────────────────────────────────────────────── */
.d-none    { display: none; }
.d-block   { display: block; }
.d-inline  { display: inline; }

/* ── Text color ──────────────────────────────────────────── */
.text-muted     { color: #888; }
.text-secondary { color: #666; }
.text-brown     { color: var(--hn-green-dark); }
.text-hn-green  { color: var(--hn-green); }
.text-danger    { color: #c0392b; }
.text-success   { color: #27ae60; }
.text-warning   { color: #e67e22; }
.text-white     { color: #fff; }

/* ── Font size (consolidated scale) ──────────────────────── */
.fs-xs   { font-size: .75rem; }
.fs-sm   { font-size: .84rem; }
.fs-lg   { font-size: .9rem; }
.fs-xl   { font-size: .95rem; }

/* ── Font weight ─────────────────────────────────────────── */
.fw-400  { font-weight: 400; }
.fw-600  { font-weight: 600; }
.fw-bold { font-weight: 700; }

/* ── Text alignment ──────────────────────────────────────── */
.text-center { text-align: center; }
.text-right  { text-align: right; }
.text-left   { text-align: left; }
.nowrap      { white-space: nowrap; }
.lh-snug     { line-height: 1.3; }
.lh-relaxed  { line-height: 1.7; }

/* ── Spacing ─────────────────────────────────────────────── */
.m-0  { margin: 0; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: .25rem; }
.mb-2 { margin-bottom: .5rem; }
.mb-3 { margin-bottom: .75rem; }
.mb-4 { margin-bottom: 1rem; }
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: .25rem; }
.mt-2 { margin-top: .5rem; }
.mt-3 { margin-top: .75rem; }
.mr-1 { margin-right: .25rem; }
.mr-2 { margin-right: .5rem; }

.p-0  { padding: 0; }
.p-2  { padding: .5rem; }
.p-3  { padding: .75rem; }
.px-3 { padding-left: .75rem; padding-right: .75rem; }
.py-2 { padding-top: .5rem; padding-bottom: .5rem; }

/* ── Borders & radius ────────────────────────────────────── */
.rounded    { border-radius: 4px; }
.rounded-lg { border-radius: 8px; }
.border-gray { border: 1px solid #ccc; }

/* ── Width ───────────────────────────────────────────────── */
.w-full { width: 100%; }

/* ── Common combos ───────────────────────────────────────── */
.meta-label { color: #888; font-size: .75rem; }
.prose      { color: #444; font-size: .88rem; line-height: 1.7; }
.flex-row   { display: flex; align-items: center; gap: .5rem; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.flex-between-wrap { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: .5rem; }
.hn-hidden  { display: none !important; }

/* ============================================================
   Extracted component styles (moved from inline <style> blocks)
   ============================================================ */

/* ── Cookie banner (from partials/cookie-banner.php) ─────── */
#hn-cookie-banner {
    display: block;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 9000;
    background: var(--hn-cream);
    border-top: 2px solid var(--hn-border);
    box-shadow: 0 -2px 12px rgba(0,0,0,.15);
    font-size: .85rem;
    color: var(--hn-text);
}
#hn-cookie-banner.hn-cookie-hidden { display: none; }
#hn-cookie-banner-inner {
    max-width: 900px;
    margin: 0 auto;
    padding: .9rem 1.1rem;
}
#hn-cookie-title { margin: 0 0 .3rem; font-size: .95rem; }
#hn-cookie-intro { margin: 0 0 .75rem; color: #555; line-height: 1.45; }
.hn-cookie-rows { display: flex; flex-direction: column; gap: .5rem; margin-bottom: .85rem; }
.hn-cookie-row {
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 5px;
    padding: .55rem .75rem;
}
.hn-cookie-row-label {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .25rem;
}
.hn-cookie-toggle-label,
.hn-cookie-row-name { font-weight: 600; color: var(--hn-green-dark); cursor: pointer; }
.hn-cookie-always { font-size: .75rem; color: #666; white-space: nowrap; }
.hn-cookie-row-desc { margin: 0; color: #555; line-height: 1.4; font-size: .8rem; }
.hn-cookie-row-desc a { color: var(--hn-green); }
.hn-cookie-switch { position: relative; display: inline-block; width: 38px; height: 22px; flex-shrink: 0; }
.hn-cookie-switch input { opacity: 0; width: 0; height: 0; }
.hn-cookie-slider {
    position: absolute; inset: 0;
    background: #ccc;
    border-radius: 22px;
    transition: background .2s;
    cursor: pointer;
}
.hn-cookie-slider::before {
    content: '';
    position: absolute;
    left: 3px; bottom: 3px;
    width: 16px; height: 16px;
    background: #fff;
    border-radius: 50%;
    transition: transform .2s;
}
.hn-cookie-switch input:checked + .hn-cookie-slider { background: var(--hn-green); }
.hn-cookie-switch input:checked + .hn-cookie-slider::before { transform: translateX(16px); }
.hn-cookie-switch input:focus-visible + .hn-cookie-slider { outline: 2px solid var(--hn-green-dark); outline-offset: 2px; }
.hn-cookie-actions { display: flex; gap: .6rem; flex-wrap: wrap; justify-content: flex-end; }
.hn-cookie-btn {
    padding: .4rem 1rem;
    border: 2px solid var(--hn-green-dark);
    border-radius: 4px;
    font-size: .85rem;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    line-height: 1.4;
}
.hn-cookie-btn-primary  { background: var(--hn-green-dark); color: #fff; }
.hn-cookie-btn-secondary { background: transparent; color: var(--hn-green-dark); }
.hn-cookie-btn:hover { opacity: .85; }
@media (max-width: 520px) {
    .hn-cookie-actions { justify-content: stretch; }
    .hn-cookie-btn { flex: 1; text-align: center; }
}

/* ── Auction panel grid (horse view) ──────────────────────── */
.auction-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
@media (max-width: 500px) {
    .auction-grid { grid-template-columns: 1fr; }
}

/* ── Category image cards (shop & games index) ───────────── */
.shop-category-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 1rem;
}
.shop-category-card {
    display: block;
    text-align: center;
    background: #fff;
    border: 1px solid #c8b99a;
    border-radius: 6px;
    padding: 1rem;
    text-decoration: none;
    transition: box-shadow .15s;
}
.shop-category-card:hover { box-shadow: 0 2px 8px rgba(0,0,0,.12); }
.shop-category-emoji { font-size: 2rem; }
.shop-category-title {
    margin-top: .3rem;
    color: var(--hn-brown, #6f4e25);
    font-weight: 700;
}
.shop-help-list { padding-left: 1.2rem; }
.hn-cat-img { height: 120px; display: flex; align-items: center; justify-content: center; }
.hn-cat-img img { max-height: 120px; max-width: 100%; object-fit: contain; }
@media (max-width: 780px) {
    .shop-category-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 420px) {
    .shop-category-grid { grid-template-columns: 1fr; }
}
@media (min-width: 640px) {
    .hn-cat-img { height: 160px; }
    .hn-cat-img img { max-height: 160px; }
}

/* ── Search autocomplete (search/index.php) ──────────────── */
.hn-search-grid {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 1rem;
}
@media (max-width: 767px) {
    .hn-search-grid { grid-template-columns: 1fr 1fr; }
    .hn-search-grid > :nth-child(3) { grid-column: 1 / -1; }
}
.hn-search-box { position: relative; }
.hn-card:has(.hn-search-box) { overflow: visible; }
.hn-card:has(.hn-search-box) > .hn-card-body { overflow: visible; }
.hn-search-input {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    font-size: .9rem;
    box-sizing: border-box;
}
.hn-search-input:focus { outline: none; border-color: var(--hn-green, #4a7c2f); }
.hn-search-dropdown {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-top: none;
    border-radius: 0 0 6px 6px;
    box-shadow: 0 4px 12px rgba(0,0,0,.1);
    z-index: 50;
    max-height: 320px;
    overflow-y: auto;
}
.hn-search-item {
    padding: 8px 12px;
    cursor: pointer;
    font-size: .88rem;
    border-bottom: 1px solid #f0ebe3;
}
.hn-search-item:last-child { border-bottom: none; }
.hn-search-item:hover, .hn-search-item.active { background: #f0ebe3; }
.hn-search-empty { padding: 10px 12px; color: #999; font-size: .85rem; }

/* ── Tic-tac-toe board ───────────────────────────────────── */
.ttt-board { display: grid; grid-template-columns: repeat(3,1fr); gap: 4px; width: 240px; max-width: 100%; margin: 1rem auto; }
.ttt-cell {
    aspect-ratio: 1; display: flex; align-items: center; justify-content: center;
    font-size: 2.2rem; font-weight: 700; background: #faf7f0; border: 2px solid #c8b99a; border-radius: 6px;
    cursor: default; user-select: none; transition: background .15s;
}
.ttt-cell-x { color: #c0392b; }
.ttt-cell-o { color: #2980b9; }
.ttt-cell-btn { cursor: pointer; border: none; }
.ttt-cell-btn:hover { background: #eee8d5; }
.ttt-info { text-align: center; margin: .75rem 0; font-size: .9rem; }
.ttt-players { display: flex; justify-content: center; gap: 1.5rem; font-size: .88rem; margin-bottom: .5rem; }
.ttt-players .active-player { font-weight: 700; }
.ttt-result { text-align: center; padding: .75rem; border-radius: 6px; margin: .75rem 0; font-weight: 600; font-size: .95rem; }
.ttt-result-win  { background: #d5f5e3; color: #1e8449; }
.ttt-result-lose { background: #fadbd8; color: #922b21; }
.ttt-result-draw { background: #fef9e7; color: #9a7d0a; }

/* Username autocomplete wrapper inside the TTT lobby invite form. The
   absolute-positioned dropdown ([data-autocomplete-dropdown]) needs a
   positioned ancestor; the fixed width keeps the input from spanning
   the full card on desktop while still shrinking on mobile. */
.ttt-invite-autocomplete {
    position: relative;
    width: 220px;
    max-width: 100%;
}

/* Same wrapper pattern as .ttt-invite-autocomplete, used by the
   /messages/compose "Aan" picker. Slightly wider so the matching
   typeahead fits the longer recipient labels. */
.msg-compose-autocomplete {
    position: relative;
    width: 260px;
    max-width: 100%;
}

/* ── Zeeslag (Battleship) ────────────────────────────────── */
/* Mirrors the visual idiom of .ttt-* (faded-cream cells, brown
   border, no harsh fills) so the two multiplayer game pages feel
   like siblings. Two 8x8 grids per player during the firing phase
   (own + tracking), one grid during placement. */
.zeeslag-grids {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    justify-content: center;
    margin: .75rem 0;
}
.zeeslag-grid-wrap {
    flex: 0 0 auto;
    max-width: 100%;
}
.zeeslag-grid-label {
    text-align: center;
    font-size: .85rem;
    font-weight: 600;
    color: #6a5430;
    margin-bottom: .25rem;
}
.zeeslag-grid {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 2px;
    width: 288px;
    max-width: 100%;
    margin: 0 auto;
}
.zeeslag-cell {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #eaf2f6;
    border: 1px solid #c8b99a;
    border-radius: 3px;
    font-size: 1rem;
    font-weight: 700;
    user-select: none;
    cursor: default;
    color: transparent;
    transition: background .15s;
}
.zeeslag-cell-water { background: #eaf2f6; }
.zeeslag-cell-ship  { background: #c8b99a; }
.zeeslag-cell-preview {
    background: #d8e8c8;
    outline: 2px dashed #6a8a3a;
    outline-offset: -2px;
}
.zeeslag-cell-preview-bad {
    background: #f5d0c0;
    outline: 2px dashed #c0392b;
    outline-offset: -2px;
}
.zeeslag-cell-miss {
    background: #eaf2f6;
    color: #6a7a85;
}
.zeeslag-cell-miss::after {
    content: "•";
    font-size: 1.6rem;
    font-weight: 700;
    line-height: 1;
}
.zeeslag-cell-hit {
    background: #f5b7a6;
    color: #922b21;
}
.zeeslag-cell-hit::after {
    content: "✕";
    font-size: 1.1rem;
}
.zeeslag-cell-sunk {
    background: #c0392b;
    color: #fff;
}
.zeeslag-cell-sunk::after {
    content: "✕";
    font-size: 1.1rem;
}
.zeeslag-cell-btn {
    cursor: pointer;
}
.zeeslag-cell-btn:hover {
    background: #d4e8ee;
}
.zeeslag-cell-btn.zeeslag-cell-hit:hover,
.zeeslag-cell-btn.zeeslag-cell-miss:hover,
.zeeslag-cell-btn.zeeslag-cell-sunk:hover {
    cursor: not-allowed;
}
.zeeslag-info {
    text-align: center;
    margin: .75rem 0;
    font-size: .9rem;
}
.zeeslag-players {
    display: flex;
    justify-content: center;
    gap: 1.5rem;
    font-size: .88rem;
    margin-bottom: .5rem;
}
.zeeslag-players .active-player {
    font-weight: 700;
}
.zeeslag-result {
    text-align: center;
    padding: .75rem;
    border-radius: 6px;
    margin: .75rem 0;
    font-weight: 600;
    font-size: .95rem;
}
.zeeslag-result-win  { background: #d5f5e3; color: #1e8449; }
.zeeslag-result-lose { background: #fadbd8; color: #922b21; }
.zeeslag-waiting {
    background: #fef9e7;
    color: #9a7d0a;
    text-align: center;
    padding: .75rem;
    border-radius: 6px;
    margin: .75rem 0;
    font-size: .9rem;
}

/* Placement palette: list of ships still to be placed, plus the
   horizontal/vertical orientation toggle. Mirrors the look of the
   "filter-form" rows elsewhere in the app. */
.zeeslag-placement {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    align-items: center;
}
.zeeslag-fleet-palette {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    justify-content: center;
    align-items: center;
    margin: .5rem 0;
}
.zeeslag-fleet-ship {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .35rem .65rem;
    background: #faf7f0;
    border: 2px solid #c8b99a;
    border-radius: 6px;
    font-size: .85rem;
    font-weight: 600;
    color: #6a5430;
    cursor: pointer;
    user-select: none;
}
.zeeslag-fleet-ship.is-active {
    background: #c8b99a;
    color: #fff;
    border-color: #6a5430;
}
.zeeslag-fleet-ship.is-placed {
    opacity: .45;
    cursor: default;
    text-decoration: line-through;
}
.zeeslag-fleet-ship-tile {
    display: inline-block;
    width: .65rem;
    height: .65rem;
    background: #6a5430;
    border-radius: 1px;
    margin-right: 1px;
}
.zeeslag-orientation {
    display: inline-flex;
    gap: .25rem;
    align-items: center;
    font-size: .85rem;
    color: #6a5430;
}
.zeeslag-placement-actions {
    display: flex;
    gap: .5rem;
    justify-content: center;
    margin-top: .5rem;
}
.zeeslag-placement-hint {
    text-align: center;
    font-size: .82rem;
    color: #777;
    margin: 0 0 .5rem;
}

/* Username autocomplete wrapper inside the Zeeslag lobby invite form.
   Same idiom as .ttt-invite-autocomplete: gives the absolute-positioned
   dropdown a real width to anchor against. */
.zeeslag-invite-autocomplete {
    position: relative;
    width: 220px;
    max-width: 100%;
}

/* ── Higher-lower card game ──────────────────────────────── */
.hl-playing-card {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100px;
    height: 140px;
    background: #fff;
    border: 2px solid var(--hn-border);
    border-radius: 10px;
    box-shadow: 0 3px 12px rgba(0,0,0,.15);
    font-family: 'Georgia', serif;
}
.hl-card-value { font-size: 2rem; font-weight: 700; line-height: 1; }
.hl-card-suit { font-size: 1.8rem; line-height: 1; margin-top: .15rem; }
.hl-btn-guess {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: 10px 24px;
    border: none;
    border-radius: 6px;
    font-size: .95rem;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
    transition: background .15s, transform .1s;
    color: #fff;
}
.hl-btn-guess:hover { transform: scale(1.03); }
.hl-btn-higher { background: #27ae60; }
.hl-btn-higher:hover { background: #1e8e4e; }
.hl-btn-lower { background: #c0392b; }
.hl-btn-lower:hover { background: #a93226; }
.hl-history { display: flex; gap: .4rem; flex-wrap: wrap; margin-top: .5rem; }
.hl-history-card {
    background: #f0e8d6;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    padding: 2px 8px;
    font-size: .82rem;
    font-weight: 600;
}
.hl-multiplier-bar { display: flex; gap: 3px; margin: .75rem 0; }
.hl-mult-step {
    flex: 1;
    text-align: center;
    padding: 4px 0;
    border-radius: 4px;
    font-size: .75rem;
    font-weight: 600;
    background: #e8dfcf;
    color: #888;
}
.hl-mult-step.active { background: var(--hn-green); color: #fff; }
.hl-mult-step.next { background: #d1fae5; color: #065f46; border: 1px dashed var(--hn-green); }

/* ── Leaderboard sorting ─────────────────────────────────── */
.lb-sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.lb-sortable:hover { background: #f0ebe0; }
.lb-arrow { display: inline-block; font-size: .65rem; margin-left: 3px; color: #aaa; vertical-align: middle; }
.lb-sortable.lb-asc .lb-arrow::after { content: '▲'; color: #2d5016; }
.lb-sortable.lb-desc .lb-arrow::after { content: '▼'; color: #2d5016; }

/* Compact discipline-level cell.
   Format per discipline: <key> <level><winstpunten> <training>%
   e.g. "D B+3 65% · S B+5 55%"  or  "R L3+70 60% · T L3+90 75% · P L3+40 50%".
   Equipment bonuses and a "MAX" tag are intentionally not rendered here. */
.disc-level-cell { font-size: .82rem; white-space: nowrap; line-height: 1.45; font-variant-numeric: tabular-nums; }
.disc-level-key  { color: #9a8a66; font-weight: 700; }
.disc-level-lvl  { color: var(--hn-text); font-weight: 600; }
.disc-level-tr   { color: var(--hn-green-dark); }
.disc-level-wp   { color: #b8881a; }
.disc-level-sep  { color: #d6cdb3; margin: 0 .25em; }

/* SDG cell styling (used in leaderboard horses tab and horse tables). */
.lb-sdg-cell { font-size: .82rem; font-variant-numeric: tabular-nums; white-space: nowrap; letter-spacing: .02em; color: #6a5430; }

/* ── Leaderboard "Per ras" tab toolbar ───────────────────── */
/* Wraps the breed-selector dropdown above the top-10 table.
   Sits between .hn-tab-row and the table, so it needs its own
   left/right/top padding (the hn-card-body wrapper does not). */
.lb-breeds-toolbar {
    padding: .75rem .75rem .25rem;
}
.lb-breeds-toolbar .hn-filter-form {
    gap: .5rem;
    align-items: center;
}
/* Narrow rank column for the top-10 table on the "Per ras" tab,
   matching the visual weight of the medal emoji + double-digit rank. */
.lb-breeds-rank-col {
    width: 40px;
}

/* ── Leaderboard "Beroepen" tab ──────────────────────────── */
/* Stack of per-profession top-10 tables. Each section has a small
   green sub-heading (icon + profession name) followed by the same
   hn-table styling the other leaderboard tabs use, so the visual
   weight matches the "Per ras" tab. */
.lb-beroepen-stack {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    padding: .5rem 0;
}
.lb-beroep-section {
    border-top: 1px solid var(--hn-border);
    padding-top: .5rem;
}
.lb-beroep-section:first-child {
    border-top: 0;
    padding-top: 0;
}
.lb-beroep-title {
    font-size: .95rem;
    font-weight: 700;
    color: var(--hn-green-dark);
    margin: 0 0 .35rem;
    padding: .25rem .75rem 0;
    display: flex;
    align-items: center;
    gap: .4rem;
}
.lb-beroep-icon {
    font-size: 1.05rem;
    line-height: 1;
}
.lb-beroep-img {
    height: 1.8rem;
    width: 1.8rem;
    object-fit: contain;
    border-radius: 4px;
    flex-shrink: 0;
}
/* Small status pill in the per-profession top-10 tables. Green when the
   provider is accepting new requests, muted grey when they have paused
   their dashboard. Mirrors the visual weight of .hn-badge but uses its
   own colours so we don't repurpose the red notification-count badge. */
.lb-status-pill {
    display: inline-block;
    font-size: .7rem;
    font-weight: 700;
    line-height: 1.4;
    padding: 1px 7px;
    border-radius: 10px;
    color: #fff;
    letter-spacing: .02em;
}
.lb-status-active { background: var(--hn-green); }
.lb-status-paused { background: #9ca3af; }

/* Admin-only "Totaal" footer row appended to each beroep table when an
   admin views /leaderboard?tab=beroepen. Subtle top border + tinted
   background lift it visually away from the regular ranked rows so it
   reads as a summary, not as an 11th rank. */
.lb-beroep-total-row {
    background: var(--hn-cream, #faf6ec);
    border-top: 2px solid var(--hn-border);
}

/* ── Admin collapsible sections ──────────────────────────── */
details[open] > summary .hn-collapse-arrow { transform: rotate(180deg); }
details > summary::-webkit-details-marker { display: none; }

/* ── Legal pages ─────────────────────────────────────────── */
.hn-legal-h2 {
    font-size: 1rem;
    font-weight: 700;
    color: #2d5016;
    margin: 1.4rem 0 .4rem;
    padding-bottom: .25rem;
    border-bottom: 1px solid #e0d5c5;
}
.hn-legal-h3 {
    font-size: .9rem;
    font-weight: 700;
    color: #2d5016;
    margin: 1rem 0 .3rem;
}
.hn-legal-list {
    margin: .4rem 0 .75rem 1.25rem;
    padding: 0;
    list-style: disc;
}
.hn-legal-list li { margin-bottom: .35rem; }

/* ── Huisregels (house rules page) ───────────────────────── */
/* Narrow, centered card so the rule list stays readable on wide
   screens. Same readable column width as the legal/terms page. */
.hn-rules-card { max-width: 860px; margin: 0 auto; }
.hn-rules-card .hn-legal-h2:first-of-type { margin-top: 1.2rem; }
.hn-rules-footer { margin-top: 1.5rem; }

/* ── Stalnaam (stable name on horses) ────────────────────── */
.stalnaam {
    font-size: 0.82em;
    color: #8b7355;
    font-weight: 400;
    text-decoration: none;
    margin-left: 0.25em;
}
.stalnaam:hover { text-decoration: underline; color: #6a5430; }
.stalnaam-pre { margin-left: 0; margin-right: 0.2em; }
.stalnaam-header { font-weight: 400; font-size: 0.78em; margin-left: 0.3em; opacity: 0.85; }
.stalnaam-header .stalnaam { color: rgba(255,255,255,.7); font-size: 1em; }
.stalnaam-header .stalnaam:hover { color: rgba(255,255,255,.95); }
.stalnaam-header-pre { margin-left: 0; margin-right: 0.3em; }

/* ── Tab bar (horizontally scrollable) ───────────────────── */
.hn-tab-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;          /* Firefox */
}
.hn-tab-scroll::-webkit-scrollbar { display: none; } /* Chrome/Safari */

.hn-tab-row {
    display: flex;
    white-space: nowrap;
    border-bottom: 2px solid var(--hn-border);
    padding: 0 .75rem;
    position: relative;
}

.hn-tab-row > a {
    display: block;
    padding: .4rem .75rem;
    font-size: .82rem;
    color: #888;
    font-weight: 400;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    text-decoration: none;
}
.hn-tab-row > a:hover { text-decoration: none; color: #555; }
.hn-tab-row > a.active {
    font-weight: 700;
    color: var(--hn-green-dark);
    border-bottom-color: var(--hn-green-dark);
}

/* ── Spinner keyframes ───────────────────────────────────── */
@keyframes hn-spin { to { transform: rotate(360deg); } }

/* ════════════════════════════════════════════════════════════════════
   Feedback page (Bugs / Feedback / Ideeën) - admin-only at /feedback
   ════════════════════════════════════════════════════════════════════ */

.feedback-status {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.7rem;
    font-weight: 600;
    color: #fff;
    white-space: nowrap;
    line-height: 1.4;
}
.feedback-status--open            { background: #5b8dd9; }
.feedback-status--gepland         { background: #9b7ed1; }
.feedback-status--in-behandeling  { background: var(--hn-gold); }
.feedback-status--behandeld       { background: var(--hn-green); }
.feedback-status--geen-actie      { background: #888; }

.feedback-origin {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 1px 6px;
    background: var(--hn-cream);
    color: #555;
    border-radius: 8px;
    font-size: 0.7rem;
    font-weight: 500;
    white-space: nowrap;
}

.feedback-original-user {
    font-size: 0.82rem;
    color: var(--hn-text);
}
.feedback-original-user-sub {
    font-size: 0.72rem;
    color: #888;
    margin-top: 0.1rem;
    line-height: 1.2;
}
.feedback-created-byline {
    font-size: 0.82rem;
    color: var(--hn-text);
    margin-right: 0.5rem;
}
.feedback-muted { color: #aaa; }

.feedback-vote-count {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 10px;
    background: #f0e8d6;
    color: var(--hn-text);
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.5;
}
.feedback-vote-count--voted {
    background: var(--hn-green);
    color: #fff;
}
.feedback-vote-count--down.feedback-vote-count--voted {
    background: #c0392b;
}
.feedback-vote-count + .feedback-vote-count {
    margin-left: 0.3rem;
}
.feedback-vote-pair {
    display: inline-flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 0.3rem;
    white-space: nowrap;
}
.feedback-vote-pair .feedback-vote-count {
    flex: 0 0 auto;
    margin-left: 0;
}

.feedback-meta-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.75rem;
}
.feedback-meta-left  { display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center; }
.feedback-meta-right { display: flex; gap: 0.5rem; align-items: center; }

.feedback-vote-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 999px;
    padding: 4px 12px;
    font-size: 0.85rem;
    cursor: pointer;
    transition: background .15s, border-color .15s, color .15s;
    font-family: inherit;
    color: var(--hn-text);
}
.feedback-vote-btn:hover {
    background: #f9f4ec;
    border-color: var(--hn-green);
}
.feedback-vote-btn--voted {
    background: var(--hn-green);
    border-color: var(--hn-green);
    color: #fff;
}
.feedback-vote-btn--down:hover {
    background: #fbeae6;
    border-color: #c0392b;
}
.feedback-vote-btn--down.feedback-vote-btn--voted {
    background: #c0392b;
    border-color: #c0392b;
    color: #fff;
}
.feedback-vote-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}
.feedback-vote-emoji { font-size: 1rem; line-height: 1; }
.feedback-vote-count-num { font-weight: 600; }
.feedback-vote-group {
    display: inline-flex;
    flex-wrap: nowrap;
    gap: 0.4rem;
    align-items: center;
    white-space: nowrap;
}
.feedback-vote-group .feedback-vote-btn {
    flex: 0 0 auto;
}

.feedback-status-form {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 1rem 0;
    padding: 0.5rem 0.75rem;
    background: var(--hn-cream);
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    flex-wrap: wrap;
}
.feedback-status-label {
    font-size: 0.82rem;
    color: #555;
    font-weight: 600;
}
.feedback-status-select {
    padding: 4px 8px;
    font-size: 0.85rem;
}

.feedback-original-byline {
    font-size: 0.78rem;
    color: #888;
    margin-left: 0.25rem;
}

.feedback-screenshot {
    margin-top: 0.6rem;
    font-size: 0.85rem;
}

.feedback-references {
    margin-top: 0.75rem;
    padding: 0.5rem 0.75rem;
    background: #f9f4ec;
    border-left: 3px solid var(--hn-green);
    border-radius: 3px;
    font-size: 0.82rem;
}
.feedback-references-label {
    font-weight: 600;
    color: var(--hn-brown);
    margin-bottom: 0.25rem;
}
.feedback-references-list {
    margin: 0;
    padding-left: 1.1rem;
}
.feedback-references-list li { margin: 0.15rem 0; word-break: break-all; }

.feedback-admin-note {
    margin-top: 0.75rem;
    padding: 0.6rem 0.75rem;
    background: #fff8e1;
    border: 1px solid #ffe082;
    border-radius: 4px;
    font-size: 0.85rem;
}
.feedback-admin-note-label {
    font-weight: 700;
    color: #8a6d00;
    margin-bottom: 0.3rem;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.feedback-admin-note-body {
    color: var(--hn-text);
    line-height: 1.5;
}

.feedback-form-hint {
    margin-top: 0.25rem;
    font-size: 0.75rem;
    color: #888;
}

/* The post block itself reuses .forum-post styling. */


/* Feedback overview toolbar (sort + show-closed) */
.feedback-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 1.2rem;
    align-items: center;
    margin: 0 0 1rem 0;
    padding: 0.5rem 0.75rem;
    background: var(--hn-cream);
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    font-size: 0.85rem;
}
.feedback-toolbar-item {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    cursor: pointer;
}
.feedback-toolbar-select {
    padding: 3px 6px;
    font-size: 0.85rem;
}
.feedback-toolbar-count {
    color: #888;
    font-size: 0.78rem;
    font-weight: 500;
}
.feedback-card-footer {
    padding: 0.4rem 0.75rem;
    font-size: 0.78rem;
    border-top: 1px solid var(--hn-border);
    background: #faf7f0;
}

/* Sortable column headers on the Feedback & Ideeën overview */
.feedback-sort-link {
    color: inherit;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 0.15rem;
    cursor: pointer;
    white-space: nowrap;
}
.feedback-sort-link:hover {
    color: var(--hn-accent, #5b8dd9);
    text-decoration: underline;
}
.feedback-sort-link.is-active {
    color: var(--hn-accent, #5b8dd9);
}
.feedback-sort-arrow {
    font-size: 0.7em;
    line-height: 1;
}

/* ── Public-mode chrome (non-staff visitors of /feedback-ideeen) ──
   Public viewers see only items mods marked "openbaar". The .--list
   modifier on .feedback-vote-btn shrinks the in-table vote pills so
   they fit a row alongside the title/author/date cells without
   crowding out the other columns. */
.feedback-vote-btn--list {
    padding: 1px 8px;
    font-size: 0.78rem;
    gap: 0.25rem;
    line-height: 1.5;
}
.feedback-vote-btn--list .feedback-vote-emoji {
    font-size: 0.85rem;
}
.feedback-public-intro {
    margin-top: 0;
}
.feedback-public-badge {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 10px;
    background: #d1ecf1;
    color: #0c5460;
    font-size: 0.7rem;
    font-weight: 600;
    line-height: 1.4;
    vertical-align: middle;
    border: 1px solid #b8e0e8;
}

/* ── Mod controls block on the item view page ──
   Wraps the status changer and the new "Openbaar maken" toggle so
   they sit side-by-side with the same cream background, rather
   than stacking the visibility form onto a separate row. The
   visibility form re-uses the .feedback-status-form padding/border
   via .feedback-visibility-form so the two controls feel like one
   unified mod toolbar. */
.feedback-mod-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0 0 1rem 0;
}
.feedback-mod-controls .feedback-status-form,
.feedback-mod-controls .feedback-visibility-form {
    margin: 0;
}
.feedback-visibility-form {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    background: var(--hn-cream);
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    flex-wrap: wrap;
}
.feedback-visibility-label {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    color: var(--hn-text);
    cursor: pointer;
    margin: 0;
}
.feedback-visibility-toggle {
    margin: 0;
    cursor: pointer;
}

/* ════════════════════════════════════════════════════════════════════
   Helpdesk page (/helpdesk and /helpdesk/queue)
   Mirrors the Feedback & Ideeën design language: status / priority pills,
   filter bar above an hn-table queue, and a chronological message thread
   with distinct user vs staff message bubbles.
   ════════════════════════════════════════════════════════════════════ */

.helpdesk-status {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.7rem;
    font-weight: 600;
    color: #fff;
    white-space: nowrap;
    line-height: 1.4;
}
.helpdesk-status--open            { background: #5b8dd9; }
.helpdesk-status--in-behandeling  { background: var(--hn-gold); }
.helpdesk-status--behandeld       { background: var(--hn-green); }

.helpdesk-priority {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: 0.7rem;
    font-weight: 600;
    color: #fff;
    white-space: nowrap;
    line-height: 1.4;
}
.helpdesk-priority--laag    { background: #888; }
.helpdesk-priority--normaal { background: #5b8dd9; }
.helpdesk-priority--hoog    { background: #c0392b; }

.helpdesk-category-pill {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 1px 6px;
    background: var(--hn-cream);
    color: #555;
    border-radius: 8px;
    font-size: 0.7rem;
    font-weight: 500;
    white-space: nowrap;
}

.helpdesk-claimer-pill {
    display: inline-block;
    padding: 1px 6px;
    background: #f9f4ec;
    color: var(--hn-text);
    border-radius: 8px;
    font-size: 0.78rem;
    border: 1px solid var(--hn-border);
}

.helpdesk-claimer {
    font-weight: 600;
    color: var(--hn-text);
}

.helpdesk-meta-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.75rem;
}
.helpdesk-meta-left  { display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center; }
.helpdesk-meta-right { display: flex; gap: 0.5rem; align-items: center; }

.helpdesk-action-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    margin: 0 0 1rem 0;
    padding: 0.5rem 0.75rem;
    background: var(--hn-cream);
    border: 1px solid var(--hn-border);
    border-radius: 4px;
}

.helpdesk-claim-form { display: inline-block; }

/* ── Filter bar above the staff queue ─────────────────────── */
.helpdesk-filter-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: flex-end;
    margin-bottom: 1rem;
    padding: 0.75rem;
    background: var(--hn-cream);
    border: 1px solid var(--hn-border);
    border-radius: 4px;
}
.helpdesk-filter-group {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}
.helpdesk-filter-label {
    font-size: 0.75rem;
    color: #555;
    font-weight: 600;
}
.helpdesk-filter-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    cursor: pointer;
    font-size: 0.85rem;
    padding-bottom: 0.25rem;
}

/* ── Message thread (chronological, oldest first) ─────────── */
.helpdesk-thread {
    display: flex;
    flex-direction: column;
}
.helpdesk-message {
    padding: 0.75rem 1rem;
    border-bottom: 1px solid var(--hn-border);
}
.helpdesk-message:last-child { border-bottom: none; }
.helpdesk-message--user  { background: #fff; }
.helpdesk-message--staff { background: #f5f8fc; }

/* Author + role-tag grouping inside .forum-post-meta. The parent uses
   space-between, so without this wrapper the HELPDESK pill ends up
   floating in the visual center between the username (left) and the
   timestamp (right). Wrapping author + tag in this inline-flex anchors
   the tag right next to the name. */
.helpdesk-author-block {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
}

.helpdesk-staff-tag {
    display: inline-block;
    padding: 1px 6px;
    background: #5b8dd9;
    color: #fff;
    border-radius: 8px;
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    vertical-align: middle;
}

/* Admin variant of .helpdesk-staff-tag: dark red (matches .hn-admin-name
   #8b0000) so an admin reply visually pairs with the red username
   styling. Used by view.php when an admin posts on a ticket, replacing
   the blue "Helpdesk" pill. */
.helpdesk-admin-tag {
    display: inline-block;
    padding: 1px 6px;
    background: #8b0000;
    color: #fff;
    border-radius: 8px;
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    vertical-align: middle;
}

/* Assign-to-staffer dropdown sits at the right end of the action bar.
   Keeps the dropdown + button on one line and gives the small "Toewijzen
   aan:" label enough breathing room from the unclaim/close buttons that
   precede it. ms-auto pushes the whole form to the right when there's
   horizontal slack so it visually separates from the destructive
   actions. */
.helpdesk-assign-form {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    margin-left: auto;
}
.helpdesk-assign-label {
    font-size: 0.75rem;
    color: #555;
    font-weight: 600;
    margin: 0;
}
.helpdesk-assign-select {
    min-width: 12rem;
    padding: 0.25rem 0.4rem;
    font-size: 0.85rem;
}

/* Priority re-triage dropdown. Sits at the LEFT end of the action bar
   so the larger assign form's ms-auto pushes it visually away. Same
   compact look as .helpdesk-assign-* (small label + narrow select)
   but no auto-margin: it anchors next to the claim/unclaim buttons,
   and is also the only control moderators see inside the bar. */
.helpdesk-priority-form {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}
.helpdesk-priority-label {
    font-size: 0.75rem;
    color: #555;
    font-weight: 600;
    margin: 0;
}
.helpdesk-priority-select {
    min-width: 7rem;
    padding: 0.25rem 0.4rem;
    font-size: 0.85rem;
}

/* Yellow notice shown above the reply editor when an admin is about to
   post on a ticket they are not the claimer of: explains that the reply
   will reopen + claim a closed ticket, or take over an in_behandeling
   ticket from the current staffer. Same tone as other warning banners
   on the site (amber border, cream fill) so it reads as "heads-up, this
   has a side-effect" rather than "error". */
.helpdesk-takeover-notice {
    padding: 0.5rem 0.75rem;
    background: #fff8e1;
    border: 1px solid #e6c454;
    border-left: 4px solid #d4a017;
    border-radius: 4px;
    font-size: 0.85rem;
    color: #5c4a16;
}

/* ── Closed-ticket banner (top of the detail page) ─────────── */
/* Sits directly under the back link and tells the user the thread is
   locked before they scroll past the entire ticket body. Green so the
   tone reads as "resolved" rather than "error", lock icon for instant
   recognisability, and a built-in reopen button for the asker. */
.helpdesk-closed-banner {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.6rem;
    padding: 0.6rem 0.9rem;
    background: #e6f4ea;
    border: 1px solid #9ccfae;
    border-left: 4px solid var(--hn-green);
    border-radius: 4px;
    color: #1f5132;
    font-size: 0.92rem;
    font-weight: 600;
}
.helpdesk-closed-banner-icon {
    font-size: 1.1rem;
    line-height: 1;
}
.helpdesk-closed-banner-text {
    flex: 1 1 auto;
    min-width: 0;
}

/* Trainer dashboard – response-time chart in the statistics card */
.trainer-response-chart-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.4rem;
    font-size: 0.88rem;
    color: var(--hn-text, #333);
}
.trainer-response-chart {
    position: relative;
    height: 260px;
}
.trainer-response-chart-empty {
    padding: 0.75rem 0.25rem 0;
}

/* ── Admin dashboard: shared-IP detection ──────────────────── */
/* Inline user-activity link used across the admin dashboard
   (currently-online table, suspicious-players table, shared-IP table).
   Each link opens an activity modal for that user via JS. */
.hn-activity-link {
    font-weight: 700;
    color: var(--hn-text);
    text-decoration: underline dotted;
    cursor: pointer;
}
.hn-activity-link:hover {
    color: var(--hn-brown);
}

.hn-shared-ip-empty {
    padding: .75rem 1rem;
    color: #6a5430;
    font-size: .9rem;
}
.hn-shared-ip-ip {
    display: block;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .82rem;
    color: var(--hn-brown);
    font-weight: 600;
}
.hn-shared-ip-count {
    display: block;
    font-size: .72rem;
    color: #8a7b5c;
    margin-top: 1px;
}
.hn-shared-ip-note {
    padding: .6rem .85rem;
    margin: 0;
    font-size: .78rem;
    color: #6a5430;
    background: #faf7ee;
    border-top: 1px solid var(--hn-border);
    line-height: 1.45;
}
.hn-shared-ip-note code {
    background: #ede4ce;
    color: #5a4432;
    padding: 0 .3rem;
    border-radius: 3px;
    font-size: .82em;
}

/* ── Admin: IP watchlist form ───────────────────────────────── */
/* "Nieuw IP toevoegen" form on /admin/ip-watchlist. Each labeled
   field is a vertical stack; the reason field grows to fill remaining
   space so long reasons still fit on one line, and the IP / expiry
   fields keep a stable minimum width so the row doesn't reflow into
   chaos on narrow viewports. */
.ip-watchlist-form .ip-watchlist-field {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    min-width: 12rem;
}
.ip-watchlist-form .ip-watchlist-field-grow {
    flex: 1 1 18rem;
}
.ip-watchlist-form .ip-watchlist-field input,
.ip-watchlist-form .ip-watchlist-checkbox + button {
    /* let inputs inherit the form-control look already used elsewhere; no
       overrides here, but the selector exists so future tweaks have a
       single hook. */
}

/* ── Admin dashboard: suspicious activity (fast-click detection) ─────── */
/* Used by the "Verdachte activiteit gedetecteerd" card on /admin. The
   reaction-time data behind these classes is captured in Task::attempt
   with millisecond precision (see hn_task_log.reaction_ms), so a
   sub-500ms median is a strong bot signal and gets the boldest tint. */
.hn-card-header-warn {
    background: #fef2f2;
    color: #991b1b;
}
.hn-suspicion-table {
    font-size: .85rem;
}
.hn-suspicion-fast {
    color: #dc2626;
    font-weight: 600;
}
.hn-suspicion-fast-strong {
    color: #dc2626;
    font-weight: 700;
}
.hn-suspicion-veryfast-cell {
    color: #b91c1c;
    font-weight: 700;
}
.hn-suspicion-median-cell {
    font-variant-numeric: tabular-nums;
}
.hn-suspicion-median-fast {
    background: #fef3c7;
    color: #92400e;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.hn-suspicion-median-veryfast {
    background: #fee2e2;
    color: #991b1b;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.hn-suspicion-note {
    font-size: .78rem;
    color: #888;
    padding: .5rem 1rem;
}
.hn-risk-pill {
    color: #fff;
    padding: 1px 8px;
    border-radius: 4px;
    font-size: .78rem;
    font-weight: 700;
}
.hn-risk-high { background: #dc2626; }
.hn-risk-med  { background: #ea580c; }
.hn-risk-low  { background: #ca8a04; }
.hn-activity-stats {
    display: flex;
    gap: 1.5rem;
    flex-wrap: wrap;
}
.hn-activity-stats + .hn-activity-stats {
    margin-top: .5rem;
    padding-top: .5rem;
    border-top: 1px dashed var(--hn-border);
}

/* One soft tint per IP group. Every row of a group shares the same tint
   so accounts sharing an IP cluster visually. The five tints rotate
   across groups so adjacent groups stay distinguishable. */
.hn-ipgroup-g0 td { background: #fbf4ea; }
.hn-ipgroup-g1 td { background: #edf2e5; }
.hn-ipgroup-g2 td { background: #e7eff4; }
.hn-ipgroup-g3 td { background: #faeee0; }
.hn-ipgroup-g4 td { background: #f2e9f1; }

.hn-ipgroup-start td {
    border-top: 2px solid #cfc4a8;
}

/* ─── Weiland (admin pasture editor) ───────────────────────────────────
   Grid editor lives inside a single hn-card. The toolbar is a row of
   "tool" buttons that drive what a canvas click does (draw fence, drop
   trough, erase). The canvas itself sits in a fixed-aspect wrapper so
   the page layout doesn't collapse on narrow screens - overflow scrolls
   horizontally rather than letting the canvas shrink, because shrinking
   would put cell coordinates out of sync with click pixel positions. */
.weiland-page {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.weiland-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    padding: .65rem .85rem;
    background: var(--hn-cream);
    border: 1px solid var(--hn-border);
    border-radius: 6px;
}
.weiland-toolbar-group {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    align-items: center;
}
.weiland-toolbar-spacer {
    flex: 1 1 100%;
}
@media (min-width: 769px) {
    .weiland-toolbar-spacer {
        flex: 1;
    }
}
.weiland-toolbar-label {
    font-size: .78rem;
    font-weight: 700;
    color: var(--hn-brown);
    text-transform: uppercase;
    letter-spacing: .4px;
}

/* Tool buttons in the weiland editor reuse the site-standard
 * .btn-hn / .btn-hn-sm / .btn-hn-muted classes - no custom button
 * styling lives here on purpose. The only weiland-specific tweak is
 * a small inline gap so the count badges don't bump the icon. */
.weiland-toolbar .btn-hn[data-tool] {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}

/* Icon-only tool buttons: square footprint, image fills most of it,
 * count badge floats in the bottom-right corner like the colour
 * swatches. Keeping these compact lets the whole toolbar fit on one
 * line on phones and prevents the Wissen / save buttons from
 * scrolling off-screen.
 *
 * We also override the .btn-hn green fill here: the tool icons are
 * mostly brown/wood tones and disappear against dark green, so the
 * weiland tool buttons use a cream background in both active and
 * muted states, with a brown border + ring to indicate selection. */
.btn-hn.weiland-tool-btn {
    padding: 2px;
    line-height: 1;
    position: relative;
    background: var(--hn-cream);
    color: var(--hn-brown);
    border: 2px solid var(--hn-border);
}
.btn-hn.weiland-tool-btn:hover:not(:disabled),
.btn-hn.weiland-tool-btn:focus:not(:disabled),
.btn-hn.weiland-tool-btn:active:not(:disabled) {
    background: #fffaf0;
    color: var(--hn-brown);
}
/* Active tool = no .btn-hn-muted class. Highlight with a brown ring
 * (matches the colour-swatch active state) instead of switching to
 * the default green fill which would hide the brown artwork. */
.btn-hn.weiland-tool-btn:not(.btn-hn-muted) {
    background: var(--hn-cream);
    color: var(--hn-brown);
    border-color: var(--hn-brown);
    box-shadow: 0 0 0 2px var(--hn-cream), 0 0 0 4px var(--hn-brown);
}
.btn-hn.weiland-tool-btn:not(.btn-hn-muted):hover:not(:disabled) {
    background: #fffaf0;
}
.weiland-tool-btn .weiland-count {
    position: absolute;
    bottom: -6px;
    right: -6px;
    margin: 0;
    min-width: 1.1em;
    padding: 0 .25em;
    font-size: .62rem;
    line-height: 1.3;
    color: #fff;
    background: rgba(0, 0, 0, .55);
    border: 1px solid rgba(255, 255, 255, .7);
    box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
}
.hn-icon-tool {
    display: block;
    width: 40px;
    height: 40px;
    object-fit: contain;
}
.weiland-tool-glyph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    font-size: 1.8rem;
    line-height: 1;
}
/* The ❌ glyph renders much heavier than the cartoon icons, so we
 * scale just the eraser tool's glyph down a notch to keep the visual
 * weight balanced across the toolbar. */
.weiland-tool-glyph--eraser {
    font-size: 1.25rem;
}

/* Fence-colour picker (swatch buttons that sit inline with the Hek tool
 * button so it's clear which colour the next fence segment will use).
 * The fill comes from the inline `--swatch-color` CSS variable so the
 * same class works for any colour the server defines. The strip is
 * hidden via the standard `hidden` HTML attribute when a non-fence tool
 * is active. */
.weiland-fence-colors {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    margin-right: .35rem;
}
.weiland-fence-colors[hidden] {
    display: none;
}
.weiland-color-swatch {
    width: 32px;
    height: 32px;
    padding: 0;
    border: 2px solid rgba(0, 0, 0, 0.35);
    border-radius: 5px;
    background: var(--swatch-color, #6a4a2a);
    cursor: pointer;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.15);
    transition: transform .08s ease-in-out, box-shadow .08s ease-in-out;
}
.weiland-color-swatch:hover {
    transform: scale(1.08);
}
.weiland-color-swatch.is-active {
    border-color: var(--hn-brown);
    box-shadow: 0 0 0 2px var(--hn-cream), 0 0 0 4px var(--hn-brown);
}
.weiland-color-swatch:focus-visible {
    outline: 2px solid var(--hn-brown);
    outline-offset: 2px;
}

.weiland-canvas-wrap {
    background: #2d5016;
    border: 2px solid var(--hn-green-dark);
    border-radius: 6px;
    padding: 0;
    line-height: 0;
    /* On desktop the canvas would otherwise dominate the card body
     * (square aspect → very tall on wide screens), so we cap at 80% of
     * the available width and center it. Mobile drops back to the full
     * card width below to keep tap targets large enough. */
    max-width: 80%;
    margin-inline: auto;
}
@media (max-width: 768px) {
    .weiland-canvas-wrap {
        max-width: 100%;
    }
}
/* Compact variant used on public profile pages - renders the same canvas
 * at roughly half the width (and therefore half the height, since the
 * canvas preserves its aspect ratio via height:auto). */
.weiland-canvas-wrap--compact {
    max-width: 50%;
    margin-inline: 0;
}
@media (max-width: 768px) {
    .weiland-canvas-wrap--compact {
        max-width: 100%;
    }
}
.weiland-canvas {
    display: block;
    width: 100%;
    height: auto;
    /* No hard CSS cap - the canvas scales up to fill the card body. The
     * JS keeps the backing resolution at (displaySize × devicePixelRatio)
     * so text and sprites stay crisp even on a 1440px monitor. */
    /* The canvas backing resolution is matched to (CSS size × devicePixelRatio)
     * in JS, so we explicitly want smooth (non-pixelated) scaling - a
     * crisp-edges/pixelated rule here would chunkify the sprites and the
     * nametag text. */
    image-rendering: auto;
    /* Default is the normal arrow; JS flips the cursor to 'pointer' only
     * while the mouse is actually over a horse sprite (view/move modes).
     * Edit mode overrides this per-tool below. */
    cursor: default;
    background: #5fa14a;
}
.weiland-canvas.tool-erase  { cursor: cell; }
.weiland-canvas.tool-fence  { cursor: crosshair; }
.weiland-canvas.tool-gate   { cursor: crosshair; }
.weiland-canvas.tool-trough { cursor: copy; }

/* Tab-style switcher between the three Weiland modes. */
.weiland-mode-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem;
    margin-bottom: .75rem;
    border-bottom: 1px solid var(--hn-border);
}
.weiland-mode-tab {
    display: inline-block;
    padding: .45rem .85rem;
    border: 1px solid transparent;
    border-bottom: none;
    border-radius: 6px 6px 0 0;
    color: var(--hn-brown);
    font-weight: 600;
    font-size: .9rem;
    text-decoration: none;
    margin-bottom: -1px;
    background: transparent;
}
.weiland-mode-tab:hover {
    background: var(--hn-cream);
    color: var(--hn-brown);
    text-decoration: none;
}
.weiland-mode-tab.is-active {
    background: #fff;
    border-color: var(--hn-border);
    border-bottom-color: #fff;
    color: var(--hn-green-dark);
    cursor: default;
}

.weiland-horse-picker {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem;
    padding: .65rem .85rem;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
}
.weiland-horse-picker label {
    font-size: .82rem;
    font-weight: 600;
    color: var(--hn-brown);
}
.weiland-horse-picker select {
    flex: 1 1 220px;
    min-width: 200px;
    padding: .35rem .5rem;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    background: #fff;
    color: var(--hn-brown);
    font-size: .9rem;
}
.weiland-horse-picker .weiland-picker-hint {
    flex-basis: 100%;
    font-size: .75rem;
    color: #6a5430;
    line-height: 1.35;
}

/* Inline variant of the picker that lives inside the editor card just
 * above the toolbar (instead of in its own separate hn-card below the
 * canvas). Slightly tighter padding so it doesn't dominate the card. */
.weiland-horse-picker.weiland-horse-picker-inline {
    margin-bottom: .6rem;
    padding: .5rem .65rem;
    background: var(--hn-cream);
}

/* Horse-selection panel on /pasture: shown on the interactive view
 * while a horse sprite is picked up, providing a "back to stable"
 * shortcut and an explicit cancel so the user can always back out of
 * the pick-up & drop. */
.weiland-selection-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .5rem;
}

/* Bottom-of-page strip on /pasture (interactive view) listing horses
 * still in the stable. Each card is a button: clicking arms the horse
 * for placement so the next click on the canvas drops it into a cell
 * via /pasture/place-horse. The strip scrolls horizontally on narrow
 * screens so a long stable list never blows out the layout. */
.weiland-horse-strip {
    display: flex;
    flex-wrap: nowrap;
    gap: .5rem;
    overflow-x: auto;
    padding-bottom: .35rem;
}
.weiland-horse-strip-empty {
    margin: 0;
    padding: .65rem .85rem;
    background: var(--hn-cream);
    border: 1px dashed var(--hn-border);
    border-radius: 5px;
    color: #6a5430;
    font-size: .82rem;
    flex: 1 1 auto;
}
/* Note above the strip listing how many of the player's horses are
   intentionally hidden (entered in a race or under a caretaker contract).
   Same warm palette as the empty placeholder but a solid info-style
   border so it doesn't look like another empty-state message. */
.weiland-horse-strip-note {
    margin: 0 0 .55rem;
    padding: .55rem .8rem;
    background: #fff7e0;
    border: 1px solid #d49a1f;
    border-radius: 5px;
    color: #5a4221;
    font-size: .82rem;
    line-height: 1.35;
}
.weiland-horse-strip-card {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: .25rem;
    flex: 0 0 auto;
    width: 92px;
    padding: .4rem .35rem .35rem;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    color: var(--hn-brown);
    font: inherit;
    cursor: pointer;
    transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.weiland-horse-strip-card:hover {
    border-color: var(--hn-green-dark);
    background: var(--hn-cream);
}
.weiland-horse-strip-card.is-active {
    border-color: #d49a1f;
    background: #fff7e0;
    box-shadow: 0 0 0 2px rgba(212, 154, 31, 0.35);
}
.weiland-horse-strip-sprite {
    width: 56px;
    height: 56px;
    object-fit: contain;
    image-rendering: -webkit-optimize-contrast;
    image-rendering: crisp-edges;
}
.weiland-horse-strip-sprite.is-foal {
    width: 42px;
    height: 42px;
}
.weiland-horse-strip-name {
    font-size: .78rem;
    font-weight: 600;
    line-height: 1.15;
    text-align: center;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: 100%;
}

.weiland-pasture-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: .5rem;
}
.weiland-pasture-card {
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 5px;
    padding: .55rem .7rem;
    font-size: .82rem;
    color: var(--hn-brown);
}
.weiland-pasture-card-head {
    font-weight: 700;
    color: var(--hn-green-dark);
    margin-bottom: .2rem;
}
.weiland-pasture-card-warn {
    color: #c0392b;
    font-weight: 600;
    font-size: .76rem;
    margin-top: .25rem;
}
.weiland-pasture-empty {
    padding: .75rem;
    background: var(--hn-cream);
    border: 1px dashed var(--hn-border);
    border-radius: 5px;
    color: #6a5430;
    font-size: .85rem;
    text-align: center;
}

/* Teaser shown to players below WEILAND_MIN_RANK on /pasture so the
   read-only viewer doesn't look broken (no fences, no horses) and they
   know what unlocks at rank 2. Same look as .weiland-pasture-empty
   but with a leading 🌾 from the strong tag and a touch more breathing
   room since it's a top-of-card notice instead of a list placeholder. */
.weiland-locked-notice {
    padding: .85rem 1rem;
    background: var(--hn-cream);
    border: 1px dashed var(--hn-border);
    border-radius: 5px;
    color: #6a5430;
    font-size: .85rem;
    line-height: 1.45;
    margin-bottom: .75rem;
}
.weiland-locked-notice strong { color: #5a4221; }

.weiland-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    font-size: .75rem;
    color: #6a5430;
    margin-top: .25rem;
}
.weiland-legend-item {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
}
.weiland-legend-swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 2px;
    border: 1px solid var(--hn-border);
}
.weiland-legend-swatch.is-grass    { background: #5fa14a; border-color: #3e7a30; }
.weiland-legend-swatch.is-fence    { background: #6a4a2a; border-color: #4a3220; }
.weiland-legend-swatch.is-gate     {
    background: linear-gradient(to right,
        #4a3220 0%, #4a3220 18%, #6a4a2a 18%, #6a4a2a 78%,
        transparent 78%, transparent 100%);
    border-color: #4a3220;
}
.weiland-legend-swatch.is-pasture  { background: rgba(40, 110, 40, .35); border-color: #2d5016; }
.weiland-legend-swatch.is-trough   { background: #4aa3df; border-color: #2d5a7a; }
.weiland-legend-swatch.is-haybale  { background: #d9b25f; border-color: #8c6b28; }

/* Inline hay-bale icon used in toolbar labels, legends and inventory
 * tiles. Sized to sit inline with text like an emoji glyph. The image
 * lives in /public/images/shop-hooi.png (the same asset used in the shop). */
.hn-icon-hooi {
    display: inline-block;
    height: 1.1em;
    width: auto;
    vertical-align: -0.2em;
}
.hn-icon-hooi.is-stock {
    height: 48px;
    vertical-align: middle;
}

/* Remaining-count badges on toolbar buttons / colour swatches. Shown
 * next to the tool icon so the admin always knows how many copies they
 * still own in their zadelkamer. Value "0" flips to a muted red so it
 * also doubles as a "can't place" indicator. */
.weiland-count {
    display: inline-block;
    min-width: 1.2em;
    padding: 0 .3em;
    margin-left: .25rem;
    border-radius: 10px;
    background: rgba(0, 0, 0, .35);
    color: #fff;
    font-size: .72rem;
    font-weight: 700;
    line-height: 1.4;
    text-align: center;
}
.weiland-toolbar [data-tool][data-remaining="0"] .weiland-count,
.weiland-color-swatch[data-remaining="0"] .weiland-count {
    background: #a83230;
}
.weiland-count-on-swatch {
    position: absolute;
    bottom: -6px;
    right: -6px;
    min-width: 1.1em;
    padding: 0 .25em;
    font-size: .62rem;
    line-height: 1.3;
    border: 1px solid rgba(255, 255, 255, .7);
    box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
}
.weiland-color-swatch { position: relative; }
/* Out-of-stock state: we deliberately do NOT dim the buttons or
 * swatches. Lowering the opacity muddies the cartoon icons and
 * shifts the apparent colour of the swatches (e.g. a brown swatch
 * washes into beige), making them look like different items. The
 * red "0" count badge is the stock signal on its own. We still flip
 * the cursor so empty tools clearly read as not-clickable. */
.weiland-toolbar [data-tool][data-remaining="0"],
.weiland-color-swatch[data-remaining="0"] {
    cursor: not-allowed;
}

/* ── Weilandvoorraad (inventory tile strip) ───────────────────────
 * Mirror the look of the Voervoorraad cards on the inventory page.
 * Each tile shows an emoji, an optional fence-colour swatch, the
 * item name, and a Voervoorraad-style "N×" quantity. */
.weiland-stock-grid {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
}
.weiland-stock-tile {
    flex: 1;
    min-width: 110px;
    background: var(--hn-cream);
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    padding: .75rem .5rem;
    text-align: center;
}
.weiland-stock-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .25rem;
    height: 52px;
}
.weiland-stock-emoji {
    font-size: 2rem;
    line-height: 1;
}
.weiland-stock-swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, .35);
}
/* Fence-colour modifiers - keep in sync with WEILAND_FENCE_COLORS in
 * config/game_constants.php. */
.weiland-stock-swatch.is-brown { background: #6a4a2a; }
.weiland-stock-swatch.is-black { background: #1c1c1c; }
.weiland-stock-swatch.is-white { background: #f4f0e6; }
.weiland-stock-swatch.is-red   { background: #a83230; }
.weiland-stock-label {
    font-size: .78rem;
    font-weight: 700;
    color: var(--hn-brown);
    text-transform: uppercase;
    letter-spacing: .4px;
    margin: .35rem 0 .2rem;
}
.weiland-stock-qty {
    font-size: 1.3rem;
    font-weight: 700;
}
.weiland-stock-qty.is-positive { color: var(--hn-green-dark); }
.weiland-stock-qty.is-zero     { color: #c0392b; }

/* Per-tile "sell N back to the shop" form. Stays inline with the qty
 * readout on desktop and wraps the rate onto a second line on narrow
 * tiles so the button label never truncates. */
.weiland-stock-sell {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .3rem;
    margin-top: .45rem;
    flex-wrap: wrap;
}
.weiland-stock-sell-qty {
    width: 3.25rem;
    padding: .2rem .3rem;
    font-size: .8rem;
}
.weiland-stock-sell-btn {
    white-space: nowrap;
    line-height: 1.1;
}
.weiland-stock-sell-rate {
    display: inline-block;
    font-size: .72rem;
    font-weight: 500;
    opacity: .85;
    margin-left: .15rem;
}

/* ── Shop - weiland category tile grid ────────────────────────────────
 * Mirrors the /shop/voer layout (image hero + description + in-stock
 * badge + price + buy form) but reads its available quantity from
 * WeilandController::getRemainingCountsForUser() so already-placed
 * fences / troughs / haybales don't inflate the stock number.
 */
.shop-tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
}
.shop-tile {
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 8px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}
.shop-tile-hero {
    position: relative;
    background: linear-gradient(135deg, rgba(45,80,22,.13) 0%, rgba(45,80,22,.06) 100%);
    border-bottom: 1px solid #e8d9bf;
    padding: 1.2rem;
    text-align: center;
    min-height: 120px;
    display: flex;
    align-items: center;
    justify-content: center;
}
/* Colour-tinted hero per fence colour - cheap way to make the four
 * fence tiles visually distinct without four different illustrations. */
.shop-tile--fence-brown .shop-tile-hero { background: linear-gradient(135deg, rgba(106,74,42,.18) 0%, rgba(106,74,42,.06) 100%); }
.shop-tile--fence-black .shop-tile-hero { background: linear-gradient(135deg, rgba(28,28,28,.18)  0%, rgba(28,28,28,.04)  100%); }
.shop-tile--fence-white .shop-tile-hero { background: linear-gradient(135deg, rgba(244,240,230,.8) 0%, rgba(244,240,230,.4) 100%); }
.shop-tile--fence-red   .shop-tile-hero { background: linear-gradient(135deg, rgba(168,50,48,.18)  0%, rgba(168,50,48,.05)  100%); }
.shop-tile-img {
    height: 90px;
    width: auto;
    object-fit: contain;
    display: block;
}
.shop-tile-emoji {
    font-size: 3rem;
    line-height: 1;
}
.shop-tile-swatch {
    position: absolute;
    right: .6rem;
    bottom: .6rem;
    width: 22px;
    height: 22px;
    border-radius: 4px;
    border: 2px solid #fff;
    box-shadow: 0 1px 2px rgba(0,0,0,.25);
}
.shop-tile-swatch.is-brown { background: #6a4a2a; }
.shop-tile-swatch.is-black { background: #1c1c1c; }
.shop-tile-swatch.is-white { background: #f4f0e6; border-color: var(--hn-border); }
.shop-tile-swatch.is-red   { background: #a83230; }
.shop-tile-body {
    padding: .75rem;
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: .4rem;
}
.shop-tile-title {
    font-weight: 700;
    font-size: 1rem;
    color: #3a2a10;
}
.shop-tile-tier {
    display: inline-block;
    padding: .1rem .35rem;
    margin-left: .25rem;
    border-radius: 3px;
    border: 1px solid currentColor;
    font-size: .62rem;
    font-weight: 600;
    vertical-align: middle;
}
.shop-tile-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-top: auto;
    padding-top: .5rem;
    border-top: 1px solid #f0e8d6;
}
.shop-tile-stock-line {
    display: flex;
    align-items: baseline;
    gap: .3rem;
}
.shop-tile-stock {
    font-weight: 700;
}
.shop-tile-stock.is-positive { color: var(--hn-green-dark); }
.shop-tile-stock.is-zero     { color: #c0392b; }
.shop-tile-price {
    font-weight: 700;
    font-size: 1.05rem;
}
.shop-tile-price.is-affordable   { color: var(--hn-green-dark); }
.shop-tile-price.is-unaffordable { color: #c0392b; }
.shop-tile-form {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin-top: .4rem;
}
.shop-tile-qty {
    width: 60px;
    padding: 5px 7px;
    font-size: .88rem;
}
.shop-tile-buy {
    flex: 1;
    text-align: center;
}
.shop-tile-buy-disabled {
    flex: 1;
    text-align: center;
}

/* ── /credits page — premium shop hub ─────────────────────────────
 * Tiles reuse the .shop-tile component above; the .credit-tile
 * helpers tweak spacing for tiles that contain forms instead of
 * a single buy button, plus a few small tokens for the page chrome
 * (balance pill, brown header, "coming soon" buy card).
 */
.hn-card-header--brown { background: var(--hn-brown); }

.credit-balance-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
    margin-bottom: .25rem;
}
.credit-balance-row--top {
    align-items: flex-start;
}
.credit-balance-pill {
    background: linear-gradient(135deg, #d4a017, #b8870f);
    color: #fff;
    border-radius: 8px;
    padding: .75rem 1.25rem;
    text-align: center;
    min-width: 140px;
}
.credit-balance-label {
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: 1px;
    opacity: .85;
}
.credit-balance-amount {
    font-size: 1.8rem;
    font-weight: 700;
    line-height: 1.2;
}
.credit-balance-text {
    flex: 1 1 16rem;
    min-width: 0;
}
/* Phone: when the row wraps, center the gold balance pill on its own
   line so it sits visually balanced above the explanatory text. */
@media (max-width: 600px) {
    .credit-balance-pill { margin-inline: auto; }
}

.credit-tile .shop-tile-body {
    gap: .55rem;
}
.credit-tile-hero {
    background: linear-gradient(135deg, rgba(212,160,23,.18) 0%, rgba(123,79,46,.06) 100%);
}
.credit-tile-hero--gold {
    background: linear-gradient(135deg, rgba(212,160,23,.30) 0%, rgba(184,135,15,.10) 100%);
}
.credit-tile-label {
    margin-bottom: -.2rem;
}
.credit-tile-select,
.credit-tile-input {
    width: 100%;
    padding: 5px 8px;
    font-size: .88rem;
}
.credit-tile-empty {
    background: #f5f0e6;
    border: 1px dashed var(--hn-border);
    border-radius: 4px;
    padding: .35rem .55rem;
}

/* Admin-only popularity badge rendered inside credit shop tiles and
 * below each credit package button. Subtle dashed gold callout so it
 * is clearly separate from regular shop copy and obviously an
 * internal/staff-only readout. Hidden entirely for non-admin users
 * (the controller passes adminStats=null and the helper returns ''). */
.credit-admin-stat {
    margin: 0;
    padding: .3rem .5rem;
    border: 1px dashed #d4a017;
    border-radius: 4px;
    background: #fff8e1;
    color: #5a3d00;
    font-size: .72rem;
    line-height: 1.3;
}
.credit-admin-stat--package {
    margin-top: .35rem;
    text-align: center;
}
.credit-admin-stat-tag {
    display: inline-block;
    margin-right: .25rem;
    padding: 0 .35rem;
    border-radius: 3px;
    background: #d4a017;
    color: #fff;
    font-size: .65rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    vertical-align: baseline;
}

.credit-buy-header {
    background: linear-gradient(135deg, #d4a017, #7b4f2e);
    color: #fff;
}
/* Inline notice boxes used inside cards on /credits:
 *  - .credit-buy-beta-notice : "this is real money during beta" warning
 *    inside the buy-credits card.
 *  - .credits-info-minor     : "under-18 needs parental consent" callout
 *    inside the about-credits info card at the top.
 * Both share the same warm gold tone so they read as the same kind of
 * call-for-attention without competing with the brown brand palette. */
.credit-buy-beta-notice,
.credits-info-minor {
    border: 1px solid #d4a017;
    background: #fff8e1;
    color: #5a3d00;
    border-radius: 8px;
    padding: .75rem 1rem;
    margin-bottom: 1rem;
}
.credit-buy-beta-notice strong,
.credits-info-minor strong {
    color: #7a5a00;
}
.credits-info-minor {
    margin-bottom: 0;
}

/* /credits: clickable Mollie package buttons rendered as a responsive
 * grid of mini-cards. Auto-fit lets us go from 4 columns on desktop
 * down to 2 (and 1 on very narrow phones) without media queries. */
.credit-package-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: .75rem;
}
.credit-package-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .15rem;
    padding: .9rem .6rem;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 8px;
    color: #3a2a10;
    cursor: pointer;
    transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease;
    font-family: inherit;
    width: 100%;
}
.credit-package-btn:hover {
    transform: translateY(-2px);
    border-color: #d4a017;
    box-shadow: 0 4px 12px rgba(123, 79, 46, 0.15);
}
.credit-package-label {
    font-size: .78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: #7b4f2e;
}
.credit-package-amount {
    font-size: 1.8rem;
    font-weight: 700;
    line-height: 1;
    color: #d4a017;
    margin-top: .15rem;
}
.credit-package-unit {
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: #8b7a5a;
}
.credit-package-price {
    font-size: 1.05rem;
    font-weight: 700;
    margin-top: .35rem;
}
.credit-package-rate {
    font-size: .7rem;
    color: #8b7a5a;
}
/* When a package is selected the confirm panel below shows its details.
 * Highlight the matching tile so it's clear which one the panel refers to. */
.credit-package-form.is-selected .credit-package-btn {
    border-color: #d4a017;
    box-shadow: 0 0 0 2px rgba(212, 160, 23, 0.35), 0 4px 12px rgba(123, 79, 46, 0.18);
    background: #fffaf0;
}

/* Confirmation panel rendered below the package grid when a package is
 * clicked. Mirrors the soft cream/gold of the buy-card so it visually
 * belongs to the same flow without competing with the brown brand bar. */
.credit-buy-confirm {
    margin-top: 1rem;
    padding: 1rem 1.1rem;
    background: #fffaf0;
    border: 1px solid #d4a017;
    border-radius: 8px;
}
.credit-buy-confirm-title {
    margin: 0 0 .5rem;
    color: #5a3d00;
    font-size: 1.05rem;
    font-weight: 700;
}
.credit-buy-confirm-summary {
    margin: 0;
    font-size: .95rem;
    color: #3a2a10;
}
.credit-buy-confirm-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

/* Row of Mollie payment-method logos rendered on the confirm panel.
 * Logos are SVG so they scale crisply; `height` controls the visible
 * size, width adjusts automatically to preserve each logo's aspect. */
.credit-payment-icons {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem .8rem;
    padding: .5rem .6rem;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
}
.credit-payment-icon {
    display: block;
    height: 28px;
    width: auto;
}

/* Horse first-time rename input on /horses/{id}: stretches to fill the
 * remaining space inside the d-flex row that contains the submit button. */
.horse-rename-input {
    flex: 1;
    min-width: 0;
    padding: 6px 8px;
}

/* /stable header showing the locked-in stable name + initials after the
 * free first-time setup. The actual rename is on /credits. */
.stable-name-display {
    display: flex;
    align-items: flex-start;
    gap: .6rem;
}
.stable-name-check {
    color: #2d5016;
    font-size: 1.3rem;
    line-height: 1.2;
}
.stable-name-value {
    font-size: 1.1rem;
}

/* ── Handelaar market gauge ─────────────────────────────────────
   Shown on /handelaar above the value/sell card. Conveys current
   buy-price climate (low → normal → high) without exposing the
   underlying scarcity multiplier or horse/player ratio. The needle
   element receives a single inline `transform: rotate(...deg)`
   computed server-side (truly dynamic per request).            */
.market-gauge {
    text-align: center;
    padding: .35rem 0 .25rem;
}
.market-gauge-dial {
    position: relative;
    width: 240px;
    height: 130px;
    margin: 0 auto;
    overflow: hidden;
}
.market-gauge-track {
    position: absolute;
    top: 0;
    left: 0;
    width: 240px;
    height: 240px;
    border-radius: 50%;
    background: conic-gradient(
        from -90deg at 50% 50%,
        #b54a3a   0deg  35deg,
        #d68b3d  35deg  70deg,
        #4a7c2f  70deg 110deg,
        #d68b3d 110deg 145deg,
        #b54a3a 145deg 180deg,
        transparent 180deg 360deg
    );
}
.market-gauge-track-mask {
    position: absolute;
    top: 26px;
    left: 26px;
    width: 188px;
    height: 188px;
    border-radius: 50%;
    background: var(--hn-cream);
}
.market-gauge-tick {
    position: absolute;
    bottom: 0;
    left: calc(50% - 1px);
    width: 2px;
    height: 110px;
    transform-origin: 50% 100%;
    pointer-events: none;
}
.market-gauge-tick::after {
    content: "";
    position: absolute;
    top: 4px;
    left: -3px;
    width: 8px;
    height: 10px;
    background: rgba(0, 0, 0, .35);
    border-radius: 1px;
}
.market-gauge-tick--low  { transform: rotate(-90deg); }
.market-gauge-tick--mid  { transform: rotate(0deg); }
.market-gauge-tick--high { transform: rotate(90deg); }
.market-gauge-needle {
    position: absolute;
    bottom: 0;
    left: calc(50% - 2px);
    width: 4px;
    height: 108px;
    background: linear-gradient(180deg, #111827 0%, #4b5563 100%);
    border-radius: 3px 3px 1px 1px;
    transform-origin: 50% 100%;
    transition: transform .9s cubic-bezier(.34, 1.56, .64, 1);
    box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
    z-index: 2;
}
.market-gauge-pivot {
    position: absolute;
    bottom: -9px;
    left: calc(50% - 9px);
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--hn-brown);
    border: 3px solid var(--hn-cream);
    z-index: 3;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .4);
}
.market-gauge-axis {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    max-width: 260px;
    margin: .4rem auto 0;
    padding: 0 .25rem;
    font-size: .72rem;
    color: var(--hn-brown);
    font-weight: 600;
    letter-spacing: .02em;
    text-transform: uppercase;
}
.market-gauge-axis-label--mid {
    text-align: center;
    opacity: .8;
}
.market-gauge-status {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    margin-top: 1rem;
    padding: .15rem .15rem;
    flex-wrap: wrap;
    justify-content: center;
}
.market-gauge-badge {
    display: inline-block;
    padding: .15rem .6rem;
    font-size: .78rem;
    font-weight: 700;
    border-radius: 999px;
    background: #e5e7eb;
    color: #1f2937;
    border: 1px solid rgba(0, 0, 0, .08);
    letter-spacing: .03em;
    text-transform: uppercase;
}
.market-gauge-price {
    font-size: .95rem;
    font-weight: 700;
    color: var(--hn-brown);
}
.market-gauge--cold     .market-gauge-badge,
.market-gauge--hot      .market-gauge-badge { background: #f3d7d2; color: #6e2316; border-color: #b54a3a; }
.market-gauge--cool     .market-gauge-badge,
.market-gauge--warm     .market-gauge-badge { background: #f4dfc4; color: #6a4220; border-color: #d68b3d; }
.market-gauge--balanced .market-gauge-badge { background: #dceacb; color: #2d5016; border-color: #4a7c2f; }
.market-gauge-desc {
    margin: .55rem auto 0;
    max-width: 380px;
    font-size: .88rem;
    color: var(--hn-text);
    line-height: 1.45;
}
@media (prefers-reduced-motion: reduce) {
    .market-gauge-needle { transition: none; }
}

/* ── Profile "Acties" pane ────────────────────────────────────────────────
   Stack the action buttons (send PM, friend request, report, block, …)
   in a single column with consistent gap and width so they read as one
   group instead of a pile of mismatched <a>/<button>/<form> elements.
   Forms are flattened to keep each button on its own row.
   width: fit-content keeps the column hugging its widest label so a
   long button (e.g. "Vriendschapsverzoek verstuurd ✕") doesn't blow up
   the rest. */
.profile-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .4rem;
}
.profile-actions form { margin: 0; }
.profile-actions .btn-hn { width: fit-content; }

/* ── User reports (admin queue) ──────────────────────────────────────────── */

.report-info {
    background: #f0ebe1;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    padding: .65rem .85rem;
    margin-bottom: 1rem;
    font-size: .88rem;
    color: #555;
    line-height: 1.5;
}
.report-info-list {
    margin: .25rem 0 0;
    padding-left: 1.2rem;
}
.report-info-list li { margin-bottom: .25rem; }

.report-tab {
    padding: 6px 14px;
    border-radius: 6px;
    text-decoration: none;
    font-size: .88rem;
    font-weight: 600;
    color: #555;
    background: transparent;
    border: 1px solid var(--hn-border);
}
.report-tab:hover:not(.is-active) { background: #f5f1e6; }
.report-tab-pending.is-active   { color: #fff; background: #f59e0b; border-color: #f59e0b; }
.report-tab-handled.is-active   { color: #fff; background: #16a34a; border-color: #16a34a; }
.report-tab-dismissed.is-active { color: #fff; background: #6b7280; border-color: #6b7280; }

.report-case {
    border: 1px solid var(--hn-border);
    border-radius: 8px;
    overflow: hidden;
    margin-bottom: 1rem;
    background: #fff;
}
.report-case-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    padding: .65rem .9rem;
    background: #f9f7f3;
    border-bottom: 1px solid var(--hn-border);
}
.report-case-count {
    font-size: .75rem;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: 999px;
    background: #e5e1d4;
    color: #555;
}
.report-case-meta { font-size: .78rem; color: #888; }
.report-list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.report-item {
    padding: .65rem .9rem;
    border-bottom: 1px dashed var(--hn-border);
}
.report-item:last-child { border-bottom: 0; }
.report-item-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    flex-wrap: wrap;
    margin-bottom: .25rem;
}
.report-item-body {
    font-size: .9rem;
    line-height: 1.55;
    color: #333;
    word-break: break-word;
    white-space: pre-wrap;
}
.report-case-actions {
    padding: .65rem .9rem;
    border-top: 1px solid var(--hn-border);
    background: #fafaf6;
}
.report-action-form { margin: 0; }
.report-note-input {
    width: 100%;
    margin-bottom: .55rem;
}
.report-case-footer {
    padding: .55rem .9rem;
    border-top: 1px solid var(--hn-border);
    background: #f7f7f2;
    font-size: .82rem;
    color: #666;
}

.report-badge {
    font-size: .72rem;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: 4px;
    text-transform: uppercase;
    letter-spacing: .03em;
}
.report-badge-pending   { background: #fef3c7; color: #92400e; }
.report-badge-handled   { background: #dcfce7; color: #166534; }
.report-badge-dismissed { background: #e5e7eb; color: #374151; }

/* ── Horse groups page (/groups, hidden) ─────────────────────────────────── */
.groups-group-card { margin-top: .85rem; }
.groups-card-list .groups-group-card { cursor: default; }

/* The header carries the drag handle, group name, badges and actions.
   Keep it wrapping cleanly on narrow viewports. */
.groups-card-header { flex-wrap: wrap; gap: .5rem; }
.groups-card-drag-handle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.4rem;
    color: rgba(255, 255, 255, .8);
    font-size: 1.05rem;
    line-height: 1;
    cursor: grab;
    user-select: none;
    touch-action: none;
}
.groups-card-drag-handle:hover { color: #fff; }
.groups-card-drag-handle:active { cursor: grabbing; }
.groups-card-actions {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
}

.groups-count-badge {
    background: rgba(0, 0, 0, .25);
    border-radius: 10px;
    padding: 1px 7px;
    font-weight: 700;
}

.groups-order-hint { line-height: 1.3; }

.groups-name-input {
    padding: 5px 8px;
    width: 100%;
    max-width: 280px;
    margin-right: .35rem;
}
.groups-new-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin: 0;
}

.groups-rename-toggle { position: relative; }
.groups-rename-toggle > summary { list-style: none; cursor: pointer; }
.groups-rename-toggle > summary::-webkit-details-marker { display: none; }
.groups-rename-form {
    position: absolute;
    right: 0;
    top: 100%;
    z-index: 5;
    background: #fff;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    padding: .5rem;
    box-shadow: 0 4px 14px rgba(0, 0, 0, .15);
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    align-items: center;
    min-width: 280px;
}

.groups-note-form { margin: 0; }
.groups-note-input {
    width: 100%;
    min-height: 3rem;
    resize: vertical;
    padding: .4rem .55rem;
    font-family: inherit;
    font-size: .88rem;
}

/* Note "preview" state: the saved text plus a small Bewerken / Toevoegen
   button. Read-only, non-clickable text so the user can copy/select it
   without accidentally triggering edit mode (the trigger is the button,
   not the paragraph). */
.groups-note-block { margin: 0; }
.groups-note-preview .groups-note-text,
.groups-note-preview .groups-note-empty {
    /* Text grows to fill available space, wraps naturally, and never
       squishes the edit button on its right. min-width:0 lets the
       paragraph shrink past its intrinsic content size when wrapping. */
    flex: 1 1 auto;
    min-width: 0;
}
.groups-note-text {
    white-space: pre-wrap;
    word-break: break-word;
    color: var(--hn-text);
}
.groups-note-empty {
    font-style: italic;
}
.groups-note-edit-btn {
    font-size: .78rem;
    flex-shrink: 0;
}

/* Disciplines block now sits at the bottom of each group card and is
   visually a "footer" - lighter top border separates it from the horse
   table without competing with the card frame. */
.groups-disc-block {
    border-top: 1px dashed var(--hn-border);
    padding-top: .65rem;
}

.groups-disc-row { row-gap: .4rem; }
.groups-disc-pill {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .25rem .65rem;
    border: 1px solid var(--hn-border);
    border-radius: 999px;
    background: #fff;
    font-size: .82rem;
    cursor: pointer;
    user-select: none;
    transition: background-color .12s ease, color .12s ease;
}
.groups-disc-pill:hover { background: #f5efde; }
.groups-disc-pill input { margin: 0; cursor: pointer; }
.groups-disc-pill.is-off {
    background: #f1ede2;
    color: #8a8071;
    text-decoration: line-through;
}

.groups-move-select {
    padding: 3px 6px;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    background: #fff;
    font-size: .8rem;
    max-width: 180px;
}

.groups-sort-row { row-gap: .35rem; }
.groups-sort-select {
    padding: 3px 6px;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    background: #fff;
    font-size: .82rem;
}
.groups-drag-hint { line-height: 1.3; }

/* Drag handle column shown only when sort_mode = 'manual'. The handle
   itself is the only grabbable area; clicking elsewhere on the row still
   works as a normal "open horse detail" link via data-href. */
.groups-drag-handle {
    display: inline-block;
    padding: 0 .35rem;
    color: #8a7a55;
    font-size: 1.05rem;
    line-height: 1;
    cursor: grab;
    user-select: none;
    touch-action: none;
}
.groups-drag-handle:hover { color: var(--hn-brown); }
.groups-drag-handle:active { cursor: grabbing; }

/* SortableJS state classes. The ghost is the placeholder left behind in
   the list while dragging; chosen is the source row; drag is the floating
   clone that follows the cursor. */
.groups-row-ghost  { opacity: .35; background: #f5efde; }
.groups-row-chosen { background: #fef9e6; }
.groups-row-drag   {
    background: #fff;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
    border-radius: 4px;
}
.groups-card-ghost { opacity: .45; }
.groups-card-chosen { box-shadow: 0 0 0 2px rgba(125, 99, 50, .18); }
.groups-card-drag {
    box-shadow: 0 8px 22px rgba(0, 0, 0, .2);
    border-radius: 8px;
}

/* ──────────────────────────────────────────────────────────────
   Public Homepage (launch / future www)
   Used by views/layouts/public.php + views/home/*.php. Drops both
   sidebars, replaces the in-app user-stats area with a public nav,
   and lays out the Welkom + Statistieken/Inloggen + Registreren
   stack used by the guest landing page.
   ────────────────────────────────────────────────────────────── */

/* Body wrapper - no sidebar gutters and no center separator line.
   Uses the #hn-body ID in the selector so we beat the base
   #hn-body { background: ... } rule (ID specificity), otherwise
   the in-app dark-green-left + cream-right sidebar gradients keep
   showing through and the page looks like it's missing its sidebar. */
#hn-body.hn-body-public {
    background: var(--hn-cream);
}
#hn-body.hn-body-public::after { display: none; }

/* Header: replace the in-app stats area with our public nav links.
   The header itself keeps its sticky green-gradient look from
   #hn-header above; we just empty out the sidebar mirror zone and
   fill the rest with two nav clusters. ID specificity used so the
   override actually beats the base #hn-header rule, otherwise the
   first nav pill (Home) sits flush against the left viewport edge. */
#hn-header.hn-header-public { padding: 0 1.25rem; gap: .5rem; }

.hn-public-nav {
    display: flex;
    align-items: center;
    gap: .25rem;
    flex-wrap: wrap;
}
.hn-public-nav-auth { margin-left: auto; }

.hn-public-nav-link {
    display: inline-block;
    padding: 4px 12px;
    color: rgba(255,255,255,.85);
    font-size: .85rem;
    font-weight: 600;
    border-radius: 4px;
    background: rgba(0,0,0,.18);
    text-decoration: none;
    transition: background .15s, color .15s;
    white-space: nowrap;
}
.hn-public-nav-link:hover {
    background: rgba(0,0,0,.32);
    color: #fff;
    text-decoration: none;
}
.hn-public-nav-link.active {
    background: rgba(255,255,255,.2);
    color: #fff;
}
.hn-public-nav-link.hn-public-nav-cta {
    background: var(--hn-gold);
    color: #fff;
}
.hn-public-nav-link.hn-public-nav-cta:hover {
    background: #b8870f;
    color: #fff;
}

/* Center column fills the space the sidebars used to take, capped
   at a comfortable reading width. */
#hn-content.hn-public-content {
    flex: 1;
    margin: 0 auto;
    width: 100%;
    max-width: 960px;
    padding: 1.25rem 1rem 2rem;
    background: var(--hn-cream);
}

/* Welcome / intro card: bigger lead text than a normal hn-card body */
.hn-welcome-card .hn-card-body { padding: 1rem 1.1rem; }
.hn-welcome-lead {
    font-size: 1rem;
    line-height: 1.55;
    color: var(--hn-text);
    margin: 0 0 .7rem;
}

/* Twin-pane (Statistieken | Inloggen) row. Auto-fit so the panes
   stack vertically below ~600px without media-query gymnastics. */
.hn-public-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1rem;
    margin-bottom: 1rem;
}
.hn-public-row > .hn-card { margin-bottom: 0; }

/* Statistieken pane */
.hn-stats-list {
    list-style: none;
    margin: 0 0 .9rem;
    padding: 0;
}
.hn-stats-list li {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: .35rem 0;
    border-bottom: 1px dashed #e8dfcf;
}
.hn-stats-list li:last-child { border-bottom: none; }
.hn-stats-label {
    color: var(--hn-brown);
    font-size: .88rem;
    font-weight: 600;
}
.hn-stats-value {
    color: var(--hn-green-dark);
    font-size: 1.05rem;
    font-weight: 800;
}
.hn-stats-recent { margin-top: .6rem; }
.hn-stats-recent-title {
    color: var(--hn-brown);
    font-size: .82rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    margin-bottom: .3rem;
}
.hn-stats-recent-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: .35rem .6rem;
    font-size: .88rem;
}

/* Inloggen pane (re-uses .auth-card spacing but fits inside hn-card) */
.hn-login-form { display: block; }
.hn-login-label {
    display: block;
    font-size: .82rem;
    font-weight: 600;
    margin-bottom: 3px;
    color: var(--hn-text);
}
.hn-login-label + .hn-login-input { margin-bottom: .85rem; }
.hn-login-input {
    width: 100%;
    box-sizing: border-box;
    padding: 7px 10px;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    font-size: .9rem;
    font-family: inherit;
    background: #fff;
}
.hn-login-input:focus {
    outline: none;
    border-color: var(--hn-green);
    box-shadow: 0 0 0 2px rgba(74,124,47,.18);
}
.hn-login-submit {
    width: 100%;
    padding: 9px;
    font-size: .92rem;
    font-weight: 700;
    margin-top: .25rem;
}
.hn-login-forgot {
    margin: .65rem 0 0;
}

/* Big gold Registreren CTA below the twin panes */
.hn-public-cta {
    display: block;
    width: 100%;
    text-align: center;
    padding: 1rem 1.25rem;
    font-size: 1.15rem;
    font-weight: 800;
    letter-spacing: .02em;
    border-radius: 6px;
    margin: .5rem 0 1rem;
    text-decoration: none;
}

/* Contact form */
.hn-contact-form { display: block; }
.hn-contact-label {
    display: block;
    font-size: .82rem;
    font-weight: 600;
    margin-bottom: 3px;
    color: var(--hn-text);
}
.hn-contact-label + .hn-contact-input { margin-bottom: .85rem; }
.hn-contact-input {
    width: 100%;
    box-sizing: border-box;
    padding: 7px 10px;
    border: 1px solid var(--hn-border);
    border-radius: 4px;
    font-size: .9rem;
    font-family: inherit;
    background: #fff;
}
.hn-contact-input:focus {
    outline: none;
    border-color: var(--hn-green);
    box-shadow: 0 0 0 2px rgba(74,124,47,.18);
}
.hn-contact-textarea {
    min-height: 9rem;
    resize: vertical;
    line-height: 1.45;
}
.hn-contact-submit {
    padding: 8px 18px;
    font-size: .9rem;
    font-weight: 700;
    margin-top: .25rem;
}

/* Informatie / FAQ page: a single hn-card holds the intro paragraph
   plus all four Q&A blocks. Hairlines separate each block so the
   page reads as one FAQ instead of a stack of mini-cards. */
.hn-info-intro {
    color: var(--hn-text);
    font-size: 1rem;
    line-height: 1.6;
    margin: 0 0 1rem;
}
.hn-faq { display: block; }
.hn-faq-item {
    padding: 1.05rem 0 .25rem;
    border-top: 1px solid var(--hn-border);
}
.hn-faq-item:last-child { padding-bottom: .15rem; }
.hn-faq-question {
    color: var(--hn-green-dark);
    font-size: 1.1rem;
    font-weight: 700;
    margin: 0 0 .5rem;
}
.hn-faq-answer {
    color: var(--hn-text);
    font-size: .95rem;
    line-height: 1.6;
    margin: 0 0 .8rem;
}
.hn-faq-answer:last-child { margin-bottom: 0; }

/* Screenshots grid: responsive previews that link to the full file.
   Tiles stretch to fill the row (auto-fit collapses unused tracks) and
   the image keeps its natural aspect ratio - no cropping, since the
   whole point of a screenshot gallery is that you can read the UI. */
.hn-screenshots-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1rem;
}
.hn-screenshot-tile {
    display: block;
    border: 1px solid var(--hn-border);
    border-radius: 6px;
    overflow: hidden;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .08);
    transition: transform .15s, border-color .15s, box-shadow .15s;
}
.hn-screenshot-tile:hover {
    border-color: var(--hn-green);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, .15);
    text-decoration: none;
}
.hn-screenshot-tile img {
    display: block;
    width: 100%;
    height: auto;
}

/* ── Screenshots lightbox (native <dialog>) ─────────────────────────
   Activated by /public/js/screenshots-lightbox.js on the /screenshots
   page. The HTML is built from JS; only these styles need to ship. */
.hn-lightbox {
    border: 0;
    padding: 0;
    background: transparent;
    color: #fff;
    max-width: 100vw;
    max-height: 100vh;
    width: 100vw;
    height: 100vh;
}
.hn-lightbox::backdrop {
    background: rgba(20, 18, 12, .88);
    backdrop-filter: blur(2px);
}
.hn-lightbox-inner {
    position: relative;
    width: 100%;
    height: 100%;
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-rows: 1fr auto;
    align-items: center;
    box-sizing: border-box;
    padding: 1rem;
}
.hn-lightbox-stage {
    grid-column: 1 / -1;
    grid-row: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    min-height: 0;
    touch-action: pan-y;
}
.hn-lightbox-img {
    display: block;
    max-width: 96%;
    max-height: calc(100vh - 5rem);
    width: auto;
    height: auto;
    border-radius: 4px;
    box-shadow: 0 8px 30px rgba(0, 0, 0, .55);
    background: #fff;
    user-select: none;
    -webkit-user-drag: none;
    cursor: zoom-out;
}
.hn-lightbox-btn {
    position: absolute;
    background: rgba(0, 0, 0, .45);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, .35);
    border-radius: 999px;
    cursor: pointer;
    line-height: 1;
    padding: 0;
    transition: background .12s, border-color .12s, transform .12s;
    z-index: 2;
}
.hn-lightbox-btn:hover,
.hn-lightbox-btn:focus-visible {
    background: rgba(0, 0, 0, .75);
    border-color: var(--hn-gold);
    outline: none;
}
.hn-lightbox-btn:active { transform: scale(.96); }
.hn-lightbox-close {
    top: .6rem;
    right: .6rem;
    width: 38px;
    height: 38px;
    font-size: 1.4rem;
}
.hn-lightbox-prev,
.hn-lightbox-next {
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    font-size: 2rem;
}
.hn-lightbox-prev:active,
.hn-lightbox-next:active { transform: translateY(-50%) scale(.96); }
.hn-lightbox-prev { left: .6rem; }
.hn-lightbox-next { right: .6rem; }
.hn-lightbox-foot {
    grid-column: 1 / -1;
    grid-row: 2;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    padding: .6rem .2rem 0;
    color: rgba(255, 255, 255, .85);
    font-size: .85rem;
}
.hn-lightbox-counter {
    color: rgba(255, 255, 255, .65);
    white-space: nowrap;
}
body.hn-lightbox-open { overflow: hidden; }

@media (max-width: 600px) {
    .hn-lightbox-inner { padding: .35rem; }
    .hn-lightbox-img { max-height: calc(100vh - 4rem); }
    .hn-lightbox-prev,
    .hn-lightbox-next {
        width: 38px;
        height: 38px;
        font-size: 1.6rem;
    }
    .hn-lightbox-foot { font-size: .78rem; padding-top: .35rem; }
}

/* Phone tweaks: header nav can wrap beneath itself rather than
   crushing into a single line */
@media (max-width: 600px) {
    #hn-header.hn-header-public {
        height: auto;
        padding: .35rem .5rem;
        flex-wrap: wrap;
        gap: .35rem;
    }
    .hn-public-nav-auth { margin-left: 0; }
    .hn-public-nav-link {
        padding: 3px 9px;
        font-size: .78rem;
    }
    .hn-public-cta {
        font-size: 1rem;
        padding: .85rem 1rem;
    }
}

/* ── Dashboard "Welkom terug" stats grid ───────────────────────
   Small tiles (Geld, Rang, Paarden, Online, Vandaag online) shown
   above the leaderboard. Auto-fit so tiles wrap on narrow screens.
*/
.dash-stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: .5rem;
}
.dash-stat-tile {
    background: #f5f0e8;
    border-radius: 5px;
}
.dash-stat-label {
    font-size: .7rem;
    text-transform: uppercase;
}
.dash-stat-value {
    font-size: 1.1rem;
}
.dash-stat-value-rank {
    color: var(--hn-brown);
}

/* ── /admin/users (player + role flag editor) ─────────────── */
.admin-users-table th,
.admin-users-table td {
    vertical-align: middle;
}
.admin-users-table input[type="checkbox"] {
    cursor: pointer;
    transform: scale(1.1);
}
.admin-users-table input[type="checkbox"]:disabled {
    cursor: not-allowed;
    opacity: .5;
}
.admin-users-row-inactive td {
    opacity: .55;
}
.admin-users-row-inactive a {
    color: #888;
}
.admin-users-activity-link {
    margin-left: .35rem;
    text-decoration: none;
    font-size: .95rem;
    opacity: .55;
    border-bottom: none;
}
.admin-users-activity-link:hover {
    opacity: 1;
    text-decoration: none;
}

/* ── Raskleuren / coat-color inheritance picker (Uitleg) ──────
   Used by the #raskleuren section in views/extra/uitleg.php.
   Renders the breed + parent-color picker, the resulting
   stacked-bar visualisation, and the percentage list below it.
   The seven coat hexes are "content" colors (the actual horse
   coats) and intentionally match the in-game palette. */

.color-swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 3px;
    border: 1px solid rgba(42, 37, 32, .25);
    vertical-align: middle;
    flex-shrink: 0;
}

.color-bg-bruin    { background: #6b4423; }
.color-bg-zwart    { background: #2a2520; }
.color-bg-grijs    { background: #6e7376; }
.color-bg-vos      { background: #a35a2a; }
.color-bg-schimmel { background: #cdc7bb; }
.color-bg-palomino { background: #d5a64a; }
.color-bg-valk     { background: #b89968; }

/* Picker form: three dropdowns laid out as flex tiles so they
   stack vertically on narrow viewports. */
.color-picker {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin: .75rem 0 1rem;
}
.color-picker-field {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    flex: 1 1 200px;
    min-width: 180px;
}
.color-picker-label {
    font-size: .78rem;
    font-weight: 600;
    color: #5c4a2a;
    text-transform: uppercase;
    letter-spacing: .03em;
}
.color-picker-select {
    padding: .4rem .55rem;
    border: 1px solid #c8b99a;
    border-radius: 5px;
    background: #faf8f4;
    color: #3a2010;
    font-size: .92rem;
    cursor: pointer;
}
.color-picker-select:focus {
    outline: none;
    border-color: #4a7c2f;
    box-shadow: 0 0 0 2px rgba(74, 124, 47, .25);
}

/* Result panel: the bar + list of percentages. */
.color-picker-result {
    background: #f9f4e8;
    border: 1px solid #e0d8c2;
    border-radius: 6px;
    padding: .75rem .9rem 1rem;
    margin-bottom: 1rem;
}
.color-picker-result-title {
    font-size: .85rem;
    font-weight: 600;
    color: #5c4a2a;
    margin: 0 0 .55rem;
    text-transform: uppercase;
    letter-spacing: .03em;
}
.color-picker-result-body {
    min-height: 4.5rem;
}

/* Horizontal stacked-bar visualisation. Segments take a dynamic
   width (computed per-request from probabilities) so an inline
   style="width: X%" is correct here per the workspace's
   no-inline-css exception. */
.rk-bar {
    display: flex;
    width: 100%;
    height: 22px;
    border-radius: 4px;
    overflow: hidden;
    border: 1px solid rgba(42, 37, 32, .15);
    background: #efe8d8;
    margin-bottom: .65rem;
}
.rk-bar-seg {
    display: block;
    height: 100%;
    transition: width .25s ease;
}

.rk-result-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: .25rem .9rem;
}
.rk-result-row {
    display: flex;
    align-items: center;
    gap: .5rem;
    font-size: .9rem;
    color: #3a2010;
}
.rk-result-name {
    flex: 1;
}
.rk-result-pct {
    font-weight: 600;
    color: #5c4a2a;
    font-variant-numeric: tabular-nums;
}

/* Compact list used in the Uitzonderingen sub-cards. */
.rk-list {
    margin: .25rem 0 .25rem;
    padding-left: 1.1rem;
    font-size: .85rem;
}
.rk-list li {
    margin-bottom: .2rem;
}
.rk-list .color-swatch {
    margin-right: 4px;
}
