/* Components: buttons, inputs, badges, dots, cards, KPI, tables (and their
   card mode under 720 px), pagination, tabs, pills, switch, toast, empty,
   lock card, meta grid, list rows, chips, alerts, maintenance. */

/* ---- buttons ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 18px; border-radius: var(--radius-md); border: 1px solid transparent;
  font: 600 14px/20px var(--font-sans); text-decoration: none; white-space: nowrap;
  background: transparent; color: var(--ink); vertical-align: middle;
}
.btn:hover { text-decoration: none; }
.btn-primary { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.btn-primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
.btn-secondary { background: var(--surface-200); border-color: var(--control-border); color: var(--ink); }
.btn-secondary:hover { background: var(--surface-100); }
.btn-danger { background: var(--surface-200); border-color: var(--danger); color: var(--danger); }
.btn-danger:hover { background: var(--danger-soft); }
/* Zero padding left "Kopiraj link" a 74x20 target. .btn is already an
   inline-flex box that centres its label, so a 24 px floor costs nothing:
   in .portal-actions the row is 42 px tall regardless. */
.btn-link { padding: 0; border: 0; color: var(--brand); font-weight: 600; min-height: 24px; }
.btn-link:hover { text-decoration: underline; }
.btn-sm { padding: 6px 12px; font-size: 13px; line-height: 18px; }
.btn-lg { padding: 14px 28px; font-size: 15px; line-height: 22px; }
/* An unavailable control still has to be readable: --disabled-ink measured
   1,67:1 as ink on --surface-200 and again as the fill under --on-brand, and
   the pager renders its unavailable direction as a <span class="btn">, which
   is not a disabled form control and has no exemption to stand on. */
.btn[disabled], .btn[aria-disabled="true"] { color: var(--disabled-ink-strong); border-color: var(--control-border); background: var(--surface-200); cursor: default; }
.btn-primary[disabled] { background: var(--disabled-ink-strong); border-color: var(--disabled-ink-strong); color: var(--on-brand); }
/* On offer but outside the package (Excel on every list): muted, with a lock;
   the link still leads to the page that says which package has it. */
.btn-locked, .btn-locked:hover { color: var(--ink-muted); }
.btn-locked::after { content: "\1F512"; font-size: 11px; line-height: 1; }

/* ---- inputs ---- */
.input, .select, .textarea {
  display: block; width: 100%; padding: 10px 12px;
  border: 1px solid var(--control-border); border-radius: var(--radius-md);
  background: var(--surface-200); color: var(--ink); font: 400 14px/20px var(--font-sans);
}
/* No `padding-right: 28px` here: nothing in this product ever drew an arrow
   in that space, so the browser's own chevron was painted over it and the
   option text stopped 28 px short of nothing. The engine reserves the room
   its own control needs. */
.textarea { resize: vertical; min-height: 80px; }
.input::placeholder, .textarea::placeholder { color: var(--ink-muted); }
.input:focus, .select:focus, .textarea:focus { outline: var(--focus); outline-offset: 0; border-color: var(--brand); }
.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--danger); }
.field { margin-bottom: var(--space-3); }
.field > label, .field > .label { display: block; font: var(--text-label); color: var(--ink-muted); margin-bottom: 6px; }
.field-error { font: var(--text-label); color: var(--danger-ink); margin-top: 6px; }
.help { font: var(--text-body-small); color: var(--ink-muted); margin-top: 6px; }
.form-errors { background: var(--danger-soft); color: var(--danger-ink); border-radius: var(--radius-md); padding: 10px 14px; margin-bottom: var(--space-3); font-size: 14px; }
.form-errors ul { margin: 0; padding-left: 18px; }
.form-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--border); }
.form-narrow { max-width: 480px; }
.autosave { font: var(--text-body-small); color: var(--ink-muted); min-height: 18px; margin-top: 6px; }
.autosave.is-error { color: var(--danger-ink); }

/* ---- alerts ---- */
.alert { border-radius: var(--radius-md); padding: 10px 14px; font-size: 14px; margin-bottom: var(--space-3); }
.alert-ok { background: var(--accent-soft); color: var(--accent-ink); }
.alert-err { background: var(--danger-soft); color: var(--danger-ink); }
.alert-info { background: var(--brand-soft); color: var(--brand); display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.alert-info code { word-break: break-all; }

/* ---- badges ---- */
.badge { display: inline-block; font: 600 11px/14px var(--font-sans); padding: 3px 9px; border-radius: var(--radius-sm); white-space: nowrap; vertical-align: middle; background: var(--neutral-soft); color: var(--ink-muted); }
.badge-open { background: var(--warning-soft); color: var(--warning-ink); }
.badge-awarded { background: var(--accent-soft); color: var(--accent-ink); }
.badge-expiring { background: var(--danger-soft); color: var(--danger-ink); }
.badge-prep { background: var(--brand-soft); color: var(--brand); }
.badge-applied { background: var(--accent-soft); color: var(--accent-ink-strong); }
.badge-dropped { background: var(--neutral-soft); color: var(--ink-muted); }
.badge-role { background: var(--brand-soft); color: var(--brand); }
.badge-active { background: var(--accent-soft); color: var(--accent-ink-strong); }
.badge-invited { background: var(--warning-soft); color: var(--warning-ink); }
.badge-inactive { background: var(--neutral-soft); color: var(--ink-muted); }
.badge-suspended { background: var(--danger-soft); color: var(--danger-ink); }
.badge-plan-basic { background: var(--neutral-soft); color: var(--ink-muted); }
.badge-plan-advanced { background: var(--brand-soft); color: var(--brand); }
.badge-plan-demo { background: var(--warning-soft); color: var(--warning-ink); }
/* --accent under white is 3,30:1 at 11px/600, which is not large text and
   needs 4,5:1. Its darker sibling in the same file measures 5,02:1. */
.badge-recommended { background: var(--accent-ink-strong); color: var(--on-brand); padding: 4px 10px; }
.badge-interno { background: var(--danger); color: var(--on-brand); }

/* ---- dots and oblast ---- */
.dot { display: inline-block; width: 6px; height: 6px; border-radius: 3px; background: var(--ink-muted); flex: 0 0 auto; vertical-align: middle; }
.dot-lg { width: 8px; height: 8px; border-radius: 4px; }
.dot[data-oblast="klima"] { background: var(--oblast-klima); }
.dot[data-oblast="it"] { background: var(--oblast-it); }
.dot[data-oblast="vozila"] { background: var(--oblast-vozila); }
.dot[data-oblast="farmacija"] { background: var(--oblast-farmacija); }
.dot[data-oblast="osiguranje"] { background: var(--oblast-osiguranje); }
.dot[data-tone="accent"] { background: var(--accent); }
.dot[data-tone="warning"] { background: var(--warning); }
.dot[data-tone="brand"] { background: var(--brand); }
.oblast { display: inline-flex; align-items: center; gap: 6px; color: var(--ink-muted); }
/* An oblast label is one word and never breaks letter by letter, whatever
   `overflow-wrap: anywhere` a cell around it carries: in a narrow cell
   "Osiguranje" read "Osiguran / je". (screen_core.css says the same for the
   detail page's meta grid.) */
table.data td .oblast { overflow-wrap: normal; }
.oblast-ds { font: var(--text-caption); color: var(--ink-muted); }

/* ---- cards ---- */
.card { background: var(--surface-200); border: 1px solid var(--border); border-radius: var(--radius-md); padding: var(--space-4); }
.card-tight { padding: 20px; }
.stack > * + * { margin-top: var(--space-3); }
.card-title { font: 600 13px/18px var(--font-sans); margin-bottom: var(--space-2); }
.card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding-bottom: var(--space-3); margin-bottom: var(--space-3); border-bottom: 1px solid var(--border); }
.card-head h2, .card-head .h2 { font: 600 15px/22px var(--font-sans); }
.panel { background: var(--surface-200); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--space-5); }
.row-gap > * { margin: 0 8px 8px 0; }

/* ---- KPI ---- */
.kpi-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-3); margin-bottom: var(--space-5); }
.kpi { background: var(--surface-200); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 20px; min-width: 0; }
.kpi-label { font: var(--text-label); color: var(--ink-muted); text-transform: uppercase; letter-spacing: 0.02em; }
.kpi-value { font: var(--text-h1); margin-top: var(--space-2); font-variant-numeric: tabular-nums; }
.kpi-hint { font: var(--text-caption); color: var(--ink-muted); margin-top: 4px; }
.kpi-warning .kpi-value { color: var(--warning); }
.kpi-danger .kpi-value { color: var(--danger); }
.kpi-accent .kpi-value { color: var(--accent); }
/* Four across only where the shell is the sidebar one: at 1024 px the four
   columns were 164 px each, narrower than the same card on a 360 px phone,
   and the label broke into three lines. Two across down to 1199 px keeps the
   threshold the same as the shell's and .team-grid's. */
@media (max-width: 1199px) { .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 399px) { .kpi-grid { grid-template-columns: 1fr; } }

/* ---- tables ---- */
.table-wrap { background: var(--surface-200); border: 1px solid var(--border); border-radius: var(--radius-md); overflow-x: auto; margin-bottom: var(--space-3); }
/* app.js marks a wrapper whose table is wider than the wrapper. macOS draws
   no scrollbar until something scrolls, so the mark carries the only signal
   there is: a shaded right edge, and a tab stop, because the links inside
   the rows never reach the columns that are cut off. */
.table-wrap[data-scrolls] { box-shadow: inset -10px 0 10px -10px var(--shadow); }
.table-wrap[data-scrolls]:focus-visible { outline: var(--focus); outline-offset: 2px; }
table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
table.data th { text-align: left; font: var(--text-label); color: var(--ink-muted); text-transform: uppercase; letter-spacing: 0.02em; padding: 10px 20px; border-bottom: 1px solid var(--border); white-space: nowrap; }
table.data td { padding: 14px 20px; border-bottom: 1px solid var(--border); vertical-align: middle; }
table.data td.c-title { min-width: 220px; }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: var(--surface-100); }
.td-muted { color: var(--ink-muted); }
.td-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.c-value { font-weight: 600; }
.td-expired { color: var(--ink-muted); }
.td-expired .caption { display: block; }
.td-note { font-size: 13px; color: var(--ink-muted); max-width: 240px; }
/* Between 1200 and 1279 px the sidebar is back and Moji tenderi has 878 px for
   a table that wants 905: the note is the only column that can give, and it is
   clipped to one line with a tooltip at either width, so 60 px costs nothing. */
@media (min-width: 720px) and (max-width: 1279px) { .td-note { max-width: 180px; } }
/* The note wraps. It used to be one clipped line whose tail lived in a
   title= - hover only, so a touch reader and a keyboard reader never saw it
   at all, and this is the column their own team writes. The column keeps its
   width budget (the table has to fit beside the sidebar) and the cell grows
   downwards instead; three lines hold every note the fixture has, and a
   longer one is a click away in the row's own page. */
.note-clip { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; overflow-wrap: anywhere; }
tr.row-link { position: relative; }
tr.row-link td.c-title a { color: var(--ink); font-weight: 500; }
tr.row-link td.c-title a::after { content: ""; position: absolute; inset: 0; }
tr.row-link td.c-title a:hover { text-decoration: none; }
tr.row-link td a:not(.c-title a), tr.row-link td button { position: relative; z-index: 1; }
th[aria-sort] .sortlink { color: inherit; display: inline-flex; align-items: center; gap: 6px; }
th[aria-sort] .sortlink:hover { color: var(--brand); text-decoration: none; }
/* The neutral caret says "this column can be sorted" and nothing else does
   for a sighted reader (aria-sort carries it for the rest). On --disabled-ink
   it measured 1,67:1 on the white header, against the 3:1 WCAG 1.4.11 asks of
   a graphical object that conveys state. */
.caret { width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid var(--control-border); }
.caret-asc { border-top: 0; border-bottom: 5px solid var(--brand); }
.caret-desc { border-top: 5px solid var(--brand); }
table.rank .c-rank { width: 48px; font-weight: 700; }
table.rank .c-name { font-weight: 600; }
table.rank .c-bar { width: 26%; min-width: 120px; }
.rbar { display: block; width: 100%; height: 8px; }
.rbar-bg { fill: var(--surface-100); }
.rbar-fill { fill: var(--brand); }
/* A data table under the top bar shell keeps a readable minimum and lets the
   wrapper scroll rather than squeezing six columns into 380 px. */
@media screen and (max-width: 1023px) { table.data { min-width: 640px; } }

/* card mode under 720 px: each row becomes a small card, cells carry their
   header. `screen` on purpose: an A4 sheet with a 12 mm margin is 703 px
   wide, so on paper this block used to turn every list into a column of
   label-and-value stacks with no table header at all. */
@media screen and (max-width: 719px) {
  .table-wrap-cards { overflow: visible; background: transparent; border: 0; }
  table.data.cards, table.data.cards tbody { display: block; min-width: 0; }
  table.data.cards thead { display: none; }
  table.data.cards tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 4px 12px; padding: 12px 16px; background: var(--surface-200); border: 1px solid var(--border); border-radius: var(--radius-md); margin-bottom: var(--space-2); }
  table.data.cards td { display: block; padding: 0; border: 0; }
  table.data.cards td::before { content: attr(data-label); display: block; font: var(--text-caption); color: var(--ink-muted); text-transform: uppercase; letter-spacing: 0.02em; }
  table.data.cards td.c-title { grid-column: 1; grid-row: 1; font-size: 15px; }
  table.data.cards td.c-title::before { display: none; }
  table.data.cards td.c-badge { grid-column: 2; grid-row: 1; text-align: right; }
  table.data.cards td.c-badge::before { display: none; }
  table.data.cards td.c-badge ~ td.c-badge { grid-row: auto; grid-column: 1 / -1; text-align: left; }
  table.data.cards td.c-badge ~ td.c-badge::before { display: block; }
  table.data.cards td:not(.c-title):not(.c-badge) { grid-column: 1 / -1; }
  /* Two short cells share a line instead of taking one each with a caption
     above: a card of 25 rows was 6,8 to 8,8 phone screens and above the fold
     0 to 2 rows fitted. The captions stay - what a figure is must not be
     guesswork - only the empty half of each line goes. */
  table.data.cards td.c-pair:not(.c-title):not(.c-badge) { grid-column: span 1; }
  /* A cell with no column name of its own (a row of buttons, a colspan
     "nothing here yet") would otherwise draw an empty caption line. */
  table.data.cards td.c-actions::before, table.data.cards td:not([data-label])::before { display: none; }
  table.data.cards td.td-num { text-align: left; }
  table.data.cards td.c-value { font-size: 17px; }
  table.data.cards .td-note { max-width: none; }
  table.data.cards .note-clip { -webkit-line-clamp: none; line-clamp: none; display: block; }
  table.data.cards tbody tr:hover { background: var(--surface-200); }
  table.rank .c-bar { width: auto; }
}

/* ---- pagination ---- */
.pager { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); flex-wrap: wrap; margin: var(--space-3) 0; font: var(--text-body-small); color: var(--ink-muted); }
.pager-links { display: flex; gap: var(--space-2); }
/* Only where a page of 25 rows is seven screens tall: on a wide screen the
   same links are a scroll away and a second copy is only noise. */
.pager-top { display: none; }
@media (max-width: 719px) { .pager-top { display: flex; } }

/* ---- tabs and pills ---- */
.tabs { display: flex; gap: var(--space-4); border-bottom: 1px solid var(--border); margin-bottom: var(--space-4); overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tabs a { flex: 0 0 auto; padding: 10px 4px; font: 600 14px/20px var(--font-sans); color: var(--ink-muted); border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap; }
.tabs a:hover { color: var(--ink); text-decoration: none; }
.tabs a[aria-current] { color: var(--brand); border-bottom-color: var(--brand); }
.pills { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.pill { display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px; border-radius: var(--radius-md); border: 1px solid var(--control-border); background: var(--surface-200); color: var(--ink); font: 500 13px/18px var(--font-sans); }
.pill:hover { text-decoration: none; background: var(--surface-100); }
.pill[aria-current] { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.pill-count { font: var(--text-caption); opacity: 0.85; }

/* ---- filters ---- */
.filters { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: var(--space-3); }
.filters-q { flex: 1 1 240px; max-width: 360px; }
.filters-select { flex: 0 1 auto; min-width: 160px; }
.filters .pills { margin-left: auto; }
/* Every control stays on the page at every width (owner rule: filters are
   never hidden); on a phone they stack, one under the other. */
@media (max-width: 719px) {
  .filters { flex-direction: column; align-items: stretch; gap: var(--space-2); }
  .filters-q, .filters-select { max-width: none; flex: 0 0 auto; }
  .filters .pills { margin-left: 0; }
  .filters .pill { flex: 1 1 auto; justify-content: center; }
}
/* A choice with nothing behind it stays on offer, greyed: never removed. */
.pill-empty:not([aria-current]) { color: var(--ink-muted); background: var(--surface-100); }

/* ---- sorting in card mode ---- */
/* From 720 px the sortable <th> is the control and this one would be a second
   source for the same state; under it <thead> is display:none, so without
   these two selects the sort had no control anywhere on the screen. */
.sort-phone { display: none; }
.sort-field label { display: block; font: var(--text-label); color: var(--ink-muted); margin-bottom: 4px; }
.sort-field .select:disabled { background: var(--surface-100); color: var(--ink-muted); }
@media (max-width: 719px) {
  .sort-phone { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-2); margin-bottom: var(--space-3); }
}

/* ---- switch ---- */
.switch-form { display: inline-block; margin: 0; }
/* 24 px tall, the WCAG 2.2 target minimum: the dot and the travel grow with
   it, so the switch looks the same and is 4 px easier to hit. */
.switch { position: relative; width: 44px; height: 24px; border-radius: 12px; border: 0; padding: 0; background: var(--border); vertical-align: middle; }
.switch[aria-checked="true"] { background: var(--brand); }
.switch-dot { position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 10px; background: var(--surface-200); transition: left 0.12s; }
.switch[aria-checked="true"] .switch-dot { left: 22px; }
.toggle-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: 12px 0; border-bottom: 1px solid var(--border); font-size: 14px; }
.toggle-row:last-child { border-bottom: 0; }

/* ---- toast ---- */
/* [data-empty] and not the `hidden` attribute: `hidden` takes the element
   out of the accessibility tree, and a live region that is out of the tree
   at the moment its text is written announces nothing at all. The element
   stays in the tree, empty, and only stops being seen and clicked. */
.toast { position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: 300; max-width: min(92vw, 460px); background: var(--ink); color: var(--surface-200); padding: 12px 18px; border-radius: var(--radius-md); font: 500 13.5px/1.45 var(--font-sans); box-shadow: 0 10px 30px var(--shadow); transition: opacity 0.12s; }
.toast[data-empty] { opacity: 0; pointer-events: none; }
.toast a { color: var(--surface-200); text-decoration: underline; }

/* ---- empty and lock ---- */
.empty { text-align: center; color: var(--ink-muted); padding: var(--space-5) var(--space-3); background: var(--surface-200); border: 1px dashed var(--border); border-radius: var(--radius-md); margin-bottom: var(--space-3); }
.empty-title { font: 600 15px/22px var(--font-sans); color: var(--ink); }
.empty-hint { margin: 6px 0 var(--space-3); }
.lock { display: flex; gap: 14px; align-items: flex-start; background: var(--surface-200); border: 1px dashed var(--border); border-radius: var(--radius-md); padding: 16px 18px; margin: 0 0 var(--space-3); }
.lock-icon { font-size: 22px; line-height: 1; margin-top: 2px; }
.lock-title { font: 600 15px/22px var(--font-sans); }
.lock-pitch { margin-top: 4px; font-size: 13.5px; color: var(--ink-muted); }
.lock-tier, .lock-contact { margin-top: 6px; font: var(--text-body-small); color: var(--ink-muted); }

/* ---- meta grid, list rows, chips ---- */
.meta { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-4); margin-bottom: var(--space-4); }
.meta-label { font: var(--text-label); color: var(--ink-muted); text-transform: uppercase; letter-spacing: 0.02em; margin-bottom: 4px; }
.meta-value { font: 600 15px/22px var(--font-sans); overflow-wrap: anywhere; }
/* 15px/600 is not large text: --warning is 3,19:1 on --surface-200 and
   --warning-ink, the same hue one step down, is 5,02:1. The KPI values keep
   --warning, where 28px/700 clears the large-text threshold. */
.meta-warning { color: var(--warning-ink); }
@media (max-width: 719px) { .meta { grid-template-columns: 1fr 1fr; gap: var(--space-3); } }
@media (max-width: 399px) { .meta { grid-template-columns: 1fr; } }
.list-rows { list-style: none; padding: 0; margin: 0; }
.list-row { display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-3); padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 13px; line-height: 18px; }
.list-row:last-child { border-bottom: 0; }
.list-row a { color: var(--ink); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--space-3); }
.chip { display: inline-flex; align-items: center; gap: 4px; background: var(--brand-soft); color: var(--brand); border-radius: var(--radius-md); padding: 4px 10px; font: 600 12px/16px var(--font-sans); }
.chip-x { background: none; border: 0; padding: 0 2px; color: inherit; font-size: 15px; line-height: 1; }
.chip-x:hover { color: var(--danger); }
.portal-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
/* "Otvori original obavještenje na ejn.gov.ba" is 332 px of nowrap button and
   pushed the whole page sideways below 352 px (iPhone SE is 320). The label
   stays; the button wraps. */
.portal-actions .btn { white-space: normal; text-align: center; }
/* The sentence that used to be the Portal button's title=. It takes its own
   line under the buttons, whatever the flex row does above it. */
.portal-help { flex: 0 0 100%; margin-top: var(--space-1); max-width: 68ch; }

/* ---- swatches (styleguide) ---- */
.swatches { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-bottom: var(--space-3); }
.swatch { width: 96px; }
.swatch-color { display: block; height: 40px; border-radius: var(--radius-sm); border: 1px solid var(--border); margin-bottom: 4px; }
.swatch-color[data-token="surface-100"] { background: var(--surface-100); }
.swatch-color[data-token="surface-200"] { background: var(--surface-200); }
.swatch-color[data-token="border"] { background: var(--border); }
.swatch-color[data-token="ink"] { background: var(--ink); }
.swatch-color[data-token="ink-muted"] { background: var(--ink-muted); }
.swatch-color[data-token="brand"] { background: var(--brand); }
.swatch-color[data-token="brand-hover"] { background: var(--brand-hover); }
.swatch-color[data-token="accent"] { background: var(--accent); }
.swatch-color[data-token="warning"] { background: var(--warning); }
.swatch-color[data-token="danger"] { background: var(--danger); }
.swatch-color[data-token="category-blue"] { background: var(--category-blue); }
.swatch-color[data-token="category-teal"] { background: var(--category-teal); }
.swatch-color[data-token="category-violet"] { background: var(--category-violet); }
.swatch-color[data-token="category-rose"] { background: var(--category-rose); }
.swatch-color[data-token="category-indigo"] { background: var(--category-indigo); }

/* ---- maintenance page ---- */
.page-maint { display: grid; place-items: center; padding: 24px 16px; }
.maint { max-width: 460px; width: 100%; text-align: center; padding: 32px 28px; }
.maint-logo { font: var(--text-label); letter-spacing: 0.14em; text-transform: uppercase; color: var(--brand); }
.maint h1 { margin: 14px 0 8px; }
.maint p { color: var(--ink-muted); margin-bottom: 10px; }
.maint-contact { font: var(--text-body-small); margin-top: 18px; }
.maint-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: var(--brand); margin-right: 8px; vertical-align: middle; animation: maint-pulse 1.6s ease-in-out infinite; }
@keyframes maint-pulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }

/* ---- forced colours (Windows high contrast, "Increase contrast") ---------
   The engine throws the palette away and paints with the user's own four or
   five system colours. Three states in this product are carried by a
   background or by a transparent border, and all three were measured gone:
   the chosen filter pill and its two siblings all came back white on
   CanvasText with the same border, the four Podesavanja tabs all came back
   with an opaque bottom border (a `transparent` border is forced opaque, so
   every tab read as selected), and the staff switches were a white dot on a
   white track on a white card with no boundary anywhere. aria-current and
   aria-checked stayed correct throughout, so this is a sighted high-contrast
   reader's defect only. forced-color-adjust: none is used exactly where the
   state IS the fill and there is no other way to draw it. */
@media (forced-colors: active) {
  .pill[aria-current] { forced-color-adjust: none; background: Highlight; color: HighlightText; border-color: Highlight; }
  .tabs a { border-bottom-color: Canvas; }
  .tabs a[aria-current] { border-bottom-color: LinkText; }
  .switch { border: 1px solid ButtonText; }
  .switch[aria-checked="true"] { forced-color-adjust: none; background: Highlight; }
  .switch[aria-checked="true"] .switch-dot { background: HighlightText; }
  .switch[aria-checked="false"] .switch-dot { background: ButtonText; }
  /* A dot whose only job is a colour: keep it drawn, with an edge. */
  .dot { border: 1px solid CanvasText; }
}

/* ---- file control ----
   The native input stays the control, transparent, stretched over the row:
   the label, the tab stop and the picker are all still the browser's, only
   the words "Choose File / No file chosen" - which the browser writes in its
   own UI language, not the document's - are replaced by two elements this
   page names itself. The box is aria-hidden; the chosen name is read through
   aria-describedby, and app.js writes it. */
.file-row { position: relative; display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.file-input { position: absolute; left: 0; top: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
.file-fake { pointer-events: none; }
.file-name { font: var(--text-body-small); color: var(--ink-muted); min-width: 0; overflow-wrap: anywhere; }
.file-input:focus-visible ~ .file-fake { outline: var(--focus); outline-offset: 2px; border-radius: var(--radius-sm); }

/* The second line inside a table cell: a city under an institution, an
   article under a rule, the note under an event. Used by Ugovori koji ističu
   and Rokovi i prigovor, and it had no rule at all until now - both screens
   were running the two lines together into one sentence. */
.sub {
  display: block;
  font: var(--text-caption);
  color: var(--ink-muted);
  margin-top: 2px;
}
