/* =============================================================================
   UltiGameMate — authored global stylesheet
   Single source for everything that is NOT an app-shell component.
   Loaded after tokens.css, before components.css.
   App screens (sidebar + .app-main) are styled in components.css.
   Dark theme, committed. All values come from tokens.css.
   ============================================================================= */

/* --- 1. reset + base --------------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  color-scheme: dark;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-block-size: 100dvh;
  display: flex;
  flex-direction: column;
  overflow-x: clip; /* safety net: nothing should scroll the page sideways */
  background: var(--bg-page);
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: var(--fz-body);
  font-weight: 400;
  line-height: var(--lh-body);
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
svg,
video {
  max-inline-size: 100%;
  block-size: auto;
}

::selection {
  background: color-mix(in srgb, var(--accent-hi) 30%, transparent);
  color: var(--text-primary);
}

::placeholder {
  color: var(--text-muted);
}

:focus-visible {
  outline: 2px solid var(--accent-hi);
  outline-offset: var(--space-3xs);
}

@view-transition {
  navigation: auto;
}

/* --- 2. typography (global — app screens included) ------------------------- */
h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  line-height: var(--lh-snug);
  letter-spacing: 0;
  text-transform: none;
  color: var(--text-heading);
  text-wrap: balance;
}

h1 {
  font-size: var(--fz-2xl);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
}

h2 {
  font-size: var(--fz-xl);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
}

h3 {
  font-size: var(--fz-lg);
}

h4 {
  font-size: var(--fz-md);
}

h5,
h6 {
  font-size: var(--fz-body);
}

p {
  margin: 0;
}

strong,
b {
  color: var(--text-primary);
  font-weight: 700;
}

small,
.fz-sm {
  font-size: var(--fz-sm);
}

sup {
  font-size: 0.62em;
  line-height: 0;
  vertical-align: super;
  font-family: var(--font-mono);
}

code,
kbd,
samp {
  font-family: var(--font-mono);
  font-size: 0.92em;
}

main:not(.app-main) .articles ul {
  margin-block-end: var(--space-2xl);
}

/* prose blocks inside content pages + un-migrated app content pages
   (a bare <section> straight inside .app-main: rules / leagues / profile) */
main:not(.app-main) :is(p, li),
.app-main > section:not([class]) :is(p, li) {
  font-family: var(--font-body);
  font-size: var(--fz-body);
  line-height: var(--lh-body);
  text-wrap: pretty;
}

.app-main > section:not([class]) {
  display: flow-root;
}

.app-main > section:not([class]) p {
  margin-block: 0 var(--flow-md);
}

.app-main > section:not([class]) :is(ul, ol) {
  margin-block: 0 var(--flow-md);
  padding-inline-start: var(--indent-list);
}

.app-main > section:not([class]) li {
  margin-block: var(--flow-3xs) 0;
}

.app-main > section:not([class]) li::marker {
  color: var(--accent-lo);
}

.app-main > section:not([class]) :is(h2, h3, h4) {
  margin-block: var(--flow-lg) var(--flow-2xs);
}

.app-main > section:not([class]) > :first-child {
  margin-block-start: 0;
}

main:not(.app-main) {
  --flow: var(--flow-md);
}

main:not(.app-main) h1 {
  margin-block-end: var(--flow-3xs);
}

/* h2 carries the section break now that <hr> is gone */
main:not(.app-main) h2 {
  margin-block: var(--flow-xl) var(--flow-2xs);
}

main:not(.app-main) :is(h1, h2) + h2 {
  margin-block-start: var(--flow-sm);
}

main:not(.app-main) h3 {
  margin-block: var(--flow-lg) var(--flow-3xs);
}

main:not(.app-main) h4 {
  margin-block: var(--flow-md) var(--flow-3xs);
}

main:not(.app-main) p {
  margin-block: 0 var(--flow);
}

main:not(.app-main) :is(ul, ol) {
  margin-block: 0 var(--flow);
  padding-inline-start: var(--indent-list);
}

main:not(.app-main) li {
  margin-block: var(--flow-3xs) 0;
}

main:not(.app-main) li::marker {
  color: var(--accent-lo);
}

/* --- 3. links — two treatments ------------------------------------------- */
a {
  color: var(--accent-hi);
  text-decoration: none;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
  transition: color var(--dur) var(--ease);
}

a:is(:hover, :focus-visible) {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
}

/* links inside flowing text always carry an underline — but not buttons or
   action links that happen to sit inside a <p> (e.g. <p><a class="btn">…) */
:is(main, section, dialog, .app-main)
  :is(p, li)
  a:not(.btn, .cta, .link, [class*='btn--'], [class*='cta--']) {
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, var(--accent-hi) 40%, transparent);
}

/* keep component links flat regardless of surrounding context */
:is(main, section, dialog, .app-main) :is(p, li) :is(a.btn, a.cta, a.link) {
  text-decoration: none;
}

/* chrome links (nav / footer): muted, colour shift only */
:is(.site-nav, .site-footer__nav, header nav, footer nav) a {
  color: var(--text-secondary);
  text-decoration: none;
}

:is(.site-nav, .site-footer__nav, header nav, footer nav) a:is(:hover, :focus-visible) {
  color: var(--text-primary);
  text-decoration: none;
}

/* standalone action link ("View all scores →") — one class */
.link {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-xs);
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-family: var(--font-body);
  font-weight: 600;
  color: var(--accent-hi);
  text-decoration: none;
  text-transform: none;
  cursor: pointer;
}

.link:is(:hover, :focus-visible) {
  text-decoration: underline;
}

.link--sm {
  font-size: var(--fz-sm);
}

.link--muted {
  color: var(--text-secondary);
}

.link--muted:is(:hover, :focus-visible) {
  color: var(--text-primary);
}

.link svg {
  inline-size: 1em;
  block-size: 1em;
}

/* --- 4. buttons — one system ------------------------------------------- */
.btn,
.cta:not(.cta--x) {
  --_bg: var(--bg-elevated);
  --_fg: var(--text-primary);
  --_bd: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-sm);
  min-inline-size: 0;

  /* Not margin-block-start: this shorthand's specificity (.cta:not(.cta--x),
     two classes) beats the admin/app spacing rule's margin-block-start
     (components.css, one class + one type selector), silently cancelling it
     and sticking the button to whatever precedes it. Leaving block-start
     unset here means nothing competes with that rule's declaration; the
     initial value (0) still applies everywhere this component's own rule
     doesn't override it. */
  margin-inline: 0;
  margin-block-end: 0;
  padding: var(--pad-md) var(--pad-2xl);
  border: var(--border-hairline) solid var(--_bd);
  border-radius: var(--radius-sm);
  background: var(--_bg);
  color: var(--_fg);
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 600;
  line-height: 1;
  letter-spacing: var(--tracking-label);
  text-transform: none;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    filter var(--dur) var(--ease),
    background var(--dur) var(--ease),
    border-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}

/* Hover: a glow ring in the button's own fill colour + a touch brighter.
   No transform — a moving target flickers when the cursor approaches from below. */
.btn:is(:hover, :focus-visible),
.cta:not(.cta--x):is(:hover, :focus-visible) {
  filter: brightness(1.12);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--_bg) 35%, transparent);
  text-decoration: none;
}

/* ghost / secondary buttons fill in on hover (transparent → solid) */
.btn--ghost:is(:hover, :focus-visible),
.cta--alt:is(:hover, :focus-visible),
.cta--inline:is(:hover, :focus-visible),
.cta--ghost:is(:hover, :focus-visible) {
  --_bg: var(--bg-elevated);
  --_fg: var(--text-primary);
}

.btn:active,
.cta:not(.cta--x):active {
  filter: brightness(1);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--_bg) 50%, transparent);
}

.btn:disabled,
.btn[disabled],
.cta:disabled,
.cta[disabled] {
  opacity: 0.5;
  filter: none;
  cursor: not-allowed;
}

.btn--primary,
.cta:not(.cta--alt, .cta--inline, .cta--share, .cta--ghost, .cta--x) {
  --_bg: var(--accent-hi);
  --_fg: var(--accent-ink);
  font-weight: 700;
}

.btn--primary:is(:hover, :focus-visible),
.cta:not(.cta--alt, .cta--inline, .cta--share, .cta--ghost, .cta--x):is(:hover, :focus-visible) {
  --_bg: var(--accent-bright);
  filter: none;
}

.btn--ghost,
.cta--alt,
.cta--inline,
.cta--ghost {
  --_bg: transparent;
  --_fg: var(--text-secondary);
  --_bd: var(--line-strong);
}

.btn--ghost:is(:hover, :focus-visible),
.cta--alt:is(:hover, :focus-visible),
.cta--ghost:is(:hover, :focus-visible) {
  --_fg: var(--text-primary);
}

.btn--sm,
.cta--small {
  padding: var(--pad-sm) var(--pad-xl);
  font-size: var(--fz-xs);
}

.btn--lg,
.cta--medium {
  padding: var(--pad-lg) var(--pad-3xl);
  font-size: var(--fz-body);
}

.btn--block,
.cta--block {
  display: flex;
  inline-size: 100%;
}

/* content-page CTAs (marketing / rules / calculator) read bigger than app chrome */
main:not(.app-main) :is(.btn, .cta):not(.btn--sm, .cta--small, .cta--x, .cta--inline, .icon-btn) {
  padding: var(--pad-xl) var(--pad-3xl);
  font-size: var(--fz-md);
}

.btn--sticky,
.cta--sticky {
  position: sticky;
  inset-block-end: var(--space-md);
}

.cta--share {
  --_bg: transparent;
  --_fg: var(--accent-hi);
  --_bd: color-mix(in srgb, var(--accent) 55%, transparent);
  border-radius: var(--radius-pill);
  padding: var(--pad-sm) var(--pad-xl);
  font-size: var(--fz-xs);
}

:is(.btn, .cta):has(img) img,
:is(.btn, .cta) svg {
  inline-size: 1.15em;
  block-size: 1.15em;
  flex: none;
}

/* transient states set by window.runButton / flashButton */
.cta.done,
.cta.error,
.cta.running,
.btn.done,
.btn.error,
.btn.running {
  position: relative;
  color: transparent;
  pointer-events: none;
  cursor: default;
}

.cta.done::after,
.cta.error::after,
.cta.running::after,
.btn.done::after,
.btn.error::after,
.btn.running::after {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--_fg, var(--text-primary));
}

.cta.done::after,
.btn.done::after {
  content: '✔ Done';
}

.cta.error::after,
.btn.error::after {
  content: '✕ Error';
}

.cta.running::after,
.btn.running::after {
  content: '…';
}

/* small square icon button — banner dismiss, dialog close */
.icon-btn,
.cta--x,
.close-x {
  display: inline-grid;
  place-items: center;
  inline-size: 2.25rem;
  block-size: 2.25rem;
  flex: none;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: var(--fz-md);
  line-height: 1;
  cursor: pointer;
  transition:
    background var(--dur) var(--ease),
    color var(--dur) var(--ease);
}

.icon-btn:is(:hover, :focus-visible),
.cta--x:is(:hover, :focus-visible),
.close-x:is(:hover, :focus-visible) {
  background: var(--bg-elevated);
  color: var(--text-primary);
}

.icon-btn svg,
.cta--x svg,
.close-x svg {
  inline-size: var(--icon-md);
  block-size: var(--icon-md);
}

/* --- 5. forms ---------------------------------------------------------- */
:is(input, select, textarea):not([type='checkbox'], [type='radio']) {
  inline-size: 100%;
  max-inline-size: 30rem;
  margin: 0;
  padding: var(--pad-md) var(--pad-lg);
  background: var(--bg-input);
  color: var(--text-primary);
  border: var(--border-hairline) solid var(--line-strong);
  border-radius: var(--radius-sm);
  font: inherit;
  font-family: var(--font-body);
}

textarea {
  max-inline-size: none;
  min-block-size: 6rem;
  resize: vertical;
}

:is(input, select, textarea):focus-visible {
  outline: 0;
  border-color: var(--accent-hi);
  box-shadow: var(--ring);
}

:is(input, select, textarea)[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}

label {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fz-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-over);
  text-transform: uppercase;
  color: var(--text-secondary);
}

label + :is(input, select, textarea) {
  margin-block-start: var(--space-2xs);
}

select {
  appearance: none;
  background-image: var(
    --caret,
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%239595a0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
  );
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  background-size: 1.15rem;
  padding-inline-end: var(--space-2xl);
}

input[type*='datetime']::-webkit-calendar-picker-indicator {
  filter: invert(0.7);
  cursor: pointer;
}

input:is([type='checkbox'], [type='radio']) {
  accent-color: var(--accent);
  inline-size: 1.15rem;
  block-size: 1.15rem;
  margin: 0;
  cursor: pointer;
}

/* Label wrapping its own checkbox — keeps box and text on one baseline. */
main:not(.app-main) .combobox__list {
  padding-block: var(--space-xs);
  padding-inline: var(--space-xs);
}

main:not(.app-main) .combobox__list li {
  margin: 0;
}

.checkbox-label {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  cursor: pointer;
}

.checkbox-label:has(input:disabled) {
  opacity: 0.55;
  cursor: not-allowed;
}

fieldset {
  margin: 0 0 var(--space-lg);
  padding: var(--space-lg);
  border: var(--border-hairline) solid var(--line);
  border-radius: var(--radius-md);
  background: var(--bg-card);
}

fieldset:has(.column) {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-md) var(--space-lg);
}

@media (max-width: 34rem) {
  fieldset:has(.column) {
    grid-template-columns: 1fr;
  }
}

legend {
  padding: 0;
  grid-column: 1 / -1;
  font-family: var(--font-body);
  font-size: var(--fz-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-over);
  text-transform: uppercase;
  color: var(--text-secondary);
}

.column {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  min-inline-size: 0;
}

.column.separator {
  border-block-start: var(--border-hairline) solid var(--line);
  margin-block-start: var(--space-xs);
  padding-block-start: var(--space-lg);
}

.column small {
  color: var(--text-muted);
  font-size: var(--fz-xs);
}

form
  :is(input, select, textarea, .cta, .btn, button)
  + :is(input, select, textarea, .cta, .btn, button) {
  margin-block-start: var(--space-sm);
}

/* --- 6. tables ------------------------------------------------------ */
.twrap {
  inline-size: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin-block: var(--space-lg);
  border-radius: var(--radius-md);
  background: var(--bg-card);
}

/* a table hidden while its data loads (or because there's nothing to show)
   collapses its wrapper too, instead of leaving an empty styled box on screen */
.twrap:has(> table.hidden) {
  display: none;
}

.twrap table {
  inline-size: 100%;
  min-inline-size: 28rem;
  border-collapse: collapse;
  table-layout: auto;
  font-family: var(--font-body);
  font-size: var(--fz-sm);
}

.twrap :is(th, td) {
  padding: var(--pad-lg) var(--pad-xl);
  text-align: start;
  vertical-align: middle;
}

.twrap :is(th, td):first-child {
  white-space: nowrap;
}

.twrap th {
  color: var(--text-secondary);
  font-size: var(--fz-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-over);
  text-transform: uppercase;
  white-space: nowrap;
}

.twrap td {
  color: var(--text-body);
}

.twrap :is(thead, tbody) tr + tr :is(th, td),
.twrap tbody tr:first-child :is(th, td) {
  border-block-start: var(--border-hairline) solid var(--line);
}

.twrap tbody tr:hover {
  background: color-mix(in srgb, var(--bg-elevated) 60%, transparent);
}

.twrap .m,
.twrap td[colspan] {
  text-align: end;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.twrap :is(.current-user, tr.current-user) td {
  background: var(--accent-wash);
  color: var(--text-primary);
  font-weight: 700;
}

.twrap td img {
  display: inline-block;
  inline-size: 1.6rem;
  block-size: 1.6rem;
  vertical-align: middle;
}

.twrap .cta,
.twrap .btn {
  margin: 0;
}

/* empty table: show a resting message instead of a bare header */
.twrap table:not(:has(tbody tr)) thead {
  opacity: 0.5;
}

/* --- 7. announcement / install banner ----------------------------- */
.banner {
  border: 0;
  background: var(--accent-wash);
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  text-align: center;
}

.banner--warning {
  background: var(--gold-wash);
}

.banner--error {
  background: color-mix(in srgb, var(--neg) 14%, transparent);
}

/* Floating card, bottom-right, clear of the mobile tab bar and the toast stack
   (see .toast__wrapper lift below). Used by the PWA install prompt
   (partials/install.liquid) and the onboarding walkthrough prompt
   (built in src/js/walkthrough.js). */
.banner--sticky {
  position: fixed;
  z-index: 45;
  inset-block-start: auto;
  inset-block-end: calc(var(--tabbar-h) + var(--space-md));
  inset-inline-start: auto;
  inset-inline-end: var(--space-md);
  inline-size: min(22rem, calc(100vw - 2 * var(--space-md)));
  background: var(--bg-elevated);
  border: var(--border-hairline) solid var(--line-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  text-align: start;
}

@media (min-width: 64rem) {
  .banner--sticky {
    /* no tab bar on desktop */
    inset-block-end: var(--space-lg);
    inset-inline-end: var(--space-lg);
  }
}

.banner__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs) var(--space-sm);
  inline-size: min(var(--w-wide), 100%);
  margin-inline: auto;
  padding: 0.55rem var(--pad-inline);
}

.banner--sticky .banner__inner {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: var(--space-sm);
  inline-size: auto;
  margin: 0;
  padding: var(--space-md);

  /* keep the first line of text clear of the corner close button */
  padding-inline-end: calc(var(--space-md) + 1.5rem);
}

.banner--sticky .banner__message {
  color: var(--text-body);
  font-size: var(--fz-sm);
  line-height: var(--lh-ui);
}

.banner--sticky .banner__btn {
  flex: none;
}

.banner--sticky .banner__dismiss {
  position: absolute;
  inset-block-start: var(--space-2xs);
  inset-inline-end: var(--space-2xs);
  inline-size: 1.75rem;
  block-size: 1.75rem;
}

.banner--sticky .banner__dismiss svg {
  inline-size: var(--icon-sm);
  block-size: var(--icon-sm);
}

/* When the install prompt is already occupying the bottom-right slot, stack the
   walkthrough prompt above it (mirrors the .toast__wrapper lift). */
body:has(.banner--sticky#install-banner:not([style*='none'])) #walkthrough-banner {
  inset-block-end: calc(var(--tabbar-h) + var(--space-md) + 8.5rem);
}

@media (min-width: 64rem) {
  body:has(.banner--sticky#install-banner:not([style*='none'])) #walkthrough-banner {
    inset-block-end: calc(var(--space-lg) + 8.5rem);
  }
}

.banner__inner :is(p, a) {
  margin: 0;
  display: inline;
}

.banner__inner a {
  color: var(--accent-hi);
  text-decoration: underline;
}

/* --- 8. public header -------------------------------------------- */
.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: 30;
  border: 0;
  background: color-mix(in srgb, var(--bg-page) 82%, transparent);
  backdrop-filter: blur(10px);
}

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm) var(--space-lg);
  inline-size: min(var(--w-wide), 100%);
  margin-inline: auto;
  padding: 0.8rem var(--pad-inline);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  text-decoration: none;
  color: var(--text-primary);
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
}

.brand :is(svg, img) {
  inline-size: 1.5rem;
  block-size: 1.5rem;
  border-radius: var(--radius-xs);
  color: var(--accent-hi);
}

.brand__name {
  font-size: var(--fz-md);
}

/* "Ulti·Game·Mate" — highlight the middle so the wordplay reads */
.brand__accent {
  color: var(--accent-hi);
}

.site-header nav.site-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) clamp(0.75rem, 0.3rem + 1.6vw, 1.75rem);
  margin-inline-start: auto;
}

/* when the header wraps: brand ↔ Sign in on row 1, nav centred on its own row */
@media (max-width: 44rem) {
  .site-header__inner {
    row-gap: var(--space-xs);
    padding-block: var(--space-xs);
  }

  .site-header nav.site-nav {
    order: 1;
    flex-basis: 100%;
    margin-inline-start: 0;
    justify-content: center;
  }

  .site-header .btn {
    margin-inline-start: auto;
  }
}

.site-header .site-nav a {
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 600;
}

/* current-page indicator */
.site-header .site-nav a[aria-current='page'] {
  color: var(--accent-hi);
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 0.4em;
  text-decoration-color: currentColor;
}

.site-header .btn {
  flex: none;
}

/* Bottom rule under any non-site-header <header> — in practice just .page-head
   on app/admin screens now that the legacy admin header (nav-app.liquid) is
   gone. */
header:not(.site-header) {
  border-block-end: var(--border-hairline) solid var(--line);
}

/* --- 9. footers ------------------------------------------------ */
.site-footer {
  margin-block-start: var(--space-3xl);
  border: 0;
  background: var(--bg-sunken);
}

.site-footer__inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  inline-size: min(var(--w-wide), 100%);
  margin-inline: auto;
  padding: var(--space-2xl) var(--pad-inline);
}

.site-footer .brand__name {
  font-size: var(--fz-sm);
}

.site-footer__brand {
  margin-block-end: var(--flow-xs);
}

.site-footer__tag {
  margin: var(--flow-3xs) 0 var(--flow-2xs);
  color: var(--text-secondary);
  font-size: var(--fz-sm);
}

.site-footer__nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs) var(--space-lg);
  margin-block: var(--gap-2xs) var(--flow-2xs);
}

.site-footer__nav a {
  font-family: var(--font-body);
  font-size: var(--fz-sm);
}

.site-footer__legal {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fz-xs);
}

.site-footer__legal a {
  color: var(--text-secondary);
}

/* app footer (footer-app.liquid: <footer> > <div> > logo + text columns).
   Only app pages get this footer; public pages get .site-footer. */
footer:not(.site-footer) {
  border-block-start: var(--border-hairline) solid var(--line);
  background: var(--bg-sunken);
}

footer:not(.site-footer) > div {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-md) var(--space-lg);
  inline-size: 100%;
  max-inline-size: var(--w-wide);
  margin-inline: auto;
  padding: var(--space-xl) var(--pad-inline);

  /* The mobile tab bar is position:fixed and floats over whatever's beneath
     it, including this footer once the page is scrolled all the way down —
     without this, its last row of links sits underneath the bar and can't
     be tapped. Reset below alongside the bar itself disappearing. */
  padding-block-end: calc(var(--tabbar-h) + var(--space-xl));
}

/* on desktop the app content is left-anchored beside the sidebar — align the
   footer's inner content to the same left edge (bg still spans full width) */
@media (min-width: 64rem) {
  footer:not(.site-footer) > div {
    max-inline-size: none;
    margin-inline: 0;
    padding-inline: calc(var(--sidebar-w) + clamp(2rem, 1rem + 3vw, 4rem))
      clamp(2rem, 1rem + 3vw, 4rem);
    padding-block-end: var(--space-xl);
  }
}

footer:not(.site-footer) img {
  inline-size: clamp(4rem, 3rem + 4vw, 5.5rem);
  block-size: auto;
  opacity: 0.9;
}

footer:not(.site-footer) div div {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
}

footer:not(.site-footer) p {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fz-sm);
}

/* onboarding tour clip — framed like a card so it reads as a screen capture,
   not a stray image. Only ever inside the walkthrough dialog
   (src/_includes/partials/walkthrough-dialog.liquid); the dashboard and the two
   How-to-play pages open that dialog rather than embedding the clip inline. */
.tour-clip {
  overflow: hidden;
  max-inline-size: 40rem;
  border-radius: var(--radius-md);
  background: var(--bg-card);
}

.tour-clip :is(img, video) {
  display: block;
  inline-size: 100%;
  block-size: auto;
}

/* --- 10. results / calculator vocabulary (.picks/.pick/.game/...) --- */
.picks,
.cards {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  margin-block: var(--space-md) 0;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: var(--space-sm);
  align-items: start;
}

.pick,
.select {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs) var(--space-md);
  margin: 0;
  padding: var(--space-md);
  border: 0;
  border-radius: var(--radius-md);
  background: var(--bg-card);
}

.select {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.game,
.pick .game,
.select .game {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-sm);
  inline-size: auto;
  margin-inline: auto;
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  font-weight: 600;
  text-align: start;
}

/* inside a table, .game shrink-wraps to its own row's content width and is
   then centered within the cell — since content width varies row to row
   (team name length), each row's vs/score ends up centered on a different
   point instead of lining up down the column. Stretch it to the full cell
   width so the flexible name spans absorb that difference instead, keeping
   vs/score at one fixed horizontal position for every row. */
.twrap .game {
  inline-size: 100%;
  margin-inline: 0;
}

.game:has(span:nth-child(5)) {
  display: grid;
  grid-template-columns: 1fr auto auto auto 1fr;
  align-items: center;
}

.game.round {
  grid-column: 1 / -1;
  inline-size: 100%;
  min-block-size: 3.5rem;
  padding: var(--space-sm) var(--space-md);
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
}

.game:is(button) {
  cursor: pointer;
}

.game:is(button):is(:hover, :focus-visible) {
  color: var(--accent-hi);
}

.game span {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* the two team names are the only elements guaranteed to always be present
   (crest icons are conditional on clubIcon() finding a match) — giving them
   equal flex so the vs/score group between them stays centered regardless
   of name length or whether a crest rendered on either side */
.game span:first-of-type,
.game span:last-of-type {
  flex: 1 1 0;
  min-inline-size: 0;
}

.game span:first-of-type {
  text-align: end;
}

.game span:last-of-type {
  text-align: start;
}

.game img {
  flex: none;
  inline-size: 1.4em;
  block-size: 1.4em;
}

.game small {
  color: var(--text-muted);
  font-family: var(--font-body);
  font-weight: 400;
}

.game :is(strong) {
  color: var(--text-primary);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.score {
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

/* --- result card (home "example scores", results page) -------------------- */
.result-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  margin-block: var(--space-md) 0;
}

.result-card {
  display: grid;
  gap: var(--space-sm);
  inline-size: 100%;
  margin: 0;
  padding: var(--space-md) var(--space-lg);
  border: var(--border-hairline) solid transparent;
  border-radius: var(--radius-md);
  background: var(--bg-card);
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
  transition:
    background var(--dur) var(--ease),
    border-color var(--dur) var(--ease);
}

.result-card:is(:hover, :focus-visible) {
  background: var(--bg-elevated);
  border-color: var(--line-strong);
}

.result-card__head {
  text-align: center;
}

.result-card__stage {
  font-family: var(--font-body);
  font-size: var(--fz-xs);
  font-weight: 600;
  letter-spacing: var(--tracking-over);
  text-transform: uppercase;
  color: var(--text-secondary);
  text-align: center;
}

.result-card__date {
  font-weight: 400;
  letter-spacing: 0;
  color: var(--text-muted);
}

.result-card__match {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-sm) var(--space-md);
}

.result-card__team {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  min-inline-size: 0;
  font-family: var(--font-body);
  font-size: var(--fz-md);
  font-weight: 600;
  color: var(--text-primary);
}

.result-card__team:first-child {
  flex-direction: row-reverse;
}

/* team crest — a soft light halo (not a solid disc) so a dark-on-transparent
   logo (e.g. Spurs' navy crest) still separates from the dark page, without
   painting a flat chip behind every icon. No size here on purpose:
   window.clubIcon() (base.liquid) stamps this class onto every crest it
   renders, across many contexts that each already size their own icons
   (.game img, .pick-option__team img, .twrap td img, etc.) via HTML
   width/height or their own CSS — this only adds the glow. */
.crest {
  flex: none;
  object-fit: contain;
  mix-blend-mode: exclusion;
  filter: brightness(1.5);
}

/* crest inside a .game row (match-detail dialog title) — beat the generic
   `.game img` size rule and give it a touch more presence */
.game .crest {
  inline-size: 1.75rem;
  block-size: 1.75rem;
}

.result-card__score {
  padding: var(--pad-3xs) var(--pad-md);
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  font-family: var(--font-mono);
  font-size: var(--fz-lg);
  font-weight: 700;
  color: var(--text-heading);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.result-card:is(:hover, :focus-visible) .result-card__score {
  background: var(--bg-card);
}

/* points badge fills in solid + pops on hover (transform only — no reflow) */
.result-card:is(:hover, :focus-visible) .points {
  background: var(--pos);
  color: var(--bg-sunken);
  transform: scale(1.08);
}

.result-card__note {
  margin: 0;
  font-size: var(--fz-sm);
  color: var(--text-secondary);
  line-height: var(--lh-ui);
  text-align: center;
}

.points {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-2xs);
  margin: 0;
  padding: var(--pad-2xs) var(--pad-md);
  border-radius: var(--radius-pill);
  background: var(--pos-wash);
  color: var(--pos);
  font-family: var(--font-mono);
  font-size: var(--fz-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  transition:
    background var(--dur) var(--ease),
    color var(--dur) var(--ease),
    transform var(--dur) var(--ease);
}

.points::before {
  content: none;
}

.pills {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-block: 0 var(--space-md);
}

.pill,
.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-xs);
  margin: 0;
  padding: var(--pad-xs) var(--pad-lg);
  border: var(--border-hairline) solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--bg-card);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: var(--fz-xs);
  font-weight: 600;
  cursor: pointer;
}

.pill:has(input:checked),
.tag.active {
  background: var(--accent-wash);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  color: var(--accent-hi);
}

.pill input,
.select input[type='checkbox'],
.cards input[type='checkbox'] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

:is(.select, .pill):has(input[disabled]) {
  opacity: 0.55;
  filter: grayscale(0.5);
}

.card {
  border: 0;
  border-radius: var(--radius-md);
  background: var(--bg-card);
  padding: var(--space-md);
}

.card:has(input:checked) {
  background: var(--accent-wash);
  outline: var(--border-hairline) solid color-mix(in srgb, var(--accent) 55%, transparent);
}

.card time {
  font-size: var(--fz-xs);
  color: var(--text-muted);
}

.boost {
  margin: 0;
  padding: var(--pad-xs) var(--pad-lg);
  border: var(--border-hairline) solid var(--line);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--accent-hi);
  font-family: var(--font-body);
  font-size: var(--fz-xs);
  font-weight: 600;
  cursor: pointer;
}

.boost:is(:hover, :focus-visible) {
  background: var(--accent-wash);
}

.boost.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

details {
  border: var(--border-hairline) solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--pad-lg) var(--pad-xl);
  background: var(--bg-card);
  font-family: var(--font-body);
}

details[open] {
  padding-block-end: var(--flow-sm);
}

summary {
  font-family: var(--font-body);
  font-weight: 600;
  color: var(--text-primary);
  cursor: pointer;
}

.twrap:has(.saving) {
  position: relative;
  opacity: 0.7;
  pointer-events: none;
}

.twrap:has(.saving)::after {
  content: 'Saving…';
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: color-mix(in srgb, var(--bg-card) 88%, transparent);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  animation: ugm-pulse 1.2s ease-in-out infinite;
}

@keyframes ugm-pulse {
  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.55;
  }
}

/* --- 12. toast -------------------------------------------------- */
.toast__wrapper {
  position: fixed;
  inset-block-end: calc(var(--tabbar-h) + var(--space-md));
  inset-inline: 0;
  z-index: 60;
  display: grid;
  justify-content: center;
  pointer-events: none;
}

/* Fire several toasts in quick succession (e.g. auto-save "Saved." while picking
   matches fast) and a vertical column would climb the screen. Instead pile them
   in one grid cell: newest in front at full size, each older one nudged up and
   scaled back, the 4th and beyond hidden. */
.toast__wrapper > .toast {
  grid-area: 1 / 1;
  align-self: end;
  justify-self: center;
  z-index: 1;
  transform-origin: bottom center;
  transition:
    transform 0.3s cubic-bezier(0.34, 1.4, 0.64, 1),
    visibility 0.3s;
}

.toast__wrapper > .toast:nth-last-child(2) {
  transform: translateY(-10px) scale(0.93);
}

.toast__wrapper > .toast:nth-last-child(3) {
  transform: translateY(-18px) scale(0.86);
}

.toast__wrapper > .toast:nth-last-child(n + 4) {
  transform: translateY(-24px) scale(0.8);
  visibility: hidden;
}

/* Below the sidebar breakpoint the install card spans nearly the full width
   just above the tab bar, so lift the toast stack above it while it's showing.
   (JS clears the card's inline style to "" when visible, "display: none" while
   hidden.) On desktop the card sits in the bottom-right corner and the toasts
   are bottom-centre, so no lift is needed there. */
@media (max-width: 63.99rem) {
  body:has(.banner--sticky:not([style*='none'])) .toast__wrapper {
    inset-block-end: calc(var(--tabbar-h) + var(--space-md) + 7.5rem);
  }
}

.toast {
  max-inline-size: min(24rem, calc(100vw - 2 * var(--space-md)));
  padding: var(--pad-md) var(--pad-2xl);
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: var(--border-hairline) solid var(--line-strong);
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--fz-sm);
  text-align: center;
  box-shadow: var(--shadow-lg);
}

.toast--error {
  border-color: var(--neg);
}

.toast--success {
  border-color: var(--accent);
}

/* --- 13. generic dialogs (score-dialog lives in components.css) --- */
dialog:not(.score-dialog) {
  margin: auto;
  border: 0;
  border-radius: var(--radius-lg);
  background: var(--bg-card);
  color: var(--text-body);
  box-shadow: var(--shadow-lg);
  padding: var(--space-lg);
  inline-size: min(34rem, calc(100vw - 2 * var(--space-md)));
}

/* The match-stats edit dialog packs several fieldsets and six action buttons
   in — the generic 34rem width is cramped for it specifically. */
#edit-dialog {
  inline-size: min(46rem, calc(100vw - 2 * var(--space-md)));
}

/* The walkthrough clip is a portrait phone screen-capture, so size the dialog
   to the video — capped to the viewport — rather than forcing a wide box, and
   let it scroll if head + clip + foot still can't fit. Selector carries a type
   so it matches the specificity of `dialog:not(.score-dialog)` above and wins
   on source order. */
dialog.walkthrough-dialog {
  inline-size: fit-content;
  max-inline-size: calc(100vw - 2 * var(--space-md));
  max-block-size: calc(100dvh - 2 * var(--space-md));
  overflow: auto;
}

.walkthrough-dialog__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-block-end: var(--space-md);
  font-weight: 600;
}

/* `dialog form[method='dialog']` adds a top margin (for footer close-forms that
   sit below content) — cancel it here so the × lines up with the title. Selector
   out-specifies that rule (class + type + attr). */
.walkthrough-dialog__head form[method='dialog'] {
  margin-block-start: 0;
}

/* wrap the portrait video tightly (no card-coloured gutters) and centre it */
.walkthrough-dialog .tour-clip {
  inline-size: fit-content;
  max-inline-size: 100%;
  margin-inline: auto;
}

/* drive the size off height so the dialog never outgrows the viewport; width
   follows from the video's intrinsic aspect ratio (its width/height attrs) */
.walkthrough-dialog .tour-clip video {
  block-size: min(80dvh, 50rem);
  inline-size: auto;
  max-inline-size: 100%;
  margin-inline: auto;
}

.walkthrough-dialog__foot {
  margin-block-start: var(--space-md);
  margin-block-end: 0;
  font-size: var(--fz-sm);
  color: var(--text-muted);
}

dialog::backdrop {
  background: rgb(0 0 0 / 0.6);
}

main:not(.app-main) dialog h2 {
  font-size: var(--fz-lg);
  margin-block-start: var(--space-sm);
  margin-block-end: var(--space-sm);
}

dialog form[method='dialog'] {
  margin-block-start: var(--space-md);
}

/* button row in a dialog footer (a whole method='dialog' form for simple
   confirm dialogs, or a wrapper div for forms with fields above the buttons) */
dialog form[method='dialog']:not(:has(fieldset, label)),
dialog .dialog-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--gap-sm);
}

.dialog-actions
  :is(input, select, textarea, .cta, .btn, button)
  + :is(input, select, textarea, .cta, .btn, button) {
  margin-block-start: unset;
}

dialog .twrap tbody td:last-of-type {
  text-align: right;
}

/* 5-column match-detail row inside a dialog collapses on narrow screens */
@media (max-width: 30rem) {
  dialog .game:has(span:nth-child(5)) {
    grid-template-columns: 1fr auto 1fr;
  }

  dialog .game:has(span:nth-child(5)) span:is(:nth-child(1), :nth-child(5)) {
    display: none;
  }
}

/* --- 14. blog ------------------------------------------------- */
section.blog {
  padding-block-start: var(--space-xl);
}

section.blog img {
  margin-block: var(--space-lg);
  border-radius: var(--radius-md);
  border: var(--border-hairline) solid var(--line);
}

/* --- 15. page layout (non-app pages) ------------------------- */
main {
  flex: 1;
  display: flex;
  flex-direction: column;
}

main:not(.app-main) {
  inline-size: 100%;
  max-inline-size: var(--w-wide);
  margin-inline: auto;
  padding: clamp(1.75rem, 1rem + 3vw, 3.5rem) var(--pad-inline) var(--space-3xl);
}

/* sections fill the frame so their left edge lines up with the header, footer
   and each other; only the running-text measure (p / li) is held narrower */
main:not(.app-main) > section {
  inline-size: 100%;
}

main:not(.app-main) > section + section {
  margin-block-start: var(--gap-section);
}

/* --- 16. utilities ----------------------------------------- */
.hidden,
[hidden] {
  display: none !important;
}

.visually-hidden {
  position: absolute !important;
  inline-size: 1px;
  block-size: 1px;
  padding: 0;
  margin: calc(var(--border-hairline) * -1);
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.text-muted {
  color: var(--text-muted);
}

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