/*
 * The one stylesheet for every page referees use (the Django admin keeps its own).
 *
 * The design language is the "Tableau d'affichage" scoreboard chosen from the
 * card mockups: a dark ground, dark panels, amber for what matters now, red for
 * what is off, condensed capitals for labels. Everything below is built from
 * the tokens in section 2, and pages compose the shared components of section
 * 4 rather than carrying styles of their own.
 *
 *   1. Fonts        self-hosted Barlow, see ../fonts/README.md
 *   2. Tokens       colour, type, spacing, shape
 *   3. Base         document, text, links, focus
 *   4. Components   header, page, panel, facts, messages, forms, buttons,
 *                   tabs, status line, match cards, steps
 */

/* 1. Fonts ------------------------------------------------------------------ */

@font-face {
  font-family: "Barlow";
  src: url("../fonts/barlow-latin-400.81419bf5abb5.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Barlow";
  src: url("../fonts/barlow-latin-500.8e986e86d57e.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Barlow";
  src: url("../fonts/barlow-latin-600.a1e2a2d7d4c0.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Barlow Condensed";
  src: url("../fonts/barlow-condensed-latin-500.24f826a7cf9c.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Barlow Condensed";
  src: url("../fonts/barlow-condensed-latin-600.ba4513495282.woff2") format("woff2");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Barlow Condensed";
  src: url("../fonts/barlow-condensed-latin-700.e1180918b873.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}

/* 2. Tokens ----------------------------------------------------------------- */

:root {
  /* A single dark look, by design: the scoreboard does not have a day mode.
     color-scheme itself is set on the site's own containers, not here; see
     section 3. */
  --bg: #0b0d10;
  --surface: #161a20;
  --surface-sunken: #0d1014;
  --border: #262c35;
  --border-strong: #2b323c;

  /* Form fields: a shade lighter than the ground, with a quiet outline. */
  --field-bg: #14181e;
  --field-border: #2e3541;
  --field-border-hover: #3b4350;
  --field-height: 52px;
  /* One line of help or error text under each field, reserved in advance. */
  --message-line: 20px;

  --text: #e9edf2;
  --text-muted: #9aa4b1;
  --text-faint: #6f7a88;

  /* Amber: what matters now (the next match, the main action, focus). */
  --accent: #ffb627;
  --accent-glow: rgb(255 182 39 / 0.16);
  --on-accent: #14171c;
  /* Red: what is off (cancelled, refused, wrong). */
  --danger: #ff5d5d;
  --danger-glow: rgb(255 93 93 / 0.14);
  /* Green: what just worked (a successful check). */
  --success: #3ccf7e;

  /* One colour per federation, for league badges. */
  --fed-sbl: #5b3cc4;
  --fed-swb: #d0262f;
  --fed-afbb: #1d63c8;
  --fed-avb: #12805a;

  --font-display: "Barlow Condensed", "Arial Narrow", sans-serif;
  --font-text: "Barlow", system-ui, sans-serif;
  --tracking-label: 0.08em;
  --tracking-eyebrow: 0.16em;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  --radius: 8px;
  --radius-small: 6px;
  --touch: 44px;
  --page-width: 40rem;
}

/* 3. Base ------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

/*
 * Dark form controls, but only inside the site's own containers. Declared on
 * the whole document, it made browser extensions' frames (Bitwarden's autofill
 * menu and save dialog) paint an opaque white backdrop: a frame whose colour
 * scheme differs from the page it sits in loses its transparency.
 */
.site-header,
.page {
  color-scheme: dark;
}

html {
  scrollbar-color: var(--border-strong) var(--bg);
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-text);
  font-size: 16px;
  line-height: 1.45;
  -webkit-text-size-adjust: 100%;
}

h1,
h2,
h3 {
  margin: 0;
  font-family: var(--font-display);
  text-transform: uppercase;
  text-wrap: balance;
}

h1 {
  font-size: 30px;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.05;
}

h2 {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  color: var(--text-muted);
}

p {
  margin: 0;
}

a {
  color: var(--text);
  text-decoration-color: var(--accent);
  text-underline-offset: 3px;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}

/* 4. Components -------------------------------------------------------------- */

/*
 * Site header: brand, navigation, and the sign-out action set apart from it.
 * On a phone the brand and sign-out share the first row, at opposite ends,
 * and the navigation takes the row below; wider screens fit all on one row.
 */
.site-header {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "brand account"
    "nav nav";
  align-items: center;
  gap: var(--space-3) var(--space-4);
  padding: var(--space-3) var(--space-4);
  padding-top: calc(var(--space-3) + env(safe-area-inset-top, 0px));
  border-bottom: 1px solid var(--border);
}

@media (min-width: 40rem) {
  .site-header {
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "brand nav account";
    gap: var(--space-5);
  }
}

.site-header > .brand {
  grid-area: brand;
  justify-self: start;
}

.site-nav {
  grid-area: nav;
}

.site-account {
  grid-area: account;
  justify-self: end;
  margin: 0;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-display);
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
}

.brand::before {
  content: "";
  width: 12px;
  height: 12px;
  border-radius: 2px;
  background: var(--accent);
}

.site-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-4);
}

.site-nav a {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
  text-decoration: none;
}

.site-nav a:hover {
  color: var(--text);
}

.site-nav a[aria-current="page"] {
  color: var(--accent);
}

/* Page body: one centred column, phone first. */
.page {
  display: grid;
  gap: var(--space-5);
  max-width: var(--page-width);
  margin-inline: auto;
  padding: var(--space-5) var(--space-4) var(--space-6);
  padding-bottom: calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
}

.page--narrow {
  --page-width: 24rem;
  padding-top: 12vh;
}

.page-intro {
  display: grid;
  gap: var(--space-2);
}

.lead {
  color: var(--text-muted);
  max-width: 60ch;
}

/* A small amber caption above something, like "Prochain match". */
.eyebrow {
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--accent);
}

.eyebrow--danger {
  color: var(--danger);
}

/* Panel: the scoreboard surface everything sits on. */
.panel {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.panel--highlight {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 0 26px var(--accent-glow);
}

.panel--alert {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px var(--danger), 0 0 26px var(--danger-glow);
}

/* Facts: label and value pairs, such as a status. */
.facts {
  display: grid;
  grid-template-columns: minmax(9rem, max-content) 1fr;
  gap: var(--space-2) var(--space-4);
  margin: 0;
}

.facts dt {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
  padding-top: 1px;
}

.facts dd {
  margin: 0;
}

.facts .is-ok {
  color: var(--accent);
}

.facts .is-error {
  color: var(--danger);
}

@media (max-width: 30rem) {
  .facts {
    grid-template-columns: 1fr;
    gap: 0;
  }

  .facts dd + dt {
    margin-top: var(--space-3);
  }
}

/* Messages: confirmations after an action. */
.messages {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.message {
  padding: var(--space-3) var(--space-4);
  background: var(--surface);
  border: 1px solid var(--accent);
  border-radius: var(--radius);
}

.message--error {
  border-color: var(--danger);
}

/*
 * Forms: styles Django's own form output ({{ form }}: one <div> per field
 * holding label, help text, errors and input, in that order), so any form
 * looks right without markup of its own.
 *
 * Layout: the label above a large outlined field, then one message line.
 *
 * An error must not move anything when it appears, so every field reserves
 * that line, and the help text and the error share it: while there is an
 * error the help stays in place but invisible, keeping its height. Screen
 * readers still reach both through the field's aria-describedby, which Django
 * sets. The placement is by grid area, whatever order Django writes them in.
 */
.form {
  display: grid;
  gap: var(--space-4);
}

.form > div {
  display: grid;
  grid-template-areas: "label" "field" "message";
  grid-template-rows: auto auto minmax(var(--message-line), auto);
  row-gap: var(--space-2);
}

.form > div > label {
  grid-area: label;
}

.form > div > :is(input, select, textarea) {
  grid-area: field;
}

.form > div > :is(.helptext, .errorlist) {
  grid-area: message;
}

.form > div:has(> .errorlist) > .helptext {
  visibility: hidden;
}

/* The form-wide error ("wrong login or password") goes after the submit
   button, where nothing follows it that it could push down. */
.form > .errorlist.nonfield {
  order: 1;
}

.form label {
  font-size: 15px;
  font-weight: 500;
  color: var(--text-muted);
}

.form input:is([type="text"], [type="password"], [type="email"], [type="number"]),
.form select,
.form textarea {
  width: 100%;
  min-height: var(--field-height);
  padding: var(--space-3) var(--space-4);
  background: var(--field-bg);
  border: 1px solid var(--field-border);
  border-radius: var(--radius-small);
  color: var(--text);
  /* 16px keeps phones from zooming into the field. */
  font: 500 16px/1.3 var(--font-text);
  transition: border-color 0.15s, box-shadow 0.15s;
}

.form :is(input, select, textarea):hover {
  border-color: var(--field-border-hover);
}

.form :is(input, select, textarea):focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

/* Same specificity as the field rule above, and later, so it wins. */
.form :is(input, select, textarea)[aria-invalid="true"] {
  border-color: var(--danger);
}

.form :is(input, select, textarea)[aria-invalid="true"]:focus-visible {
  box-shadow: 0 0 0 3px var(--danger-glow);
}

.form .helptext {
  font-size: 14px;
  color: var(--text-faint);
}

/* A checkbox sits before its label, with the message line under the label. */
.form > div:has(> input[type="checkbox"]) {
  grid-template-areas: "field label" ". message";
  grid-template-columns: auto 1fr;
  grid-template-rows: auto minmax(var(--message-line), auto);
  gap: var(--space-1) var(--space-3);
  align-items: center;
}

.form > div:has(> input[type="checkbox"]) > label {
  color: var(--text);
}

.form input[type="checkbox"] {
  width: 22px;
  height: 22px;
  margin: 0;
  accent-color: var(--accent);
}

.errorlist {
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--danger);
  font-size: 14px;
}

.errorlist.nonfield {
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}

/* Buttons. */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--touch);
  padding: var(--space-2) var(--space-5);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: var(--radius-small);
  color: var(--on-accent);
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
}

.button:hover {
  filter: brightness(1.08);
}

.button svg {
  width: 18px;
  height: 18px;
  flex: none;
}

/* A secondary action: outlined, never competing with the amber main one. */
.button--quiet {
  background: transparent;
  border-color: var(--field-border-hover);
  color: var(--text);
}

.button--quiet:hover {
  filter: none;
  border-color: var(--text-muted);
}

.button--small {
  gap: var(--space-2);
  min-height: 40px;
  padding: var(--space-1) var(--space-3);
  font-size: 15px;
  font-weight: 600;
}

.button--block {
  width: 100%;
}

/* An action that just worked: green, with a check mark before the label. */
.button--success {
  gap: var(--space-2);
  border-color: var(--success);
  color: var(--success);
}

.button--success:hover {
  border-color: var(--success);
}

/* An action under way (see js/app.js): a spinner before the label. */
.button[aria-busy="true"] {
  gap: var(--space-2);
  cursor: progress;
}

/* Running it again: the earlier success no longer holds. */
.button--success[aria-busy="true"] {
  border-color: var(--field-border-hover);
  color: var(--text);
}

.button[aria-busy="true"]::before {
  content: "";
  width: 16px;
  height: 16px;
  flex: none;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Actions: a row of buttons, with an optional hint beside them. */
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
}

.hint {
  margin: 0;
  font-size: 14px;
  color: var(--text-faint);
}

/* Tabs: sibling views of one page, such as upcoming and past matches. */
.tabs {
  display: flex;
  gap: var(--space-5);
  border-bottom: 1px solid var(--border);
}

.tabs a {
  margin-bottom: -1px;
  padding: var(--space-2) 0 10px;
  border-bottom: 2px solid transparent;
  color: var(--text-muted);
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-decoration: none;
  text-transform: uppercase;
}

.tabs a:hover {
  color: var(--text);
}

.tabs a[aria-current="page"] {
  border-bottom-color: var(--accent);
  color: var(--text);
}

/* Status line: one quiet line on how fresh the page's data is. */
.status-line {
  margin: 0;
  font-size: 14px;
  color: var(--text-muted);
}

.status-line--error {
  color: var(--danger);
}

.empty {
  margin: var(--space-5) 0;
  color: var(--text-muted);
  text-align: center;
}

/*
 * Match cards: the scoreboard. The date panel on the right has a fixed width,
 * so cards line up whatever their content; amber marks the next match, red a
 * match that is off (cancelled by hand, or no longer assigned by basketplan).
 */
.match-list {
  display: grid;
  gap: var(--space-3);
}

.match {
  --fed: var(--text-faint);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "flag flag"
    "tags when"
    "teams when"
    "venue when"
    "colleagues action";
  gap: 6px var(--space-3);
  padding: var(--space-3) 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-family: var(--font-display);
}

.match p {
  margin: 0;
}

.match[data-fed="sbl"] {
  --fed: var(--fed-sbl);
}

.match[data-fed="swb"] {
  --fed: var(--fed-swb);
}

.match[data-fed="afbb"] {
  --fed: var(--fed-afbb);
}

.match[data-fed="avb"] {
  --fed: var(--fed-avb);
}

.match-when {
  grid-area: when;
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: start;
  width: 92px;
  padding: 7px 10px 8px;
  background: var(--surface-sunken);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-small);
  line-height: 1.05;
  text-transform: uppercase;
}

.match-weekday,
.match-month {
  font-size: 12px;
  letter-spacing: 0.14em;
  color: var(--text-muted);
}

.match-day {
  font-size: 36px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #f4f6f8;
  text-shadow: 0 0 10px rgb(244 246 248 / 0.18);
}

.match-time {
  width: 100%;
  margin-top: 6px;
  padding-top: 5px;
  border-top: 1px solid var(--border-strong);
  font-size: 20px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  text-align: center;
  color: var(--accent);
  text-shadow: 0 0 10px rgb(255 182 39 / 0.3);
}

.match-flag {
  grid-area: flag;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--accent);
}

.match-tags {
  grid-area: tags;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) 6px;
  font-size: 13px;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}

.match-fed {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-weight: 700;
  color: var(--text);
}

.match-fed::before {
  content: "";
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: var(--fed);
}

.match-role {
  color: var(--accent);
}

.match-teams {
  grid-area: teams;
  display: flex;
  flex-direction: column;
  font-size: 21px;
  font-weight: 600;
  line-height: 1.1;
  text-transform: uppercase;
  overflow-wrap: anywhere;
}

.match-vs {
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0.2em;
  opacity: 0.6;
}

.match-venue {
  grid-area: venue;
}

.match-colleagues {
  grid-area: colleagues;
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Tapped on a phone: tall enough to hit, without making the card taller. */
.match-venue,
.match-colleague {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: fit-content;
  max-width: 100%;
  min-height: 32px;
  font-family: var(--font-text);
  font-size: 14px;
  color: #c2cad4;
  text-decoration: none;
}

a.match-venue:hover,
a.match-colleague:hover {
  color: var(--text);
  text-decoration: underline;
}

.match-venue svg,
.match-colleague svg {
  width: 15px;
  height: 15px;
  flex: none;
}

.match-colleague--missing {
  color: var(--text-faint);
}

.match-action {
  grid-area: action;
  align-self: end;
  justify-self: end;
}

.match-action button {
  min-height: 32px;
  padding: 0;
  background: none;
  border: 0;
  font-family: var(--font-text);
  font-size: 13px;
  color: var(--text-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

.match-action button:hover {
  color: var(--text);
}

.match--next {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent), 0 0 26px var(--accent-glow);
}

.match--next .match-when {
  border-color: var(--accent);
  box-shadow: 0 0 12px var(--accent-glow);
}

/* The red counterpart of the amber "next" treatment. */
.match--off {
  border-color: var(--danger);
  box-shadow: 0 0 0 1px var(--danger), 0 0 26px var(--danger-glow);
}

.match--off .match-when {
  border-color: var(--danger);
  box-shadow: 0 0 12px var(--danger-glow);
}

.match--off .match-when > *,
.match--off .match-teams {
  text-decoration: line-through;
  text-decoration-thickness: 2px;
  text-decoration-color: var(--danger);
}

.match--off .match-day,
.match--off .match-time,
.match--off .match-teams {
  color: var(--text-faint);
  text-shadow: none;
}

.match--off .match-flag {
  color: var(--danger);
}
/* Steps: numbered instructions, such as subscribing a calendar. */
.steps {
  display: grid;
  gap: var(--space-2);
  margin: 0;
  padding-left: var(--space-5);
}

.steps li::marker {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--accent);
}