/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 */

/*
 * Self-hosted fonts (Inter and Oswald, both SIL Open Font License 1.1),
 * downloaded from Google Fonts rather than loaded from their CDN. Only the
 * latin/latin-ext subsets are included -- this app's content is German, so
 * cyrillic/greek/vietnamese would just be dead weight. Both are variable
 * fonts (one file covers a whole weight range), hence the font-weight
 * ranges below instead of one @font-face per discrete weight.
 */

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/inter-latin-496a588b.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/assets/inter-latin-ext-5a6754fc.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Oswald";
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
  src: url("/assets/oswald-latin-ce25d91a.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Oswald";
  font-style: normal;
  font-weight: 500 700;
  font-display: swap;
  src: url("/assets/oswald-latin-ext-a3243b45.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/*
 * Design tokens. --color-accent is the app's one deliberately loud color
 * (currently BVB yellow, the club the original app was built around) --
 * kept as a single custom property, not hardcoded throughout, so it can
 * become per-user (favorite team) later without a rewrite.
 */

:root {
  color-scheme: light dark;

  --font-body: "Inter", system-ui, sans-serif;
  --font-display: "Oswald", system-ui, sans-serif;

  --color-accent: #fde100;
  --color-accent-text: #14141a;

  --color-bg: #f4f4f2;
  --color-surface: #ffffff;
  --color-surface-muted: #ececea;
  --color-text: #16161a;
  --color-text-muted: #5c5c63;
  --color-border: #dcdcd8;

  --color-success: #1e7e34;
  --color-danger: #c8102e;
  /* "Warning"/neutral-outcome color (currently only the draw/partial-points
     dots in Tippübersicht) -- goldenrod's own hue, not derived from
     --color-accent, since --color-accent is a much brighter brand yellow
     that was never meant to double as this and reads poorly under the
     white text these dots always carry. */
  --color-warning: #daa520;

  --tippuebersicht-background: var(--color-surface);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #131316;
    --color-surface: #1c1c21;
    --color-surface-muted: #232328;
    --color-text: #f2f2f0;
    --color-text-muted: #a3a3aa;
    --color-border: #313138;

    --color-success: #4caf50;
    --color-danger: #ff6b6b;
    --color-warning: #e8c252;
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--color-bg);
  color: var(--color-text);
  line-height: 1.5;
}

a {
  color: inherit;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.2;
  /* Browser default heading margins are large (~1.3rem+ top and bottom)
     and were never intentional here -- replaced with one deliberate,
     smaller bottom-only margin. */
  margin: 0 0 0.75rem;
}

h1 {
  text-transform: uppercase;
  letter-spacing: 0.03em;
  display: inline-block;
  padding-bottom: 0.4rem;
  border-bottom: 3px solid var(--color-accent);
}

table {
  border-collapse: collapse;
  width: 100%;
}

th, td {
  padding: 0.5rem 0.75rem;
  text-align: left;
  border-bottom: 1px solid var(--color-border);
}

button, input[type="submit"] {
  font-family: var(--font-body);
  font-size: 0.95rem;
  padding: 0.5rem 1rem;
  border-radius: 0.4rem;
  border: 1px solid var(--color-border);
  background: var(--color-surface-muted);
  color: var(--color-text);
  cursor: pointer;

  &:hover {
    border-color: var(--color-text-muted);
  }
}

input:not([type="number"]), select, textarea {
  font-family: var(--font-body);
  font-size: 0.95rem;
  padding: 0.45rem 0.6rem;
  border-radius: 0.4rem;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
}

input[type="number"] {
  font-family: var(--font-body);
  border: 1px solid var(--color-border);
  border-radius: 0.3rem;
  padding: 0.15rem;
  background: var(--color-surface);
  color: var(--color-text);
}

/* Persistent header/nav */

.app-header {
  background: var(--color-accent);
  color: var(--color-accent-text);
  position: sticky;
  top: 0;
  z-index: 100;
}

.app-header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 1.25rem;
}

.app-header__brand {
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-decoration: none;
  color: var(--color-accent-text);
}

.app-header__toggle {
  display: none;
  flex-direction: column;
  gap: 4px;
  background: none;
  border: 0;
  padding: 0.5rem;
  cursor: pointer;

  span {
    display: block;
    width: 22px;
    height: 2px;
    background: var(--color-accent-text);
  }
}

.app-nav {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  flex-wrap: wrap;
  padding: 0 1.25rem 0.75rem;
}

.app-nav__link {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: 0.95rem;
  color: var(--color-accent-text);
  text-decoration: none;
  padding: 0.4rem 0.75rem;
  border-radius: 0.3rem;
  cursor: pointer;

  &:hover {
    background: rgb(0 0 0 / 0.08);
  }

  &.app-nav__link--disabled {
    opacity: 0.5;
    cursor: not-allowed;

    &:hover {
      background: none;
    }
  }
}

.app-nav__submenu {
  position: relative;

  summary {
    list-style: none;

    &::-webkit-details-marker {
      display: none;
    }
  }
}

.app-nav__submenu-panel {
  position: absolute;
  top: 100%;
  left: 0;
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 0.4rem;
  box-shadow: 0 8px 24px rgb(0 0 0 / 0.18);
  padding: 0.5rem;
  min-width: 22rem;
  z-index: 10;
}

.app-nav__subsubmenu {
  summary {
    padding: 0.35rem 0.5rem;
    font-weight: 600;
    cursor: pointer;
    border-radius: 0.3rem;

    &:hover {
      background: var(--color-surface-muted);
    }
  }

  div {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding: 0.25rem 0.75rem 0.5rem;

    a {
      font-size: 0.9rem;
      text-decoration: underline;
    }
  }
}

.app-nav__logout-form {
  margin: 0;
}

.app-nav__logout {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: 0.95rem;
  background: none;
  border: 1px solid var(--color-accent-text);
  color: var(--color-accent-text);
  padding: 0.35rem 0.75rem;
  border-radius: 0.3rem;
  cursor: pointer;

  &:hover {
    background: rgb(0 0 0 / 0.08);
  }
}

@media (max-width: 768px) {
  .app-header__toggle {
    display: flex;
  }

  .app-nav {
    display: none;
    flex-direction: column;
    align-items: flex-start;
    padding: 0 1.25rem 1rem;

    &.is-open {
      display: flex;
    }
  }

  .app-nav__submenu-panel {
    position: static;
    box-shadow: none;
    border: 0;
    padding-left: 0.75rem;
    min-width: 0;
  }
}

/* Page/panel chrome shared by every view */

.page {
  max-width: 68rem;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 3rem;
}

.panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 0.6rem;
  padding: 1.5rem;
  box-shadow: 0 2px 10px rgb(0 0 0 / 0.06);
}

.flash {
  margin: 0 0 1rem;
  padding: 0.6rem 1rem;
  border-radius: 0.4rem;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
}

.flash--alert {
  border-color: var(--color-danger);
  color: var(--color-danger);
}

.flash--notice {
  border-color: var(--color-success);
  color: var(--color-success);
}

/* Tippabgabe */

.tippabgabe-opponent {
  display: inline-block;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: 999px;
  padding: 0.4rem 1rem;
  margin: 0 0 1rem;
  font-size: 0.95rem;
}

.matchday-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 1rem 0 1.5rem;
}

.matchday-nav__link {
  font-family: var(--font-display);
  font-weight: 600;
  text-decoration: none;
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 0.4rem;
  padding: 0.4rem 0.9rem;

  &:hover {
    border-color: var(--color-accent);
    background: var(--color-surface-muted);
  }
}

.match-group-heading {
  font-size: 1rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
  margin: 1.75rem 0 0.6rem;
}

.match-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.match-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 0.75rem;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: 0.5rem;
  padding: 0.6rem 1rem;

  &.match-row--locked {
    opacity: 0.75;
  }
}

.match-row__team {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-width: 0;

  &.match-row__team--home {
    justify-content: flex-end;
    text-align: right;
  }

  &.match-row__team--away {
    justify-content: flex-start;
    text-align: left;
  }
}

.match-row__name {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.match-row__name-short {
  display: none;
}

.match-row__logo, .match-row__logo--placeholder {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
}

.match-row__logo {
  object-fit: contain;
}

.match-row__score {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  flex-shrink: 0;
  position: relative;
}

.match-row__score-sep {
  color: var(--color-text-muted);
  font-weight: 600;
}

.match-row__score--final {
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 600;
  min-width: 4.5rem;
  text-align: center;
}

/* Selector is "input.match-row__input", not just ".match-row__input": the
   global `input[type="number"]` reset above has specificity (0,1,1), which
   beats a bare class (0,1,0) regardless of source order and was silently
   overriding the status background colors below. Adding the element type
   here (and so to every nested &.is-* rule) raises this past that reset. */
input.match-row__input {
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  text-align: center;
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 600;
  appearance: none;
  -moz-appearance: textfield;
  transition: background-color 0.4s ease, border-color 0.4s ease;

  &::-webkit-inner-spin-button,
  &::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
  }

  &:focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-accent) 40%, transparent);
  }

  /* Save status is conveyed by the input's own background/border (like
     the old app did), not a separate text badge next to it. */
  &.is-saving {
    background: color-mix(in srgb, var(--color-accent) 50%, var(--color-surface));
    border-color: var(--color-accent);
  }

  &.is-saved {
    background: color-mix(in srgb, var(--color-success) 45%, var(--color-surface));
    border-color: var(--color-success);
  }

  &.is-error {
    background: color-mix(in srgb, var(--color-danger) 45%, var(--color-surface));
    border-color: var(--color-danger);
  }
}

/* Tabelle */

/* table-layout: fixed hands the Name column exactly the space left over
   after the other columns, rather than the auto layout's content-based
   sizing -- which is what forced a max-width guess onto .tabelle-user
   that truncated names even when the table had room to spare. The other
   columns only ever hold a couple of digits, so they get small, explicit
   widths; Name, left unconstrained, absorbs 100% minus that fixed sum and
   is the only column that ever needs to ellipsize. (The "width: 1%" +
   nowrap trick some tables use for this doesn't apply here -- it's an
   auto-layout technique, and under table-layout: fixed it collapses
   those columns to near nothing instead of shrinking them to content.) */
.tabelle-table {
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
}

.tabelle-table th:nth-child(1),
.tabelle-table td:nth-child(1) {
  width: 2.25rem;
}

.tabelle-table th:nth-child(n+3),
.tabelle-table td:nth-child(n+3) {
  width: 2.75rem;
  white-space: nowrap;
}

.tabelle-table th {
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
  padding: 0.5rem 0.75rem;
  border-bottom: 2px solid var(--color-border);
}

.tabelle-table td {
  padding: 0.65rem 0.75rem;
}

.tabelle-table tbody tr {
  transition: background-color 0.15s ease;

  &:not(:last-child) td {
    border-bottom: 1px solid var(--color-border);
  }

  &:hover {
    background: var(--color-surface-muted);
  }

  &.tabelle-row--you {
    background: color-mix(in srgb, var(--color-accent) 12%, transparent);
  }
}

.tabelle-table th:nth-child(n+3),
.tabelle-table td:nth-child(n+3) {
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.tabelle-position {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--color-text-muted);
}

.tabelle-points {
  font-weight: 700;
}

.tabelle-user {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
}

.tabelle-avatar {
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.tabelle-username {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}

/* Ligatabelle */

/* Real standings need more columns than Tabelle (Sp/S/U/N/Tore/Diff/Punkte
   vs. just TF/TG/D/P), too many to force into a fixed-width table without
   crowding the team-name column on narrow screens -- so unlike Tabelle,
   this scrolls horizontally instead, same as Tippübersicht's wide grid. */
.ligatabelle-table-wrapper {
  overflow-x: auto;
}

.ligatabelle-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;

  th, td {
    white-space: nowrap;
  }

  th:nth-child(n+3),
  td:nth-child(n+3) {
    text-align: center;
    font-variant-numeric: tabular-nums;
  }

  th {
    font-family: var(--font-display);
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
    padding: 0.5rem 0.75rem;
    border-bottom: 2px solid var(--color-border);
  }

  td {
    padding: 0.65rem 0.75rem;
  }

  tbody tr {
    transition: background-color 0.15s ease;

    &:not(:last-child) td {
      border-bottom: 1px solid var(--color-border);
    }

    &:hover {
      background: var(--color-surface-muted);
    }
  }
}

.ligatabelle-position {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--color-text-muted);
}

.ligatabelle-points {
  font-weight: 700;
}

.ligatabelle-team {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  max-width: 14rem;
}

.ligatabelle-logo {
  width: 1.5rem;
  height: 1.5rem;
  object-fit: contain;
  flex-shrink: 0;
}

.ligatabelle-team-name-full,
.ligatabelle-team-name-short {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ligatabelle-team-name-short {
  display: none;
}

/*
 * Below ~640px, .page's padding plus .panel's own padding/border stacked
 * on top of it (a "card inside a card") was eating 88px of horizontal
 * space and ~48px of vertical space before any real content -- measured
 * on an iPhone 12 Pro (390px wide), team names inside a match row were
 * left with only 35px to render in. .page goes edge-to-edge (no padding
 * of its own) and .panel becomes the single full-width/full-height
 * container, carrying all the padding itself rather than splitting it
 * between two nested boxes; it also drops its border/radius/shadow so it
 * reads as the page's content area rather than a floating card.
 */
@media (max-width: 640px) {
  .page {
    padding: 0;
  }

  .panel {
    padding: 1.25rem 1rem 2rem;
    border-left: 0;
    border-right: 0;
    border-radius: 0;
    box-shadow: none;
  }

  .matchday-nav {
    margin: 0.5rem 0 1rem;
  }

  .match-group-heading {
    margin: 1.25rem 0 0.5rem;
  }

  .match-row {
    padding: 0.5rem 0.6rem;
    gap: 0.4rem;
  }

  .match-row__team {
    gap: 0.4rem;
  }

  .match-row__logo, .match-row__logo--placeholder {
    width: 26px;
    height: 26px;
  }

  .match-row__name-full {
    display: none;
  }

  .match-row__name-short {
    display: inline;
  }

  .tabelle-table th,
  .tabelle-table td {
    padding: 0.5rem 0.4rem;
  }

  .ligatabelle-table th,
  .ligatabelle-table td {
    padding: 0.5rem 0.4rem;
  }

  .ligatabelle-mobile-hide {
    display: none;
  }

  .ligatabelle-team-name-full {
    display: none;
  }

  .ligatabelle-team-name-short {
    display: inline;
  }
}

/* Login */

.auth-card {
  max-width: 22rem;
  margin: 0 auto;
  text-align: center;
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 1.5rem;
  text-align: left;
}

.auth-form__field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.auth-form__field label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text-muted);
}

.auth-form__field input:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-accent) 40%, transparent);
}

/* Selector is "input.auth-form__submit", not just ".auth-form__submit":
   the global "input[type=submit]" rule above has specificity (0,1,1),
   which beats a bare class (0,1,0) regardless of source order -- the same
   issue previously found with .match-row__input vs input[type=number]. */
input.auth-form__submit {
  margin-top: 0.5rem;
  padding: 0.6rem 1rem;
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-accent-text);

  &:hover {
    background: color-mix(in srgb, var(--color-accent) 85%, black);
    border-color: color-mix(in srgb, var(--color-accent) 85%, black);
  }
}

.auth-card__link {
  display: inline-block;
  margin-top: 1rem;
  font-size: 0.9rem;
  color: var(--color-text-muted);
}

.auth-form__errors {
  margin: 0;
  padding: 0.6rem 1rem;
  border: 1px solid var(--color-danger);
  border-radius: 0.4rem;
  background: var(--color-surface-muted);
  color: var(--color-danger);
  font-size: 0.9rem;
  list-style: none;
}

.auth-form__errors li + li {
  margin-top: 0.3rem;
}

/* Home dashboard */

.dashboard {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr 1fr 0.9fr;
  grid-template-areas:
    "reminder  reminder   sidebar"
    "bilanz    gegner     sidebar"
    "tabelle   extremes   sidebar"
    "tabelle   extremes   sidebar"
    "ergebnisse spieltag torschuetzen";
}

.dash-slot--reminder { grid-area: reminder; }
.dash-slot--bilanz { grid-area: bilanz; }
.dash-slot--gegner { grid-area: gegner; }
.dash-slot--tabelle { grid-area: tabelle; }
.dash-slot--extremes { grid-area: extremes; }
.dash-slot--spieltag { grid-area: spieltag; }
.dash-slot--ergebnisse { grid-area: ergebnisse; }
.dash-slot--sidebar { grid-area: sidebar; }
.dash-slot--torschuetzen { grid-area: torschuetzen; }

.dash-card {
  height: 100%;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: 0.5rem;
  padding: 1rem;
}

.dash-card__title {
  font-family: var(--font-display);
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
  margin: 0 0 0.75rem;
}

.dash-reminder {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  background: color-mix(in srgb, var(--color-accent) 15%, var(--color-surface));
  border-color: var(--color-accent);
}

.dash-reminder__text {
  font-size: 0.95rem;
}

.dash-reminder__link {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
  text-decoration: none;
  color: var(--color-text);
  border: 1px solid var(--color-accent);
  border-radius: 0.4rem;
  padding: 0.4rem 0.9rem;

  &:hover {
    background: var(--color-accent);
  }
}

.dash-stat-card__profile {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.dash-stat-card__profile--placeholder {
  margin-bottom: 0;
  opacity: 0.6;
}

.dash-stat-card__avatar {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  object-fit: cover;
}

.dash-stat-card__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.1rem;
}

.dash-stat-card__stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.75rem 1rem;
  margin: 0;

  dt {
    font-size: 0.75rem;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
  }

  dd {
    margin: 0;
    font-family: var(--font-display);
    font-size: 1.2rem;
    font-weight: 600;
  }
}

/* Overrides .dash-card's own "height: 100%" specifically for this card.
   It shares its 2-row grid span with the compact Tabelle card (unlike
   every other dash-card, which has its own single-row slot), and
   Tabelle's row count is dynamically sized in JS to match *this* card's
   rendered height (dynamic_tabelle_controller.js). If this card also
   stretched to 100% of the shared row track, the two would drive each
   other's height in a feedback loop: Tabelle reads this card's height to
   decide how many rows to reveal, more revealed rows grow the shared
   grid row, which (via height: 100%) grows this card right back,
   triggering another read -- converging on "show every row" instead of
   this card's own true content height. Left at its natural (auto)
   height, it's a stable, honest measurement for Tabelle to match.
*/
.dash-extreme-card {
  height: auto;
}

.dash-extremes__section {
  text-align: center;
}

.dash-extremes__section + .dash-extremes__section {
  margin-top: 1.125rem;
}

.dash-extreme-card__users {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem;
}

.dash-extreme-card__user {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  text-align: center;
}

.dash-extreme-card__avatar {
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  object-fit: cover;
}

.dash-extreme-card__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
}

.dash-extreme-card__more-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 50%;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--color-text-muted);
}

.dash-tabelle {
  width: 100%;
  border-collapse: collapse;
}

.dash-tabelle td {
  padding: 0.4rem 0.3rem;
  border-bottom: 1px solid var(--color-border);
}

.dash-tabelle tbody tr:last-child td {
  border-bottom: 0;
}

.dash-tabelle__row--you {
  background: color-mix(in srgb, var(--color-accent) 12%, transparent);
}

.dash-tabelle__omitted td {
  text-align: center;
  padding: 0.2rem 0.3rem;
  line-height: 1;
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--color-text-muted);
}

.dash-tabelle__position {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--color-text-muted);
  width: 1.75rem;
}

.dash-tabelle__user {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.dash-tabelle__avatar {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.dash-tabelle__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-tabelle__points {
  font-weight: 700;
  text-align: right;
}

.dash-ligatabelle {
  width: 100%;
  border-collapse: collapse;
}

.dash-ligatabelle td {
  padding: 0.35rem 0.3rem;
  border-bottom: 1px solid var(--color-border);
  font-size: 0.9rem;
}

.dash-ligatabelle tbody tr:last-child td {
  border-bottom: 0;
}

.dash-ligatabelle__position {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--color-text-muted);
  width: 1.5rem;
}

.dash-ligatabelle__team {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.dash-ligatabelle__logo {
  width: 1.25rem;
  height: 1.25rem;
  object-fit: contain;
  flex-shrink: 0;
}

.dash-ligatabelle__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-ligatabelle__points {
  font-weight: 700;
  text-align: right;
}

.dash-torschuetzen {
  width: 100%;
  border-collapse: collapse;
}

.dash-torschuetzen td {
  padding: 0.35rem 0.3rem;
  border-bottom: 1px solid var(--color-border);
  font-size: 0.9rem;
}

.dash-torschuetzen tbody tr:last-child td {
  border-bottom: 0;
}

.dash-torschuetzen__position {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--color-text-muted);
  width: 1.5rem;
}

.dash-torschuetzen__scorer {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.dash-torschuetzen__logo {
  width: 1.25rem;
  height: 1.25rem;
  object-fit: contain;
  flex-shrink: 0;
}

.dash-torschuetzen__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-torschuetzen__goals {
  font-weight: 700;
  text-align: right;
}

/* The list itself is the grid (not each <li>) so the 3 columns are sized
   once across every row, not independently per row -- an <li> sizing its
   own "auto" score/date column let that column's width vary row to row
   (e.g. "2:0" vs "10:2", or a longer date string), which pushed the two
   "1fr" side columns to different widths per row and threw off the home
   team's flex-end alignment. display: contents drops each <li>'s own box
   so its children become direct grid items of the list.
   The two team columns are a fixed, equal width (not "1fr") and the grid
   is centered via justify-content, rather than stretched to the card's
   full width -- "1fr" was claiming half the card each, leaving a big gap
   between the home logo (flex-end within that wide column) and the
   date/score column instead of sitting close to it. */
.dash-match-list {
  display: grid;
  grid-template-columns: 7rem auto 7rem;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.85rem;
}

.dash-match-list__item {
  display: contents;
}

.dash-match-list__team {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-width: 0;

  /* "&--home"/"&--away" (bare suffix concatenation, no leading ".") is
     Sass syntax, not valid native CSS nesting -- "--home" alone isn't a
     parseable selector fragment, so the browser silently dropped both
     rules, leaving justify-content at its default ("normal", effectively
     flex-start) for both sides regardless of which one this was. */
  &.dash-match-list__team--home {
    justify-content: flex-end;
    text-align: right;
  }

  &.dash-match-list__team--away {
    justify-content: flex-start;
    text-align: left;
  }
}

.dash-match-list__logo {
  width: 1.25rem;
  height: 1.25rem;
  object-fit: contain;
  flex-shrink: 0;
}

.dash-match-list__name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dash-match-list__score {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  white-space: nowrap;
  text-align: center;
}

@media (max-width: 640px) {
  .dashboard {
    grid-template-columns: 1fr;
    grid-template-areas:
      "reminder"
      "bilanz"
      "gegner"
      "tabelle"
      "extremes"
      "ergebnisse"
      "spieltag"
      "sidebar"
      "torschuetzen";
  }
}

.map-fullscreen {
  position: fixed !important;
  top: 0;
  left: 0;
  width: 100vw !important;
  height: 100vh !important;
  z-index: 10000;
}

body.map-fullscreen-open {
  overflow: hidden;
}

/*
 * Tippübersicht grid. Ported from an old React implementation's CSS. Team
 * crop tuning below originally keyed off class selectors, but several of
 * our real Team#slug values start with a digit (e.g. "1-fc-koln"), which
 * isn't a valid CSS class identifier — so team matching uses a
 * [data-team-slug] attribute selector instead. The original also nested
 * ".left"/".right" without "&", which (as plain CSS nesting) targets
 * *descendant* elements — but the "animation-name" override inside
 * ".right" below only makes sense if ".left"/".right" are additional
 * classes on the *same* element as their parent, so this port uses
 * explicit "&.left"/"&.right" compound-class nesting throughout.
 * --tippuebersicht-background is defined once, near the top of this file,
 * as part of the shared design tokens (theme-aware there).
 */

.tippuebersicht-table-wrapper {
  overflow: auto hidden;
  scroll-timeline-name: --tippuebersicht-table-x;
  scroll-timeline-axis: inline;
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
}

.tippuebersicht-table {
  --first-column-width: 150px;
  --column-width: 140px;
  --border-width: 2px;
  --header-padding: 8px;
  --border: var(--border-width) solid black;
  --row-height: 48px;
  /* --column-count is set inline per-render (see the view), since the
     number of player columns varies by matchday/season rather than being
     fixed at 9 as in the original. */
  --table-width: calc(var(--first-column-width) + var(--column-count, 9) * (var(--column-width) + var(--border-width)));

  width: var(--table-width);
  font-size: 16px;
  border-collapse: separate;
  border: 0;
  border-spacing: 0;
  background: var(--tippuebersicht-background);
  grid-area: 1 / 1;

  th, td {
    position: relative;
    padding: 0;
  }

  td {
    border-bottom: 0;
    vertical-align: top;

    div {
      margin-top: 4px;
      width: 50%;
      float: left;
      text-align: center;
    }
  }

  /* sticky first column */
  th:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--tippuebersicht-background);
  }

  /* borders */
  th:not(:last-child),
  td:not(:last-child) {
    border-right: var(--border);
  }

  thead tr:last-child th,
  tbody tr:not(:last-child) th,
  tbody tr:nth-last-child(2) td
  {
    border-bottom: var(--border);
  }

  /* measurements */
  col {
    width: calc(var(--column-width) + var(--border-width));

    &:first-child {
      width: calc(var(--first-column-width) + var(--border-width));
    }

    &:last-child {
      width: var(--column-width);
    }
  }

  tbody tr {
    height: calc(var(--row-height) + var(--border-width));

    &:last-child {
      height: var(--row-height);
    }
  }

  /* player names padding and alignment */
  thead tr:first-child th {
    text-align: left;
    padding-left: var(--header-padding);
    padding-top: var(--header-padding);
    padding-bottom: calc(var(--header-padding) / 2);
  }

  thead tr:nth-child(2) th {
    text-align: right;
    padding-right: var(--header-padding);
    padding-bottom: var(--header-padding);
    padding-top: calc(var(--header-padding) / 2);
  }

  svg {
    position: absolute;
    top: 0;
    z-index: 1;
    left: 0;
    font-weight: 400;

    text {
      font-size: 1.75em;
      dominant-baseline: middle;
      text-anchor: middle;
      font-family: Verdana, sans-serif;
      stroke-linejoin: round;

      &.small {
        font-weight: bold;
        font-size: .75em;
      }
    }
  }
}

.team-icon {
  --s: var(--row-height);

  width: var(--s);
  height: var(--s);
  background: var(--tippuebersicht-background, #f5f5f5);
  overflow: hidden;
  top: 0;
  display: grid;
  position: absolute;
  place-items: center;

  &.right {
    right: 0;
  }

  /* stylelint-disable declaration-block-single-line-max-declarations */
  &.left[data-team-slug="eintracht-frankfurt"]        { --z: 1.2;   --x: -10; --y: 0;   }
  &.right[data-team-slug="eintracht-frankfurt"]        { --z: 1.2;   --x: 10;  --y: 0;   }
  &.left[data-team-slug="bayer-04-leverkusen"]         { --z: 1.4;   --x: -6;  --y: 0;   }
  &.right[data-team-slug="bayer-04-leverkusen"]        { --z: 1.4;   --x: 6;   --y: 0;   }
  &.left[data-team-slug="fc-bayern-munchen"]           { --z: 1.2;   --x: -10; --y: 0;   }
  &.right[data-team-slug="fc-bayern-munchen"]          { --z: 1.2;   --x: 10;  --y: 0;   }
  &.left[data-team-slug="1-fsv-mainz-05"]              { --z: 1.3;   --x: -12; --y: 0;   }
  &.right[data-team-slug="1-fsv-mainz-05"]             { --z: 1.3;   --x: 12;  --y: 0;   }
  &.left[data-team-slug="1-fc-union-berlin"]           { --z: 2;     --x: 45;  --y: 0;   }
  &.left[data-team-slug="borussia-monchengladbach"]    { --z: 1.4;   --x: -14; --y: -30; }
  &.right[data-team-slug="borussia-monchengladbach"]   { --z: 1.4;   --x: 14;  --y: -30; }
  &.left[data-team-slug="sc-freiburg"]                 { --z: 1.15;  --x: -6;  --y: -18; }
  &.right[data-team-slug="sc-freiburg"]                { --z: 1.15;  --x: 6;   --y: -18; }
  &.left[data-team-slug="hamburger-sv"]                { --z: 1.5;   --x: 0;   --y: 0;   }
  &.left[data-team-slug="rb-leipzig"]                  { --z: 1.9;   --x: -4;  --y: 0;   }
  &.right[data-team-slug="rb-leipzig"]                 { --z: 1.9;   --x: 4;   --y: 0;   }
  &.left[data-team-slug="tsg-hoffenheim"]              { --z: 1.2;   --x: -10; --y: -8;  }
  &.right[data-team-slug="tsg-hoffenheim"]             { --z: 1.2;   --x: 10;  --y: -8;  }
  &.left[data-team-slug="sv-werder-bremen"]            { --z: 1.2;   --x: -9;  --y: -24; }
  &.right[data-team-slug="sv-werder-bremen"]           { --z: 1.2;   --x: 9;   --y: -24; }
  &.left[data-team-slug="fc-augsburg"]                 { --z: 1.05;  --x: -4;  --y: -6;  }
  &.right[data-team-slug="fc-augsburg"]                { --z: 1.05;  --x: 4;   --y: -6;  }
  &.left[data-team-slug="borussia-dortmund"]           { --z: 1.15;  --x: -4;  --y: 0;   }
  &.right[data-team-slug="borussia-dortmund"]          { --z: 1.15;  --x: 4;   --y: 0;   }
  &.left[data-team-slug="vfb-stuttgart"]               { --z: 1.2;   --x: -4;  --y: 4;   }
  &.right[data-team-slug="vfb-stuttgart"]              { --z: 1.2;   --x: 4;   --y: 4;   }
  &.left[data-team-slug="1-fc-koln"]                   { --z: 1.9;   --x: 26;  --y: -55; }
  &.right[data-team-slug="1-fc-koln"]                  { --z: 1.9;   --x: 42;  --y: -55; }
  /* stylelint-enable */
}

.team-icon-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  transform-origin: center;
  transform:
    translate(
      calc(var(--x, 0) * var(--s) / 100),
      calc(var(--y, 0) * var(--s) / 100)
    )
    scale(var(--z, 1));
  filter: contrast(.8) saturate(.8) brightness(1.1);
}

@keyframes tippuebersicht-table-fade-left {
  0% { opacity: 0; }
  10% { opacity: 1; }
  100% { opacity: 1; }
}

@keyframes tippuebersicht-table-fade-right {
  0% { opacity: 1; }
  90% { opacity: 1; }
  100% { opacity: 0; }
}

.tippuebersicht-table-fade { display: none }

@supports (scroll-timeline-name: --t) and (animation-timeline: --t) {
  @media (width < 1400px) {
    .tippuebersicht-table-fade {
      display: block;
      position: sticky;
      z-index: 1;
      width: 20px;
      height: 100%;
      animation-timeline: --tippuebersicht-table-x;
      animation-name: tippuebersicht-table-fade-left;
      grid-area: 1 / 1;

      &.left {
        background: linear-gradient(to right, black, rgb(0 0 0 / 0%));
        left: 141px;
      }

      &.right {
        animation-name: tippuebersicht-table-fade-right;
        background: linear-gradient(to left, black, rgb(0 0 0 / 0%));
        right: 0;
        float: right;
        margin-left: auto;
      }
    }
  }
}

/* Settings */

.settings-section {
  max-width: 32rem;
  background: var(--color-surface-muted);
  border: 1px solid var(--color-border);
  border-radius: 0.5rem;
  padding: 1.25rem;
  margin-bottom: 1.5rem;
}

.settings-section__title {
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
  margin: 0 0 1rem;
}

.settings-section__hint {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin: 0 0 1rem;
}

.avatar-picker {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  flex-wrap: wrap;
}

.avatar-picker__current {
  width: 5rem;
  height: 5rem;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--color-border);
}

.avatar-cropper__stage {
  width: 280px;
  max-width: 100%;
  margin-bottom: 1rem;
}

.avatar-cropper__stage img {
  display: block;
  max-width: 100%;
}

/* Cropper.js's own crop box is a plain square; rounding its view box and
   face turns it into the fixed circular window users pan/zoom an image
   behind (see avatar_cropper_controller.js). The uploaded file stays a
   plain square PNG regardless -- same as every other avatar in the app,
   all displayed circular via a plain border-radius: 50% elsewhere
   (.avatar-picker__current above, .dash-stat-card__avatar, etc). */
.avatar-cropper .cropper-view-box,
.avatar-cropper .cropper-face {
  border-radius: 50%;
}

.avatar-cropper__field {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.avatar-cropper__field label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text-muted);
}

.avatar-cropper__field input[type="range"] {
  flex: 1;
}

.avatar-cropper__actions {
  display: flex;
  gap: 0.75rem;
}

.avatar-cropper__save,
.avatar-cropper__cancel {
  font-family: var(--font-display);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.6rem 1rem;
  border-radius: 0.4rem;
  cursor: pointer;
}

.avatar-cropper__save {
  border: 1px solid var(--color-accent);
  background: var(--color-accent);
  color: var(--color-accent-text);

  &:hover {
    background: color-mix(in srgb, var(--color-accent) 85%, black);
    border-color: color-mix(in srgb, var(--color-accent) 85%, black);
  }
}

.avatar-cropper__cancel {
  border: 1px solid var(--color-border);
  background: none;
  color: var(--color-text);

  &:hover {
    background: var(--color-surface);
  }
}
