/* =============================================================================
   rating_ohom - design layer
   -----------------------------------------------------------------------------
   AdminLTE ships every card header as a solid colour bar, and this project had
   inherited that on 144 of its 175 headers: when everything is emphasised,
   nothing is, and a warning looks exactly like a decoration.  It also has ~44
   data tables wider than their card, which pushes the whole page sideways.

   Loaded after adminlte.min.css; presentation only.  Templates need no change
   beyond dropping bg-* from a header - the overrides below make untouched
   markup behave as well.

   1. Card headers neutral; colour marks meaning (status), never decoration.
   2. One table style: quiet header, tabular figures, right-aligned numbers,
      ellipsis instead of wrapping, scroll inside the card.
   3. Badges tinted-outline; a saturated fill is reserved for a decision.
   4. Type and spacing come from one scale, not from per-page inline styles.
   ========================================================================== */

:root {
  /* neutrals: the page is 90% of the surface, so these carry the design */
  --ui-ink: #1c2530;
  --ui-ink-soft: #5b6673;
  --ui-ink-faint: #8a949f;
  --ui-line: #e2e6eb;
  --ui-line-soft: #eef1f4;
  --ui-surface: #fff;
  --ui-surface-alt: #f6f8fa;
  --ui-canvas: #f2f4f7;

  /* one brand hue, used for actions and emphasis - nothing else */
  --ui-accent: #14608f;
  --ui-accent-hover: #0f4e75;
  --ui-accent-soft: #eaf1f7;
  --ui-accent-line: #c6dcea;

  /* semantics: only ever applied to a real status, never to decorate */
  --ui-ok: #16704a;
  --ui-ok-soft: #e9f3ee;
  --ui-ok-line: #c3e0d1;
  --ui-warn: #8a5d00;
  --ui-warn-soft: #fbf3e2;
  --ui-warn-line: #ecd9ab;
  --ui-bad: #a9271f;
  --ui-bad-soft: #fbedec;
  --ui-bad-line: #f0cdc9;
  --ui-info: #2f6d8f;
  --ui-info-soft: #eaf2f6;
  --ui-info-line: #c9dfe9;

  /* Slightly darker partners of each semantic, for *text sitting on a tint*.
     #a9271f on #fbedec is short of AA; the -ink value is the same hue pushed
     down until it passes. Badges and alerts must use these, not the base
     colour, or the two stop agreeing with each other. */
  --ui-accent-ink: #0e4568;
  --ui-ok-ink: #12563a;
  --ui-warn-ink: #6b4800;
  --ui-bad-ink: #86201a;
  --ui-info-ink: #245a76;

  /* Solid versions: where a fill is genuinely wanted (a button, a badge that
     reports a decision) rather than a tint. */
  --ui-ok-strong: #115c3c;
  --ui-warn-solid: #d69e00;
  --ui-warn-strong: #c8901c;
  --ui-bad-strong: #8d1f18;

  /* surfaces that are neither ink nor semantic */
  --ui-track: #eceff3;
  --ui-row-hover: #f2f7fc;
  --ui-neutral: #98a3af;
  --ui-neutral-line: #cdd4dc;
  --ui-rule: #c3ccd5;
  --ui-silver: #9aa5b1;
  --ui-bronze: #9c5b52;

  /* categorical ramp for charts: muted, and separable under deuteranopia.
     Deliberately replaces the #28a745 / #63ed40 / #3b8bba / #6a11cb that the
     templates each invented for themselves. */
  --ui-series-1: #14608f;
  --ui-series-2: #17717a;
  --ui-series-3: #b07d24;
  --ui-series-4: #6b4a7a;
  --ui-series-5: #5b7a4a;
  --ui-series-6: #566472;
  --ui-series-7: #9c5b52;
  --ui-series-8: #3f7fa6;

  --ui-radius: 4px;
  --ui-row-pad: .55rem .75rem;
  --ui-gap: 1rem;
}

/* --------------------------------------------------------------- page frame */
.content-wrapper { background: var(--ui-canvas); }
.content { padding-top: .25rem; }

/* The base owns the one column that the sidebar offsets, and each page opens its
   .page-body inside it.  Two defences: an un-migrated page that still opens a second
   .content-wrapper must not be pushed another 250px right (AdminLTE's offset selector
   is body:not(.x):not(.y):not(.z) .content-wrapper, so nothing short of !important
   beats it), and the inner column must not paint a second canvas or claim the
   viewport height the outer one already has. */
.content-wrapper .content-wrapper,
.content-wrapper .page-body {
  margin-left: 0 !important;
  height: auto;
  background: transparent;
}

/* AdminLTE pads ".content-wrapper > .content".  The pages' sections now sit one level
   deeper, so they need the same inset or their cards touch the window edge. */
.page-body > .content { padding: 0 0.5rem; }

/* Alerts stretched to the full width of a 27-inch screen read as a banner, not as
   feedback; cap the measure and keep them quiet. */
#flashMessages { max-width: 1200px; }

#flashMessages .alert {
  box-shadow: 0 1px 2px rgba(28, 37, 48, .07);
  font-size: .875rem;
}

#flashMessages .alert .close { text-shadow: none; }

.content-header h1,
.content-header .h1 {
  font-size: 1.35rem;
  font-weight: 600;
  letter-spacing: -.01em;
  margin-bottom: .15rem;
}

.content-header p.text-muted { font-size: .82rem; margin-bottom: 0; }
.main-sidebar .nav-link { font-size: .855rem; }
.main-sidebar .nav-link p { margin-bottom: 0; }

/* ------------------------------------------------------------------- cards */
.card {
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  box-shadow: none;
  margin-bottom: var(--ui-gap);
}

.card-body { color: var(--ui-ink); }

/* A header is a label, not a banner. */
.card > .card-header {
  background: var(--ui-surface);
  border-bottom: 1px solid var(--ui-line-soft);
  padding: .65rem .9rem;
  border-radius: var(--ui-radius) var(--ui-radius) 0 0;
}

.card > .card-header .card-title,
.card > .card-header h1,
.card > .card-header h3,
.card > .card-header h4 {
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ui-ink-soft);
  margin: 0;
  line-height: 1.4;
  max-width: 68%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card > .card-header .card-tools,
.card > .card-header .float-right {
  display: flex;
  align-items: center;
  gap: .4rem;
  font-size: .78rem;
}

.card-outline > .card-header { background: var(--ui-surface); }
.card-outline-primary { border-top-color: var(--ui-accent); }
.card-outline-success { border-top-color: var(--ui-ok); }
.card-outline-warning { border-top-color: var(--ui-warn); }

/* Neutralise the legacy "every header is a colour bar" classes: the markup stays
   valid, it simply stops shouting. */
.card > .card-header.bg-primary,
.card > .card-header.bg-success,
.card > .card-header.bg-info,
.card > .card-header.bg-secondary,
.card > .card-header.bg-light,
.card > .card-header.bg-cyan,
.card > .card-header.bg-blue,
.card > .card-header.bg-olive,
.card > .card-header.bg-lime {
  background: var(--ui-surface) !important;
  color: var(--ui-ink) !important;
  border-bottom: 1px solid var(--ui-line-soft);
}

/* A genuinely alarming header keeps its signal, as a tint rather than a block. */
.card > .card-header.bg-danger,
.card > .card-header.bg-warning { color: var(--ui-ink) !important; }
.card > .card-header.bg-danger {
  background: var(--ui-bad-soft) !important;
  border-bottom: 1px solid var(--ui-bad-line);
}
.card > .card-header.bg-warning {
  background: var(--ui-warn-soft) !important;
  border-bottom: 1px solid var(--ui-warn-line);
}

.card-footer {
  background: var(--ui-surface-alt);
  border-top: 1px solid var(--ui-line-soft);
  padding: .6rem .9rem;
}

/* -------------------------------------------------------------- KPI boxes */
.small-box {
  position: relative;
  display: block;
  border-radius: var(--ui-radius);
  padding: .9rem 1rem 0;
  box-shadow: none;
  overflow: hidden;
}
.small-box .inner { position: static; padding-bottom: .6rem; }
.small-box .inner h3 { font-size: 1.55rem; font-weight: 600; line-height: 1.2; }
.small-box .inner h3 small { font-size: .95rem; font-weight: 500; opacity: .75; }
.small-box .inner p { font-size: .78rem; opacity: .92; margin-bottom: 0; }
.small-box .icon {
  position: absolute;
  top: .6rem;
  right: .8rem;
  font-size: 2.1rem;
  opacity: .18;
  width: auto;
  height: auto;
  line-height: 1;
  transition: none;
}
.small-box:hover .icon { opacity: .24; transform: none; }
.small-box > .small-box-footer {
  display: block;
  background: rgba(0, 0, 0, .07);
  font-size: .76rem;
  padding: .3rem 1rem;
  margin: 0 -1rem;
}
/* The old markup repeated display:flex on every column to line the boxes up. */
.row-equal > [class*="col-"] { display: flex; flex-direction: column; }
.row-equal > [class*="col-"] > * { flex: 1 1 auto; }

/* ---------------------------------------------------------------- charts */
.card-body > canvas { max-width: 100%; }
.chart-box { position: relative; height: 260px; }
.chart-box-sm { position: relative; height: 190px; }
.chart-box-lg { position: relative; height: 340px; }

/* --------------------------------------------------------------- badges */
.badge {
  font-weight: 500;
  font-size: .72rem;
  letter-spacing: .01em;
  padding: .22em .55em;
  border-radius: 10rem;
}

/* Filled badges are reserved for decision states; everything else is quiet. */
.badge-light, .badge-secondary {
  background: var(--ui-surface) !important;
  color: var(--ui-ink-soft) !important;
  border: 1px solid var(--ui-line);
}
.badge-info {
  background: var(--ui-info-soft) !important;
  color: var(--ui-info-ink) !important;
  border: 1px solid var(--ui-info-line);
}
.badge-success {
  background: var(--ui-ok-soft) !important;
  color: var(--ui-ok-ink) !important;
  border: 1px solid var(--ui-ok-line);
}
.badge-warning {
  background: var(--ui-warn-soft) !important;
  color: var(--ui-warn-ink) !important;
  border: 1px solid var(--ui-warn-line);
}
.badge-danger {
  background: var(--ui-bad-soft) !important;
  color: var(--ui-bad-ink) !important;
  border: 1px solid var(--ui-bad-line);
}
.badge-primary {
  background: var(--ui-accent-soft) !important;
  color: var(--ui-accent-ink) !important;
  border: 1px solid var(--ui-accent-line);
}

/* Progress bars: thinner and flatter than the AdminLTE default. */
.progress { border-radius: 10rem; background: var(--ui-track); }
.progress .progress-bar { transition: width .35s ease; }

/* ---------------------------------------------------------------- tables */
.table-wrap { position: relative; overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* An unwrapped table still scrolls instead of tearing the layout open - the single
   largest fix for tables crossing the card and the viewport. */
.card-body, .table-responsive { overflow-x: auto; }

.table {
  color: var(--ui-ink);
  margin-bottom: 0;
  font-size: .855rem;
  border-color: var(--ui-line-soft);
}

.table > thead > tr > th,
.table > thead > th {
  background: var(--ui-surface-alt);
  border-top: 0;
  border-bottom: 1px solid var(--ui-line) !important;
  color: var(--ui-ink-soft);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .03em;
  text-transform: uppercase;
  white-space: nowrap;
  padding: .5rem .75rem;
  vertical-align: bottom;
}

.table > tbody > tr > td {
  padding: var(--ui-row-pad);
  vertical-align: middle;
  border-top: 1px solid var(--ui-line-soft);
}

.table tbody tr:last-child > td { border-bottom: 0; }

/* Zebra striping fought with hover and with the row borders; keep one mechanism. */
.table-striped > tbody > tr:nth-child(odd) > td { background: transparent; }
.table-hover > tbody > tr:hover > td { background: var(--ui-row-hover); }
.table-bordered { border: 1px solid var(--ui-line-soft); }
.table-sm > tbody > tr > td,
.table-sm > thead > tr > th,
.table-sm > thead > td { padding: .4rem .6rem; }

/* Figures must be comparable at a glance: tabular, right aligned, never wrapped. */
.table .num,
.table td.text-right,
.table th.text-right {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: right;
}

/* Long free text ellipsises instead of stretching a row to three lines. */
.cell-truncate {
  max-width: 1px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cell-lead { font-weight: 500; }
.cell-sub {
  display: block;
  font-size: .76rem;
  color: var(--ui-ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.col-rank { width: 3rem; color: var(--ui-ink-soft); font-variant-numeric: tabular-nums; }
.col-actions { width: 6.5rem; text-align: right; white-space: nowrap; }
.col-date { width: 7rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
.col-status { width: 8.5rem; white-space: nowrap; }
.table .empty-row td {
  text-align: center;
  color: var(--ui-ink-soft);
  padding: 2.2rem 1rem;
  font-size: .85rem;
}

/* --------------------------------------------------------------- controls */
.btn { font-size: .82rem; border-radius: var(--ui-radius); }
.btn-xs { font-size: .74rem; padding: .15rem .45rem; }
.btn-sm { font-size: .78rem; }
.form-control, .custom-select { font-size: .855rem; border-radius: var(--ui-radius); }
.form-control:focus {
  border-color: var(--ui-accent-line);
  box-shadow: 0 0 0 .15rem rgba(20, 96, 143, .12);
}
label { margin-bottom: .2rem; font-size: .82rem; color: var(--ui-ink-soft); }

/* ------------------------------------------------------------------------ forms
   Both portals render one of two form pages from 51 view call sites, and until the
   fields were rendered explicitly each of those pages dumped raw {{form}} into a
   card: full-bleed inputs on a 27-inch screen, no error styling, and labels mixing
   ":" with "*" because individual fields set their own label_suffix.  A capped
   measure and one field rhythm fixes all of them at once. */
.ui-form { max-width: 760px; }
.ui-form .ui-field { margin-bottom: .95rem; }
.ui-form label { display: block; font-weight: 600; }
.ui-form .form-control, .ui-form .custom-select { width: 100%; }
.ui-form textarea.form-control { min-height: 96px; }
.ui-form input[type=file] { font-size: .82rem; padding-top: .35rem; }
.ui-required { color: var(--ui-bad); font-weight: 700; }
.ui-field-errors { margin-top: .3rem; font-size: .8rem; color: var(--ui-bad-ink); }
.ui-field-invalid > label { color: var(--ui-bad-ink); }
.ui-field-invalid .form-control, .ui-field-invalid .custom-select { border-color: var(--ui-bad); }
/* A disabled field still has to be readable - it is data, not a mistake. */
.ui-field-disabled .form-control, .ui-field-disabled .custom-select {
  background: var(--ui-surface-alt);
  border-color: var(--ui-line);
  color: var(--ui-ink-soft);
}

.alert {
  border-radius: var(--ui-radius);
  border-width: 1px;
  font-size: .85rem;
  padding: .7rem .9rem;
}

.nav-tabs { border-bottom-color: var(--ui-line); }
.nav-tabs .nav-link {
  font-size: .82rem;
  border: 0;
  border-bottom: 2px solid transparent;
  color: var(--ui-ink-soft);
  padding: .5rem .9rem;
}
.nav-tabs .nav-link:hover { color: var(--ui-ink); }
.nav-tabs .nav-link.active {
  background: transparent;
  border-bottom-color: var(--ui-accent);
  color: var(--ui-ink);
  font-weight: 600;
}

.pagination { flex-wrap: wrap; margin-bottom: 0; }
.page-link { font-size: .8rem; color: var(--ui-accent); }
.page-item.active .page-link {
  background: var(--ui-accent);
  border-color: var(--ui-accent);
  color: #fff;
}

.list-group-item {
  border-color: var(--ui-line-soft);
  padding: .55rem .85rem;
  font-size: .855rem;
}

dl dt { color: var(--ui-ink-soft); font-weight: 500; font-size: .8rem; }
dl dd { font-size: .88rem; margin-bottom: .5rem; }

/* ------------------------------------------------------------- utilities */
.text-soft { color: var(--ui-ink-soft) !important; }
.tnum { font-variant-numeric: tabular-nums; }
.nowrap { white-space: nowrap; }
.stack-tight > * + * { margin-top: .35rem; }
.divider { height: 1px; background: var(--ui-line-soft); margin: .75rem 0; }
.fs-sm { font-size: .8rem; }
.fs-xs { font-size: .74rem; }
.sticky-actions { position: sticky; top: .75rem; }
.mw-full { max-width: 100%; }

a:focus-visible, .btn:focus-visible, .form-control:focus-visible,
[role="button"]:focus-visible {
  outline: 2px solid rgba(20, 96, 143, .55);
  outline-offset: 1px;
}

/* ------------------------------------------------------------- responsive */
@media (max-width: 991.98px) {
  .card > .card-header .card-title { max-width: 100%; }
  .table { font-size: .82rem; }
  .chart-box { height: 220px; }
}

@media (max-width: 575.98px) {
  .content-header h1 { font-size: 1.15rem; }
  .small-box .inner h3 { font-size: 1.3rem; }
  .col-actions, .col-status { width: auto; }
  .card > .card-header .card-tools { flex-wrap: wrap; }
}

/* ----------------------------------------------------------------- print */
@media print {
  .main-sidebar, .main-header, .main-footer, .content-header, .no-print,
  .card-tools, .pagination { display: none !important; }
  .content-wrapper { margin: 0 !important; background: #fff; }
  .card { break-inside: avoid; border: 1px solid #d5d9de; }
  .table-wrap, .card-body, .table-responsive { overflow: visible !important; }
  .badge {
    border: 1px solid #b9c0c7 !important;
    background: #fff !important;
    color: var(--ui-ink) !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }

/* ------------------------------------------------- legacy markup safeguards */
/* Several drill-down templates pre-date the card rules above and emit their own
   structure: a bare .card-header as a sibling of the card it belongs to, an empty
   <div class="card"></div> left as a spacer, or a table dropped straight into
   <section class="content">.  Rather than hand-edit fifteen long files with
   unbalanced divs, the sheet absorbs those cases. */

.card:empty,
.card > .card-header:only-child:empty,
.small-box .icon:empty { display: none; }

/* A card-header that ended up outside a card still reads as a section title. */
.content > .card-header,
.content-wrapper .card + .card-header,
.content > .card-body {
  background: var(--ui-surface);
  border: 1px solid var(--ui-line);
  border-radius: var(--ui-radius);
  padding: .65rem .9rem;
  margin: 1rem 0 0;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--ui-ink-soft);
}

.content > .card-body { padding: .9rem; font-weight: 400;
                       letter-spacing: 0; text-transform: none; color: var(--ui-ink); }

/* Tables that are not inside a .card-body still need to scroll, not stretch the page. */
.content > table,
.content section > table,
.table-striped, .table-hover, .table-bordered { max-width: 100%; }
.content > table, .content section > table {
  display: block;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* An unstyled <th> list (these templates open <thead> without a <tr>) would otherwise
   inherit browser defaults that clash with the rest of the table. */
.table thead:not(:has(tr)) { display: table-header-group; }

/* Long identifiers (emails, filenames, transliterated names) must not set column
   width; they wrap inside their own cell. */
.table td, .table th { min-width: 0; overflow-wrap: anywhere; }
.table td.text-right, .table td.col-date, .table td.col-status,
.table th.text-right, .table td.nowrap, .table td.col-actions { overflow-wrap: normal; }

/* Definition lists used for detail pages: keep label/value columns stable. */
dl.row dt { flex-basis: 40%; max-width: 40%; }
dl.row dd { flex-basis: 60%; max-width: 60%; }

/* Forms in a card footer should not stretch a table row. */
td form, .table form { display: inline; margin: 0; }

}

/* ---------------------------------------------------------- review timeline */
/* A vertical rule with dots: the reader's eye follows the order without counting. */
.ui-timeline {
  list-style: none;
  margin: 0;
  padding: 0 0 0 .25rem;
  position: relative;
}

.ui-timeline::before {
  content: "";
  position: absolute;
  left: .93rem;
  top: .6rem;
  bottom: .6rem;
  width: 2px;
  background: var(--ui-line);
}

.ui-timeline-item {
  position: relative;
  padding: 0 0 1rem 2.2rem;
}

.ui-timeline-item:last-child { padding-bottom: 0; }

.ui-timeline-dot {
  position: absolute;
  left: 0;
  top: .05rem;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  background: var(--ui-surface);
  border: 2px solid var(--ui-line);
  color: var(--ui-ink-soft);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .72rem;
}

.ui-timeline-success .ui-timeline-dot { border-color: var(--ui-ok-line); color: var(--ui-ok); }
.ui-timeline-warning .ui-timeline-dot { border-color: var(--ui-warn-line); color: var(--ui-warn); }
.ui-timeline-danger .ui-timeline-dot { border-color: var(--ui-bad-line); color: var(--ui-bad); }
.ui-timeline-primary .ui-timeline-dot { border-color: var(--ui-accent-line); color: var(--ui-accent-hover); }

.ui-timeline-note {
  margin: .35rem 0 0;
  padding: .5rem .7rem;
  background: var(--ui-surface-alt);
  border-left: 3px solid var(--ui-line);
  border-radius: 0 var(--ui-radius) var(--ui-radius) 0;
  font-size: .82rem;
  color: var(--ui-ink);
  max-height: 12rem;
  overflow: auto;
}

.ui-timeline-warning .ui-timeline-note { border-left-color: var(--ui-warn-line); background: var(--ui-warn-soft); }
.ui-timeline-danger .ui-timeline-note { border-left-color: var(--ui-bad-line); background: var(--ui-bad-soft); }

/* ------------------------------------------------------------ hall of fame */
.hall-card { border-color: var(--ui-line); }
.hall-card .card-body { padding-bottom: .75rem; }
.hall-medal { font-size: 1.5rem; line-height: 1; margin-bottom: .35rem; }
.hall-bronze { color: var(--ui-bronze) !important; }
.hall-points {
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
  margin-top: .35rem;
}
.hall-rank-1 { border-top: 3px solid var(--ui-warn-solid); }
.hall-rank-2 { border-top: 3px solid var(--ui-silver); }
.hall-rank-3 { border-top: 3px solid var(--ui-bronze); }
.tab-content { min-height: 6rem; }


/* Steps the reader should reach first on a long history. */
.ui-timeline-latest > .ui-timeline-body { font-weight: 500; }
/* ===========================================================================
   COLOUR CONTRACT
   ---------------------------------------------------------------------------
   AdminLTE ships Bootstrap's default utility palette (its blue, its
   green, its red and its amber) and this project layered its own hexes on
   top of that, so one dashboard could show three blues and two greens at once -
   which reads as unfinished rather than as designed.

   Everything coloured is pulled back onto the tokens above. The utilities keep
   working (no template had to change) but each now means one thing: primary is
   the brand hue, and the other three appear only on a real status.
   =========================================================================== */

/* --- brand hue: replaces Bootstrap's #007bff everywhere ------------------- */
.btn-primary {
  background-color: var(--ui-accent) !important;
  border-color: var(--ui-accent) !important;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active,
.btn-primary:not(:disabled):not(.disabled).active {
  background-color: var(--ui-accent-hover) !important;
  border-color: var(--ui-accent-hover) !important;
}
.btn-outline-primary { color: var(--ui-accent); border-color: var(--ui-accent-line); }
.btn-outline-primary:hover {
  background: var(--ui-accent); border-color: var(--ui-accent); color: #fff;
}
.btn-success { background-color: var(--ui-ok) !important; border-color: var(--ui-ok) !important; }
.btn-success:hover { background-color: var(--ui-ok-strong) !important; border-color: var(--ui-ok-strong) !important; }
.btn-danger { background-color: var(--ui-bad) !important; border-color: var(--ui-bad) !important; }
.btn-danger:hover { background-color: var(--ui-bad-strong) !important; border-color: var(--ui-bad-strong) !important; }
/* A solid amber button is both the loudest thing on a toolbar and impossible to
   label: dark ink on the strong amber only just reaches 3:1, and white on it
   reaches 2.1:1. A tinted button with ink text clears AA and stops shouting. */
.btn-warning {
  background: var(--ui-warn-soft) !important;
  border-color: var(--ui-warn-line) !important;
  color: var(--ui-warn-ink) !important;
}
.btn-warning:hover, .btn-warning:focus, .btn-warning:active,
.btn-warning:not(:disabled):not(.disabled).active {
  background: var(--ui-warn-solid) !important;
  border-color: var(--ui-warn-strong) !important;
  color: #2b2000 !important;
}
.btn-outline-danger { color: var(--ui-bad); border-color: var(--ui-bad-line); }
.btn-outline-danger:hover { background: var(--ui-bad); border-color: var(--ui-bad); color: #fff; }
.btn-outline-secondary { color: var(--ui-ink-soft); border-color: var(--ui-neutral-line); }
.btn-outline-secondary:hover { background: var(--ui-surface-alt); color: var(--ui-ink); }
.btn-link { color: var(--ui-accent); }

a { color: var(--ui-accent); }
a:hover { color: var(--ui-accent-hover); }

.bg-primary { background-color: var(--ui-accent) !important; }
.bg-success { background-color: var(--ui-ok) !important; }
.bg-danger { background-color: var(--ui-bad) !important; }
.bg-warning { background-color: var(--ui-warn-solid) !important; }
.bg-info { background-color: var(--ui-info) !important; }
.bg-secondary { background-color: var(--ui-ink-soft) !important; }

.text-primary { color: var(--ui-accent) !important; }
.text-success { color: var(--ui-ok) !important; }
.text-danger { color: var(--ui-bad) !important; }
.text-warning { color: var(--ui-warn) !important; }
.text-info { color: var(--ui-info) !important; }
.text-muted { color: var(--ui-ink-soft) !important; }
.text-secondary { color: var(--ui-ink-faint) !important; }

.custom-control-input:checked ~ .custom-control-label::before,
.custom-checkbox .custom-control-input:checked ~ .custom-control-label::before {
  background-color: var(--ui-accent); border-color: var(--ui-accent);
}
.custom-range::-webkit-slider-thumb { background: var(--ui-accent); }
.form-control.is-invalid, .was-validated .form-control:invalid { border-color: var(--ui-bad); }

.form-control:focus, .custom-select:focus { border-color: var(--ui-accent-line); }
.btn:focus, .btn.focus { box-shadow: 0 0 0 .15rem rgba(20, 96, 143, .25); }
/* --- KPI boxes: a surface with an accent, not a saturated slab ------------
   Twenty-five of these sat at the top of every dashboard as solid orange,
   green, blue or red blocks with white text. Four side by side is a Christmas
   tree, and the number you are meant to read is the least contrasted thing on
   it. They are now quiet cards - white surface, ink figure - with a coloured
   rule and a tinted glyph carrying the meaning instead. */
.small-box {
  background: var(--ui-surface) !important;
  color: var(--ui-ink) !important;
  border: 1px solid var(--ui-line);
  border-top: 3px solid var(--ui-accent-line);
  box-shadow: none;
}
.small-box.bg-primary, .small-box.bg-info, .small-box.bg-cyan { border-top-color: var(--ui-accent); }
.small-box.bg-success { border-top-color: var(--ui-ok); }
.small-box.bg-warning { border-top-color: var(--ui-warn-solid); }
.small-box.bg-danger { border-top-color: var(--ui-bad); }
.small-box.bg-secondary, .small-box.bg-light { border-top-color: var(--ui-rule); }

.small-box .inner h3 { color: var(--ui-ink) !important; }
.small-box .inner h3 small { color: var(--ui-ink-soft) !important; }
.small-box .inner p { color: var(--ui-ink-soft) !important; opacity: 1; }
.small-box .icon { color: var(--ui-ink-faint) !important; opacity: .3; }
.small-box.bg-primary .icon, .small-box.bg-info .icon, .small-box.bg-cyan .icon { color: var(--ui-accent) !important; }
.small-box.bg-success .icon { color: var(--ui-ok) !important; }
.small-box.bg-warning .icon { color: var(--ui-warn-solid) !important; }
.small-box.bg-danger .icon { color: var(--ui-bad) !important; }
.small-box:hover { color: var(--ui-ink); }
.small-box:hover .icon { opacity: .4; transform: none; }
.small-box > .small-box-footer {
  background: var(--ui-surface-alt);
  color: var(--ui-accent) !important;
  border-top: 1px solid var(--ui-line-soft);
}
.small-box > .small-box-footer:hover {
  background: var(--ui-accent-soft);
  color: var(--ui-accent-hover) !important;
}

/* AdminLTE forces white text onto any bg-* utility; undo that now the block
   underneath is light, or the label would be invisible. Declaring the surface
   here as well - at .class.class specificity - means the repaint cannot be
   overtaken by the single-class utilities no matter where they sit in the file. */
.small-box.bg-primary, .small-box.bg-success, .small-box.bg-danger,
.small-box.bg-warning, .small-box.bg-info, .small-box.bg-secondary,
.small-box.bg-light, .small-box.bg-cyan {
  background: var(--ui-surface) !important;
  color: var(--ui-ink) !important;
}

/* --- progress bars: one hue, the others reserved for status --------------- */
.progress-bar { background-color: var(--ui-accent) !important; }
.progress-bar.bg-primary { background-color: var(--ui-accent) !important; }
.progress-bar.bg-info { background-color: var(--ui-info) !important; }
.progress-bar.bg-success { background-color: var(--ui-ok) !important; }
.progress-bar.bg-warning { background-color: var(--ui-warn-solid) !important; }
.progress-bar.bg-danger { background-color: var(--ui-bad) !important; }
.progress-bar.bg-secondary { background-color: var(--ui-neutral) !important; }

/* --- alerts: tinted, never a slab of saturated colour -------------------- */
.alert-primary { background: var(--ui-accent-soft); border-color: var(--ui-accent-line); color: var(--ui-accent-ink); }
.alert-success { background: var(--ui-ok-soft); border-color: var(--ui-ok-line); color: var(--ui-ok-ink); }
.alert-warning { background: var(--ui-warn-soft); border-color: var(--ui-warn-line); color: var(--ui-warn-ink); }
.alert-danger { background: var(--ui-bad-soft); border-color: var(--ui-bad-line); color: var(--ui-bad-ink); }
.alert-info { background: var(--ui-info-soft); border-color: var(--ui-info-line); color: var(--ui-info-ink); }
.alert-secondary { background: var(--ui-surface-alt); border-color: var(--ui-line); color: var(--ui-ink-soft); }

/* --- rows and cells painted by a bg-* utility ---------------------------- */
.table td.bg-success, .table td.bg-danger, .table td.bg-warning,
.table th.bg-success, .table th.bg-danger, .table th.bg-warning {
  color: var(--ui-ink) !important;
}
.table .table-success, .table td.table-success, .table th.table-success { background: var(--ui-ok-soft); }
.table .table-danger, .table td.table-danger, .table th.table-danger { background: var(--ui-bad-soft); }
.table .table-warning, .table td.table-warning, .table th.table-warning { background: var(--ui-warn-soft); }
.table .table-primary, .table td.table-primary, .table th.table-primary { background: var(--ui-accent-soft); }
.table .table-secondary, .table td.table-secondary, .table th.table-secondary { background: var(--ui-surface-alt); }

/* --- medal icons: hue matches the metal, not a random gold ---------- */
.hall-medal .fa-trophy { color: var(--ui-warn-solid); }
.hall-medal .fa-medal { color: var(--ui-silver); }

/* --- .bg-purple was a one-off badge colour the sheet now owns ------- */
.bg-purple { background: var(--ui-series-4) !important; color: #fff !important; }
/* focus and form states live with the control definitions above */

/* ===========================================================================
   Forced password change
   ========================================================================== */

/* The dialog is emitted already-shown so it works with JavaScript disabled; that
   means Bootstrap's JS-generated .modal-backdrop never exists, so the sheet supplies
   its own.  Both sit above the sidebar (1038) and the navbar (1034): the point of the
   gate is that nothing else on the page is reachable until it is answered. */
.password-gate-backdrop {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1040;
  background: rgba(28, 37, 48, .55);
}

.password-gate { z-index: 1050; overflow-y: auto; }
.password-gate .modal-content { border: 1px solid var(--ui-line); box-shadow: var(--ui-shadow-pop, 0 12px 28px rgba(28, 37, 48, .22)); }
.password-gate-header {
  background: var(--ui-accent);
  border-bottom: 1px solid var(--ui-accent-hover);
  color: #fff;
  border-radius: var(--ui-radius) var(--ui-radius) 0 0;
}
.password-gate-header .modal-title { font-size: 1.05rem; font-weight: 600; }
.password-gate label { font-weight: 600; color: var(--ui-ink-soft); }
/* ---------------------------------------------------------------------------------
 * Category coverage donuts - department dashboard, rectorate panel
 *
 * Ten categories, one ring each, four across on a laptop.  The ring carries the
 * decision mix; the number inside it is the one a rector reads first, so it sits in
 * the hole instead of competing with the name below.  An idle category is flagged by
 * the top rule rather than by painting its whole card red: the queue inside it is
 * information, not an error.
 * --------------------------------------------------------------------------------- */
.coverage-cell {
  position: relative;
  height: 100%;
  padding: .5rem .65rem .6rem;
  border: 1px solid var(--ui-line);
  border-top: 3px solid var(--ui-ok);
  border-radius: var(--ui-radius, 4px);
  background: var(--ui-surface);
}
.coverage-cell.is-idle {
  border-top-color: var(--ui-bad);
  background: var(--ui-bad-soft);
}
.coverage-ring { position: relative; height: 110px; }
.coverage-count {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 1.2rem;
  font-weight: 600;
  color: var(--ui-ink);
  pointer-events: none;          /* the hover belongs to the chart, not to the text */
}
.coverage-name {
  margin-top: .35rem;
  font-size: .78rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--ui-ink);
  min-height: 2.5em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.coverage-meta { font-size: .7rem; line-height: 1.3; color: var(--ui-ink-soft); }
.coverage-chip {
  display: inline-block;
  width: .7rem;
  height: .7rem;
  margin-right: .3rem;
  border-radius: 2px;
  background: var(--ui-line);
}
.password-gate code {
  padding: .1rem .35rem;
  border-radius: 3px;
  background: var(--ui-surface-alt);
  border: 1px solid var(--ui-line);
  color: var(--ui-ink);
}

