    .ti{font-size:18px;line-height:1;vertical-align:middle;display:inline-block}
    .ti-sm{font-size:16px}
    .ti-lg{font-size:22px}
    .nav-item .ni .ti, .bni .ico .ti{font-size:20px}
    .stat-ico .ti{font-size:26px}
    .empty-state .ei .ti{font-size:42px}
    .sex-btn .ssym{font-size:22px;line-height:1.2;margin-bottom:3px;font-style:normal;display:block}
    :root{--bg:#0a1628;--bg2:#0f2040;--card:#152a4e;--card2:#1a3360;--blue:#4a9eff;--gold:#f0a500;--txt:#e8edf5;--txt2:#8ba3c4;--muted:#4d6a8a;--bdr:#1e3a5f;--bdr2:#2a4d7a;--cock:#4a9eff;--hen:#ff6eb0;--ok:#22c55e;--err:#ef4444;--warn:#f59e0b;--r:10px;--sw:242px}
    *{box-sizing:border-box;margin:0;padding:0}
    *{scrollbar-width:thin;scrollbar-color:var(--bdr2) transparent}
    ::-webkit-scrollbar{width:10px;height:10px}
    ::-webkit-scrollbar-track{background:transparent}
    ::-webkit-scrollbar-thumb{background:var(--bdr2);border-radius:20px}
    ::-webkit-scrollbar-thumb:hover{background:var(--blue)}
    ::-webkit-scrollbar-corner{background:transparent}
    html{overflow-x:hidden}
    body{font-family:'Nunito',sans-serif;background:var(--bg);color:var(--txt);min-height:100vh;display:flex;overflow-x:hidden}
    h1,h2,h3,h4{font-family:'Rajdhani',sans-serif;letter-spacing:.03em}

    /* AUTH */
    #auth-screen{display:none;flex:1;align-items:center;justify-content:center;padding:20px;min-height:100vh;flex-direction:column;gap:12px}
    .auth-box{background:var(--card);border:1px solid var(--bdr);border-radius:16px;padding:38px 40px;width:100%;max-width:420px}
    @media(max-width:480px){#auth-screen{padding:14px}.auth-box{padding:28px 18px}}
    .auth-logo{text-align:center;margin-bottom:24px}.auth-logo .icon{font-size:44px;line-height:1;margin-bottom:6px}
    .auth-logo h1{font-size:28px;font-weight:700;color:var(--gold)}.auth-logo p{font-size:15px;color:var(--muted);margin-top:2px}
    .auth-tabs{display:flex;background:var(--bg2);border-radius:8px;padding:3px;margin-bottom:20px}
    .auth-tab{flex:1;text-align:center;padding:8px;border-radius:6px;cursor:pointer;font-size:16px;font-weight:600;color:var(--txt2);transition:all .2s}
    .auth-tab.active{background:var(--card2);color:var(--txt)}
    .auth-note{background:rgba(240,165,0,.08);border:1px solid rgba(240,165,0,.2);border-radius:8px;padding:11px 14px;font-size:15px;color:var(--gold);margin-bottom:14px;display:none}
    .auth-note.show{display:block}
    .auth-fg{display:flex;flex-direction:column;gap:5px;margin-bottom:14px}
    .auth-lbl{font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--txt2)}
    .auth-inp{background:var(--bg2);border:1px solid var(--bdr);border-radius:8px;padding:10px 13px;color:var(--txt);font-family:'Nunito',sans-serif;font-size:16px;outline:none;transition:border-color .2s;width:100%}
    .auth-inp:focus{border-color:var(--blue)}
    .auth-err{background:rgba(239,68,68,.08);border:1px solid rgba(239,68,68,.25);border-radius:8px;padding:10px 13px;font-size:15px;color:#fca5a5;margin-bottom:14px;display:none}
    .auth-err.show{display:block}
    .auth-btn{width:100%;padding:11px;background:var(--gold);color:#0a1628;border:none;border-radius:8px;font-family:'Nunito',sans-serif;font-size:15px;font-weight:700;cursor:pointer;margin-top:4px;transition:background .2s}
    .auth-btn:hover{background:#ffb820}.auth-btn:disabled{opacity:.6;cursor:not-allowed}
    .lang-toggle-auth{display:flex;gap:6px;align-items:center}
    .lang-btn{background:var(--card);border:1px solid var(--bdr);color:var(--txt2);border-radius:6px;padding:5px 12px;font-size:14px;font-weight:700;cursor:pointer;transition:all .2s;font-family:'Nunito',sans-serif}
    .lang-btn.active{background:var(--gold);color:#0a1628;border-color:var(--gold)}
    .lang-btn-flag{display:inline-flex;align-items:center;justify-content:center;padding:5px 8px}
    .lang-btn-flag .flag{width:28px;height:20px;object-fit:cover;border-radius:2px;display:block}
    .cc-tabs{display:flex;gap:4px;background:var(--bg2);padding:4px;border-radius:8px;border:1px solid var(--bdr)}
    .cc-tab{background:transparent;border:none;color:var(--txt2);border-radius:6px;padding:7px 16px;font-size:14px;font-weight:700;cursor:pointer;transition:all .2s;font-family:'Nunito',sans-serif}
    .cc-tab.active{background:var(--gold);color:#0a1628}

    /* APP */
    #app{display:none;flex:1;min-width:0}
    /* z-index:150, above .bnav's 100 - on mobile #sb is a fixed 100vh panel that slides over the
   page, and .bnav (the fixed bottom nav bar) is a later sibling in the DOM; at equal z-index
   .bnav would paint on top and cover/eat clicks on whatever of #sb's bottom (.sb-foot: language
   row, user card, logout button) falls behind it. Harmless while #sb is off-screen (closed). */
#sb{width:var(--sw);flex-shrink:0;background:var(--bg2);border-right:1px solid var(--bdr);display:flex;flex-direction:column;position:sticky;top:0;height:100vh;z-index:150;transition:transform .3s}
    .sb-logo{padding:18px 20px 14px;border-bottom:1px solid var(--bdr);position:relative}
    .sb-logo h1{font-size:21px;font-weight:700;color:var(--gold)}.sb-logo p{font-size:13px;color:var(--muted);letter-spacing:.08em;text-transform:uppercase;margin-top:2px}
    .sb-close-btn{position:absolute;top:14px;right:14px;width:26px;height:26px;border-radius:50%;background:transparent;border:1px solid var(--bdr2);color:var(--muted);display:none;align-items:center;justify-content:center;cursor:pointer;font-size:15px;transition:all .2s;z-index:5}
    .sb-close-btn:hover{background:#e5484d;color:#fff;border-color:#e5484d}
    /* overscroll-behavior:contain stops a scroll gesture that hits the top/bottom of this
       internal scroll area from "chaining" into the page behind it - on mobile, dragging inside
       the open hamburger menu was scrolling the background content underneath it. */
    /* min-height:0 - a flex item's default min-height is auto, meaning it refuses to shrink
       below its own content's natural height even with flex:1 + overflow-y:auto set. In tall
       (portrait) viewports #sb's 100vh is roomy enough that this never surfaces; rotate to
       landscape (much shorter viewport) and the nav list's natural height now exceeds it, so the
       flex column overflows past #sb's own box instead of scrolling internally - and since the
       background is intentionally scroll-locked while the menu is open (body.sb-locked), that
       overflow was simply clipped, making the lower nav items unreachable. */
    .sb-nav{padding:6px 0;flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}
    .nav-sec{font-size:16px;font-weight:700;letter-spacing:normal;text-transform:uppercase;color:var(--txt2);padding:10px 20px 8px}
    .nav-item{display:flex;align-items:center;gap:11px;padding:9px 20px;cursor:pointer;color:var(--txt2);font-size:16px;font-weight:500;border-left:3px solid transparent;transition:all .2s;user-select:none}
    .nav-item:hover{background:var(--card);color:var(--txt)}.nav-item.active{background:rgba(74,158,255,.1);color:var(--blue);border-left-color:var(--blue)}
    .nav-item .ni{width:20px;text-align:center;font-size:16px}
    .sb-foot{padding:12px 20px;border-top:1px solid var(--bdr)}
    .sb-user{font-size:15px;color:var(--txt2);margin-bottom:8px;display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:8px;border:1px solid var(--bdr);transition:background .2s,border-color .2s}
    .sb-user:hover{background:var(--card);border-color:var(--bdr2)}
    .sb-user strong{color:var(--txt);display:block}
    .sb-user-info{flex:1;min-width:0}
    .sb-user-gear{font-size:18px;color:var(--muted);flex-shrink:0;transition:color .2s}
    .sb-user:hover .sb-user-gear{color:var(--blue)}
    .role-badge{font-size:12px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:1px 7px;border-radius:20px;display:inline-block;margin-top:3px}
    .rb-admin{background:rgba(240,165,0,.15);color:var(--gold)}.rb-user{background:rgba(74,158,255,.12);color:var(--blue)}
    .imp-bar{background:rgba(240,165,0,.12);border:1px solid rgba(240,165,0,.3);border-radius:6px;padding:7px 10px;margin-bottom:8px;font-size:14px;color:var(--gold)}
    .imp-bar button{background:none;border:none;color:var(--err);cursor:pointer;font-size:14px;font-weight:700;padding:0;margin-left:6px}
    .sb-lang-label{padding:0;margin:0 0 10px}
    .sb-lang{display:flex;gap:6px;margin-bottom:14px}
    .sb-lang .lang-btn{flex:1;padding:5px 3px}
    .sb-lang .lang-btn .flag{width:30px;height:22px}
    .sb-collapse-btn{position:absolute;top:14px;right:-15px;width:30px;height:30px;border-radius:50%;background:var(--card2);border:1px solid var(--bdr2);color:var(--txt);display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:101;transition:all .2s;font-size:17px;font-weight:700}
    .sb-collapse-btn:hover{background:var(--blue);color:#fff;border-color:var(--blue)}
    .sb-collapse-btn i{transition:transform .3s}
    @media(min-width:961px){
      #sb.collapsed .sb-close-btn{display:none}
      #sb.collapsed{width:74px}
      #sb.collapsed .sb-collapse-btn i{transform:rotate(180deg)}
      #sb.collapsed .sb-logo{padding:18px 0 14px;text-align:center;position:relative}
      #sb.collapsed .sb-logo h1,#sb.collapsed .sb-logo p{display:none}
      #sb.collapsed .sb-logo::after{content:'G';font-family:'Rajdhani',sans-serif;font-size:22px;font-weight:700;color:var(--gold)}
      #sb.collapsed .nav-sec{display:none}
      #sb.collapsed .nav-item{padding:11px 0;justify-content:center;gap:0}
      #sb.collapsed .nav-item span:not(.ni){display:none}
      #sb.collapsed .nav-item .ni{font-size:19px}
      #sb.collapsed .sb-lang{display:none}
      #sb.collapsed .sb-user{justify-content:center;padding:9px 0}
      #sb.collapsed .sb-user-info{display:none}
      #sb.collapsed .sb-foot .btn span{display:none}
      #sb.collapsed .sb-foot .btn{padding:8px;justify-content:center}
    }

    /* MAIN */
    #main{flex:1;min-width:0;display:flex;flex-direction:column;min-height:100vh}
    #topbar{background:var(--bg2);border-bottom:1px solid var(--bdr);padding:0 24px;height:58px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:50}
    .tb-left{display:flex;align-items:center;gap:10px;flex:1;min-width:0}
    /* A long page title (e.g. "Dodaj novega goloba") used to wrap onto 2-3 lines inside this
       fixed-height 58px bar, spilling past it and overlapping the quick-add/add buttons on the
       right - truncate with ellipsis instead so the bar's height is never exceeded. */
    .tb-title{font-family:'Rajdhani',sans-serif;font-size:20px;font-weight:600;letter-spacing:.04em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
    #content{padding:24px;flex:1}

    /* BUTTONS */
    .btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:var(--r);font-family:'Nunito',sans-serif;font-size:15px;font-weight:600;cursor:pointer;border:none;transition:all .2s}
    .btn-p{background:var(--gold);color:#0a1628}.btn-p:hover{background:#ffb820;transform:translateY(-1px);box-shadow:0 4px 12px rgba(240,165,0,.3)}
    .btn-s{background:var(--card);color:var(--txt);border:1px solid var(--bdr2)}.btn-s:hover{background:var(--card2)}
    .btn-d{background:rgba(239,68,68,.1);color:var(--err);border:1px solid rgba(239,68,68,.3)}.btn-d:hover{background:rgba(239,68,68,.2)}
    .btn-ok{background:rgba(34,197,94,.1);color:var(--ok);border:1px solid rgba(34,197,94,.3)}.btn-ok:hover{background:rgba(34,197,94,.2)}
    .btn-sm{padding:5px 10px;font-size:14px}.btn:disabled{opacity:.5;cursor:not-allowed;transform:none!important}
    .btn-back{width:48px;height:48px;padding:0;justify-content:center;font-size:22px;flex-shrink:0;background:var(--card2)!important;border-color:var(--blue)!important;color:var(--blue)!important}
    .btn-back:hover{background:var(--blue)!important;color:var(--bg)!important}
    .rr-back-label{display:none}
    .zoom-ctrl{display:inline-flex;align-items:center;gap:6px;background:rgba(240,165,0,.1);border:1px solid rgba(240,165,0,.35);border-radius:24px;padding:5px;flex-shrink:0}
    .zoom-ctrl .btn{padding:8px 13px;border-radius:20px;font-size:16px}
    .zoom-ctrl-pct{font-size:16px;font-weight:700;color:var(--gold);min-width:46px;text-align:center;user-select:none}

    /* CARD */
    /* min-width:0/max-width:100% - a grid/flex item's automatic minimum size otherwise defaults
       to its content's own intrinsic width (e.g. inside .race-split's 1fr column, or #race-zoom-
       wrap at high in-app zoom), which can force the card - and its column/container - wider
       than the actual viewport instead of letting it shrink to fit. No effect in normal block
       flow (a block's own auto min-width there is already 0), so this is a no-op everywhere
       .card isn't a flex/grid item. */
    .card{background:var(--card);border:1px solid var(--bdr);border-radius:var(--r);padding:20px;min-width:0;max-width:100%}

    /* STATS */
    .stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:12px;margin-bottom:22px}
    .stat{background:var(--card);border:1px solid var(--bdr);border-radius:var(--r);padding:16px;position:relative;overflow:hidden}
    .stat::before{content:'';position:absolute;top:0;left:0;right:0;height:3px}
    .stat.blue::before{background:var(--blue)}.stat.gold::before{background:var(--gold)}.stat.cock::before{background:var(--cock)}.stat.hen::before{background:var(--hen)}.stat.ok::before{background:var(--ok)}
    .stat-ico{position:absolute;right:12px;top:12px;font-size:24px;opacity:.65}
    /* Matches each card's own top-border accent (.stat.X::before above) - the icon used
       to just be the default text color at low opacity regardless of card type, so male/
       female/etc cards all looked the same gray instead of picking up their color. */
    .stat.blue .stat-ico{color:var(--blue)}.stat.gold .stat-ico{color:var(--gold)}
    .stat.cock .stat-ico{color:var(--cock)}.stat.hen .stat-ico{color:var(--hen)}
    .stat.ok .stat-ico{color:var(--ok)}
    .stat-val{font-family:'Rajdhani',sans-serif;font-size:32px;font-weight:700;line-height:1;margin-bottom:2px}
    /* overflow-wrap: single-word labels like "NEDODELJENIH" have nowhere to break at normal
       whitespace rules, so a narrow forced column (see the inline 3-column ring-stats grid)
       overflowed past .stat's overflow:hidden and got clipped mid-word instead of wrapping. */
    .stat-lbl{font-size:13px;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;overflow-wrap:break-word}

    /* PIGEON TABLE */
    .ptable-wrap{overflow-x:auto}
    .rr-mobile-cards{display:none}
    /* Pretvornik koordinat lat/lng field groups - 2 columns on desktop/tablet, 1 on phone
       (overridden below at max-width:980px). */
    .cc-fields-grid{grid-template-columns:1fr 1fr;gap:20px}
    .loft-mobile-cards{display:none}
    /* Back-to-top FAB (klubske/individualne dirke, RFID pages) - fades in past the scroll
       threshold set in _ensureRrTopBtnListener, on both desktop and mobile. bottom:24px on
       desktop (no bottom nav to clear); the mobile media query below raises it above .bnav. */
    .rr-top-btn{display:flex;position:fixed;right:20px;bottom:24px;width:46px;height:46px;
      border-radius:50%;background:var(--blue);color:#fff;align-items:center;justify-content:center;
      font-size:20px;border:none;cursor:pointer;box-shadow:0 4px 14px rgba(0,0,0,.4);z-index:99;
      opacity:0;pointer-events:none;transition:opacity .2s}
    .rr-top-btn.show{opacity:1;pointer-events:auto}
    /* Reserve room for the fixed back-to-top FAB (46px wide, right:20px) so a table's trailing
       action column (trash icons etc.) never sits directly under it - the FAB floats above the
       page regardless of table width, so it can visually collide with whatever happens to be at
       that same screen position when scrolled. */
    .rr-desktop-table{padding-right:70px}
    /* Moji golobi: three tiers, not just a single table/cards toggle - a tablet-width screen
       still fits the real table fine once sire/dam (the two least-essential columns) are
       dropped; only below phone width does the whole table give way to the per-bird card list
       (loft-desktop-table/loft-mobile-cards, own breakpoint from the klubske dirke Plasman
       table's rr- pair since that one didn't need a tablet-specific step). */
    /* Breakpoints below are set to where THIS table actually stops fitting, not to generic
       phone/tablet widths. Measured min widths (10 columns, ellipsis caps and all): the full
       table needs ~1480px, and ~1170px once sire/dam are dropped. The old ladder (drop
       sire/dam at 980, cards at 640) meant a 1366 laptop at 100% zoom already scrolled the
       table sideways, and anyone combining Windows display scaling with Chrome zoom - which
       compound, so 150% x 150% turns a 1920 screen into an 853px viewport - scrolled at every
       width from ~1500 all the way down to 640. */
    @media(max-width:1500px){
      .loft-col-tablet-hide{display:none}
    }
    @media(max-width:1200px){
      .loft-desktop-table{display:none}
      .loft-mobile-cards{display:block}
    }
    @media(max-width:640px){
      .activity-title-l2{display:block}
    }
    .ptable{width:100%;border-collapse:collapse;font-size:15px}
    .ptable th{text-align:left;padding:9px 8px;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.09em;color:var(--txt2);border-bottom:2px solid var(--bdr);white-space:nowrap}
    .ptable td{padding:10px 8px;border-bottom:1px solid var(--bdr);vertical-align:middle;white-space:nowrap}
    .ptable tr:hover td{background:rgba(255,255,255,.02);cursor:pointer}
    /* Below the width where .ptable tables stop fitting, let cells wrap onto a second line
       instead of holding every cell on one line and pushing the overflow into .ptable-wrap's
       horizontal scrollbar. Headers are the worst offenders - uppercase + .09em letter-spacing
       on phrases like "Korekcija na 10 km odklona" reserves a lot of unbreakable width for text
       that reads fine on two lines. This covers the tables that have no card fallback at all
       (the info-distance / info-tockovanje explainer tables and the breed tables in the
       add/edit modals); the race and loft tables switch to cards at their own breakpoints. */
    @media(max-width:1400px){
      .ptable th{white-space:normal;overflow-wrap:break-word}
      .ptable td{white-space:normal}
      /* Identifier-style values stay on one line: "SLO-2026-115-1113" and 16-char RFID hex have
         hyphens/length that make them legal break points, but splitting one across two lines is
         harder to read than a slightly wider column. Applied via .nw where markup opts in. */
      .ptable th.nw,.ptable td.nw{white-space:nowrap}
      /* #loft-table truncates these columns with text-overflow:ellipsis (see the nth-child rules
         below), which only works while the cell is nowrap - let them keep clipping rather than
         growing rows taller. */
      #loft-table th:nth-child(2),#loft-table td:nth-child(2),
      #loft-table th:nth-child(5),#loft-table td:nth-child(5),
      #loft-table th:nth-child(6),#loft-table td:nth-child(6),
      #loft-table th:nth-child(7),#loft-table td:nth-child(7),
      #loft-table th:nth-child(8),#loft-table td:nth-child(8){white-space:nowrap}
    }
    /* Ported from golobar2 - same overflow problem (10 columns, several free-text ones
       forcing horizontal scroll to see a pigeon's full row), same fix: tighter padding
       plus ellipsis-truncate the columns that don't need their natural full width (ring
       format is fixed/predictable, color/bloodline/sire/dam names are the long ones).
       Scoped to #loft-table (Moji golobi), not the bare .ptable class - .ptable is shared by
       several unrelated tables (klubske/individualne dirke stats, RFID lists, ...) whose own
       5th/6th/7th column is something else entirely (e.g. training's stats table has a full
       "DD.MM.YYYY HH:mm:ss" timestamp in column 5) and was getting truncated by these
       position-based rules meant only for this table's columns. */
    #loft-table th:nth-child(2),#loft-table td:nth-child(2){max-width:190px;overflow:hidden;text-overflow:ellipsis}
    #loft-table td:nth-child(2) div{overflow:hidden;text-overflow:ellipsis}
    #loft-table th:nth-child(5),#loft-table td:nth-child(5){max-width:100px;overflow:hidden;text-overflow:ellipsis}
    #loft-table th:nth-child(6),#loft-table td:nth-child(6){max-width:100px;overflow:hidden;text-overflow:ellipsis}
    #loft-table th:nth-child(7),#loft-table td:nth-child(7){max-width:150px;overflow:hidden;text-overflow:ellipsis}
    #loft-table th:nth-child(8),#loft-table td:nth-child(8){max-width:150px;overflow:hidden;text-overflow:ellipsis}
    #loft-table td:nth-child(7) div,#loft-table td:nth-child(8) div{overflow:hidden;text-overflow:ellipsis}
    /* Dejanja header centered above the now right-aligned action buttons (see pigeonRows()
       in js/app.js). */
    #loft-table th:nth-child(10){text-align:center}
    .p-ring-link{color:var(--gold);font-weight:700;font-size:14px;letter-spacing:.06em;text-transform:uppercase;cursor:pointer}
    .p-ring-link:hover{text-decoration:underline}
    .p-name-sub{font-size:14px;color:var(--txt2);margin-top:1px}
    /* "Ni prispel" advisory flag - a bird that never came home from its last race/training.
       Greyed, not hidden: it stays fully clickable and adding it just raises a confirm (the
       server enforces the same via a 409 unless confirm_not_returned is sent). Deliberately
       distinct from .is_active=0 "deaktiviran", which really does remove a ring from every
       picker. Confirming clears the flag and the row returns to normal. */
    .entry-row.ring-nr{opacity:.5}
    .entry-row.ring-nr .ename,.entry-row.ring-nr .eband{color:var(--txt2)}
    .entry-row.ring-nr:hover{opacity:.8}
    /* Moji golobi rows mix one-line cells (Spol, Leto, Barva) with two-line ring+name cells
       (Obroč, Oče, Mati), and .ptable's vertical-align:middle centred each cell independently -
       so a ring whose cell had a name under it sat higher than a ring whose cell didn't, and the
       rings no longer lined up reading left to right across the row (e.g. Oče "…-0523" sitting
       ~9px below Mati "…-251"). Top-align instead so every cell's FIRST line shares one baseline
       and the name sub-lines hang below it. Scoped to this table - .ptable is shared with the
       race/RFID tables, whose cells are uniformly single-line and read better centred. */
    #loft-table td{vertical-align:top}
    /* The checkbox and the Uredi/Rodovnik/delete buttons are controls, not text - keep them
       optically centred against the full row height rather than pinned to its first line. */
    #loft-table td:first-child,#loft-table td:last-child{vertical-align:middle}
    .sex-icon{font-size:16px}

    /* SEARCH BAR */
    .sbar{display:flex;gap:8px;margin-bottom:16px;align-items:center;flex-wrap:wrap}
    .sw{position:relative;flex:1;min-width:180px}
    .sico{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--muted);font-size:17px;pointer-events:none}
    .sinp{width:100%;background:var(--card);border:1px solid var(--bdr);border-radius:var(--r);padding:11px 13px 11px 36px;color:var(--txt);font-family:'Nunito',sans-serif;font-size:16px;outline:none;transition:border-color .2s}
    .sinp:focus{border-color:var(--blue)}
    .sinp::placeholder{color:var(--txt2);opacity:1}
    /* min-width:0 - several places use this as a flex:1 child next to a fixed-width icon+label
       (e.g. "Dodelitev za: [select]"); without it a flex item's default min-width is its content
       width, so a long selected option (like "- Jaz (Admin) -") refuses to shrink and overflows
       the row's rounded container on a narrow phone instead of letting text-overflow below handle it. */
    .fsel{background:var(--card);border:1px solid var(--bdr);border-radius:var(--r);padding:8px 11px;color:var(--txt);font-family:'Nunito',sans-serif;font-size:15px;outline:none;cursor:pointer;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis}
    .fsel:focus{border-color:var(--blue)}

    /* FORM */
    .fgrid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
    .fg{display:flex;flex-direction:column;gap:4px}.fg.full{grid-column:1/-1}
    .flbl{font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--txt2)}
    .finp,.fsel2,.ftxt{background:var(--bg2);border:1px solid var(--bdr);border-radius:8px;padding:8px 11px;color:var(--txt);font-family:'Nunito',sans-serif;font-size:16px;outline:none;transition:border-color .2s;width:100%}
    .finp:focus,.fsel2:focus,.ftxt:focus{border-color:var(--blue)}.ftxt{resize:vertical;min-height:76px}
    /* Edge's built-in password-reveal eye icon renders dark by default, invisible on our dark fields. */
    input[type="password"]::-ms-reveal{filter:invert(1);cursor:pointer}
    .dt-wrap{position:relative;max-width:230px}
    .dt-wrap .finp[type="datetime-local"]{width:100%;max-width:none;color-scheme:dark;padding-right:44px;cursor:pointer;border-color:var(--warn)}
    .dt-wrap .finp[type="datetime-local"]:focus{border-color:var(--blue)}
    .dt-wrap .finp[type="datetime-local"]::-webkit-calendar-picker-indicator{opacity:0;position:absolute;inset:0;width:100%;height:100%;margin:0;cursor:pointer}
    .dt-icon{position:absolute;top:50%;right:6px;transform:translateY(-50%);width:26px;height:26px;display:flex;align-items:center;justify-content:center;background:var(--card);border:1px solid var(--warn);border-radius:6px;color:var(--warn);font-size:15px;pointer-events:none;transition:border-color .15s,color .15s}
    .dt-wrap:hover .dt-icon{border-color:var(--blue);color:var(--blue)}
    .facts{display:flex;gap:10px;margin-top:20px;padding-top:16px;border-top:1px solid var(--bdr)}
    .form-errors{background:rgba(239,68,68,.08);border:1px solid rgba(239,68,68,.3);border-radius:8px;padding:11px 14px;margin-bottom:16px;font-size:15px;color:#fca5a5;display:none}
    .form-errors.show{display:block}.form-errors ul{margin:6px 0 0 16px;display:flex;flex-direction:column;gap:3px}
    .req{margin-left:1px;font-size:15px}
    .vis-toggle{display:flex;background:var(--bg2);border:1px solid var(--bdr);border-radius:8px;overflow:hidden}
    .vis-opt{flex:1;padding:8px 11px;text-align:center;cursor:pointer;font-size:15px;font-weight:600;transition:all .2s;color:var(--txt2)}
    .vis-opt.active-pub{background:rgba(34,197,94,.15);color:var(--ok)}.vis-opt.active-priv{background:rgba(77,106,138,.15);color:var(--muted)}
    .ring-parts{display:grid;grid-template-columns:90px 70px 80px 1fr;gap:6px}
    .ring-sep{display:flex;align-items:center;justify-content:center;color:var(--muted);font-weight:700;font-size:16px;padding:0 2px;align-self:end;padding-bottom:10px}
    .ring-preview{font-size:13px;color:var(--gold);margin-top:4px;font-family:monospace;letter-spacing:.04em}
    .parent-row{display:flex;gap:6px;align-items:center}
    .parent-row .finp{flex:1}
    .radio-row{display:flex;gap:16px;align-items:center;margin-top:2px}
    .radio-opt{display:flex;align-items:center;gap:5px;cursor:pointer;font-size:16px}
    .radio-opt input{accent-color:var(--gold);width:15px;height:15px}
    .sex-btns{display:flex;gap:6px;margin-top:2px}
    .sex-btn{flex:1;padding:10px 6px 8px;border-radius:10px;border:2px solid var(--bdr);background:var(--bg2);color:var(--txt2);cursor:pointer;font-family:'Nunito',sans-serif;font-size:13px;font-weight:700;text-align:center;transition:all .2s;user-select:none;letter-spacing:.04em;text-transform:uppercase}
    .sex-btn .ssym{display:block;font-size:22px;line-height:1.2;margin-bottom:3px;font-style:normal}
    .sex-btn:hover{border-color:var(--bdr2);background:var(--card)}
    .sex-btn.active-cock{background:rgba(74,158,255,.15);border-color:var(--cock);color:var(--cock)}
    .sex-btn.active-hen{background:rgba(255,110,176,.15);border-color:var(--hen);color:var(--hen)}
    .sex-btn.active-unk{background:rgba(77,106,138,.2);border-color:var(--muted);color:var(--txt)}

    /* PEDIGREE */
    .ped-picker-btn .ti{font-size:15px}
    .ped-scroll{padding:4px 0 16px;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}
    .ped-tree{display:flex;align-items:flex-start;position:relative}
    /* Sizes below are the on-screen browsing view - comfortable to scroll/scan, not
       print-sized. The PDF export (downloadPedigreePdf()) renders inside a wrapper with
       class .pdf-snapshot, which gets its own larger overrides for all of these - see the
       ".pdf-snapshot pedigree export sizing" block right after this one. */
    .ped-gen{display:flex;flex-direction:column;flex-shrink:0;width:var(--ped-box-w,214px)}
    /* Subject cell is the page's focal point (per real pedigree certificates) - wider
       than ancestor columns and its notes are never clamped (see .pnode.subj .pn-notes) */
    .ped-gen-subj{width:calc(var(--ped-box-w,214px) + 48px)}
    .ped-gen-hdr{font-size:12px;font-weight:700;letter-spacing:0;text-transform:uppercase;color:var(--muted);text-align:left;padding:7px 10px;border-bottom:1px solid var(--bdr);white-space:nowrap;height:31px;box-sizing:border-box}
    .ped-gen-nodes{display:flex;flex-direction:column}
    /* Deepest 2 generations only (see GRID_COLS in vPed()) - column COUNT is set inline
       per instance as --ped-grid-cols (varies by depth); box width comes from the same
       --ped-box-w used everywhere else, so it automatically matches on-screen vs PDF
       export sizing without needing its own override. Sibling pairs land in the same row
       regardless of column count, so connector brackets still work. */
    .ped-gen-grid{width:fit-content}
    .ped-gen-nodes-grid{display:grid;grid-template-columns:repeat(var(--ped-grid-cols,2),var(--ped-box-w,214px));gap:8px 16px}
    .ped-gen-grid .ped-gen-hdr{white-space:normal;height:auto}
    /* Grid's default align-items:stretch already makes both .ped-cells in a row match the
       row's tallest height, but stretching the cell alone isn't enough - the .pnode inside
       it (a flex child, not the grid item itself) still renders at its own natural height by
       default, sitting at the top of the now-taller cell. That means two sibling boxes in
       the same row (near-universal once notes text differs) end up with DIFFERENT vertical
       centers despite being "the same row" - and the connector reaching the shorter one
       travels straight through the taller one on its way there. Stretching .pnode to fill
       the cell keeps both boxes' centers aligned, which is what the connector math assumes. */
    .ped-gen-grid .pnode{flex:1;display:flex;flex-direction:column;justify-content:center}
    .ped-cell{flex:0 0 auto;display:flex;flex-direction:column;align-items:stretch;padding:3px 8px;min-height:52px;position:relative}
    /* margin-left clears the vertical GOLOB<->father/mother connector stub (drawn at
       pnode-left+16px in layoutPedigreeTree()) so the label text never overlaps the line */
    .ped-parent-lbl{font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--muted);margin-bottom:3px;text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    .pnode{background:var(--card);border:1px solid var(--bdr);border-radius:8px;padding:6px 10px;width:100%;position:relative;overflow:hidden;transition:border-color .15s,background .15s}
    .pnode::before{content:'';position:absolute;top:0;left:0;right:0;height:3px}
    .pnode.cock::before{background:var(--cock)}.pnode.hen::before{background:var(--hen)}.pnode.unk::before{background:var(--muted)}
    .pnode.subj{border-color:var(--gold);background:rgba(240,165,0,.06);box-shadow:0 0 0 1px rgba(240,165,0,.2)}
    .pnode.link{cursor:pointer}.pnode.link:hover{border-color:var(--blue);background:var(--card2)}
    .pnode.empty{background:rgba(255,255,255,.02);border-style:dashed;opacity:.4}
    .pnode.foreign{border-style:dashed;opacity:.85}
    .pn-ring{font-size:13px;color:var(--gold);font-weight:700;letter-spacing:.06em;text-transform:uppercase;white-space:normal;word-break:break-word}
    /* text-shadow layered on top of font-weight:700 - the ♂/♀ glyphs themselves often don't
       have a distinct bold variant in most fonts, so weight alone barely changes how thick
       they look; the shadow fakes a bolder stroke. */
    .pn-sex{font-weight:700;letter-spacing:0;text-transform:none;text-shadow:0 0 1px currentColor,0 0 1px currentColor}
    .pnode.cock .pn-sex{color:var(--cock)}.pnode.hen .pn-sex{color:var(--hen)}
    .pn-name{font-family:'Rajdhani',sans-serif;font-size:14px;font-weight:600;margin:1px 0;white-space:normal;word-break:break-word}
    .pn-owner{font-size:12px;color:var(--txt2);opacity:.75;white-space:normal;word-break:break-word;margin-bottom:1px}
    .pn-info{font-size:15px;color:#b3874f;white-space:normal;word-break:break-word}
    /* font-size here is a fallback only - resetPedigreeNotesSize() always sets it inline
       (same 14px on-screen / 14px in PDF export, for every ancestor generation alike) so it
       can never drift out of sync with .pnode.subj .pn-notes below. Depth is handled purely
       by how many LINES show (.pn-notes-g3/g4's -webkit-line-clamp), never by size. */
    .pn-notes{margin-top:2px;font-size:14px;line-height:1.3;color:var(--txt2);white-space:normal;word-break:break-word}
    /* Fixed (not dynamic/export-only) line cap for the two deepest, least-consulted
       generations only - great-grandparents keep a generous 7 lines, great-great-grandparents
       (16 boxes, the generation most responsible for the tree's overall height) still get the
       tighter cut but not so tight real achievement text gets lost after 2 short lines. No
       font-size here - same size as every other generation, only the line count differs.
       Parents/grandparents/subject are never clamped at all. */
    .pn-notes-g3{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:7;overflow:hidden;text-overflow:ellipsis}
    .pn-notes-g4{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:5;overflow:hidden;text-overflow:ellipsis}
    /* Subject box is the page's focal point - full Opombe/Dosežki text, never clamped.
       font-size must match resetPedigreeNotesSize()'s SIZE constant exactly (14px on-screen)
       since this is the one .pn-notes case that function deliberately skips. */
    .pnode.subj .pn-notes{-webkit-line-clamp:unset;max-height:none;overflow:visible;font-size:14px}
    .pn-empty{font-size:13px;color:var(--muted);font-style:italic}
    .ped-conn{flex-shrink:0;width:80px}
    .pdf-snapshot .ped-conn{width:52px}
    .ped-svg{position:absolute;top:0;left:0;pointer-events:none;overflow:visible}
    .ped-svg line{stroke:var(--bdr2);stroke-width:2.5}
    /* #ped-export-root shrink-wraps to whichever of the letterhead/tree is intrinsically
       widest (fit-content, not the full page/card width) - without this, the letterhead
       row stretches to the container's full width regardless of how narrow the tree
       below it actually is, which both strands the ring number far from the photo/text
       block and forces the PDF capture width (read from this element's scrollWidth) to
       balloon far beyond the real content, shrinking everything in the exported page. */
    .ped-export-root{width:fit-content;max-width:100%}
    /* Photo first (left, only when the subject has one - see pedLetterheadHtml()), owner
       info flows immediately right of it, ring number follows in normal flow (no
       margin-left:auto - that only makes sense once the row is already fit-content). */
    /* justify-content:center - the two boxes are a fixed compact width (not flex:1
       stretching edge to edge), centered as a unit within whatever width the tree below
       ends up being, so there's breathing room on both sides rather than the header
       spanning the full page width. */
    .ped-letterhead{display:flex;justify-content:center;padding:8px 4px 16px;margin-bottom:14px;border-bottom:1px solid var(--bdr);overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}
    /* Fixed width/height, not content-driven - a letterhead should read as one consistent
       block regardless of how much of it a given pigeon/user has filled in. */
    .plh-box{border:1px solid var(--bdr);border-radius:8px;padding:16px 20px;display:flex;flex:none;width:660px;height:230px;box-sizing:border-box}
    .plh-box-main{gap:16px}
    .plh-left{display:flex;flex-direction:column;justify-content:center;gap:5px;flex:1;min-width:0}
    /* Persistent "Label: value" row - the label never disappears the way a placeholder
       would once a value is typed, matching a real letterhead form rather than a bare
       input. .plh-inp still keeps its own hover/focus affordance for editing in place. */
    .plh-fld{display:flex;align-items:center;gap:6px}
    .plh-fld .plh-inp{flex:1;min-width:0;padding-left:2px}
    /* Size hierarchy, largest to smallest: ring > pigeon name > fancier name > address/
       email/phone (which stay equal to each other, but still comfortably large - see
       .plh-ring / .plh-pigeon-name / .plh-static-label-strong+.plh-name / .plh-static-label+.plh-inp below). */
    .plh-static-label{color:var(--txt2);font-size:17px;flex-shrink:0;white-space:nowrap;font-family:'Nunito',sans-serif}
    .plh-static-label-strong{color:var(--txt);font-weight:700;font-size:19px;font-family:'Rajdhani',sans-serif}
    .plh-pigeon-name{font-family:'Nunito',sans-serif;font-size:17px;color:var(--txt2)}
    /* Border/background visible by default (not just on hover) - these are editable
       fields, and an invisible-until-hovered input was reading as plain static text with
       no clue it could be clicked into. */
    .plh-inp{background:var(--bg2);border:1px dashed var(--bdr);border-radius:6px;padding:2px 6px;color:var(--txt2);font-family:'Nunito',sans-serif;font-size:17px;outline:none;transition:border-color .15s,background .15s;width:100%}
    .plh-inp:hover{border-color:var(--blue)}
    .plh-inp:focus{border-style:solid;border-color:var(--blue)}
    /* Non-owner viewing someone else's pigeon's pedigree - inputs are readonly (save would
       403 anyway), styled flat/non-interactive so it doesn't look like a live editable field */
    .plh-inp[readonly]{border-style:solid;border-color:transparent;cursor:default}
    .plh-inp[readonly]:hover{border-color:transparent}
    .plh-name{font-family:'Rajdhani',sans-serif;font-size:19px;font-weight:700;color:var(--txt)}
    .plh-ring{flex-shrink:0;font-family:'Rajdhani',sans-serif;font-size:26px;font-weight:700;color:var(--gold);letter-spacing:.03em;white-space:nowrap}
    /* Rendered as a background-image div, not an <img> with object-fit: html2canvas
       doesn't support object-fit and stretches replaced elements to fill their box,
       which distorted uploaded photos badly in the PDF export. background-size:cover
       is handled correctly by html2canvas. A fixed-size square (not aspect-ratio-driven -
       .plh-box is now a fixed height, so a fixed pixel side matches it exactly) so the box
       reads the same whether or not a given pigeon has a photo; see plh-photo-empty for
       the placeholder shown when it doesn't. */
    .plh-photo{width:196px;height:196px;flex-shrink:0;background-size:contain;background-repeat:no-repeat;background-position:center;border-radius:8px;border:1px solid var(--bdr)}
    .plh-photo-empty{display:flex;align-items:center;justify-content:center;text-align:center;font-size:13px;color:var(--muted);font-style:italic;background:rgba(255,255,255,.02);border-style:dashed;transition:border-color .2s,color .2s}
    .plh-photo-empty:hover{border-color:var(--blue);color:var(--blue)}
    /* touch-action:none stops mobile browsers panning/scrolling the page while the user
       is drawing on the pad - without it, a touch drag both draws AND scrolls. */
    .sig-canvas{width:100%;max-width:440px;height:180px;background:#fff;border-radius:8px;border:1px solid var(--bdr2);touch-action:none;cursor:crosshair}
    .sig-preview{background:#fff;border-radius:8px;border:1px solid var(--bdr2);display:inline-flex;padding:10px 16px;margin-bottom:14px}
    .sig-preview img{max-width:220px;max-height:90px;display:block}
    /* Bottom-left - the tree grows rightward with each generation, so the space under the
       subject/early columns is the one spot guaranteed to stay empty regardless of how
       deep a given pedigree goes (unlike the right edge, which a shallow tree leaves
       empty but a full 5-generation one fills right up to). */
    .ped-signature{width:220px;margin:28px auto 4px 0;text-align:center}
    /* The saved stroke is deliberately dark ink (see initSignaturePad's strokeStyle) so it
       reads correctly on the PDF's white page - on this dark on-screen background that
       same dark-on-transparent PNG is nearly invisible. Inverted (then desaturated/
       brightened toward pure white) just for this on-screen preview; the PDF export never
       touches this rule; it draws the original untouched image straight onto the white
       page (see _pdfSignatureFooter()). */
    .ped-sig-img{max-width:190px;max-height:78px;display:block;margin:0 auto;filter:invert(1) grayscale(1) brightness(2)}
    .ped-sig-line{border-top:1px solid rgba(255,255,255,.35);margin-top:6px}
    .ped-sig-name{font-family:'Rajdhani',sans-serif;font-size:16px;font-weight:700;color:#fff;margin-top:6px}
    .ped-sig-label{font-size:12px;color:#fff;text-transform:uppercase;letter-spacing:.05em;margin-top:2px}
    @media print{#sb,#topbar,.bnav,.sbar,.ped-actions{display:none!important}body{background:#fff!important;color:#000!important}.card{background:#fff!important;border-color:#ddd!important}.pnode{border-color:#ccc!important;background:#fff!important}.pn-ring{color:#9a6e00!important}.pn-name{color:#000!important}.pn-info,.pn-notes{color:#555!important}.ped-svg line:not([style]){stroke:#666!important}.pnode.subj{border-color:#9a6e00!important;background:rgba(154,110,0,.04)!important}#main{margin-left:0!important}.plh-inp{border-color:transparent!important;background:transparent!important;color:#000!important}.plh-static-label{color:#555!important}.plh-inp::placeholder{color:transparent!important}.ped-sig-line{border-color:#999!important}.ped-sig-name{color:#000!important}.ped-sig-label{color:#555!important}}
    /* Same print-safe overrides as @media print above, but toggled via JS as a class - html2canvas
       snapshots the live screen-media DOM and never sees @media print rules, so the PDF export
       needs this duplicate applied while it captures, otherwise dark-theme text (near-white) is
       invisible against the white PDF page. */
    .pdf-snapshot .pnode{border-color:#ccc!important;background:#fff!important}
    .pdf-snapshot .pn-ring{color:#9a6e00!important}
    .pdf-snapshot .pn-name{color:#000!important}
    .pdf-snapshot .pn-owner{color:#666!important}
    .pdf-snapshot .pn-info{color:#8a6642!important}
    .pdf-snapshot .pn-notes{color:#555!important}
    .pdf-snapshot .plh-pigeon-name{color:#555!important}
    /* Empty ancestor slots inherit .pnode.empty's opacity:.4, tuned for sitting on a dark
       on-screen card background - left alone here it also applies inside the PDF's white
       page, making the box/text render almost invisible instead of a legible "no data"
       placeholder. Full opacity + explicit print-safe colors instead. */
    .pdf-snapshot .pnode.empty{opacity:1;border-color:#ddd!important;background:#fafafa!important}
    .pdf-snapshot .pn-empty{color:#999!important}
    .pdf-snapshot .plh-box{border-color:#ccc!important}
    .pdf-snapshot .plh-ring{color:#9a6e00!important}
    .pdf-snapshot .plh-static-label{color:#555!important}
    .pdf-snapshot .plh-static-label-strong{color:#000!important}
    .pdf-snapshot .plh-photo-empty{color:#888!important;border-color:#ccc!important}
    .pdf-snapshot .ped-sig-line{border-color:#999!important}
    .pdf-snapshot .ped-sig-name{color:#000!important}
    .pdf-snapshot .ped-sig-label{color:#555!important}
    /* Only overrides the neutral parent-stub segments (no inline style) - the colored
       sire/dam segments already carry their own inline stroke (index.html's line()
       helper) and var(--cock)/var(--hen) read fine on white already, so forcing them all
       gray here was silently erasing the blue/pink coloring in every PDF export. */
    .pdf-snapshot .ped-svg line:not([style]){stroke:#666!important}
    .pdf-snapshot .pnode.subj{border-color:#9a6e00!important;background:rgba(154,110,0,.04)!important}
    .pdf-snapshot .plh-inp{border-color:transparent!important;background:transparent!important;color:#555!important}
    .pdf-snapshot .plh-inp.plh-name{color:#000!important}
    /* Pedigree export sizing - print needs to read comfortably from an arm's length away,
       which is noticeably bigger than what's comfortable for on-screen browsing/scrolling
       (the un-prefixed rules earlier in this file). Same properties, just scaled up only
       inside the off-screen .pdf-snapshot wrapper downloadPedigreePdf() captures. */
    .pdf-snapshot{--ped-box-w:268px}
    .pdf-snapshot .ped-gen-hdr{font-size:14px}
    .pdf-snapshot .ped-gen-nodes-grid{gap:10px 20px}
    .pdf-snapshot .ped-parent-lbl{font-size:12px}
    .pdf-snapshot .pnode{padding:8px 12px}
    .pdf-snapshot .pn-ring{font-size:17px}
    .pdf-snapshot .pn-name{font-size:18px}
    .pdf-snapshot .pn-info{font-size:18px}
    /* Notes font-size is NOT overridden here - resetPedigreeNotesSize() already sets it
       inline to a uniform 14px for every ancestor generation once it detects the
       .pdf-snapshot ancestor (see its isExport check), and .pnode.subj .pn-notes below
       matches that same value directly. Depth is still only ever the line-clamp count. */
    .pdf-snapshot .pnode.subj .pn-notes{font-size:14px}
    .pdf-snapshot .pn-empty{font-size:16px}

    /* STATIC PEDIGREE - dark-theme on screen (same as the rest of the app; shown live in
       the pedigree view when S.pedExportMode==='static', see vPed()) with print-safe
       .pdf-snapshot overrides for the actual export, mirroring the same on-screen/export
       split every other pedigree element already uses. Box height per column comes purely
       from flexbox (flex:1 dividing pst-grid's fixed height evenly among however many
       boxes that column has - 3/4/8/16) - overflow:hidden on each box clips whatever text
       doesn't fit rather than growing the box. */
    .pst-page{font-family:'Nunito',sans-serif}
    /* Header shrunk specifically for Static mode (both the on-screen preview and the PDF
       export) - freeing space for the grid, which is the part that actually needed more
       room, mattered more than keeping the exact same header size as Dynamic mode here.
       Font sizes are reduced to actually fit that shorter box (not just the box height
       alone - a fixed height with unshrunk text was overflowing past the border, "Phone:"
       spilling out below it). */
    .pst-page .plh-box{height:170px}
    .pst-page .plh-photo{width:148px;height:148px}
    .pst-page .plh-ring{font-size:20px}
    .pst-page .plh-pigeon-name{font-size:14px}
    .pst-page .plh-static-label,.pst-page .plh-inp{font-size:14px}
    .pst-page .plh-static-label-strong,.pst-page .plh-inp.plh-name{font-size:15px}
    .pst-page .plh-left{gap:3px}
    .pst-grid{display:flex;gap:8px;height:843px;margin-top:10px}
    /* Static mode's signature never touches .pst-grid - it used to be appended in-flow
       after the grid, which pushed total content past one page's height and made the PDF
       export's page-fit clamp squish the WHOLE rendered image (grid included) to force it
       back onto one page. It's now treated like the "Generated by Golobar.si" brand
       footer: small, bottom-left, and for the actual PDF export drawn directly onto the
       page by downloadPedigreePdfStatic() in the blank margin below the grid image -
       never part of the html2canvas-rasterized content at all, so the grid is always its
       full, untouched 843px. This on-screen rule just keeps the live preview's version
       small and left-aligned to match. */
    .pst-page .ped-signature{width:140px;margin:14px 0 0;text-align:left}
    .pst-page .ped-sig-img{max-width:110px;max-height:40px;margin:0}
    .pst-page .ped-sig-name{font-size:11px;margin-top:3px}
    .pst-page .ped-sig-label{font-size:9px;margin-top:1px}
    .pst-col{display:flex;flex-direction:column;gap:3px;flex:1;min-width:0}
    .pst-box{flex:1;min-height:0;overflow:hidden;border:1px solid var(--bdr);border-radius:4px;padding:3px 6px;box-sizing:border-box;position:relative;background:var(--card);cursor:pointer;transition:border-color .15s,background .15s}
    .pst-box:hover{border-color:var(--blue);background:var(--card2)}
    .pst-box::before{content:'';position:absolute;top:0;left:0;bottom:0;width:3px}
    .pst-cock::before{background:var(--cock)}.pst-hen::before{background:var(--hen)}.pst-unk::before{background:var(--muted)}
    /* The box for the pigeon the whole pedigree is built for - same gold highlight the
       Dynamic tree gives its .pnode.subj, just not applied anywhere else in Static mode. */
    .pst-subj{border-color:var(--gold);background:rgba(240,165,0,.06);box-shadow:0 0 0 1px rgba(240,165,0,.2)}
    .pst-empty{display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:11px;font-style:italic;cursor:default}
    .pst-empty:hover{border-color:var(--bdr);background:var(--card)}
    /* Every text type is ONE fixed size, the same at every generation depth - no tiering
       by column anymore. A box's fixed (flex-divided) height is what varies, not the
       font; whatever text fits at this size fits, the rest is clipped by overflow:hidden
       on .pst-box. Kept as three separate rules (not one shared selector) only because
       ring/name/notes still have distinct roles (weight/color), not distinct sizes. */
    .pst-ring{font-weight:700;color:var(--gold);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:12px}
    .pst-sex{font-weight:700}
    .pst-cock .pst-sex{color:var(--cock)}.pst-hen .pst-sex{color:var(--hen)}
    .pst-name{font-weight:600;color:var(--txt);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:12px}
    .pst-owner{color:var(--txt2);opacity:.75;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:13px}
    .pst-info{color:#b3874f;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:12px}
    /* Subject/parents (tier 0-1) get a generous flex-divided box height (the same slack
       visible as empty space in their boxes) - let their bloodline line wrap to a 2nd line
       instead of ellipsis-truncating there, same as the Dynamic tree already does. Deeper
       tiers (2-3) keep single-line - their boxes are too short to spare the extra line
       without crowding out notes text. */
    .pst-tier-0 .pst-info,.pst-tier-1 .pst-info{white-space:normal;text-overflow:clip;word-break:break-word}
    /* -webkit-line-clamp instead of a plain overflow:hidden block - a hard pixel-height
       clip was slicing the last visible line of notes text in half at the box edge
       (breaking mid-word/mid-letter, not at a space); line-clamp always cuts at a full
       line boundary and adds an ellipsis, so text only ever breaks where a real line
       break (i.e. a space) already was. Per-tier count is a rough budget for that tier's
       much smaller share of the fixed page height - still just a budget, not exact: a
       box with unusually short lines may show one line less than it had room for, never
       more, which is the safe direction to be wrong in. */
    .pst-notes{color:var(--txt2);line-height:1.25;overflow:hidden;margin-top:2px;font-size:12px;display:-webkit-box;-webkit-box-orient:vertical;white-space:normal;word-break:break-word;text-overflow:ellipsis}
    .pst-tier-0 .pst-notes{-webkit-line-clamp:14}
    .pst-tier-1 .pst-notes{-webkit-line-clamp:10}
    .pst-tier-2 .pst-notes{-webkit-line-clamp:4}
    .pst-tier-3 .pst-notes{-webkit-line-clamp:2}
    .pdf-snapshot .pst-box{border-color:#ccc!important;background:#fff!important}
    .pdf-snapshot .pst-subj{border-color:#9a6e00!important;background:rgba(154,110,0,.04)!important}
    .pdf-snapshot .pst-empty{color:#aaa!important}
    .pdf-snapshot .pst-ring{color:#9a6e00!important}
    .pdf-snapshot .pst-name{color:#000!important}
    .pdf-snapshot .pst-info{color:#8a6642!important}
    .pdf-snapshot .pst-notes{color:#555!important}

    /* DETAIL */
    .dh{display:flex;gap:18px;align-items:flex-start;margin-bottom:20px;flex-wrap:wrap}
    .dav{width:68px;height:68px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:28px;flex-shrink:0;border:3px solid;font-family:'Rajdhani',sans-serif;font-weight:700}
    .dr{font-size:14px;color:var(--gold);font-weight:700;letter-spacing:.1em;text-transform:uppercase;margin-bottom:5px}
    .dt h2{font-size:28px;font-weight:700;line-height:1.1}
    .dbadges{display:flex;gap:7px;flex-wrap:wrap;margin-top:7px}
    .igrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(165px,1fr));gap:9px}
    .iitem{background:var(--bg2);border:1px solid var(--bdr);border-radius:8px;padding:10px 12px}
    .ilbl{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;color:var(--muted);margin-bottom:3px}
    .ival{font-size:15px;font-weight:600;font-family:'Rajdhani',sans-serif}
    .tabs{display:flex;gap:2px;background:var(--bg2);border-radius:8px;padding:3px;margin-bottom:18px;width:fit-content}
    .tab-btn{padding:7px 16px;border-radius:6px;cursor:pointer;font-size:15px;font-weight:600;color:var(--txt2);border:none;background:none;font-family:'Nunito',sans-serif;transition:all .2s}
    .tab-btn.active{background:var(--card2);color:var(--txt)}

    /* BREEDING */
    .breed-table{width:100%;border-collapse:collapse;font-size:15px;margin-top:8px}
    .breed-table th{text-align:left;padding:8px 10px;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);border-bottom:2px solid var(--bdr)}
    .breed-table td{padding:9px 10px;border-bottom:1px solid var(--bdr);vertical-align:middle}
    .breed-table tr:hover td{background:rgba(255,255,255,.02)}

    /* BADGES */
    .badge{font-size:13px;font-weight:600;padding:2px 8px;border-radius:20px;letter-spacing:.03em;white-space:nowrap;display:inline-block}
    .b-cock{background:rgba(74,158,255,.15);color:var(--cock)}.b-hen{background:rgba(255,110,176,.15);color:var(--hen)}.b-unk{background:rgba(77,106,138,.15);color:var(--muted)}
    .b-yr{background:rgba(240,165,0,.12);color:var(--gold)}.b-col{background:rgba(255,255,255,.06);color:var(--txt2)}
    .b-own{background:rgba(74,158,255,.12);color:var(--blue)}
    .b-status{background:rgba(245,158,11,.1);color:var(--warn);border:1px solid rgba(245,158,11,.2)}

    /* ADMIN TABLE */
    .atable{width:100%;border-collapse:collapse;font-size:15px}
    .atable th{text-align:left;padding:9px 13px;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);border-bottom:2px solid var(--bdr)}
    .atable td{padding:10px 13px;border-bottom:1px solid var(--bdr);vertical-align:middle}
    .atable tr:hover td{background:rgba(255,255,255,.02)}
    .activity-time{font-family:monospace;font-weight:700;color:var(--ok);letter-spacing:.02em}
    /* No white-space:nowrap here (unlike most short badge-style labels) - "Danes zaznanih
       golobov: N" is a full sentence, long enough in some languages that forcing it onto one
       line can push past the viewport at high in-app zoom (#race-zoom-wrap). It already has a
       real space to wrap at, so dropping nowrap is enough - no overflow-wrap needed. */
    .activity-count{font-weight:700;font-size:22px;text-transform:uppercase;color:var(--gold);margin-bottom:20px}
    /* A fixed mobile px size (previously 21px) still wrapped in the field on a real phone - the
       bold uppercase fallback there renders noticeably wider than the Nunito metrics this was
       tuned against (webfont load timing/substitution varies by device). Scaling with vw instead
       of a fixed px keeps this proportional to the ACTUAL viewport on any phone, with enough
       margin (clamp floor/ceiling) to absorb that kind of font-width variance rather than being
       tuned to one specific measurement that turned out not to generalize. */
    @media(max-width:640px){.activity-count{font-size:clamp(15px,4.5vw,21px)}}
    .status-dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:5px}
    .dot-ok{background:var(--ok)}.dot-off{background:var(--err)}.dot-warn{background:var(--warn)}
    .live-badge{display:inline-flex;align-items:center;gap:7px;background:rgba(34,197,94,.14);color:var(--ok);border:1px solid rgba(34,197,94,.4);border-radius:20px;padding:4px 12px;font-size:14px;font-weight:700}
    .live-dot{width:9px;height:9px;border-radius:50%;background:var(--ok);display:inline-block;box-shadow:0 0 0 0 rgba(34,197,94,.6);animation:livePulse 1.5s ease-in-out infinite}
    @keyframes livePulse{0%,100%{box-shadow:0 0 0 0 rgba(34,197,94,.6)}50%{box-shadow:0 0 0 6px rgba(34,197,94,0)}}
    .prep-badge{display:inline-flex;align-items:center;gap:7px;background:rgba(245,158,11,.14);color:var(--warn);border:1px solid rgba(245,158,11,.4);border-radius:20px;padding:4px 12px;font-size:14px;font-weight:700}
    .prep-dot{width:9px;height:9px;border-radius:50%;background:var(--warn);display:inline-block;box-shadow:0 0 0 0 rgba(245,158,11,.6);animation:prepPulse 1.5s ease-in-out infinite}
    @keyframes prepPulse{0%,100%{box-shadow:0 0 0 0 rgba(245,158,11,.6)}50%{box-shadow:0 0 0 6px rgba(245,158,11,0)}}
    .done-badge{display:inline-flex;align-items:center;gap:7px;background:rgba(139,163,196,.16);color:var(--txt2);border:1px solid rgba(139,163,196,.45);border-radius:20px;padding:4px 12px;font-size:14px;font-weight:700}
    .done-dot{width:9px;height:9px;border-radius:50%;background:var(--txt2);display:inline-block}

    /* TOGGLE */
    .toggle-wrap{display:flex;align-items:center;gap:10px;font-size:16px}
    .toggle{position:relative;display:inline-block;width:44px;height:24px;flex-shrink:0}
    .toggle input{opacity:0;width:0;height:0}
    .tslider{position:absolute;cursor:pointer;inset:0;background:var(--bdr2);border-radius:24px;transition:.3s}
    .tslider:before{content:'';position:absolute;width:18px;height:18px;left:3px;bottom:3px;background:#fff;border-radius:50%;transition:.3s}
    input:checked+.tslider{background:var(--ok)}
    input:checked+.tslider:before{transform:translateX(20px)}

    /* MODAL */
    .modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.65);z-index:500;display:flex;align-items:center;justify-content:center;padding:20px}
    .modal{background:var(--card);border:1px solid var(--bdr2);border-radius:14px;padding:24px;width:100%;max-width:560px;max-height:80vh;display:flex;flex-direction:column;gap:14px}
    .modal-hdr{display:flex;align-items:center;justify-content:space-between}
    .modal-hdr h3{font-size:17px;font-weight:700}
    .modal-close{background:none;border:none;color:var(--txt2);cursor:pointer;font-size:20px;line-height:1;padding:2px 6px;border-radius:6px}
    .modal-close:hover{background:var(--card2);color:var(--txt)}
    .modal-list{overflow-y:auto;flex:1;display:flex;flex-direction:column;gap:5px}
    .modal-item{display:flex;align-items:center;gap:10px;padding:9px 12px;background:var(--bg2);border:1px solid var(--bdr);border-radius:8px;cursor:pointer;transition:all .2s}
    .modal-item:hover{border-color:var(--blue);background:var(--card2)}

    /* MISC */
    .shdr{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
    .shdr h2{font-size:18px;font-weight:700}
    .divider{height:1px;background:var(--bdr);margin:20px 0}
    .empty-state{text-align:center;padding:50px 20px;color:var(--muted)}
    .empty-state .ei{font-size:42px;margin-bottom:12px}.empty-state h3{font-size:18px;color:var(--txt2);margin-bottom:8px}.empty-state p{font-size:16px;margin-bottom:16px}
    .auth-fail{padding-top:20px;color:var(--err)}
    .auth-fail h3{font-size:56px;color:var(--err);margin-bottom:14px}
    .auth-fail p{font-size:32px;color:var(--err);line-height:1.4;margin-bottom:0}
    @media(max-width:640px){.auth-fail{padding-top:6px}.auth-fail h3{font-size:34px;margin-bottom:10px}.auth-fail p{font-size:20px}}
    .rlist{display:flex;flex-direction:column;gap:6px}
    .ritem{display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--bg2);border:1px solid var(--bdr);border-radius:8px;cursor:pointer;transition:all .2s}
    .ritem:hover{border-color:var(--bdr2);background:var(--card)}
    .rdot{width:9px;height:9px;border-radius:50%;flex-shrink:0}.rdot.cock{background:var(--cock)}.rdot.hen{background:var(--hen)}.rdot.unk{background:var(--muted)}
    .loader{display:flex;align-items:center;justify-content:center;padding:56px;color:var(--muted);font-size:16px;gap:10px}
    .spin{display:inline-block;width:20px;height:20px;border:2px solid var(--bdr2);border-top-color:var(--blue);border-radius:50%;animation:spin .7s linear infinite}
    @keyframes spin{to{transform:rotate(360deg)}}
    .toast{position:fixed;bottom:20px;right:20px;background:var(--card);border:1px solid var(--bdr2);border-radius:10px;padding:10px 15px;font-size:16px;font-weight:600;z-index:2000;display:flex;align-items:center;gap:8px;box-shadow:0 8px 24px rgba(0,0,0,.4);animation:fadeUp .3s ease-out;transition:opacity .4s}
    .toast.ok{border-left:4px solid var(--ok)}.toast.err{border-left:4px solid var(--err)}
    .ham{display:none;background:none;border:none;color:var(--txt);font-size:22px;cursor:pointer;padding:4px}
    .bnav{display:none;position:fixed;bottom:0;left:0;right:0;background:var(--bg2);border-top:1px solid var(--bdr);z-index:100;padding:6px 0 8px}
    .bnitems{display:flex;justify-content:space-around}
    /* text-align:center - align-items:center on the flex column only centers the label's block
       as a whole; a 2-line label ("Nadzorna" / "plošča") still left-aligns each line within that
       block by default, so the shorter second line reads as shifted left of the first. */
    .bni{display:flex;flex-direction:column;align-items:center;gap:2px;padding:4px 10px;cursor:pointer;color:var(--txt2);font-size:12px;font-weight:600;transition:color .2s;user-select:none;text-align:center}
    .bni .ico{font-size:20px;display:flex;align-items:center;justify-content:center;height:22px;line-height:1}.bni.active{color:var(--blue)}
    .info-section{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-bottom:22px}
    @media(max-width:980px){
      /* No explicit height here on purpose - top:0 + bottom:0 from inset already pins #sb to
         span exactly the current viewport, recalculated by the browser on every reflow (resize,
         orientation change, mobile browser chrome show/hide). An explicit height:100vh here used
         to fight those offsets: 100vh reports the *layout* viewport height, which on mobile can
         differ from the true *visual* viewport (browser chrome behaves differently across
         portrait/landscape) - after rotating to landscape this let #sb's box run taller than the
         screen actually visible, pushing its bottom (and .sb-foot below it) down past the fold
         and behind/overlapping .bnav instead of stopping exactly at it. */
      #sb{position:fixed;inset:0 auto 0 0;transform:translateX(-100%);width:270px}#sb.open{transform:translateX(0);box-shadow:4px 0 20px rgba(0,0,0,.5)}
      /* #sb is an overlay here (position:fixed), not part of the page's own scroll flow - without
         this, scrolling inside the open menu (once .sb-nav's own overscroll-behavior:contain
         boundary is reached, or via iOS's momentum scroll) still scrolled the background content
         underneath it. Toggled on body by the hamburger/close buttons and go() - see there. */
      body.sb-locked{overflow:hidden}
      /* overflow:hidden above still lets touch-driven panning/rubber-band through on some
         tablets - a press-and-drag starting on #sb's own non-scrolling chrome (.sb-logo,
         .sb-foot) or on #main peeking out beside a fixed-width #sb on a wide "mobile-layout"
         tablet could still scroll the background. touch-action:none blocks panning at the
         gesture level on exactly those non-scrolling regions - deliberately NOT on body or #sb
         itself, since touch-action restrictions cascade to descendants and can't be re-enabled
         by them, which would also break .sb-nav's own overflow-y:auto touch-scrolling above. */
      body.sb-locked .sb-logo,body.sb-locked .sb-foot,body.sb-locked #main{touch-action:none}
      #main{padding-bottom:65px}#content{padding:14px}#topbar{padding:0 14px}
      /* Vnos RFID's ring-inventory stats are forced to 3 equal columns inline (#rfid-ring-stats) -
         fine on desktop, but on a phone each ~100px column is too narrow for a label like
         "NEDODELJENIH" to sit on one line, and break-word wrapping (see .stat-lbl) still looks
         ugly at that width. 2 columns gives each card roughly double the room. !important because
         it's overriding an inline style, which normally beats an external stylesheet regardless
         of selector weight. */
      #rfid-ring-stats{grid-template-columns:repeat(2,1fr)!important}
      /* Room is tight next to the hamburger + a truncating title - shrink the two topbar
         action buttons rather than let them force the title's ellipsis to kick in too early. */
      #topbar-quickadd-btn,#topbar-add-btn{padding:7px 10px;font-size:13px;white-space:nowrap}
      .ham{display:block}.bnav{display:block}.fgrid{grid-template-columns:1fr}
      .sb-collapse-btn{display:none}
      .sb-close-btn{display:flex}
      .stats-grid{grid-template-columns:1fr 1fr}.ring-parts{grid-template-columns:1fr 1fr}
      .info-section{grid-template-columns:1fr}
      .ped-actions{justify-content:center}
      /* Siblings/offspring tables on the pigeon detail page reflow into stacked cards -
         scoped to .ped-breed-table specifically, NOT the bare .breed-table class, which is
         also used by the CSV import mapping/preview tables and must keep its normal table
         layout there (inline <select> mapping controls don't make sense as stacked cards). */
      .ped-breed-table,.ped-breed-table tbody,.ped-breed-table tr{display:block}
      .ped-breed-table thead{display:none}
      .ped-breed-table td{display:block;white-space:normal;padding:4px 0;border-bottom:none}
      .ped-breed-table tr{background:var(--bg2);border:1px solid var(--bdr);border-radius:10px;padding:12px;margin-bottom:8px}
      .ped-breed-table tr:last-child{margin-bottom:0}
      .ped-breed-table tr:hover td{background:none}
      .ped-breed-table td:first-child{padding-top:0;padding-bottom:8px;margin-bottom:6px;border-bottom:1px solid var(--bdr)}
      .ped-breed-table td[data-label]{display:flex;justify-content:space-between;align-items:center;gap:10px;font-size:14px;text-align:right}
      .ped-breed-table td[data-label]::before{content:attr(data-label);color:var(--muted);font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;flex-shrink:0;text-align:left}
      /* Letterhead is a fixed 660x230 box (see .plh-box) - too wide for a phone, so scale
         it down to fit instead of relying on the horizontal scrollbar. Scale is computed
         from the actual viewport so it fits any phone width (floor of 0.4 keeps text from
         going illegibly small on very narrow devices - .ped-letterhead's overflow-x:auto
         stays as a fallback for that edge case). transform-origin:top center shrinks
         symmetrically, so the negative margins below just claw back the now-unused layout
         space transform doesn't remove on its own. PDF export is unaffected - both
         downloadPedigreePdf() and its static variant capture html2canvas at an explicit
         windowWidth well above 768px, so this media query never applies during export.
         Divisor is 660px, not bare 660 - dividing a length by a plain number keeps the
         calc() result typed as a <length>, which scale() rejects outright (verified via
         CSS.supports() - it silently dropped the whole transform declaration, so the box
         was never actually scaling at all). Length divided by length yields the unitless
         <number> scale() actually needs. 40px subtracted from 100vw matches the actual
         horizontal padding stacking up around the box - #content's 14px + .ped-letterhead's
         4px, both sides (36px total) plus a hair of slack - snug rather than leaving the
         box noticeably smaller than the space actually available. */
      .plh-box{
        --plh-scale:max(0.4, min(1, calc((100vw - 40px) / 660px)));
        transform:scale(var(--plh-scale));
        transform-origin:top center;
        margin-left:calc(330px * (var(--plh-scale) - 1));
        margin-right:calc(330px * (var(--plh-scale) - 1));
        margin-bottom:calc(230px * (var(--plh-scale) - 1));
      }
      /* Static mode's box is only 170px tall (see .pst-page .plh-box{height:170px} above),
         not the 230px the base rule's margin-bottom compensation assumes. Reusing that
         230px figure here under-reserved layout space by 60px*(1-scale), and since
         .ped-letterhead clips overflow-y, the shortfall silently cropped the bottom of the
         scaled box - the photo's lower edge and the last field row (phone number) - on
         narrow phones. Overriding just the margin-bottom math with the real 170px height
         (same --plh-scale custom property, already set by the base rule above) reserves
         the correct amount of space instead. */
      .pst-page .plh-box{margin-bottom:calc(170px * (var(--plh-scale) - 1))}
    }
    @keyframes fadeUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
    .fin{animation:fadeUp .3s ease-out}
    .smtp-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}
    @media(max-width:600px){.smtp-grid{grid-template-columns:1fr}}

    /* RFID MODULE */
    .scan-inp{font-family:monospace;font-size:18px;letter-spacing:.12em;text-align:center;padding:14px 16px!important;border-color:var(--blue)!important;background:rgba(74,158,255,.04);transition:background .4s,border-color .4s}
    @keyframes scanFlashOk{0%{background:rgba(34,197,94,.25);border-color:var(--ok)}100%{background:rgba(74,158,255,.04);border-color:var(--blue)}}
    @keyframes scanFlashErr{0%{background:rgba(239,68,68,.25);border-color:var(--err)}100%{background:rgba(74,158,255,.04);border-color:var(--blue)}}
    .scan-ok{animation:scanFlashOk .7s ease-out forwards}
    .scan-err{animation:scanFlashErr .7s ease-out forwards}
    .race-split{display:grid;grid-template-columns:1fr 1fr;gap:16px}
    /* Declared after the base rule above (equal specificity - later wins), not inside the
       shared @media(max-width:980px) block near the top of this file, which this same class
       used to sit in: this unconditional 2-column rule was added later, physically after that
       block, so it silently won the cascade at every width including mobile - "Prispeli"/
       "Neprispeli" never actually collapsed to one column despite the mobile rule existing. */
    @media(max-width:980px){.race-split{grid-template-columns:1fr}}
    /* Predvideni cas prihoda (ETA) - the speed rows are a lot of vertical scroll for two values
       per row, so the list is folded into two explicit halves side by side, each with its own
       Hitrost/Prihod header (a single spanning header left the right half unlabelled). Collapses
       to one column on mobile at the same 980px breakpoint .race-split uses, where the two halves
       simply stack and still read in order. */
    .eta-box{max-width:640px;margin:0 auto;padding:16px 18px 10px;border:1px solid var(--bdr2);border-radius:var(--r);background:var(--bg2)}
    .eta-cols{display:grid;grid-template-columns:1fr 1fr;gap:4px 30px;max-width:600px;margin:0 auto}
    .eta-head{display:grid;grid-template-columns:1fr 1fr;gap:18px;padding:0 12px 6px;margin-bottom:6px;border-bottom:1px solid var(--bdr2);font-size:12px;font-weight:700;letter-spacing:.08em;color:var(--txt);text-transform:uppercase}
    /* Stacked on mobile the two halves are one list again, so the second half's Hitrost/Prihod
       header would repeat itself mid-table - hide it, and drop the row-gap so the halves join
       seamlessly instead of showing a seam where the split happens to fall. */
    @media(max-width:980px){.eta-cols{grid-template-columns:1fr;max-width:340px;gap:0}.eta-col:nth-child(2) .eta-head{display:none}.eta-box{max-width:380px;padding:14px 10px 8px}}
    .race-counter{font-family:'Rajdhani',sans-serif;font-size:20px;font-weight:700;text-align:center;padding:12px 20px;background:var(--card);border:1px solid var(--bdr);border-radius:var(--r);margin-bottom:14px;letter-spacing:.03em;line-height:1;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:6px 14px;text-transform:uppercase;color:#B8D8FF}
    .rc-ret{color:var(--ok);font-size:22px}.rc-mis{color:var(--warn);font-size:22px}.rc-sep{color:var(--muted);font-size:18px}
    .entry-row{display:flex;align-items:center;gap:8px;padding:9px 12px;border-radius:8px;border:1px solid var(--bdr);background:var(--bg2);font-size:15px;margin-bottom:5px}
    .entry-row.returned{border-color:rgba(34,197,94,.35);background:rgba(34,197,94,.05)}
    .entry-row.missing{border-color:rgba(245,158,11,.2);background:rgba(245,158,11,.03)}
    .en{font-size:13px;font-weight:700;color:var(--txt2);width:22px;flex-shrink:0;text-align:right}
    .ename{font-weight:600;flex:1;min-width:0;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
    /* white-space:nowrap alone (as this used to be) never overflows its OWN box - it just grows
       the box to fit, which is exactly the problem at high in-app zoom (see #race-zoom-wrap):
       ring codes like "SLO-2026-115-920" rendered at up to 1.6x size can exceed the real mobile
       viewport before flex-wrap ever gets a chance to move them to their own line. overflow-wrap
       lets a code break mid-string only once it's genuinely out of room - normal-width contexts
       (desktop, or mobile at 100% zoom) are unaffected since the text already fits on one line. */
    .eband{font-size:13px;color:var(--gold);font-family:monospace;letter-spacing:.04em;white-space:normal;overflow-wrap:anywhere;min-width:0;max-width:100%}
    /* Shared by owner-name spans sitting next to a .eband/.ename in the same flex row (missing-
       pigeon cards, per-competitor breakdown) - a fancier's nickname/loft name is free text with
       no guaranteed break points, so it needs the same overflow-wrap safety net as .eband once
       zoomed past what the row can otherwise fit on one line. */
    .erow-owner{overflow-wrap:anywhere;min-width:0;max-width:100%}
    .etime{font-size:13px;font-family:monospace;color:var(--ok);white-space:nowrap}
    .race-card{background:var(--card);border:1px solid var(--bdr);border-radius:var(--r);padding:16px 18px;cursor:pointer;transition:border-color .2s}
    .race-card:hover{border-color:var(--blue)}
    .rs-draft{color:var(--warn)}.rs-active{color:var(--ok)}.rs-completed{color:var(--muted)}
    .rfid-assign-row{display:grid;grid-template-columns:160px 1fr 1fr auto;gap:8px;align-items:center;padding:8px 0;border-bottom:1px solid var(--bdr)}
    .rfid-assign-row:last-child{border-bottom:none}
    @media(max-width:768px){.rfid-assign-row{grid-template-columns:1fr 1fr;row-gap:6px}.rfid-assign-row .rfid-id-cell{grid-column:1/-1}}
    .av-matched td{color:var(--ok)}.av-unknown td{color:var(--muted)}.av-returned td{color:var(--warn)}
    /* LocationPicker modal */
    .lp-overlay{position:fixed;inset:0;background:rgba(0,0,0,.65);z-index:2000;display:flex;align-items:center;justify-content:center;padding:16px}
    .lp-modal{background:var(--card);border:1px solid var(--bdr);border-radius:12px;width:100%;max-width:560px;max-height:90vh;display:flex;flex-direction:column;overflow:hidden}
    .lp-modal-hdr{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--bdr);font-family:'Rajdhani',sans-serif;font-size:17px;font-weight:700;flex-shrink:0}
    .lp-modal-body{overflow-y:auto;flex:1}
    .lp-tabs{display:flex;border-bottom:1px solid var(--bdr);flex-shrink:0}
    .lp-tab{flex:1;padding:10px;border:none;background:var(--bg2);cursor:pointer;font-size:15px;font-family:'Nunito',sans-serif;color:var(--txt2);font-weight:600;transition:all .2s}
    .lp-tab.active{background:var(--card);border-bottom:2px solid var(--blue);color:var(--blue)}
    .lp-panel{padding:12px}
    .lp-panel.lp-hidden{display:none}
    .lp-suggestions{max-height:150px;overflow-y:auto;border:1px solid var(--bdr);border-radius:6px;margin-top:4px;background:var(--bg2)}
    .lp-suggestion{padding:8px 12px;cursor:pointer;border-bottom:1px solid var(--bdr2);font-size:14px;color:var(--txt)}
    .lp-suggestion:hover{background:var(--card2)}
    .lp-no-result{padding:8px 12px;color:var(--muted);font-size:14px}
    .lp-coord-row{display:flex;gap:8px;align-items:flex-end;flex-wrap:wrap}
    .lp-coord-row label{flex:1;min-width:100px;font-size:14px;color:var(--txt2);display:flex;flex-direction:column;gap:3px}
    .lp-coord-inp{padding:8px 10px;border:1px solid var(--bdr);border-radius:6px;background:var(--bg2);color:var(--txt);font-size:15px;font-family:'Nunito',sans-serif;width:100%}
    .lp-map{height:250px;width:100%}
    .lp-result{padding:12px 16px;background:rgba(34,197,94,.06);border-top:1px solid rgba(34,197,94,.25)}
    .lp-result.lp-hidden{display:none}
    .lp-result-label{font-size:13px;color:var(--muted);margin-bottom:2px;text-transform:uppercase;letter-spacing:.05em}
    .lp-result-address{font-size:15px;font-weight:500;color:var(--txt)}
    .lp-result-coords{font-size:13px;color:var(--muted);font-family:monospace;margin-top:2px}
    .lp-approx-hint{display:flex;gap:6px;align-items:flex-start;margin-top:8px;padding:8px 10px;background:rgba(245,158,11,.1);border:1px solid rgba(245,158,11,.3);border-radius:6px;font-size:13px;color:var(--warn);line-height:1.4}
    .lp-approx-hint.lp-hidden{display:none}
    .lp-hint{padding:8px 12px;font-size:13px;color:var(--muted);text-align:center;border-top:1px solid var(--bdr)}
    .lp-loc-display{display:flex;align-items:center;gap:8px;padding:8px 10px;border:1px solid var(--bdr);border-radius:6px;background:var(--bg2);min-height:38px;max-width:460px;transition:border-color .25s}
    .lp-loc-text{flex:1;font-size:15px;color:var(--muted)}
    /* On narrow phones the fixed-width coordinates span (monospace, doesn't shrink) was eating
       most of this row, squeezing the flexible address text down to barely one word's width -
       it then wrapped after almost every word instead of filling the line normally. Letting the
       row wrap, and giving the address text flex-basis:100% so it claims the full line for
       itself, pushes the coordinates down to their own line below instead of fighting it for
       horizontal space. */
    @media(max-width:640px){
      .lp-loc-display{flex-wrap:wrap}
      .lp-loc-text{flex-basis:100%}
    }
    .lp-loc-text.set{color:var(--txt)}
    .lp-loc-display.lp-pulse{cursor:pointer;border-color:var(--warn);animation:lpPulse 1.4s ease-in-out infinite}
    @keyframes lpPulse{0%,100%{box-shadow:0 0 0 0 rgba(245,158,11,.45)}50%{box-shadow:0 0 0 6px rgba(245,158,11,0)}}
    /* DateTimePicker modal */
    .dtp-modal{background:var(--card);border:1px solid var(--bdr);border-radius:12px;width:100%;max-width:360px;max-height:90vh;display:flex;flex-direction:column;overflow:hidden}
    .dtp-nav{display:flex;align-items:center;justify-content:space-between;padding:12px 14px}
    .dtp-nav-label{font-family:'Rajdhani',sans-serif;font-size:17px;font-weight:700;text-transform:capitalize}
    .dtp-nav-btn{background:var(--bg2);border:1px solid var(--bdr);border-radius:6px;color:var(--txt);width:32px;height:32px;cursor:pointer;font-size:16px;display:flex;align-items:center;justify-content:center}
    .dtp-nav-btn:hover{background:var(--card2);border-color:var(--gold)}
    .dtp-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;padding:0 14px}
    .dtp-wd{text-align:center;font-size:13px;color:var(--muted);padding:4px 0;font-weight:600}
    .dtp-day{text-align:center;padding:8px 0;font-size:15px;color:var(--txt);cursor:pointer;border-radius:6px;background:transparent;border:none;font-family:'Nunito',sans-serif}
    .dtp-day:hover{background:var(--card2)}
    .dtp-day.dtp-other{color:var(--muted)}
    .dtp-day.dtp-today{color:var(--blue);font-weight:700}
    .dtp-day.dtp-sel{background:var(--gold);color:#0a1628;font-weight:700}
    .dtp-day.dtp-disabled{color:var(--muted);opacity:.35;cursor:not-allowed;pointer-events:none}
    .dtp-nav-btn:disabled{opacity:.3;cursor:not-allowed}
    .dtp-nav-btn:disabled:hover{background:var(--bg2);border-color:var(--bdr)}
    .dtp-time{display:flex;align-items:center;justify-content:center;gap:8px;padding:14px;border-top:1px solid var(--bdr);margin-top:8px}
    .dtp-time-inp{width:56px;text-align:center;padding:8px 4px;border:1px solid var(--bdr);border-radius:6px;background:var(--bg2);color:var(--txt);font-size:17px;font-family:'Nunito',sans-serif}
    .dtp-time-sep{font-size:18px;font-weight:700;color:var(--muted)}
    .dtp-actions{display:flex;gap:8px;padding:12px 14px;border-top:1px solid var(--bdr)}
    /* Race name + date/location/countdown block next to the active-race back arrow - centered
       at every width, not just mobile. */
    .rr-title,.rr-subtitle{text-align:center}
    /* Nazaj na dirke exists twice in the active-race/training markup (see the mobile
       .rr-back-top/.rr-back-inline override) - desktop keeps only the inline one pinned in the
       header row, the top-of-page duplicate is mobile-only. */
    .rr-back-top{display:none}
    /* Bottom-of-page "Nazaj na dirke" duplicate: mobile-only (see the mobile override) - on
       desktop the header's own back arrow at the top of the page is already reachable without
       scrolling, so a second copy at the bottom would be redundant there. */
    .rr-back-bottom-wrap{display:none}
    /* Zaključek dirke čez likewise exists twice (see the mobile override) - desktop keeps it on
       its own line above the header, the copy below Čas izpusta is mobile-only. */
    .rr-countdown-mobile{display:none}
    /* Koordinate exists twice too: desktop shows it inline after the race name in parens
       ("test (46.40495, 15.25674)"), mobile keeps the separate labeled "Koordinate:" line below
       Čas izpusta instead (see the mobile override). */
    .rr-coord-mobile-line{display:none}
    /* Desktop: back button pinned left, zoom/admin actions pinned right, everything else
       (title block) truly centered in between regardless of how wide the left/right groups
       are - a plain flex row can only do that if both outer groups happen to match width,
       which they don't (back button vs. a badge+PDF-button vs. just zoom). auto/1fr/auto used
       to sit here: that only centers the *middle track*, not the actual page - whichever outer
       group is wider drags the middle track's own center off the true center with it (visible
       as the title reading shifted toward the narrower side). Two equal 1fr outer tracks flank
       an auto-sized middle track instead, so the middle track's center is always the row's true
       center regardless of how unequal the two side groups are - the two 1fr tracks must not
       themselves be forced apart, so the outer items get justify-self:start/end below instead
       of stretching to fill their tracks (grid's default). */
    .rr-header{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:10px}
    .rr-header>.rr-back{justify-self:start}
    .rr-header>.rr-actions{justify-self:end}
    /* Countdown ("Zaključek dirke čez...") sits above .rr-header entirely (not nested inside
       .rr-titleblock) so it reads first, before the back button and race name, on every width -
       a nested element can never visually escape above its own parent's position. */
    .rr-countdown{font-size:18px}
    /* Relay (bridge) status banner */
    .relay-banner{align-items:center;gap:8px;padding:8px 12px;border-radius:8px;font-size:14px;font-weight:600;border:1px solid transparent;width:fit-content;max-width:100%}
    .relay-banner i{font-size:16px;flex-shrink:0}
    .relay-banner.relay-ok{background:rgba(34,197,94,.08);border-color:var(--ok);color:var(--ok)}
    .relay-banner.relay-warn{background:rgba(239,68,68,.08);border-color:var(--err);color:var(--err)}
    /* Klubske dirke active view on narrow screens: the status bar's "|" separators wrap onto
       their own dangling line once each stat stacks (nothing to visually separate at that
       point), the zoom control gets its own centered line instead of sitting flush left under
       the back button, and the auto-capture/relay status text + banner center instead of
       hugging the left edge. 1200px, not 980px like most other mobile breakpoints in this file -
       the sidebar (--sw:242px) stays visible in-flow down to 980px (it only goes off-canvas
       below that - see #sb's own breakpoint), so a viewport just above 980px still only has
       ~980-242=738px of actual content width to lay the header out in, not the ~980px this
       block's rules assume. Without the extra headroom, back+title+zoom+both admin buttons
       squeezed and wrapped mid-row on real tablet widths (e.g. 1060px) despite "looking like"
       desktop by viewport width alone. */
    @media(max-width:1200px){
      .rc-sep{display:none}
      .zoom-ctrl-wrap{flex-basis:100%;display:flex;justify-content:center;margin-top:4px}
      /* Spremeni čas izpusta / Zaključi dirko: kept as a side-by-side pair (not each split onto
         its own full-width line) and moved above the zoom control via order - admins scanning
         the race need those two actions before the text-size control, not after it. */
      .rr-race-btns{flex-basis:100%;display:flex;flex-wrap:wrap;justify-content:center;gap:10px;order:-1}
      .ac-status-row{justify-content:center;text-align:center}
      .relay-banner{margin-inline:auto}
      .relay-banner span{text-align:center}
      .rr-center-mobile{text-align:center}
      .rr-row-center{justify-content:center;text-align:center}
      /* rr-row-center only centers content *within* its own box - a flex item that's shrunk to
         fit its content (the default) has no extra room for that centering to be visible even
         after wrapping onto its own line. Force it to span the full row first. */
      .rr-full-mobile{flex:1 1 100%}
      /* Prispeli only: when a pigeon has no name on file, .ename falls back to the raw 16-char
         RFID hex code, which is meaningless to a fancier at a glance and was what forced the
         min-width fix that crowded/truncated the row on narrow screens. Ring + owner already
         identify the bird - just drop the hex code here rather than displaying it squeezed. */
      .rr-rfid-hide-mobile{display:none}
      /* Statistika dirke (Plasman): a real <table> can't reflow onto a narrow phone screen
         without either horizontal scroll or unreadably truncated cells - even after trimming
         columns it still needed to scroll. Below this breakpoint show the per-pigeon card list
         instead (.rr-mobile-cards, built by _rfidStatsCardsHtml) and hide the table entirely. */
      .rr-desktop-table{display:none}
      .rr-mobile-cards{display:block}
      /* Ring numbers (e.g. "SLO-2026-115-1152") were ellipsis-truncating on narrow phones before
         a reader could see the full number, while the owner name next to it had room to spare -
         trade a px from the ring size to the owner name so the ring fits in full. */
      .rr-card-ring{font-size:14px!important}
      .rr-card-owner{font-size:15px!important}
      /* m/min a touch smaller on mobile too - arrival time (the timer next to it) stays at its
         current size, untouched. */
      .rr-card-speed{font-size:14px!important}
      /* Race list card's PDF button - full desktop size (padding:9px 18px, 15px font, 18px icon)
         is too wide to sit on the same row as the status badge + trash icon on a phone, so it
         wrapped to its own line instead of matching the mock's single-row layout. Shrink it. */
      .race-pdf-btn{padding:6px 10px!important;font-size:13px!important}
      .race-pdf-btn i{font-size:15px!important}
      /* Generic "inline on desktop, stacked on mobile" split, same idea as the plasman header
         split below but reusable for any " · "-joined pair (klubske dirke's date/kraj izpusta
         line, currently the only user). */
      .rr-stack-sep{display:none}
      .rr-stack-item{display:block}
      /* Plasman section headers ("Prispeli plasirani - top 33% (N golobov)"): the dash-joined
         phrase wraps mid-word on narrow phones. Below this breakpoint drop the separator and
         stack main/sub as their own centered lines instead (see _plasmanHdrHtml). */
      .rr-plasman-hdr{text-align:center}
      .rr-plasman-sep{display:none}
      .rr-plasman-main,.rr-plasman-sub{display:block}
      /* Pretvornik koordinat (Zračna razdalja page): the lat/lng field groups sit side by side
         in a 2-column grid on desktop, which squeezes "Zemljepisna dolžina" and its three DMS
         inputs into half the width on a phone and cuts the label off. Stack to one column. */
      .cc-fields-grid{grid-template-columns:1fr}
      /* Back-to-top FAB on mobile: bottom:90px clears .bnav (reserved via #main
         padding-bottom:65px above) with a deliberate margin so it can never sit flush against or
         overlap the bottom nav bar, even for 2-line nav labels ("Nadzorna plošča"). Desktop's
         base rule above uses bottom:24px since there's no bottom nav to clear there. */
      .rr-top-btn{right:16px;bottom:90px}
      /* Pregled poti (route map) Izpust/Golobnjak legend - centered on mobile; the coordinate
         sub-line's desktop indent (margin-left:22px, lining it up under the address text above)
         stops making sense once the whole block is centered instead of left-aligned. */
      .rr-route-legend{text-align:center}
      .rr-route-coord{margin-left:0!important}
      /* Race name + date/countdown/location line next to the back arrow - full desktop sizes
         (22px/16px) crowd the narrow remaining width next to a 48px tap-target back button,
         wrapping the countdown mid-phrase. Smaller sizes here give the wrapped lines more room. */
      .rr-title{font-size:19px}
      .rr-subtitle{font-size:13px}
      /* Countdown ("Zaključek dirke čez...") now sits full-width above the header (see the
         .rr-countdown comment near its base rule) instead of squeezed next to the back button,
         but the desktop 18px size still wraps "45 UR, 10 MINUT" onto its own line on a phone -
         drop it further here since it has the full width to itself either way. */
      .rr-countdown{font-size:14px}
      /* Back button becomes a full labeled button, centered on its own line, instead of a
         square icon sitting flush left - desktop keeps the compact icon-only square (left,
         zoom right, title centered between) per explicit instruction; only mobile restructures
         into a stacked column so nothing skews the title block's centering. */
      .rr-header{display:flex;flex-direction:column;align-items:stretch}
      .rr-back{width:auto;height:auto;padding:6px 20px;align-self:center;justify-content:center;gap:8px;font-size:15px}
      .rr-back-label{display:inline;font-weight:700;text-transform:uppercase}
      .rr-titleblock,.rr-actions{width:100%}
      .rr-actions{justify-content:center}
      /* Nazaj na dirke: on mobile this duplicate copy sits above everything (even the
         countdown) instead of inside .rr-header, so leaving the page is reachable before
         scrolling past the countdown/title - the .rr-back inside .rr-header is hidden in its
         favor. Desktop keeps the original inline one (pinned left in the header row) and hides
         this duplicate - see the base rr-back-top rule. */
      .rr-back-top{display:flex;margin:0 auto 10px}
      .rr-back-inline{display:none}
      .rr-back-bottom-wrap{display:flex;justify-content:center;margin-top:16px}
      .rr-countdown-top{display:none}
      .rr-countdown-mobile{display:block;margin-top:4px}
      .rr-title-coord-inline{display:none}
      .rr-coord-mobile-line{display:block}
      /* Statistika dirke card header: desktop overlays the centered title behind the
         location/refresh group via absolute positioning (true-centers regardless of that
         group's width - see the header title-block comment above for the same trick). On a
         narrow phone there isn't room for both on one line, so the overlay collided with the
         location text. Drop back to normal stacked flow here instead: title first, centered,
         full width; location/refresh centered on their own line below it. */
      .rr-stats-hdr{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:0!important}
      .rr-stats-title{position:static!important;pointer-events:auto!important}
      .rr-stats-meta{justify-content:center;width:100%}
    }
    /* Spremeni čas izpusta / Zaključi dirko stretching to fill the row edge-to-edge only makes
       sense on a true phone-width screen (matches the design mock) - at tablet widths (980-1200px,
       where the block above already stacks/pairs them above the zoom control) the row is wide
       enough that flex:1 stretched each button into an oddly wide, sparse-looking bar instead of
       its natural content width. Scoped to a narrower breakpoint than the block above so tablets
       get the pairing/reordering but keep each button its own natural size. */
    @media(max-width:640px){
      .rr-race-btns>*{flex:1 1 0;min-width:0}
    }
    /* Small connection-status dot next to the race name, shown only on narrow phones (see the
       480px block below) where the full Skeniranje prihoda card is hidden - a phone doesn't run
       the antenna reader, only the club's own PC does, but a fancier checking progress from
       their phone still benefits from an at-a-glance "is the bridge even connected" signal. */
    .rr-mobile-relay-icon{display:none}
    @media(max-width:480px){
      /* Skeniranje prihoda (manual scan input + relay banner + auto-capture controls) only
         matters on the PC actually plugged into the antenna - hidden here, not removed, so
         that PC still gets full functionality if its own window happens to be this narrow;
         the input, auto-capture timer and relay poll all keep running normally underneath. */
      .rr-scan-card{display:none}
      .rr-mobile-relay-icon{display:inline-block}
    }
