/* ==========================================================================
   Solve 4 — theme layer
   --------------------------------------------------------------------------
   Loaded LAST in {% block styles %} (base/index.html), after:
     CDN bootstrap 4.3.1 / font-awesome / animate.css / katex / open-iconic /
     codemirror / highlight.js, vendor/floating-labels.css, main.css,
     typeahead.css.

   Because this file loads last, it beats all of the above at equal
   specificity without !important, and beats their !important declarations by
   simply repeating !important. Every !important below carries a trailing
   comment naming what it beats.

   The five page-scoped sheets (ranking.css, llm.css, fileTree.css,
   eventTree.css, vue/submission-report.css) are linked inside
   {% block styles %}{{ super() }} and therefore still load AFTER this file on
   their own routes — they stay authoritative there and are edited in place.

   Layer order follows the design direction §7:
      1  tokens
      2  base / reset
      3  navbar + page frame
      4  buttons
      5  page header / toolbar / language switcher
      6  tables + status chips
      7  forms
      8  badges / alerts / toasts
      9  cards / dropdowns / modals / pagination / tabs
     10  content blocks / ranking medals / footer
     11  retuned legacy values from main.css
     12  accessibility
   ========================================================================== */


/* ==========================================================================
   1. TOKENS
   Inherited by ranking.css / llm.css / fileTree.css / eventTree.css, which
   load later — custom properties cascade by inheritance, not source order.
   ========================================================================== */

:root {
  /* --- typography ------------------------------------------------------ */
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
               "Noto Sans", "Liberation Sans", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace;

  /* type scale — 16px root, compressed ~1.15 ratio for density */
  --fs-100: 0.6875rem;  /* 11px — micro labels, table headers, verdict chips */
  --fs-200: 0.75rem;    /* 12px — .small, meta lines, footer */
  --fs-300: 0.8125rem;  /* 13px — table body, badges, .btn-sm */
  --fs-400: 0.875rem;   /* 14px — buttons, form controls, nav links */
  --fs-500: 0.9375rem;  /* 15px — body copy */
  --fs-600: 1.0625rem;  /* 17px — h5, card titles */
  --fs-700: 1.25rem;    /* 20px — h4 (news/forum thread titles) */
  --fs-800: 1.5rem;     /* 24px — h3 */
  --fs-900: 1.75rem;    /* 28px — h1 page title */

  --lh-tight: 1.25;
  --lh-snug: 1.4;
  --lh-normal: 1.55;
  --lh-relaxed: 1.7;

  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  --tracking-tight: -0.015em;
  --tracking-wide: 0.04em;   /* uppercase micro-labels */

  /* --- neutral ramp (cool cast, hue ~215°) ------------------------------ */
  --surface:         #ffffff;  /* page, cards, table body */
  --surface-sunken:  #f5f7f9;  /* footer, card headers */
  --surface-subtle:  #eceff3;  /* thead-light, quiet button fill */
  --surface-inset:   #f7f9fb;  /* table-striped odd rows */
  --surface-hover:   #eef2f7;  /* table-hover, dropdown-item hover */

  --border:          #d6dbe2;  /* default 1px rules */
  --border-strong:   #b4bdc8;  /* outer table border, quiet button border */
  --border-subtle:   #e6eaee;  /* internal table verticals */

  --text-faint:      #636d79;  /* 5.26:1 on white */
  --text-muted:      #5b6773;  /* 5.78:1 on white, 5.38:1 on sunken */
  --text:            #1f2933;  /* 14.76:1 on white */
  --text-strong:     #11161c;  /* 18.18:1 on white — headings */
  --text-inverse:    #ffffff;

  --nav-bg:          #141a21;  /* white 17.51:1, nav links 8.71:1 */
  --nav-fg:          #aeb8c4;
  --nav-fg-active:   #ffffff;
  --nav-hover-bg:    rgba(255,255,255,.07);  /* lifts the literal out of 4 rules */
  --nav-rail:        2px;

  /* --- accent ----------------------------------------------------------- */
  --accent:          #15509e;  /* 7.86:1 on white; white on it 7.86:1 */
  --accent-hover:    #113f7d;  /* 10.34:1 white-on */
  --accent-active:   #0d3261;  /* 12.76:1 white-on */
  --accent-subtle:   #eaf1fb;  /* tint fill */
  --accent-on-subtle:#15509e;  /* 6.91:1 on --accent-subtle */
  --accent-ring:     rgba(21, 80, 158, 0.28);

  /* --- semantic status -------------------------------------------------- */
  /* in-flight */
  --st-waiting-fg:   #5b6773;  --st-waiting-bg:   #f1f3f6;  /* 5.78 / 5.20 */
  --st-progress-fg:  #0f5f9e;  --st-progress-bg:  #e8f1fa;  /* 6.66 / 5.84 */
  --st-skipped-fg:   #606a75;  --st-skipped-bg:   #f1f3f6;  /* 5.50 / 4.95 */

  /* terminal */
  --st-accepted-fg:  #1b6b36;  --st-accepted-bg:  #e2f3e8;  /* 6.56 / 5.69 */
  --st-unsolved-fg:  #b3261e;  --st-unsolved-bg:  #fdecea;  /* 6.54 / 5.72 */
  --st-wa-fg:        #b42318;  --st-wa-bg:        #fdeceb;  /* 6.57 / 5.75 */
  --st-tle-fg:       #8a4b08;  --st-tle-bg:       #fdf1e2;  /* 6.79 / 6.10 */
  --st-mle-fg:       #8f2a60;  --st-mle-bg:       #fbeaf2;  /* 7.87 / 6.80 */
  --st-rte-fg:       #6b2fa0;  --st-rte-bg:       #f2ecfb;  /* 8.27 / 7.15 */
  --st-rule-fg:      #8f1d2e;  --st-rule-bg:      #fbe9ec;  /* 8.81 / 7.54 */
  --st-ce-fg:        #2d3a8c;  --st-ce-bg:        #ebedf9;  /* 10.02 / 8.60 */

  /* fault — inverted, deliberately unlike every other verdict */
  --st-sys-fg:       #ffffff;  --st-sys-bg:       #3d3a46;  /* 11.10 */

  /* --- bootstrap semantic remaps ---------------------------------------- */
  --success-fg: #1b6b36; --success-bg: #e2f3e8; --success-fill: #1b6b36;
  --danger-fg:  #b3261e; --danger-bg:  #fdecea; --danger-fill:  #b3261e;
  --warning-fg: #8a4b08; --warning-bg: #fdf1e2; --warning-fill: #f2c14e;
  --info-fg:    #0f5f9e; --info-bg:    #e8f1fa; --info-fill:    #0f5f9e;
  --neutral-fg: #3b4652; --neutral-bg: #eceff3;

  /* --- spacing / radius / elevation / motion ---------------------------- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;

  --radius-xs: 2px;
  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 10px;
  --radius-pill: 999px;

  --shadow-1: 0 1px 2px rgba(17,22,28,.06);
  --shadow-2: 0 1px 3px rgba(17,22,28,.08), 0 4px 10px rgba(17,22,28,.05);
  --shadow-3: 0 8px 24px rgba(17,22,28,.12), 0 2px 6px rgba(17,22,28,.08);
  --shadow-focus: 0 0 0 3px var(--accent-ring);

  --ease: cubic-bezier(.2,.6,.3,1);
  --dur-fast: 110ms;
  --dur: 170ms;

  --nav-h: 56px;       /* kept in lockstep with .navbar > .container min-height */

  /* --- page geometry (pass 3) ------------------------------------------- */
  --page-wide:      90rem;   /* 1440px — archetype C, data tables */
  --page-narrow:    30rem;   /*  480px — archetype A, auth forms */
  --measure-auth:   26rem;   /*  416px — auth card content box */
  --measure-form:   44rem;   /*  704px — single-column form card */
  --measure-form-2: 60rem;   /*  960px — two-column form card */
  --measure-prose:  72ch;    /* ~660px at 15px — news/forum/task body copy */
  --measure-feed:   56rem;   /*  896px — a feed item: prose plus its action rail */
  --aside-w:        20rem;   /*  320px — archetype F aside */
  --aside-w-lg:     22rem;   /*  352px — archetype E aside */
  --record-act:     12rem;   /*  192px — archetype F action rail (see .record-facts--acts) */
  --gap-page:       var(--space-6);   /* 32px — main/aside column gap */
}


/* ==========================================================================
   2. BASE / RESET
   ========================================================================== */

body {
  font-family: var(--font-sans);
  font-size: var(--fs-500);
  line-height: var(--lh-normal);
  color: var(--text);
  background-color: var(--surface);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 {
  font-weight: var(--fw-semibold);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
  margin-bottom: var(--space-3);
}

h1 { font-size: var(--fs-800); }
h2 { font-size: var(--fs-700); }
h3 { font-size: var(--fs-700); }
h4 { font-size: var(--fs-600); }
h5, h6 { font-size: var(--fs-500); }

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover,
a:focus {
  color: var(--accent-hover);
  text-decoration: underline;
  text-underline-offset: 2px;
}

code, kbd, samp, pre,
.text-monospace,
.source-code-text-box,
.compilation-error-message {
  font-family: var(--font-mono);
}

/* Bootstrap paints inline <code> hot pink (#e83e8c); in a judging UI that
   reads as an error state. Quiet grey chip instead. */
code {
  color: var(--text);
  background-color: var(--surface-subtle);
  padding: .0625rem .3125rem;
  border-radius: var(--radius-xs);
  font-size: .9em;
  word-break: break-word;
}

pre {
  background-color: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  font-size: var(--fs-300);
  line-height: var(--lh-snug);
}

pre > code {
  background: none;
  padding: 0;
  font-size: inherit;
}

::selection {
  background-color: var(--accent-subtle);
  color: var(--text-strong);
}

/* .row is flexbox in BS4; main.css:371 forces the (empty) clearfix pseudo
   elements to inline-block, injecting two zero-width flex items. */
.row::before,
.row::after { display: none; }

/* --- colour utilities ---------------------------------------------------- */
.text-muted   { color: var(--text-muted) !important; }  /* beats .text-muted{color:#6c757d!important} */
.text-danger  { color: var(--danger-fg)  !important; }  /* beats .text-danger{color:#dc3545!important} */
.text-success { color: var(--success-fg) !important; }  /* beats .text-success{color:#28a745!important} */
.text-warning { color: var(--warning-fg) !important; }  /* beats .text-warning{color:#ffc107!important} */
.text-info    { color: var(--info-fg)    !important; }  /* beats .text-info{color:#17a2b8!important} */
.text-primary { color: var(--accent)     !important; }  /* beats .text-primary{color:#007bff!important} */

/* --- vertical rhythm ----------------------------------------------------- */
p { margin-bottom: var(--space-3); }

/* …but keep density where it was deliberate */
.table p,
dd p,
.ranking p,
.form-label-group p { margin-bottom: 0; }

small, .small { line-height: var(--lh-snug); }

dl.row { margin-bottom: var(--space-5); }
dl.row > dt { font-weight: var(--fw-semibold); color: var(--text-strong); }
dl.row > dd { margin-bottom: var(--space-2); color: var(--text); }

dl.row > .col-sm-3 > .btn,
dl.row > .col-sm-3 > .form-button { margin-top: 0; }


/* ==========================================================================
   3. NAVBAR + PAGE FRAME
   ========================================================================== */

.navbar.bg-dark {
  background-color: var(--nav-bg) !important;  /* beats .bg-dark{background-color:#343a40!important} */
}

.navbar.fixed-top {
  padding: 0;
  border-bottom: 1px solid rgba(255,255,255,.08);
  box-shadow: var(--shadow-1);
}

.navbar > .container,
.navbar > .container-fluid {
  min-height: var(--nav-h);
}

.solve-header.navbar-brand {
  font-size: 1.1875rem;
  font-weight: var(--fw-semibold);
  letter-spacing: .055em;
  line-height: 1.4;
  color: var(--nav-fg-active);
  padding: 0;
  margin-right: var(--space-5);
}

/* --- primary nav tabs -----------------------------------------------------
   base/header.html renders ul.navbar-nav > li.nav-item > a.nav-link, which is
   already a tab strip; the only thing it lacked was state. Active = white
   label + semibold + a 2px bottom rail seated on the navbar's own 1px edge.
   Hover = the background tint, and nothing else, so the two never collide.
   See design-direction-2.md A.4. */

.navbar-dark .navbar-nav .nav-link {
  color: var(--nav-fg);
  font-size: var(--fs-400);
  font-weight: var(--fw-medium);
  transition: background-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}

.navbar-dark .navbar-nav .nav-link:hover,
.navbar-dark .navbar-nav .nav-link:focus,
.navbar-dark .navbar-nav .show > .nav-link {
  color: var(--nav-fg-active);
  background-color: var(--nav-hover-bg);
  text-decoration: none;
}

/* .active is emitted by base/header.html; matches BS4's own
   `.navbar-dark .navbar-nav .nav-link.active{color:#fff}` specificity (0,4,0)
   and wins on source order. */
.navbar-dark .navbar-nav .nav-link.active {
  color: var(--nav-fg-active);
  font-weight: var(--fw-semibold);
  background-color: transparent;
}
.navbar-dark .navbar-nav .nav-link.active:hover,
.navbar-dark .navbar-nav .nav-link.active:focus {
  background-color: var(--nav-hover-bg);
}

.navbar-nav .nav-link > i.fas,
.navbar-nav .nav-link > i.far {
  /* The cell is a flex row at >= 992px, so a fixed `width` is ENFORCED here
     and clips the wider glyphs (fa-robot is 16px in a 12.6px box) into the
     gutter. Floor the optical column instead of capping it. */
  flex: none;
  width: auto;
  min-width: 1em;
  text-align: center;
  margin-right: var(--space-1);
  font-size: .9em;
  opacity: .7;
  transition: opacity var(--dur-fast) var(--ease);
}
.navbar-dark .navbar-nav .nav-link:hover > i.fas { opacity: .9; }
.navbar-dark .navbar-nav .nav-link.active > i.fas { opacity: 1; }

/* an external nav target (JupyterHub) marks itself; attribute-driven so any
   future target="_blank" nav link inherits it. */
.navbar-nav .nav-link[target="_blank"]::after {
  content: "\2197";              /* ↗ */
  margin-left: var(--space-1);
  font-size: .75em;
  opacity: .55;
  vertical-align: top;
}

/* ---- horizontal bar: full-height cells + the bottom rail ---------------- */
@media (min-width: 992px) {
  /* stretch the row so a nav cell is exactly --nav-h tall and the rail can sit
     on the bar's bottom edge. Brand and toggler opt back out. */
  .navbar.fixed-top > .container,
  .navbar.fixed-top > .container-fluid { align-items: stretch; }
  .navbar.fixed-top .navbar-brand,
  .navbar.fixed-top .navbar-toggler { align-self: center; }
  .navbar.fixed-top .navbar-collapse { align-items: stretch; }
  .navbar.fixed-top .navbar-nav { align-items: stretch; }
  .navbar.fixed-top .navbar-nav > .nav-item { display: flex; align-items: stretch; }

  .navbar-dark .navbar-nav .nav-link {
    display: flex;
    align-items: center;
    border-radius: 0;
    padding: 0 var(--space-3);
    /* transparent rails on BOTH edges keep the label optically centred and
       reserve the 2px, so nothing moves when .active lands */
    border-top: var(--nav-rail) solid transparent;
    border-bottom: var(--nav-rail) solid transparent;
  }
  .navbar-dark .navbar-nav .nav-link.active {
    border-bottom-color: var(--nav-fg-active);
  }

  /* a full-height cell clips a positive outline offset */
  .navbar-dark .navbar-nav .nav-link:focus-visible,
  .navbar-dark .navbar-nav .nav-link.active:focus-visible {
    outline: 2px solid var(--nav-fg-active);
    outline-offset: -3px;
    box-shadow: none;
  }
}

/* ---- 992–1200px: seven tabs + two dropdowns get tight ------------------- */
@media (min-width: 992px) and (max-width: 1199.98px) {
  .navbar-dark .navbar-nav .nav-link { padding: 0 var(--space-2); font-size: var(--fs-300); }
  .navbar-nav .nav-link > i.fas { margin-right: 2px; }
  .solve-header.navbar-brand { margin-right: var(--space-4); }
}

.navbar-toggler {
  border-color: rgba(255,255,255,.18);
  border-radius: var(--radius-sm);
  padding: var(--space-1) var(--space-2);
}

@media (max-width: 991.98px) {
  .navbar-collapse.show,
  .navbar-collapse.collapsing {
    max-height: calc(100vh - var(--nav-h));
    overflow-y: auto;
    padding-bottom: var(--space-3);
  }

  /* A bottom rail is meaningless on a vertical stack. The collapsed menu uses
     the house 3px left rail (status cells, alerts, .unanswered-question) and,
     unlike desktop, the active row DOES take a tint: hover is irrelevant on
     touch, so the tint is free and a stack of ten rows needs all three
     channels. See design-direction-2.md A.5. */
  .navbar-dark .navbar-nav .nav-link {
    display: block;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    border-left: 3px solid transparent;
    padding-left: calc(var(--space-3) - 3px);
  }
  .navbar-dark .navbar-nav .nav-link.active {
    border-left-color: var(--nav-fg-active);
    background-color: rgba(255,255,255,.06);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  }
  /* separate the primary nav from the Admin / username menus */
  .navbar-collapse .navbar-nav + .navbar-nav {
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px solid rgba(255,255,255,.10);
  }
  .navbar-dark .navbar-nav .nav-link:focus-visible {
    outline: 2px solid var(--nav-fg-active);
    outline-offset: -2px;
  }
}

/* --- skip link ------------------------------------------------------------
   base/index.html renders it as the first child of <body>, before the header
   include — deliberately NOT inside <main>, which would hand it the
   fixed-navbar offset below. */
.skip-link {
  position: fixed;
  left: var(--space-3);
  top: -4rem;
  z-index: 1040;                      /* .fixed-top navbar is 1030 */
  padding: var(--space-2) var(--space-4);
  background-color: var(--surface);
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-sm);
  font-size: var(--fs-400);
  font-weight: var(--fw-semibold);
  box-shadow: var(--shadow-2);
  transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus {
  top: var(--space-2);
  text-decoration: none;
  color: var(--accent);
}
main#main-content { scroll-margin-top: calc(var(--nav-h) + var(--space-3)); outline: none; }

/* --- fixed-navbar offset --------------------------------------------------
   main.css:1 uses `main > .container:first-child { padding: 70px 15px 20px }`,
   a magic number that misses container-fluid pages entirely —
   contest/similarity_reports.html currently renders under the navbar. */
main.flex-shrink-0 > :first-child {
  padding-top: calc(var(--nav-h) + var(--space-5));
  padding-bottom: var(--space-5);
}

/* forum/forum.html and forum/threads_list.html open a second .container inside
   {% block content %}; neutralise the double gutter and double width cap. */
main > [class*="container"] .container {
  max-width: none;
  padding-left: 0;
  padding-right: 0;
}

/* --- per-archetype container widths (pass 3 §1) ---------------------------
   Scoped inside the xl breakpoint so bootstrap's 540/720/960 steps still
   govern below 1200px; an unscoped max-width would make .container-wide
   full-bleed at 768px. The doubled .container.container-wide selector (0,2,0)
   beats bootstrap's .container (0,1,0) regardless of load order, which matters
   because ranking.css / llm.css / fileTree.css / eventTree.css load after this
   file on their own routes. */
@media (min-width: 1200px) {
  .container.container-wide   { max-width: var(--page-wide); }
  .container.container-narrow { max-width: var(--page-narrow); }
}
@media (min-width: 576px) and (max-width: 1199.98px) {
  .container.container-narrow { max-width: var(--page-narrow); }
}
@media (min-width: 1400px) {
  /* main.css:1 is `main > .container:first-child { padding: 70px 15px 20px }`,
     specificity (0,2,1); a bare .container.container-wide (0,2,0) loses to it
     on the horizontal padding. Match its shape to win. */
  main.flex-shrink-0 > .container.container-wide {
    padding-left: var(--space-6);
    padding-right: var(--space-6);
  }
}

/* --- the dead band above the footer (pass 3 §2) ---------------------------
   body is `d-flex flex-column h-100` and footer carries `mt-auto`, so the free
   vertical space on a short page belonged to the footer's auto margin and no
   archetype could centre into it. Hand it to <main> instead. On a page taller
   than the viewport there is no free space, so this computes to zero and long
   pages are bit-identical; footer.mt-auto becomes a no-op (auto margin with
   zero free space = 0) and the footer still sits at the same pixel.
   The .flex-shrink-0 class stays: the navbar-offset rule above and parity
   assertion P-11 both key on it. */
main.flex-shrink-0 { flex-grow: 1; }

/* A 100vw full-bleed child (the home hero) overflows by the scrollbar width on
   a scrolling page. `clip` — not `hidden` — contains it without creating a
   scroll container, so position: sticky still works in the forum and record
   asides. See R-14 / P-32. */
main { overflow-x: clip; }


/* ==========================================================================
   4. BUTTONS
   ========================================================================== */

.btn {
  font-family: inherit;
  font-size: var(--fs-400);
  font-weight: var(--fw-medium);
  line-height: 1.45;
  padding: .4375rem .875rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  transition: background-color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}

.btn > i.fas,
.btn > i.far,
.btn > span.oi {
  margin-right: .375rem;
  font-size: .9em;
  opacity: .85;
}

.btn-sm {
  font-size: var(--fs-300);
  padding: .25rem .5625rem;
  border-radius: var(--radius-sm);
}

.btn-lg {
  font-size: var(--fs-600);
  padding: .625rem 1.125rem;
  border-radius: var(--radius-md);
}

/* .btn-xs is used in templates but does not exist in Bootstrap 4 — it has
   been rendering silently full-size. */
.btn-xs {
  font-size: var(--fs-100);
  padding: .125rem .375rem;
  line-height: 1.4;
  border-radius: var(--radius-xs);
}

.btn-primary {
  background-color: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.btn-primary:hover,
.btn-primary:focus {
  background-color: var(--accent-hover);
  border-color: var(--accent-hover);
  color: #fff;
}
.btn-primary:not(:disabled):not(.disabled):active,
.btn-primary:not(:disabled):not(.disabled).active,
.show > .btn-primary.dropdown-toggle {
  background-color: var(--accent-active);
  border-color: var(--accent-active);
  color: #fff;
}

/* 186 of ~270 buttons in the app are btn-secondary ("Back", "Edit", "Logs").
   Quiet neutral chips are what let btn-primary and the verdict column read. */
.btn-secondary {
  background-color: var(--surface-subtle);
  border-color: var(--border-strong);
  color: var(--neutral-fg);
}
.btn-secondary:hover,
.btn-secondary:focus {
  background-color: var(--surface-hover);
  border-color: var(--text-faint);
  color: var(--text-strong);
}
.btn-secondary:not(:disabled):not(.disabled):active,
.btn-secondary:not(:disabled):not(.disabled).active,
.show > .btn-secondary.dropdown-toggle {
  background-color: var(--border);
  border-color: var(--text-faint);
  color: var(--text-strong);
}

.btn-danger {
  background-color: var(--danger-fill);
  border-color: var(--danger-fill);
  color: #fff;
}
.btn-danger:hover,
.btn-danger:focus {
  background-color: #8f1e18;
  border-color: #8f1e18;
  color: #fff;
}
.btn-danger:not(:disabled):not(.disabled):active,
.btn-danger:not(:disabled):not(.disabled).active {
  background-color: #7a1a15;
  border-color: #7a1a15;
  color: #fff;
}

/* Small destructive buttons are *triggers* that open a confirm modal; the real
   commit button lives in .modal-footer. Quiet trigger, loud confirmation. */
.btn-danger.btn-sm,
.btn-danger.btn-xs {
  background-color: var(--danger-bg);
  border-color: #f0c0bb;
  color: var(--danger-fg);
}
.btn-danger.btn-sm:hover,
.btn-danger.btn-xs:hover,
.btn-danger.btn-sm:focus,
.btn-danger.btn-xs:focus {
  background-color: #fbdbd8;
  border-color: var(--danger-fg);
  color: var(--danger-fg);
}

.btn-success {
  background-color: var(--success-fill);
  border-color: var(--success-fill);
  color: #fff;
}
.btn-success:hover,
.btn-success:focus {
  background-color: #155429;
  border-color: #155429;
  color: #fff;
}

.btn-warning {
  background-color: var(--warning-fill);
  border-color: #d8a63a;
  color: var(--text-strong);
}
.btn-warning:hover,
.btn-warning:focus {
  background-color: #e8b43a;
  border-color: #c4932f;
  color: var(--text-strong);
}

.btn-info {
  background-color: var(--info-fill);
  border-color: var(--info-fill);
  color: #fff;
}
.btn-info:hover,
.btn-info:focus {
  background-color: #0c4e82;
  border-color: #0c4e82;
  color: #fff;
}

.btn-outline-primary {
  background-color: transparent;
  border-color: var(--accent);
  color: var(--accent);
}
.btn-outline-primary:hover,
.btn-outline-primary:focus {
  background-color: var(--accent-subtle);
  border-color: var(--accent);
  color: var(--accent-on-subtle);
}

.btn-outline-secondary {
  background-color: transparent;
  border-color: var(--border-strong);
  color: var(--neutral-fg);
}
.btn-outline-secondary:hover,
.btn-outline-secondary:focus {
  background-color: var(--surface-subtle);
  border-color: var(--text-faint);
  color: var(--text-strong);
}

/* A third tier between "routine" and "destructive": an action that deletes
   nothing but that you should not fire by accident — impersonating a user is
   the case this exists for. Tinted like .btn-danger.btn-sm (quiet trigger,
   loud confirmation) but amber, so it does not claim a delete is coming. */
.btn.btn-caution {
  background-color: var(--warning-bg);
  border-color: #efd7ab;
  color: var(--warning-fg);
}
.btn.btn-caution:hover,
.btn.btn-caution:focus {
  background-color: #fbe8ca;
  border-color: var(--warning-fg);
  color: var(--warning-fg);
}

.btn.disabled,
.btn:disabled {
  opacity: .5;
  cursor: not-allowed;
  box-shadow: none;
}

/* Archetype D moved the Edit/Delete pairs out of the headings into a sibling
   .item-actions slot, so `.news .title h4 .btn` is dead. The bare heading
   selectors stay for the buttons still inside headings elsewhere. */
h1 .btn, h2 .btn, h3 .btn, h4 .btn, h5 .btn {
  vertical-align: middle;
  position: relative;
  top: -1px;
  font-weight: var(--fw-medium);
  letter-spacing: 0;
}

/* macros/render_field.html renders every WTForms SubmitField as a bare
   btn-primary, producing a 44px "OK" flush against the last field.
   Scoped away from .form-button (post_button) and .form-search (search_form),
   which also carry `class="form …"` but are inline one-button forms. */
form.form:not(.form-button):not(.form-search):not(.form-inline) input[type="submit"].btn,
form.form:not(.form-button):not(.form-search):not(.form-inline) button[type="submit"].btn {
  margin-top: var(--space-2);
  min-width: 7.5rem;
}

/* render_form.html emits its secondary actions as <a class="btn"> siblings of
   the submit; match the offset so the row sits on one line */
form.form:not(.form-button):not(.form-search):not(.form-inline) > a.btn {
  margin-top: var(--space-2);
}

/* main.css:50 `.form-search button.btn { border: 1px solid transparent
   !important }` would erase the quiet secondary border on the one search
   submit button. */
.form-search button.btn.btn-secondary {
  border-color: var(--border-strong) !important;  /* beats .form-search button.btn{border:…transparent!important} (main.css:50) */
}
.form-search button.btn.btn-secondary:hover,
.form-search button.btn.btn-secondary:focus {
  border-color: var(--text-faint) !important;     /* same */
}


/* ==========================================================================
   5. PAGE HEADER / TOOLBAR / LANGUAGE SWITCHER
   ========================================================================== */

/* --- page title + rule (base/index.html:49–59) ----------------------------
   .page-header is emitted by base/index.html; it replaces three selectors that
   depended on the row being :first-child of a [class*="container"] and would
   silently stop matching the day a template inserted anything before it. */
/* Three slots, one optical right rail: identity | tools | actions.
   NO overflow / transform / filter / will-change anywhere in this block —
   confirm_post_popup emits its .modal inline and any of those would make the
   modal's position:fixed resolve against the header instead of the viewport
   (R-7 / P-27). */
.page-header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  grid-template-areas: "id tools actions";
  align-items: center;
  column-gap: var(--space-4);
  row-gap: var(--space-3);
  margin-bottom: var(--space-3);
}

.page-header__id      { grid-area: id; min-width: 0; }
.page-header__tools   { grid-area: tools; justify-self: end; min-width: 0; }
.page-header__actions {
  grid-area: actions;
  justify-self: end;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
  justify-content: flex-end;
}

.page-header__id > h1.mb-0 {
  font-size: var(--fs-900);        /* 28px, down from Bootstrap's 40px */
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--lh-tight);
  color: var(--text-strong);
}

.page-header__meta {
  margin: var(--space-1) 0 0;
  font-size: var(--fs-200);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* search_form() lives in the header now, not in a .buttons bar. The <form>
   ITSELF is the flex row: submit-buttons.js is
   `$('.btn-submit').click(… $(this).parent().submit())`, so wrapping the
   button in anything silently breaks search-on-click (R-6 / P-24). */
.page-header__tools .form-search {
  display: inline-flex !important;   /* beats .form-search{display:inline!important} (main.css:46) */
  align-items: stretch;
  gap: 0;
  margin: 0;
}
.page-header__tools .form-search .form-control {
  width: 15rem;
  max-width: 42vw;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.page-header__tools .form-search .btn-submit {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  margin-left: -1px;
  white-space: nowrap;
}
.page-header__tools .form-search .form-control:focus { position: relative; z-index: 1; }

@media (max-width: 1199.98px) {
  .page-header {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "id actions" "tools tools";
  }
  .page-header__tools { justify-self: stretch; }
  .page-header__tools .form-search { display: flex !important; width: 100%; }
  .page-header__tools .form-search .form-control { width: auto; flex: 1 1 auto; max-width: none; }
}

@media (max-width: 575.98px) {
  .page-header {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "id" "tools" "actions";
  }
  .page-header__actions { justify-self: stretch; justify-content: flex-start; }
  .page-header__actions > .btn,
  .page-header__actions > .form-button > .btn { flex: 1 1 auto; }
}


/* ==========================================================================
   5a. ARCHETYPE A — STANDALONE AUTH CARDS
   ========================================================================== */

/* §2 handed the free vertical space to <main>; to centre INTO it the auth
   container must be a flex column that fills it. <main> has exactly one real
   child (the container; base/branding.html emits nothing off the branded
   hosts), so column flex is a no-op for every other page: the container keeps
   its auto inline margins, its max-width and its content height. */
main.flex-shrink-0 {
  display: flex;
  flex-direction: column;
}
main.flex-shrink-0 > .container-narrow {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
}

.auth-shell {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  margin-block: auto;                /* the §2 vertical centring */
  padding-block: var(--space-6);
  gap: var(--space-4);
}

.auth-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  padding: var(--space-6);
  max-width: calc(var(--measure-auth) + 2 * var(--space-6));
  width: 100%;
  margin-inline: auto;
}
.auth-card__hd {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-800);          /* 24px, one step below the page h1 */
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--lh-tight);
  color: var(--text-strong);
}
.auth-card__lead {
  margin: 0 0 var(--space-5);
  font-size: var(--fs-400);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}
.auth-card__hd + .form-card,
.auth-card__lead + .form-card { margin-top: var(--space-5); }
.auth-card__alt {
  margin: 0;
  text-align: center;
  font-size: var(--fs-400);
  color: var(--text-muted);
}
.auth-card__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-5);
}
.auth-card__actions > .btn,
.auth-card__actions > .form-button > .btn { flex: 1 1 auto; }
.auth-card__actions > .form-button { display: flex !important; flex: 1 1 auto; }  /* beats .form-button{display:inline!important} (main.css:21) */

/* inside an auth card the form card is flat — the auth card IS the surface */
.auth-card .form-card__actions {
  background: none;
  border-top: 0;
  padding: var(--space-5) 0 0;
  margin: 0;
}
.auth-card .form-card > .form-card__body { padding: 0; grid-template-columns: 1fr; }

@media (max-width: 575.98px) {
  .auth-card { padding: var(--space-5) var(--space-4); border-radius: var(--radius-md); }
  .auth-card .form-card__actions { flex-direction: column-reverse; align-items: stretch; }
  .auth-card .form-card__actions > * { width: 100%; }
}

.page-header + hr {
  margin-top: var(--space-3) !important;     /* beats .mt-0{margin-top:0!important} */
  margin-bottom: var(--space-5) !important;  /* beats .mt-0 / .p-0 collapse */
  border: 0;
  border-top: 1px solid var(--border);
  opacity: 1;
}

main [class*="container"] .row > [class*="col-"] > h1:first-child,
main [class*="container"] .row > [class*="col-"] > h2:first-child {
  margin-top: var(--space-2);
}

/* --- .buttons toolbar -----------------------------------------------------
   Archetypes C and D moved search / pagination / primary action into the page
   header and the data-card footer. The only `.buttons` left in the app is
   code_repository/list.html:91, which nests a full .row inside it. */
.buttons {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.buttons > .row { flex: 1 1 100%; }


/* ==========================================================================
   6. TABLES + STATUS CHIPS
   ========================================================================== */

.table {
  color: var(--text);
  font-size: var(--fs-300);
  line-height: var(--lh-snug);
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--space-5);
}

.table th,
.table td {
  border-top-color: var(--border-subtle);
  vertical-align: middle;
}

/* Bootstrap's .3rem is cramped vertically and loose horizontally; invert it.
   Row height ~24px -> ~28px, which is the real readability problem. */
.table-sm th,
.table-sm td { padding: .375rem .5rem; }

.table thead th {
  padding-top: var(--space-2);
  padding-bottom: var(--space-2);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-strong);
  border-top: 0;
  white-space: nowrap;
}

.table thead.thead-light th {
  background-color: var(--surface-subtle);
  color: var(--text-muted);
  border-color: var(--border-subtle);
}

.table thead.thead-dark th {
  background-color: var(--text);
  border-color: var(--text);
  color: var(--text-inverse);
}

/* macros/sorting_label.html wraps EVERY <th> label in an <a>, so with
   a{color:#007bff} + th{font-weight:bold} the header row of every table is a
   band of saturated blue bold text. This is the loudest thing in the app. */
.table thead th a,
.table thead th a:focus {
  color: inherit;
  text-decoration: none;
}
.table thead th a:hover {
  color: var(--text-strong);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--border-strong);
}

/* ranking/contest_ranking_view.html and event_ranking_view.html set
   table-layout: fixed (ranking.css:2) and give each <th> an inline width /
   min-width. Under fixed layout a nowrap label cannot widen its own column, so
   a long problem name would overflow the cell instead of wrapping. */
.ranking thead th {
  white-space: normal;
  overflow-wrap: break-word;   /* a single long token still cannot spill into
                                  the neighbouring fixed-width column */
}

/* macros/sorting_label.html now emits .th-sort (sortable) / .th-static
   (not sortable), and always renders the glyph so sorting a column cannot
   reflow the header row. The glyph is transparent at rest, .45 on hover —
   which is the only affordance a sortable header has, since the <a> is
   colour- and underline-stripped above. */
.table thead th a.th-sort { cursor: pointer; }
.table thead th .th-sort-icon {
  font-size: .625rem;
  margin-left: var(--space-1);
  vertical-align: baseline;
  opacity: 0;                                   /* reserves the width at rest */
  transition: opacity var(--dur-fast) var(--ease);
}
.table thead th a.th-sort:hover .th-sort-icon,
.table thead th a.th-sort:focus-visible .th-sort-icon { opacity: .45; }
.table thead th a.th-sort-asc .th-sort-icon,
.table thead th a.th-sort-desc .th-sort-icon { opacity: 1; }
.table thead th .th-static { color: inherit; }

/* every list template emits
   <td colspan="1000" class="text-center empty-state"> … </td>; the [colspan]
   half of the selector also covers any that have not been converted, and
   contest/contest.html:230 builds the same row in JS. */
.table tbody td[colspan],
.table tbody td.empty-state {
  text-align: center;
  padding: var(--space-6) var(--space-4);
  color: var(--text-muted);
  background-color: var(--surface);
  border-left: 0;
  border-right: 0;
}
.table tbody td[colspan] em,
.table tbody td.empty-state em {
  font-style: normal;
  font-size: var(--fs-400);
}

.empty-state { color: var(--text-muted); }
.empty-state > em { font-style: normal; font-size: var(--fs-400); }
.empty-state > i.fas {
  display: block;
  margin: 0 auto var(--space-2);
  font-size: 1.5rem;
  opacity: .35;
}

.table-bordered { border-color: var(--border-strong); }
.table-bordered th,
.table-bordered td { border-color: var(--border-subtle); }

.table-striped tbody tr:nth-of-type(odd) { background-color: var(--surface-inset); }

.table-hover tbody tr:hover {
  background-color: var(--surface-hover);
  color: var(--text);
}

/* Bootstrap 4's .table-responsive-sm wrapper (templates now carry it) is
   behaviour-identical to the :has() hack it replaces below 576px, identical
   above it, and leaves the <table> a <table>, so the per-column `width: %`
   hints survive. contest/contest.html's problems table is deliberately
   unwrapped — it holds .dropdown-menus in its cells. */
.table-responsive-sm { margin-bottom: var(--space-5); }
.table-responsive-sm > .table { margin-bottom: 0; }

/* --- status chips --------------------------------------------------------
   The per-verdict rules set only two custom properties; shared rules do all
   the painting. Custom properties inherit, so the tr-level declaration reaches
   the inner td.status. */
.status-colored { --st-fg: var(--text-muted); --st-bg: transparent; }

.status-colored[data-status="WAITING"]   { --st-fg: var(--st-waiting-fg);  --st-bg: var(--st-waiting-bg); }
.status-colored[data-status="PREPARING"],
.status-colored[data-status="SCHEDULED"],
.status-colored[data-status="RUNNING"]   { --st-fg: var(--st-progress-fg); --st-bg: var(--st-progress-bg); }
.status-colored[data-status="SKIPPED"]   { --st-fg: var(--st-skipped-fg);  --st-bg: var(--st-skipped-bg); }

.status-colored[data-status="DONE"][data-verdict="ACCEPTED"]              { --st-fg: var(--st-accepted-fg); --st-bg: var(--st-accepted-bg); }
.status-colored[data-status="DONE"][data-verdict="UNSOLVED"]              { --st-fg: var(--st-unsolved-fg); --st-bg: var(--st-unsolved-bg); }
.status-colored[data-status="DONE"][data-verdict="WRONG_ANSWER"]          { --st-fg: var(--st-wa-fg);       --st-bg: var(--st-wa-bg); }
.status-colored[data-status="DONE"][data-verdict="TIME_LIMIT_EXCEEDED"]   { --st-fg: var(--st-tle-fg);      --st-bg: var(--st-tle-bg); }
.status-colored[data-status="DONE"][data-verdict="MEMORY_LIMIT_EXCEEDED"] { --st-fg: var(--st-mle-fg);      --st-bg: var(--st-mle-bg); }
.status-colored[data-status="DONE"][data-verdict="RUNTIME_ERROR"]         { --st-fg: var(--st-rte-fg);      --st-bg: var(--st-rte-bg); }
.status-colored[data-status="DONE"][data-verdict="RULE_VIOLATION"]        { --st-fg: var(--st-rule-fg);     --st-bg: var(--st-rule-bg); }
.status-colored[data-status="DONE"][data-verdict="COMPILATION_ERROR"]     { --st-fg: var(--st-ce-fg);       --st-bg: var(--st-ce-bg); }
.status-colored[data-status="DONE"][data-verdict="SYSTEM_ERROR"]          { --st-fg: var(--st-sys-fg);      --st-bg: var(--st-sys-bg); }

/* main.css:203–323 declares the old palette at (0,3,0)/(0,4,0)/(0,5,0); these
   four selectors match those specificities exactly and win on source order. */
.status-colored[data-status].status-text,
.status-colored[data-status][data-verdict].status-text,
.status-colored[data-status].test-run .status,
.status-colored[data-status][data-verdict].test-run .status {
  color: var(--st-fg);
}

/* SYSTEM_ERROR: kill the infinite rainbow (main.css:311–318 + the four
   vendor-prefixed @keyframes). Same specificity, later source. */
.status-colored[data-status="DONE"][data-verdict="SYSTEM_ERROR"] {
  background: none;
  background-size: auto;
  -webkit-animation: none;
  animation: none;
}

/* painting layer, in three parts. A <td> cannot be display:inline-block
   without destroying table layout, so the chip IS the cell.

   Both [data-status] and [data-status][data-verdict] forms are listed because
   main.css's old palette declares the one-attribute rules at (0,3,0) and the
   DONE+verdict rules at (0,4,0)/(0,5,0); these have to match or beat each.

   (a) fill + foreground — every status-bearing cell, plus the <dd> that
   code_repository/list.html:395 uses (its JS sets data-status/data-verdict on
   #result-status-*). Without the <dd> selectors main.css:255's #c1e1c1 ACCEPTED
   fill survives there as an off-palette pastel band. */
td.status-colored.status-text[data-status],
td.status-colored.status-text[data-status][data-verdict],
dd.status-colored.status-text[data-status],
dd.status-colored.status-text[data-status][data-verdict],
tr.status-colored.test-run[data-status] > td.status.status-text,
tr.status-colored.test-run[data-status][data-verdict] > td.status.status-text {
  color: var(--st-fg);
  background-color: var(--st-bg);
  background-image: none;
}

/* (b) the 3px rail is the *verdict* marker, so it is gated on [data-verdict].
   contest/contest.html:578 emits a score cell with data-status="DONE" and no
   verdict for "not attempted"; that cell must stay blank, not grow a grey rail
   from the .status-colored default. */
td.status-colored.status-text[data-status][data-verdict],
dd.status-colored.status-text[data-status][data-verdict],
tr.status-colored.test-run[data-status][data-verdict] > td.status.status-text {
  box-shadow: inset 3px 0 0 var(--st-fg);
}

/* (c) verdict-TOKEN typography — 11px uppercase is right for the word
   "wrong answer" and wrong for a number. contest/contest.html:563 reuses the
   same classes for a contestant's score (always .text-right, holding a figure
   or a check/cross icon), so those cells keep the table's own 13px. */
td.status-colored.status-text[data-status]:not(.text-right),
td.status-colored.status-text[data-status][data-verdict]:not(.text-right),
dd.status-colored.status-text[data-status]:not(#verdict),
dd.status-colored.status-text[data-status][data-verdict]:not(#verdict),
tr.status-colored.test-run[data-status] > td.status.status-text,
tr.status-colored.test-run[data-status][data-verdict] > td.status.status-text {
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  white-space: nowrap;
}

/* the code_repository <dd> is a block in a dl.row, not a table cell, so it
   needs its own padding to clear the rail */
dd.status-colored.status-text[data-status]:not(#verdict),
dd.status-colored.status-text[data-status][data-verdict]:not(#verdict) {
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
}

/* the hero verdict on the submission report page (#verdict = specificity 1,2,0).
   R-F8: justify-self is the whole fix for the ~920px tint band. The <dd> is a
   grid item in .record-facts' `minmax(0, 1fr)` value column, so the chip was
   stretching the full measure behind a single word; a chip has to be the size
   of its word. The phone rule below collapses .record-facts to one column,
   where start-alignment is still what you want. */
dd#verdict.status-colored.status-text {
  justify-self: start;
  background-color: var(--st-bg);
  background-image: none;
  color: var(--st-fg);
  box-shadow: inset 3px 0 0 var(--st-fg);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-500);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

/* static hazard hatch — "this is not your fault, it's ours" */
td.status-colored.status-text[data-status="DONE"][data-verdict="SYSTEM_ERROR"],
dd.status-colored.status-text[data-status="DONE"][data-verdict="SYSTEM_ERROR"],
tr.status-colored.test-run[data-status="DONE"][data-verdict="SYSTEM_ERROR"] > td.status.status-text,
dd#verdict.status-colored[data-status="DONE"][data-verdict="SYSTEM_ERROR"] {
  background-image: repeating-linear-gradient(
    135deg, transparent 0 6px, rgba(255,255,255,.07) 6px 12px);
}

/* in-flight dot, replacing content:url('spinner.gif') (main.css:223–245) */
.status-colored[data-status="WAITING"].status-text::after,
.status-colored[data-status="PREPARING"].status-text::after,
.status-colored[data-status="SCHEDULED"].status-text::after,
.status-colored[data-status="RUNNING"].status-text::after,
.status-colored[data-status="WAITING"].test-run .status::after,
.status-colored[data-status="PREPARING"].test-run .status::after,
.status-colored[data-status="SCHEDULED"].test-run .status::after,
.status-colored[data-status="RUNNING"].test-run .status::after {
  content: "";
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-left: var(--space-2);
  border-radius: 50%;
  background: currentColor;
  vertical-align: middle;
  padding-left: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .status-colored[data-status="WAITING"].status-text::after,
  .status-colored[data-status="PREPARING"].status-text::after,
  .status-colored[data-status="SCHEDULED"].status-text::after,
  .status-colored[data-status="RUNNING"].status-text::after,
  .status-colored[data-status="WAITING"].test-run .status::after,
  .status-colored[data-status="PREPARING"].test-run .status::after,
  .status-colored[data-status="SCHEDULED"].test-run .status::after,
  .status-colored[data-status="RUNNING"].test-run .status::after {
    animation: st-pulse 1.4s var(--ease) infinite;
  }
}

@keyframes st-pulse { 0%, 100% { opacity: .35 } 50% { opacity: 1 } }

/* contest/contest.html:134 uses these on check/cross icons */
.accepted-background { background-color: var(--st-accepted-bg); }
.accepted-font { color: var(--st-accepted-fg); font-weight: var(--fw-semibold); }
.unsolved-font { color: var(--st-unsolved-fg); font-weight: var(--fw-semibold); }


/* ==========================================================================
   6a. ARCHETYPE C — THE DATA CARD
   A list page is: one header bar, one bounded table, one footer that says
   where you are in it and carries the destructive bulk op on that dataset.
   ========================================================================== */

/* NO overflow: hidden — contest/contest.html opens a .dropdown-menu inside a
   <td>, and confirm_post_modal renders its .modal inline. No transform or
   filter either, for the same reason (R-3, R-7, P-27). */
.data-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-1);
  margin-bottom: var(--space-5);
}
.data-card > .table-responsive-sm,
.data-card > .table-responsive { margin-bottom: 0; }   /* beats theme.css:1113 */
.data-card > .table-responsive-sm > .table,
.data-card > .table-responsive > .table,
.data-card > .table { margin-bottom: 0; }

/* The card supplies the frame; the table supplies only horizontal rules.
   This is what replaces .table-bordered on the archetype-C tables. */
.data-card .table > thead > tr > th { border-top: 0; }
.data-card .table > thead > tr:first-child > th:first-child { border-top-left-radius:  var(--radius-md); }
.data-card .table > thead > tr:first-child > th:last-child  { border-top-right-radius: var(--radius-md); }
.data-card .table > tbody > tr:last-child > td { border-bottom: 0; }
/* a card with no footer: the table's last row carries the bottom corners */
.data-card > .table-responsive-sm:last-child .table > tbody > tr:last-child > td:first-child,
.data-card > .table:last-child > tbody > tr:last-child > td:first-child { border-bottom-left-radius:  var(--radius-md); }
.data-card > .table-responsive-sm:last-child .table > tbody > tr:last-child > td:last-child,
.data-card > .table:last-child > tbody > tr:last-child > td:last-child  { border-bottom-right-radius: var(--radius-md); }

/* R-F8. A list table's trailing column is its action column: the <th> is empty
   and the cells hold buttons. Its width is a hand-set percentage sized for the
   widest row, so every other row left a dead gutter between the last button
   and the card's new right border — measured 145px on /user, 104 on /team,
   57 on /organization. Right-aligning puts that slack between the data and
   the actions, where it reads as separation instead of as a hole.
   Gated on :has(> .btn, > .form-button) so a trailing DATA column is never
   touched, and on :not(.text-*) so an explicit alignment still wins. Without
   :has() this rule simply does not apply and the gutter is as it is today. */
.data-card .table > tbody > tr > td:last-child:not(.text-center):not(.text-right):not(.text-left):has(> .btn, > .form-button) {
  text-align: right;
}

.data-card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: var(--surface-sunken);
  border-top: 1px solid var(--border);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}
.data-card__range {
  font-size: var(--fs-200);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.data-card__pager { margin-left: auto; }
.data-card__pager .pagination { margin: 0; }
/* R-F5: `.data-card__bulk` is gone, along with the rule that paired it with
   the pager. An irreversible bulk delete sat ~10px from the "1" pagination
   target on every page of the list; it now lives in .danger-zone below the
   card, and this footer carries navigation only. */

/* An admin panel that is not a table but shares the table card's chrome
   (task/list.html's .zip upload). */
.upload-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-1);
  padding: var(--space-5);
  margin-bottom: var(--space-5);
}
.upload-card > h4 {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-600);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
}
.upload-card > p { font-size: var(--fs-400); }

/* "which slice of the data am I looking at" — a sentence, not a heading */
.page-context {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-400);
  color: var(--text-muted);
}
.page-context > strong { color: var(--text); font-weight: var(--fw-semibold); }

/* two data cards side by side (task/ranking.html) */
.card-columns-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap-page);
  align-items: start;
}
.card-columns-2 > * > .section-title { margin-top: 0; }
@media (max-width: 991.98px) { .card-columns-2 { grid-template-columns: minmax(0, 1fr); } }

@media (max-width: 575.98px) {
  .data-card__foot { flex-direction: column; align-items: stretch; }
  .data-card__pager { margin: 0; }
  .data-card__pager .pagination { justify-content: center; }
}

/* --- the irreversible-actions strip (R-F5) --------------------------------
   A bulk destructive op is not part of "where am I in this list", so it does
   not share a row with the pager. It gets its own region, after the card, in
   danger colour, with its own label — far enough that a mis-click on page 2
   cannot land on it.
   NO overflow / transform / filter: confirm_post_popup emits its .modal
   inline, here as a child of .danger-zone__actions (R-7 / P-27). */
.danger-zone {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-7) 0 var(--space-5);
  padding: var(--space-3) var(--space-4);
  background: var(--danger-bg);
  border: 1px solid var(--danger-fg);
  border-radius: var(--radius-md);
}
.danger-zone__title {
  margin: 0;
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--danger-fg);
}
.danger-zone__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-left: auto;
}
@media (max-width: 575.98px) {
  .danger-zone__actions { margin-left: 0; width: 100%; }
  .danger-zone__actions > .btn { flex: 1 1 auto; }
}


/* ==========================================================================
   7. FORM CONTROLS
   ========================================================================== */

.form-control {
  font-family: inherit;
  font-size: var(--fs-400);
  color: var(--text);
  background-color: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: none;
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}

.form-control:hover:not(:focus):not(:disabled) { border-color: var(--border-strong); }

.form-control::placeholder { color: var(--text-faint); }

/* floating-label overrides — theme.css loads after vendor/floating-labels.css,
   so plain specificity wins and the vendor dir stays untouched. */
.form-label-group label.col-form-label {
  color: var(--text-muted);
  font-size: var(--fs-400);
  transition: all var(--dur-fast) var(--ease);
}

.form-label-group input:not(:placeholder-shown) ~ label,
.form-label-group textarea:not(:placeholder-shown) ~ label,
.form-label-group select:not(:placeholder-shown) ~ label {
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  letter-spacing: .02em;
  color: var(--text-muted);   /* vendor's #777 is 4.48:1 — a marginal failure */
  text-transform: uppercase;
}

/* render_field.html already emits the `error` class; nothing consumed it */
.form-label-group.error .form-control {
  border-color: var(--danger-fg);
  box-shadow: inset 2px 0 0 var(--danger-fg);
}

/* Help text needs air under the control it describes: at --space-1 the first
   line sat ~4px off the input's border and read as if it were touching it,
   and multi-line descriptions (register's School field runs to three lines)
   were set too tight to scan. The field's own gap stays smaller than the
   grid's row gap below, so the text still groups with its own control. */
.form-label-group small.text-danger,
.form-label-group small.text-muted {
  display: block;
  margin-top: var(--space-2);
  padding-left: var(--space-1);
  font-size: var(--fs-200);
  line-height: var(--lh-normal);
}

.form-check-input {
  width: 1rem;
  height: 1rem;
  margin-top: .2rem;
  accent-color: var(--accent);
}

/* keeps main.css:137's absolute positioning, drops the .mark yellow */
.form-label-group small.help-dt {
  line-height: 1.5;
  border-radius: var(--radius-sm);
  font-size: var(--fs-100);
  font-weight: var(--fw-medium);
  color: var(--text-muted);
  background-color: var(--surface);  /* also overrides BS4 mark{background:#fcf8e3} */
  padding: 0 var(--space-1);
}

.form-inline .form-control { min-width: 200px; }


/* --- the form card (pass 3, archetype B) ----------------------------------
   render_form() now emits
       form.form.form-card > .form-card__body + .form-card__actions
   so every one of the 66 render_form call sites becomes a bounded surface
   with an action footer, with no template edit.
   NO overflow / transform / filter here — confirm_post_popup emits .modal
   inline (R-7 / P-27). */
/* R-F3: left-aligned, NOT centred. The page title, the context head and the
   card now share one left edge instead of three. Centring a 704px card inside
   a 1110px container only ever produced an axis nothing else sat on. The auth
   pages are unaffected — .auth-shell lives inside .container-narrow and
   .auth-card .form-card already zeroes this margin. */
.form-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
  margin: 0;
}
.form-card--cols-1 { max-width: var(--measure-form); }
.form-card--cols-2 { max-width: var(--measure-form-2); }

/* Neutralises the 60 ad-hoc `col-md-6 col-lg-5` / `col-md-9 col-lg-7` classes
   that the call sites pass as render_form(css=…) WITHOUT editing one of them.
   The card's own max-width is the single source of measure now. */
.form-card[class*="col-"] {
  flex: none;
  width: 100%;
  max-width: var(--measure-form);
  padding-left: 0;
  padding-right: 0;
}
.form-card--cols-2[class*="col-"] { max-width: var(--measure-form-2); }

.form-card__body {
  display: grid;
  grid-template-columns: 1fr;
  align-items: start;              /* a help-texted field must not stretch its row-mate */
  /* Row gap is larger than the column gap, and larger than the gap between a
     control and its own help text, so each field reads as one group rather
     than the help drifting toward the control below it. */
  gap: var(--space-5) var(--space-4);
  padding: var(--space-5);
}
.form-card__body > * { min-width: 0; }
/* the grid's own gap is the rhythm now; drop floating-labels.css:3's 1rem */
.form-card__body > .form-label-group,
.form-card__body > .form-check { margin-bottom: 0; }
/* auto-fit, not a viewport media query: badge/create.html and friends put the
   form inside a .col-lg-6, where a viewport-driven 1fr 1fr gave 237px columns.
   The track floor makes the grid answer to the CARD's width instead. */
.form-card--cols-2 > .form-card__body {
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
}
.form-card--cols-2 > .form-card__body > .field--full { grid-column: 1 / -1; }
@media (max-width: 767.98px) {
  .form-card--cols-2 > .form-card__body { grid-template-columns: 1fr; }
}

/* Same treatment for the field-level `render_kw={"css": "col-sm-8"}` values:
   a bootstrap column inside a grid cell is a 15px gutter and a fractional
   max-width that no longer means anything. */
   `.col-form-label` must be excluded: it matches [class*="col-"] but is NOT a
   grid column — it is the floating label, and zeroing its inline padding put
   the hint text 1px from the input's border while the value the user types
   sits at 13px, so the label and its own value were off by 12px. */
.form-card__body .form-label-group > [class*="col-"]:not(.col-form-label),
.form-card__body > [class*="col-"]:not(.col-form-label) {
  flex: none;
  max-width: 100%;
  padding-left: 0;
  padding-right: 0;
}

.form-card__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-5);
  background: var(--surface-sunken);
  border-top: 1px solid var(--border);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}
/* the footer's gap governs now, not the old submit margin */
.form-card__actions input[type="submit"].btn,
.form-card__actions button[type="submit"].btn,
.form-card__actions > a.btn { margin: 0 !important; }   /* beats form.form … {margin-top} above */
.form-card__actions > .text-right { margin: 0; }        /* render_kw pull_right wrapper */

/* An empty footer (a form with neither submit nor buttons=) must not draw a
   bare sunken strip. */
.form-card__actions:empty { display: none; }

/* Opt-outs: a form inside a dropdown, a table cell, a modal or an auth card is
   already on a surface. Call sites: forum/posts_list.html:33, event/list.html,
   contest/confirm_solo_registration.html, ranking/contest_ranking_view.html. */
.dropdown-menu .form-card,
td .form-card,
.modal-content .form-card,
.auth-card .form-card {
  border: 0;
  box-shadow: none;
  background: none;
  max-width: none;
  margin: 0;
}
.dropdown-menu .form-card > .form-card__body,
td .form-card > .form-card__body,
.modal-content .form-card > .form-card__body {
  padding: 0;
  grid-template-columns: 1fr;
}
.dropdown-menu .form-card > .form-card__actions,
td .form-card > .form-card__actions,
.modal-content .form-card > .form-card__actions {
  background: none;
  border-top: 0;
  padding: var(--space-2) 0 0;
}

@media (max-width: 767.98px) {
  .form-card__body    { padding: var(--space-4); }
  .form-card__actions { padding: var(--space-3) var(--space-4); }
  .form-card__actions > .btn,
  .form-card__actions input[type="submit"].btn { flex: 1 1 100%; }
}

/* --- a form with an explainer beside it -----------------------------------
   R-F4. badge/create.html wrapped the form in `.row > .col-lg-6`, so the CARD
   was capped at the column's 540px. `.form-card[class*="col-"]` neutralises a
   col class ON the card; it cannot see a col on the card's PARENT. The card
   therefore carried form-card--cols-2 and max-width:960 while rendering 540
   wide with a 490px body, and `repeat(auto-fit, minmax(20rem, 1fr))` cannot
   fit two 320px tracks in 490px — the two-column form silently stayed one
   column, ten fields tall, with its OK footer below the fold.

   A grid page layout fixes the cause: the form column is free to reach the
   form measure and the explainer becomes a real aside, not a bootstrap half.
   At the 1110px container this leaves ~758px for the card, whose body then
   fits two 320px tracks. */
.form-explain {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--aside-w);
  gap: var(--gap-page);
  align-items: start;
}
.form-explain > .form-explain__aside > .alert:last-child { margin-bottom: 0; }
@media (max-width: 991.98px) {
  .form-explain { grid-template-columns: minmax(0, 1fr); }
  /* the explainer is reference material: below the form on a narrow screen */
  .form-explain__aside { order: 1; }
}

/* contest/submit.html notebook upload — the most important input in the app */
input[type=file] {
  height: auto;
  padding: var(--space-2);
  border: 1px dashed var(--border-strong);
  background-color: var(--surface-sunken);
  border-radius: var(--radius-sm);
  font-size: var(--fs-300);
}


/* ==========================================================================
   8. BADGES / ALERTS / TOASTS
   ========================================================================== */

.badge {
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: .02em;
  padding: .25rem .4375rem;
  border-radius: var(--radius-sm);
  border: 1px solid transparent;
  vertical-align: baseline;
}

.badge-pill {
  border-radius: var(--radius-pill);
  padding-left: .5625rem;
  padding-right: .5625rem;
}

.badge-secondary {
  background-color: var(--neutral-bg);
  border-color: var(--border);
  color: var(--neutral-fg);
}
.badge-primary {
  background-color: var(--accent-subtle);
  border-color: #c4d7f0;
  color: var(--accent-on-subtle);
}
.badge-success {
  background-color: var(--success-bg);
  border-color: #bfe0cb;
  color: var(--success-fg);
}
.badge-danger {
  background-color: var(--danger-bg);
  border-color: #f5cdc9;
  color: var(--danger-fg);
}
.badge-warning {
  background-color: var(--warning-bg);
  border-color: #efd7ab;
  color: var(--warning-fg);
}
.badge-info {
  background-color: var(--info-bg);
  border-color: #cfe2f2;
  color: var(--info-fg);
}

.badge-privilege {
  padding: .25rem .4375rem;
  margin-right: var(--space-1);
}
.badge.badge-tag {
  padding: .25rem .4375rem !important;  /* beats .badge-tag{padding:0 0 0 8px!important} (main.css:129) */
  margin-right: var(--space-1);
}

/* .eventbadge only ever appears as `badge badge-secondary badge-privilege
   eventbadge` (user/profile.html:114, event/create.html:25), so .badge already
   supplies the padding — main.css:350's extra .5rem is what made the pill
   lopsided. Declaring padding:0 here would squash the chip instead, because
   these rules win on source order rather than adding to .badge.
   (.teamspan has no template references left; kept for safety only.) */
.eventbadge,
.teamspan {
  display: inline-block;
  line-height: 1.5;
}

/* --- alerts --------------------------------------------------------------- */
.alert {
  border: 1px solid;
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  font-size: var(--fs-400);
  line-height: var(--lh-normal);
  margin-bottom: var(--space-4);
}

.alert > i.fas,
.alert > i.far {
  margin-right: var(--space-2);
  opacity: .8;
}

.alert-info {
  background-color: var(--info-bg);
  border-color: #cfe2f2;
  border-left-color: var(--info-fg);
  color: var(--info-fg);
}
.alert-warning {
  background-color: var(--warning-bg);
  border-color: #efd7ab;
  border-left-color: var(--warning-fg);
  color: var(--warning-fg);
}
.alert-danger {
  background-color: var(--danger-bg);
  border-color: #f5cdc9;
  border-left-color: var(--danger-fg);
  color: var(--danger-fg);
}
.alert-success {
  background-color: var(--success-bg);
  border-color: #bfe0cb;
  border-left-color: var(--success-fg);
  color: var(--success-fg);
}

/* --- bootstrap-notify flash toasts ---------------------------------------
   Do NOT touch position / top / right / z-index — the plugin writes those
   inline and recomputes them. */
[data-notify="container"] {
  box-shadow: var(--shadow-3);
  border-radius: var(--radius-md);
  border-left-width: 3px;
  padding: var(--space-3) var(--space-5) var(--space-3) var(--space-4);
  font-size: var(--fs-400);
}

[data-notify="message"] {
  display: block;
  line-height: var(--lh-snug);
}

[data-notify="dismiss"].close {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  font-size: 1.125rem;
  opacity: .5;
  color: inherit;
  text-shadow: none;
}
[data-notify="dismiss"].close:hover { opacity: 1; }

[data-notify="container"] .progress {
  height: 2px;
  margin-top: var(--space-2);
  background-color: rgba(17,22,28,.08);
  border-radius: var(--radius-pill);
}


/* ==========================================================================
   9. CARDS / DROPDOWNS / MODALS / PAGINATION / TABS
   ========================================================================== */

.card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
  background-color: var(--surface);
}

.card-header {
  background-color: var(--surface-sunken);
  border-bottom-color: var(--border);
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-400);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
}

.card-footer {
  background-color: var(--surface-sunken);
  border-top-color: var(--border);
  padding: var(--space-2) var(--space-3);
}

/* --- dropdowns ------------------------------------------------------------ */
.dropdown-menu {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-2);
  padding: var(--space-1);
  margin-top: var(--space-1);
  font-size: var(--fs-400);
  min-width: 11rem;
}

.dropdown-item {
  padding: .375rem .625rem;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-weight: var(--fw-regular);
}

.dropdown-item:hover,
.dropdown-item:focus {
  background-color: var(--surface-hover);
  color: var(--text-strong);
  text-decoration: none;
}

.dropdown-item.active,
.dropdown-item:active {
  background-color: var(--accent-subtle);
  color: var(--accent-on-subtle);
}

.dropdown-item > i.fas,
.dropdown-item > i.far {
  width: 1em;
  margin-right: var(--space-2);
  opacity: .65;
}

/* .form-button{display:inline!important} made the dropdown "Logout"
   post_button render inline, so it never filled its row. */
.dropdown-menu .form-button { display: block !important; }  /* beats .form-button{display:inline!important} (main.css:21) */
.dropdown-menu .form-button > .btn {
  display: block;
  width: 100%;
  text-align: left;
}

.dropdown-divider { border-top-color: var(--border); }

/* --- modals --------------------------------------------------------------- */
.modal-content {
  border: 0;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-3);
}
.modal-header {
  border-bottom-color: var(--border);
  padding: var(--space-4) var(--space-5);
}
.modal-title {
  font-size: var(--fs-600);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
}
.modal-body {
  padding: var(--space-4) var(--space-5);
  font-size: var(--fs-500);
  line-height: var(--lh-normal);
  color: var(--text);
}
.modal-footer {
  border-top-color: var(--border);
  padding: var(--space-3) var(--space-5);
  gap: var(--space-2);
}
.modal-backdrop.show {
  opacity: .45;
  background-color: #0b0f14;
}

/* --- pagination ----------------------------------------------------------- */
.pagination { gap: 2px; }

.page-link {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm) !important;  /* beats .page-item:first-child/:last-child .page-link radius (higher specificity in BS4) */
  color: var(--text-muted);
  background-color: var(--surface);
  font-size: var(--fs-300);
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
  padding: .25rem .5rem;
  min-width: 1.875rem;
  text-align: center;
}

.page-link:hover {
  background-color: var(--surface-hover);
  color: var(--text-strong);
  border-color: var(--border-strong);
  text-decoration: none;
  z-index: auto;
}

.page-item.active .page-link {
  background-color: var(--accent);
  border-color: var(--accent);
  color: #fff;
}

.page-item.disabled .page-link {
  /* --border-strong on --surface-sunken is 1.77:1 — worse than the 4.69:1
     Bootstrap shipped. WCAG exempts disabled controls, but a contrast pass
     should not go backwards. #7b8694 measures 3.48:1 and still reads inert. */
  color: #7b8694;
  background-color: var(--surface-sunken);
}

/* --- tabs / pills (no current usage; specced so future tabs inherit) ------ */
.nav-tabs { border-bottom: 1px solid var(--border); }
.nav-tabs .nav-link {
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  color: var(--text-muted);
  font-size: var(--fs-400);
  font-weight: var(--fw-medium);
  padding: var(--space-2) var(--space-3);
}
.nav-tabs .nav-link:hover { color: var(--text-strong); border-bottom-color: var(--border-strong); }
.nav-tabs .nav-link.active { color: var(--accent); background: transparent; border-bottom-color: var(--accent); }
.nav-pills .nav-link { border-radius: var(--radius-sm); color: var(--text-muted); font-size: var(--fs-400); }
.nav-pills .nav-link.active { background-color: var(--accent-subtle); color: var(--accent-on-subtle); }


/* ==========================================================================
   10. CONTENT BLOCKS / RANKING MEDALS / FOOTER
   ========================================================================== */

.news,
.forum-thread,
.forum-post {
  margin: 0 0 var(--space-6);
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--border);
  /* The item is capped, not just its prose. `.title` is a space-between row,
     so an uncapped item parks the action rail at the container's right edge —
     on the home feed that stranded Edit/Delete ~700px from the heading they
     belong to. Harmless where the item is already narrower (the forum aside). */
  max-width: var(--measure-feed);
}

/* --- archetype D: the title owns its line, the actions own a fixed slot ---
   The actions used to live INSIDE the <h4>, so their x position moved with
   every title length and the eye could not scan down either column. */
.news > .title,
.forum-thread > .title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-1);
}

.news > .title h4,
.forum-thread > .title h4 {
  margin: 0;
  min-width: 0;
  flex: 1 1 auto;
  font-size: var(--fs-700);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-strong);
}

.item-actions {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 0 0 auto;
  white-space: nowrap;
}
.item-actions > .btn { margin: 0; }
/* post_button()/confirm_post_button emit a <form> or a bare <button>; both
   must sit on the flex line rather than break it. */
.item-actions > .form-button { display: inline-flex !important; margin: 0; }  /* beats .form-button{display:inline!important} (main.css:21) */

/* A reply has no title, so its actions attach to the meta line. The meta is
   a sentence of loose text nodes — making it a flex row would put every node
   on its own line (each contiguous run becomes an anonymous flex item), so
   the actions float instead. */
.forum-post > .meta > .item-actions {
  float: right;
  margin-left: var(--space-3);
}

.news > .meta,
.forum-thread > .meta,
.forum-post > .meta {
  margin-bottom: var(--space-3);
  color: var(--text-muted);      /* was rgba(0,0,0,.5) ≈ 3.95:1 — AA failure */
  font-size: var(--fs-200);
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
}

.news > .content,
.forum-thread > .content,
.forum-post > .content {
  margin: 0;
  padding: 0 0 0 var(--space-4);
  border-left: 2px solid var(--border);
  font-size: var(--fs-500);
  line-height: var(--lh-relaxed);
  color: var(--text);
}

.news > .content > p:last-child,
.forum-thread > .content > p:last-child,
.forum-post > .content > p:last-child { margin-bottom: 0; }

/* The readability fix: cap the measure, not the page. 890px of 15px prose is
   ~110 characters a line; 72ch is ~660px. */
.news > .content,
.forum-thread > .content,
.forum-post > .content { max-width: var(--measure-prose); }

/* was news/list.html:22's inline <style type="text/css">img{max-width:100%}
   — a global tag selector shipped from inside a loop body. */
.news .content img,
.forum-thread .content img,
.forum-post .content img { max-width: 100%; height: auto; }

/* a content list has no table, so its pagination has no card footer to go in */
.list-pager {
  display: flex;
  justify-content: center;
  padding: var(--space-5) 0 0;
}
.list-pager .pagination { margin: 0; }

/* a sub-heading inside {% block content %} — one step under the page h1 */
.section-title {
  font-size: var(--fs-700);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
  letter-spacing: var(--tracking-tight);
  margin: 0 0 var(--space-4);
}

/* operations on the whole dataset rather than on this page of it */
.bulk-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.bulk-bar:empty { display: none; }

@media (max-width: 575.98px) {
  .news > .title,
  .forum-thread > .title { flex-direction: column; align-items: flex-start; gap: var(--space-2); }
  .item-actions { white-space: normal; flex-wrap: wrap; }
}

.unanswered-question {
  background-color: #fdf7e8;
  box-shadow: inset 3px 0 0 var(--warning-fg);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
}

/* contest/submit.html:23 — the four declarations that used to be an inline
   style attribute on #contest_submit_info. They do NOT live in that template's
   own <style> block: that block sits outside every {% block %} in a child of
   base/index.html and is therefore never rendered. */
#contest_submit_info {
  font-style: italic;
  color: var(--text-muted);
  margin-left: var(--space-5);
  margin-top: var(--space-6);
}

/* ==========================================================================
   10a. PANELS, PAGE LAYOUTS (archetypes E, F, G)
   A panel is the non-table equivalent of .data-card: a bounded surface with a
   sunken head and a row list. Same R-7 rule applies — NO overflow/transform.
   ========================================================================== */

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
  margin-bottom: var(--space-5);
}
.panel:last-child { margin-bottom: 0; }

.panel__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: var(--space-3);
  row-gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--border);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.panel__title {
  margin: 0;
  font-size: var(--fs-600);
  font-weight: var(--fw-semibold);
  color: var(--text-strong);
  letter-spacing: var(--tracking-tight);
}
.panel__desc {
  grid-column: 1 / -1;
  margin: 0;
  padding: 0;
  border-left: 0;
  font-size: var(--fs-400);
  color: var(--text-muted);
}
.panel__desc > p:last-child { margin-bottom: 0; }

.panel__rows { list-style: none; margin: 0; padding: 0; border-left: 0; }
.panel__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border-subtle);
}
.panel__rows > .panel__row:first-child { border-top: 0; }
.panel__row-link { font-weight: var(--fw-medium); color: var(--accent); }
.panel__row-link > .fas { width: 1.1em; margin-right: var(--space-1); color: var(--text-faint); }
.panel__row--add { background: var(--surface-inset); border-radius: 0 0 var(--radius-md) var(--radius-md); }
.panel__row--empty { color: var(--text-muted); }
.panel__scroll { padding: var(--space-3) var(--space-4); }

/* a heading row that also carries one action */
.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.section-head > .section-title { margin-bottom: 0; }

/* --- archetype E: forum index ------------------------------------------- */
.forum-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--aside-w-lg);
  gap: var(--gap-page);
  align-items: start;
}
@media (max-width: 991.98px) { .forum-layout { grid-template-columns: minmax(0, 1fr); } }
@media (min-width: 992px) {
  .forum-layout__aside > .panel {
    position: sticky;
    top: calc(var(--nav-h) + var(--space-4));
    max-height: calc(100vh - var(--nav-h) - var(--space-6));
    display: flex;
    flex-direction: column;
  }
  .forum-layout__aside .panel__scroll { overflow-y: auto; }
}

/* .forum-category keeps its class (P-6 keys on it) but the panel's own
   bottom margin is the rhythm now, not the old --space-6. */
.forum-category { margin-bottom: var(--space-5); }
.forum-post--compact {
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-4);
}
.forum-post--compact:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: 0; }
.forum-post--compact > .content { max-width: none; font-size: var(--fs-400); }

/* Supersedes the old `border-left: 1px solid` rail — the panel rows replace
   it. list-style: none is kept (P-6 / P-28). */
.forum-category .forum-subcategory-list,
.panel__rows.forum-subcategory-list {
  list-style: none;
  margin: 0;
  border-left: 0;
}

/* --- archetype F: record pages ------------------------------------------ */
/* A dl laid out as a real definition grid instead of two bootstrap columns.
   Works on ANY existing dt/dd sequence, so no template re-ordering. */
.record-facts {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--space-2) var(--space-4);
  margin: 0;
  padding: var(--space-4);
}
.record-facts > dt {
  grid-column: 1;
  margin: 0;
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-muted);
  padding-top: .15rem;
}
.record-facts > dd {
  grid-column: 2;
  margin: 0;
  min-width: 0;
  font-size: var(--fs-400);
  color: var(--text);
}
/* the col-sm-3 / col-sm-9 classes left on the dt/dd are inert in a grid */
.record-facts > dt[class*="col-"],
.record-facts > dd[class*="col-"] {
  flex: none;
  max-width: none;
  width: auto;
  padding-left: 0;
  padding-right: 0;
}
.record-facts > dd > .btn,
.record-facts > dd > .form-button { margin-left: var(--space-2); }

/* The facts block that introduces a form page (39 templates).
   -------------------------------------------------------------------------
   R-F3. The previous version centred this strip at --measure-form-2 above a
   form card centred at --measure-form, which put three left edges on one page
   (h1 at the container edge, strip at +75, card at +203) and made the WIDER,
   heavier box the read-only one. That is a hierarchy inversion: the box you
   cannot touch outranked the box you came to use.

   The fix is one box on one axis. The strip keeps the card's border but drops
   its bottom edge and bottom radii, and the form card below it drops its top
   radii, so the two render as a single surface with a sunken head — exactly
   `form-card__head`, reached without a template edit at any of the 39 call
   sites (several of which do NOT have the form as the next sibling, see
   contest/confirm_solo_registration.html and contest/register.html).

   :has() is a PROGRESSIVE enhancement here, never load-bearing: without it the
   strip is still capped at --measure-form and still starts on the h1 axis, so
   the fallback is two equal-width stacked boxes sharing one left edge — no
   inversion, no mismatch. The only thing :has() buys is widening the head to
   match a two-column card underneath it. */
.record-facts--context {
  max-width: var(--measure-form);
  margin: 0 0 var(--space-5);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
/* a two-column card below -> the head widens with it */
.record-facts--context:has(+ .form-card--cols-2) { max-width: var(--measure-form-2); }
/* adjacent to a form card -> the two fuse into one box */
.record-facts--context:has(+ .form-card) {
  margin-bottom: 0;
  border-bottom: 0;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}
.record-facts--context + .form-card {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}
.auth-card .record-facts--context,
.modal-content .record-facts--context,
td .record-facts--context { max-width: none; margin-inline: 0; }

.record-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--aside-w);
  gap: var(--gap-page);
  align-items: start;
}
@media (max-width: 991.98px) {
  .record-layout { grid-template-columns: minmax(0, 1fr); }
  .record-layout__aside { order: -1; }
}
@media (max-width: 575.98px) {
  .record-facts { grid-template-columns: minmax(0, 1fr); gap: var(--space-1) 0; }
  .record-facts > dt { grid-column: 1; }
  .record-facts > dd { grid-column: 1; margin-bottom: var(--space-3); }
}

/* --- archetype F2: the user record (user/profile.html) -------------------
   The profile was twelve equal-weight rows in one flat <dl>, each action
   glued to its value by `&nbsp;&nbsp;&nbsp;`, so every button landed at an x
   that was a function of how long that value happened to be — a ragged
   diagonal of Impersonate / Change password / Add badge / Create team down
   the page. Two things fix it: labelled sections (.panel) instead of one
   list, and a THIRD grid track that is the action rail.

   The rail is `minmax(--record-act, max-content)`: at least 192px in every
   section, so a button in "Account" and a button in "Credentials" start on
   the same x even though the two grids are independent, and wider only for a
   label that genuinely needs it — Polish runs ~30% longer than English and a
   fixed track would clip "Zmiana danych użytkownika".

   `justify-self: start` is what puts the buttons on a shared LEFT edge. A
   right-flushed rail would re-create the original defect mirrored: a two-word
   button and a four-word button would start in different places again. */
.record-facts--acts {
  grid-template-columns:
    max-content minmax(0, 1fr) minmax(var(--record-act), max-content);
  gap: var(--space-3) var(--space-4);
  align-items: baseline;
}
.record-facts--acts > dt { padding-top: 0; }
/* a section with nothing to act on (a stranger's profile, a read-only badge
   list) must not reserve 192px of nothing. Progressive: without :has() the
   rail is simply empty, which is what it was before this rule. */
.record-facts--acts:not(:has(> .record-facts__act)) {
  grid-template-columns: max-content minmax(0, 1fr);
}
.record-facts > dd.record-facts__act {
  grid-column: 3;
  justify-self: start;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
/* .record-facts > dd > .btn carries a left margin for the inline layout this
   rail replaces; inside the rail the gap already spaces the buttons. */
.record-facts > dd.record-facts__act > .btn,
.record-facts > dd.record-facts__act > .form-button { margin-left: 0; }

/* a value that is absent, not empty: "no badges yet", not a bare button */
.fact-empty { color: var(--text-muted); }

/* a row an admin sees only because they are an admin. The full words are on
   the section flag above it, so the marker inside the grid stays an icon and
   does not widen the label track. */
.fact-lock {
  margin-left: var(--space-1);
  font-size: .8em;
  color: var(--warning-fg);
}

/* The record itself is one column of equal-width sections, so the action rail
   lands on ONE x for the whole page and not one per section; team membership
   is a list the user navigates away through, so it goes in the aside rather
   than earning a fourth full-width box. Two wrappers rather than grid-row
   spans: the aside's height then never stretches a row of the record. */
.profile-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--aside-w-lg);
  gap: var(--gap-page);
  align-items: start;
}
@media (max-width: 991.98px) {
  .profile-grid { grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
}

/* the section's own gate, spelled out: "you are reading this because you are
   an admin". Only rendered when an admin is on SOMEBODY ELSE's profile. */
.panel__flag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: .0625rem .4375rem;
  background: var(--warning-bg);
  border: 1px solid #efd7ab;
  border-radius: var(--radius-pill);
  color: var(--warning-fg);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
}
.panel__title-icon { margin-right: var(--space-1); color: var(--text-faint); font-size: .85em; }
/* at phone width the flag and a two-word section title cannot share a line
   without breaking the title away from its icon — give the flag its own row */
@media (max-width: 575.98px) {
  .panel__head > .panel__flag { grid-column: 1; justify-self: start; }
}

/* a credential is read and copied, never skimmed: monospace, and one click
   takes the whole string rather than a word of it. */
.secret {
  font-family: var(--font-mono);
  -webkit-user-select: all;
  user-select: all;
}
.secret--masked {
  letter-spacing: .04em;
  color: var(--text-faint);
  -webkit-user-select: none;
  user-select: none;
}

/* The API token is 64 characters of secret with two verbs attached — a block
   with its own affordances, not a <dd> in a list of one-line facts. */
.token {
  margin: 0 var(--space-4) var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.token__label {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--fs-200);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--text-muted);
}
.token__controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
.token__input.form-control {
  flex: 1 1 20rem;
  width: auto;
  min-width: 0;
  height: auto;
  padding: var(--space-1) var(--space-2);
  background-color: var(--surface);
  font-family: var(--font-mono);
  font-size: var(--fs-200);   /* 64 hex characters have to fit without truncating */
  color: var(--text-muted);
}
.token__input.form-control:focus { color: var(--text); }
.token__empty { color: var(--text-muted); font-size: var(--fs-400); }
.token__note {
  margin: var(--space-2) 0 0;
  font-size: var(--fs-200);
  color: var(--text-muted);
}

/* A chip you can take off. The × used to be a bordered btn-danger pill parked
   next to the word, so `admin ×` read as two separate controls; it now lives
   inside the chip's own box and only goes red on hover, when it is about to
   be the thing you click. */
.badge-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding-right: .1875rem;
  margin-bottom: var(--space-1);
  vertical-align: middle;
}
.badge-chip__meta { font-weight: var(--fw-regular); color: var(--text-muted); }
.badge-chip .btn-group > .btn { margin-left: var(--space-1); }
.btn.chip-x {
  padding: 0 .3125rem;
  margin: 0;
  border: 0;
  border-radius: var(--radius-xs);
  background: transparent;
  box-shadow: none;
  color: var(--text-faint);
  font-size: var(--fs-400);
  line-height: 1.3;
}
.btn.chip-x:hover,
.btn.chip-x:focus {
  background: var(--danger-fg);
  color: var(--text-inverse);
}
/* the badge level selector is a picker, not a warning — btn-warning made a
   routine dropdown the loudest thing in the section */
.btn.chip-level {
  padding: 0 .375rem;
  margin: 0;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-xs);
  background: var(--surface);
  color: var(--text-muted);
  font-size: var(--fs-100);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide);
  line-height: 1.5;
}
.btn.chip-level:hover,
.btn.chip-level:focus {
  background: var(--surface-hover);
  border-color: var(--text-faint);
  color: var(--text-strong);
}

/* A membership row can carry a name, a member count, a "not accepted" flag
   and two buttons. In a 352px aside that does not fit on one line, so the
   whole trailing group drops to a line of its own and stays right-aligned,
   rather than fragmenting around the team name. Without :has() the row simply
   does not wrap — the pre-existing behaviour, so the fallback is no worse. */
.panel__row:has(> .panel__row-aside) { flex-wrap: wrap; }
.panel__row-aside {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-left: auto;
}
.team-count {
  display: inline-flex;
  align-items: center;
  gap: .1875rem;
  font-size: var(--fs-200);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.team-count > .fas { font-size: .85em; color: var(--text-faint); }

/* The rail needs a value column beside it. Below 768px the panel is too
   narrow to hold label + value + rail, so the rail drops to its own line and
   keeps ONE left edge there instead — the page edge. */
@media (max-width: 767.98px) {
  .record-facts--acts { grid-template-columns: max-content minmax(0, 1fr); }
  .record-facts > dd.record-facts__act { grid-column: 1 / -1; }
}
@media (max-width: 575.98px) {
  .record-facts--acts { grid-template-columns: minmax(0, 1fr); gap: var(--space-1) 0; }
  .record-facts--acts > dt { padding-top: var(--space-2); }
  .record-facts > dd.record-facts__act {
    grid-column: 1;
    margin-bottom: var(--space-3);
  }
  .token { margin-left: var(--space-3); margin-right: var(--space-3); }
  .token__controls > .btn,
  .token__controls > .form-button > .btn { flex: 1 1 auto; }
}

/* task/problem.html — the statement reads at a measure, its tables do not */
.task-body { max-width: var(--measure-prose); }
.task-body table,
.task-body pre,
.task-body img { max-width: 100%; }
.task-body > table,
.task-body > pre { max-width: none; }

/* --- archetype G: the home page ----------------------------------------- */
.home-hero {
  /* break out of .container without adding a second wrapper to base/index */
  margin-inline: calc(50% - 50vw);
  /* cancel main.flex-shrink-0 > :first-child's navbar offset so the band
     starts at the navbar's own bottom edge; both sides are the same tokens */
  margin-top: calc(-1 * (var(--nav-h) + var(--space-5)));
  margin-bottom: var(--space-6);
  padding-inline: calc(50vw - 50%);   /* lands on .container's content edge */
  padding-top: calc(var(--nav-h) + var(--space-7));
  padding-bottom: var(--space-7);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--border);
}
.home-hero__inner { max-width: var(--measure-prose); }
.home-hero__title {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-900);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  line-height: var(--lh-tight);
  color: var(--text-strong);
}
.home-hero__lead {
  margin: 0 0 var(--space-5);
  font-size: var(--fs-600);
  line-height: var(--lh-normal);
  color: var(--text-muted);
}
.home-hero__cta { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* R-F8: one column. The aside that used to sit here repeated the nav tabs
   verbatim; with it gone the feed renders at the same measure as /news, which
   is the same template under the other endpoint. The wrapper classes stay so
   the home feed keeps a hook of its own. */
.home-layout { display: grid; grid-template-columns: minmax(0, 1fr); }
/* Cap the column, not just the items, so the feed's own header ("News" plus
   Create news) sits on the same right edge as each item's action rail. */
.home-layout__main { max-width: var(--measure-feed); }
.home-more { margin: var(--space-4) 0 0; }

@media (max-width: 575.98px) {
  .home-hero { padding-bottom: var(--space-5); padding-top: calc(var(--nav-h) + var(--space-5)); }
  .home-hero__cta > .btn { flex: 1 1 100%; }
  .section-head { flex-wrap: wrap; }
  /* R-F1 moved contest/contest.html's search into a .section-head; at phone
     width the inline form has to take the whole row rather than squeeze its
     field and button into whatever is left beside the heading. */
  .section-head > .form-search { width: 100%; }
  .section-head > .form-search .form-control { flex: 1 1 auto; max-width: none; width: auto; }
}

/* "exists but not visible to you" should read as de-emphasis, not as another
   status colour */
.hidden-problem {
  background-color: var(--surface-subtle);
  color: var(--text-muted);
  font-style: italic;
}

/* --- medal rows ----------------------------------------------------------
   main.css:387–409 declares all six with !important, so these must repeat it;
   equal specificity (0,2,1) + later source order then wins. Without it on the
   hover rules, medal rows would show no hover at all. */
tr.ranking-gold:nth-of-type(2n+1)   { background-color: #fbeec2 !important; }  /* beats main.css:399 !important */
tr.ranking-gold:nth-of-type(2n)     { background-color: #f7e6ad !important; }  /* beats main.css:387 !important */
tr.ranking-silver:nth-of-type(2n+1) { background-color: #eef0f2 !important; }  /* beats main.css:403 !important */
tr.ranking-silver:nth-of-type(2n)   { background-color: #e4e8ec !important; }  /* beats main.css:391 !important */
tr.ranking-bronze:nth-of-type(2n+1) { background-color: #f8e7d8 !important; }  /* beats main.css:407 !important */
tr.ranking-bronze:nth-of-type(2n)   { background-color: #f0d8c2 !important; }  /* beats main.css:395 !important */

/* medal identity moves from the fill to a 3px rail on the rank cell */
tr.ranking-gold   > td:first-child { box-shadow: inset 3px 0 0 #a07c17; }
tr.ranking-silver > td:first-child { box-shadow: inset 3px 0 0 #8a949e; }
tr.ranking-bronze > td:first-child { box-shadow: inset 3px 0 0 #9a5f2b; }

.table-hover tbody tr.ranking-gold:hover   { background-color: #f6e2a0 !important; }  /* beats main.css medal !important */
.table-hover tbody tr.ranking-silver:hover { background-color: #dfe3e8 !important; }  /* beats main.css medal !important */
.table-hover tbody tr.ranking-bronze:hover { background-color: #ebceb4 !important; }  /* beats main.css medal !important */

/* --- footer --------------------------------------------------------------- */
.footer {
  background-color: var(--surface-sunken);
  border-top: 1px solid var(--border);
  padding-top: var(--space-4);
  padding-bottom: var(--space-4);
  font-size: var(--fs-200);
  color: var(--text-muted);
}

.footer .row { align-items: center; }

.footer .text-muted { color: var(--text-muted) !important; }  /* beats .text-muted{color:#6c757d!important} — 5.38:1 on sunken */
.footer strong { color: var(--text); font-weight: var(--fw-semibold); }

#time {
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--text);
  letter-spacing: .01em;
}
#timezone { font-size: var(--fs-100); }

/* --- language switcher --------------------------------------------------- */
.footer .col-lg-3 {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-size: 0;                 /* collapses the &nbsp; spacers; they stay in the DOM */
}

.footer .form-button { display: inline-flex !important; }  /* beats .form-button{display:inline!important} (main.css:21) */

/* unselected locale: quiet ghost chip */
.footer .form-button > .btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: 1.75rem;
  padding: 0 var(--space-2);
  font-size: var(--fs-200);
  font-weight: var(--fw-medium);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: .04em;
  background-color: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted);
  border-radius: var(--radius-sm);
}

.footer .form-button > .btn:hover {
  background-color: var(--surface);
  border-color: var(--border-strong);
  color: var(--text);
}

/* selected locale */
.footer .form-button > .btn.btn-secondary {
  background-color: var(--accent-subtle);
  border-color: #c4d7f0;
  color: var(--accent-on-subtle);
  font-weight: var(--fw-semibold);
}

/* a hairline ring stops the white stripe of the Polish flag dissolving into
   the footer */
.footer .form-button > .btn img {
  height: 10px;
  width: auto;
  display: block;
  border-radius: 1px;
  box-shadow: 0 0 0 1px rgba(17,22,28,.12);
}


/* ==========================================================================
   11. RETUNED LEGACY VALUES FROM main.css
   ========================================================================== */

.buttons > .btn,
.buttons .pagination { vertical-align: middle; }

.code-editor-saved-string { color: var(--text-muted); }  /* was #777 — 4.48:1 */

.input-output-outdated { border: solid var(--warning-fill) 2px; }

div.user_organization {
  font-size: var(--fs-200);
  line-height: var(--lh-snug) !important;  /* beats div.user_organization{line-height:.9!important} (main.css:411) */
  color: var(--text-muted);
}

/* two-line Polish names with descenders were clipping at line-height 1.05 */
.unsafe_name,
.unsafe_name_strict {
  line-height: 1.25;
  max-height: 2.5em;
}

/* the ranking penalty stack is the one place that genuinely wants crushed
   leading; ranking.css:93 pins .problem_penalty div independently */
.ranking small,
.ranking .small { line-height: 1 !important; }  /* re-asserts the compact stack after the global small hack was removed from main.css */


/* ==========================================================================
   12. ACCESSIBILITY
   ========================================================================== */

/* Bootstrap 4.3.1 predates :focus-visible and paints a ring on mouse clicks. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}
:focus:not(:focus-visible) { outline: none; }

.btn:focus,
.btn.focus { box-shadow: none; outline: none; }
.btn:focus-visible { box-shadow: var(--shadow-focus); outline: none; }

.form-control:focus {
  border-color: var(--accent);
  box-shadow: var(--shadow-focus);
  outline: none;
}

.page-link:focus { box-shadow: none; }
.page-link:focus-visible { box-shadow: var(--shadow-focus); outline: none; z-index: 3; }

.dropdown-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* the dark navbar needs a light ring */
.navbar-dark .navbar-nav .nav-link:focus-visible,
.navbar-toggler:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 2px;
  box-shadow: none;
}

@media (prefers-contrast: more) {
  :root {
    --border: #9aa4b0;
    --border-subtle: #c3cad2;
    --text-muted: #434e59;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;    /* beats animate.css .animated animation-duration */
    animation-iteration-count: 1 !important;   /* beats animate.css */
    transition-duration: 0.001ms !important;   /* beats vendor transitions */
    scroll-behavior: auto !important;
  }
  .animated,
  .animate__animated { animation: none !important; }  /* beats animate.css 3.x and 4.x */
}
