/*
 * CHAG SPORTS reusable UI primitives: buttons, cards, badges, form
 * controls, header/footer/nav chrome, toasts, skeletons, empty/error
 * states. Generic foundation only - no product components (PredictionCard,
 * PredictionSlip, ...) belong here; those come in a later milestone.
 */

/* ============ Buttons ============ */
/* Task 1.72A: every button gets the same raised "3D" bottom edge
   (--shadow-3d, the exact token/pattern introduced for the League Detail
   favourite button) that collapses to flush-with-the-page on :active,
   with the button nudged down by the shadow's own 3px depth to sell the
   "physically pressed" illusion - never a per-variant tweak, so every
   .btn-* keeps its own existing background/text colors unchanged. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
  min-height: var(--touch-target-min);
  border: 1px solid transparent;
  border-radius: var(--radius-input);
  padding: var(--space-8) var(--space-16);
  font-size: var(--font-size-button);
  font-weight: var(--font-weight-semibold);
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-3d);
  transition: background-color var(--transition-fast), border-color var(--transition-fast),
    color var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
}

.btn:active {
  box-shadow: none;
  transform: translateY(3px);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* Task 1.72 Group 2: the shared, sitewide fix for the confirmed
   button-focus finding (docs/audits/task_1_72_group_2_kickoff.md).
   Bootstrap's own compiled `.btn:focus-visible` rule
   (static/vendor/bootstrap/css/bootstrap.min.css, loaded before every
   project stylesheet) sets `outline: 0` and a `box-shadow` driven by
   `--bs-btn-focus-box-shadow` - a custom property this project never
   defines, so that box-shadow declaration is invalid at computed-value
   time and is silently dropped, leaving `outline: 0` as the only
   surviving declaration from that rule and NO visible keyboard-focus
   indicator on any `.btn` at all (live-verified in both themes with
   real Tab presses, not `.focus()` - see the audit doc). This rule is a
   single class + pseudo-class, exactly matching Bootstrap's own
   `.btn:focus-visible` selector's specificity, but is loaded AFTER
   bootstrap.min.css (this file loads later in base.html's <head>), so
   it wins the cascade deterministically on declaration order rather
   than depending on an unset Bootstrap variable resolving one way or
   another. Reuses the exact same --color-focus-ring token and 2px
   outline/offset base.css's own `a:focus-visible`/`button:focus-visible`
   rule already establishes sitewide - one coherent value, never a
   second one invented here. This supersedes, and replaces, Group 1's
   narrower `.state-block__action .btn:focus-visible` override (removed
   below) - that rule is now a strict subset of this one. `:focus-visible`
   itself (not `:focus`) is what keeps a mouse click from ever gaining a
   persistent ring - every evergreen engine this project tests
   (Chromium/Firefox/WebKit) already restricts it to keyboard/
   programmatic focus, confirmed directly during the audit (`.btn
   .matches(':focus-visible')` was false immediately after a real mouse
   click and true only after a real Tab keypress). Only `outline`/
   `outline-offset` are set - `.btn`'s own background/color/box-shadow/
   dimensions/hover/active/disabled/pressed (`aria-pressed`) rules above
   and below are completely untouched, so no button's size or resting
   appearance changes. */
.btn:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

.btn-primary {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
}

.btn-primary:hover:not(:disabled) {
  background: var(--color-primary-hover);
  color: var(--color-primary-contrast);
  text-decoration: none;
}

.btn-secondary {
  background: var(--color-secondary);
  color: var(--color-secondary-contrast);
}

.btn-secondary:hover:not(:disabled) {
  filter: brightness(1.08);
  color: var(--color-secondary-contrast);
  text-decoration: none;
}

.btn-ghost {
  background: transparent;
  color: var(--color-text-primary);
  border-color: var(--color-border-strong);
}

/* Bootstrap's own .btn:hover rule (bootstrap.min.css) sets
   `color: var(--bs-btn-hover-color)` at a higher specificity (class +
   pseudo-class) than this file's un-hovered `.btn-ghost` rule (a single
   class) - since --bs-btn-hover-color is never defined anywhere in this
   codebase, that rule wins the cascade for `color` on hover and renders
   unreadable text (confirmed: near-black text on a dark hover fill in
   dark theme). Every .btn-* variant must restate its own `color` at
   :hover so it never depends on winning that fight against Bootstrap's
   higher-specificity rule - none of these variants actually change text
   color on hover, only background, so the value is always identical to
   the variant's own base color. */
.btn-ghost:hover:not(:disabled) {
  background: var(--color-bg-secondary);
  color: var(--color-text-primary);
  text-decoration: none;
}

.btn-icon {
  padding: var(--space-8);
  width: var(--touch-target-min);
  height: var(--touch-target-min);
  min-height: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text-primary);
}

.btn-icon:hover:not(:disabled) {
  background: var(--color-bg-secondary);
  color: var(--color-text-primary);
}

.btn-icon svg {
  width: 1.25rem;
  height: 1.25rem;
}

/* ============ Card ============ */
/* `color` is set here because Bootstrap's own `.card` rule otherwise hands every card its fixed, near-black
   --bs-body-color, which never follows [data-theme="dark"]: any text inside a card without its own colour class
   (e.g. the odds on a catalogue row) was unreadable on the dark card (launch polish, axe color-contrast). */
.card {
  background: var(--color-surface);
  color: var(--color-text-primary);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-sm); /* Level 1: hoverable card */
  padding: var(--card-padding);
}

.card-elevated {
  background: var(--color-surface-elevated);
  box-shadow: var(--shadow-md); /* Level 2 */
}

/* ============ Badge / Pill ============ */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  padding: 0.2rem var(--space-8);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-caption);
  font-weight: var(--font-weight-semibold);
  background: var(--color-bg-secondary);
  color: var(--color-text-secondary);
  border: 1px solid var(--color-border);
}

.badge-primary {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  border-color: transparent;
}

.badge-accent {
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  border-color: transparent;
}

.badge-success {
  background: var(--color-success);
  color: var(--color-success-contrast);
  border-color: transparent;
}

.badge-warning {
  background: var(--color-warning);
  color: var(--color-warning-contrast);
  border-color: transparent;
}

.badge-error {
  background: var(--color-error);
  color: var(--color-error-contrast);
  border-color: transparent;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-12);
  border-radius: var(--radius-pill);
  font-size: var(--font-size-body-sm);
  font-weight: var(--font-weight-medium);
  background: var(--color-bg-secondary);
  color: var(--color-text-primary);
  border: 1px solid var(--color-border);
}

/* ============ Filters (inset segmented control) ============
   Design System V1.0 §17: "Inset segmented controls; unmistakable selected
   state." Toggle `.is-active` (or aria-pressed="true" for a <button>) on
   the selected .filter-option. */
.filter-group {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
}

.filter-option {
  display: inline-flex;
  align-items: center;
  min-height: var(--filter-option-height);
  padding: var(--space-8) var(--space-16);
  border: none;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text-secondary);
  font-size: var(--font-size-control);
  font-weight: var(--font-weight-bold);
  cursor: pointer;
  transition: background-color var(--transition-fast), color var(--transition-fast);
}

.filter-option:hover:not(.is-active) {
  color: var(--color-text-primary);
}

.filter-option.is-active,
.filter-option[aria-pressed="true"] {
  background: var(--color-surface);
  color: var(--color-text-primary);
  box-shadow: var(--shadow-sm);
}

.filter-option:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* ============ Probability / confidence bars ============
   The percentage is always rendered as real text next to the bar - never
   the bar/color alone (Design System V1.0 §10 "PREDICTION SIGNALS", §19
   "no state communicated by color alone"). Confidence uses Chag Yellow
   (§03: "confidence" is one of Yellow's named roles); probability
   breakdowns use Chag Light Blue (§03: "probability support"). */
.confidence-bar,
.probability-bar {
  height: var(--space-8);
  border-radius: var(--radius-pill);
  background: var(--color-bg-secondary);
  overflow: hidden;
}

.confidence-bar__fill,
.probability-bar__fill {
  height: 100%;
  border-radius: inherit;
  background: var(--color-accent);
  transition: width var(--transition-slow);
}

.probability-bar__fill {
  background: var(--color-intelligence);
}

@media (prefers-reduced-motion: reduce) {
  .confidence-bar__fill,
  .probability-bar__fill {
    transition: none;
  }
}

/* Confidence UI labels: four levels, Low / Medium / High / Very High
   (UI/UX spec §17 "Confidence Component" - label names; numeric
   boundaries <60/60-74/75-89/90+ from Design System V1.0 §10). The
   mapping from a raw percentage to one of these four is computed in
   exactly one place, apps/core/confidence.py - never re-derive a
   threshold here or in a template. Interface categories, not
   guarantees - always paired with the percentage number as text,
   never color/level alone. Distinct existing semantic tokens per level
   (no new confidence-specific palette): muted -> info -> intelligence ->
   accent, reserving accent (Chag Yellow) for the top tier only, in
   keeping with it staying a scarce highlight color site-wide. */
.confidence-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-8);
  border-radius: var(--radius-pill);
  font-size: var(--font-size-metadata);
  font-weight: var(--font-weight-black);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: var(--color-bg-secondary);
  color: var(--color-text-secondary);
  border: 1px solid var(--color-border);
}

.confidence-badge[data-level="low"] {
  background: var(--color-bg-secondary);
  color: var(--color-muted);
  border-color: var(--color-border);
}

.confidence-badge[data-level="medium"] {
  background: var(--color-info);
  color: var(--color-info-contrast);
  border-color: transparent;
}

.confidence-badge[data-level="high"] {
  background: var(--color-intelligence);
  color: var(--color-intelligence-contrast);
  border-color: transparent;
}

.confidence-badge[data-level="very-high"] {
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  border-color: transparent;
}

/* ============ Team form strip ============
   Compact W/D/L evidence (Design System V1.0 §07 layer 03). Result always
   renders as a letter, never a color-only dot. */
.form-strip {
  display: flex;
  gap: var(--space-4);
}

.form-strip--center {
  justify-content: center;
}

.form-pip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: var(--radius-sm);
  font-size: var(--font-size-metadata);
  font-weight: var(--font-weight-black);
  color: #ffffff;
}

.form-pip[data-result="W"] {
  background: var(--color-success);
}

.form-pip[data-result="D"] {
  background: var(--color-muted);
}

.form-pip[data-result="L"] {
  background: var(--color-error);
}

/* ============ Pick / odds panel ============
   "Prediction and odds sit on a quiet tinted surface" (Design System V1.0
   §07 layer 05) - deliberately understated, not a loud highlight box. */
.pick-panel {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-12);
  padding: var(--space-12) var(--space-16);
  background: var(--color-bg-secondary);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-input);
}

/* Task 1.72 Group 6 repair: `.text-metadata`/`.text-caption`'s own base
   color (`--color-text-muted`) measured ~4.38:1 against this panel's
   `--color-bg-secondary` background in light theme (axe-core, Homepage
   scan). `.pick-panel` is shared by prediction_card.html,
   prediction_history_card.html, and result_card.html (Design System V1.0
   §07 layer 05), so this is a single component-level fix rather than
   three page-scoped copies. `--color-text-secondary` reaches >=4.5:1 in
   both themes against this surface. (Launch polish later darkened/lifted
   the shared `--color-text-muted` token itself so it also clears AA on
   every secondary and elevated surface in both themes; this rule is kept
   because the pick panel's labels read better at the secondary weight.) */
.pick-panel .text-metadata,
.pick-panel .text-caption {
  color: var(--color-text-secondary);
}

/* ============ Tabular metric rows ============
   Label + tabular numeric value, for accuracy/performance metric lists
   (Design System V1.0 §16). */
.metric-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-12);
  padding-block: var(--space-8);
  border-bottom: 1px solid var(--color-border);
}

.metric-row:last-child {
  border-bottom: none;
}

.metric-row__label {
  font-size: var(--font-size-body-sm);
  color: var(--color-text-secondary);
}

.metric-row__value {
  font-variant-numeric: tabular-nums;
  font-weight: var(--font-weight-bold);
  color: var(--color-text-primary);
}

/* ============ Dark analytical surface ============
   Always-navy surface regardless of theme - premium/analytical contexts
   (Design System V1.0 §15: "Premium surfaces should feel more analytical,
   not merely more colorful") and the Prediction Slip's navy sidecar
   workspace (§12, not built yet - this is the surface treatment only). */
.surface-analytical {
  background: var(--brand-navy);
  color: #ffffff;
  border-radius: var(--radius-card);
  padding: var(--card-padding);
}

/* Every typography utility that sets its own explicit (theme-swapping)
   text color directly matches an element inside .surface-analytical with
   HIGHER priority than the inherited white above - a directly-matched
   rule always wins over an inherited one, regardless of specificity. In
   dark theme --color-text-primary resolves to near-white already, but in
   *light* theme it's navy, which would render as invisible navy-on-navy
   text (caught when Task 1.17's homepage put a <h2 class="text-h2">
   inside a .surface-analytical Premium panel - the same class of bug
   Task 1.16 found and fixed locally for the Prediction Slip). Listed
   explicitly rather than a blanket `*` selector, so this stays cheap and
   predictable. */
.surface-analytical .text-body,
.surface-analytical .text-body-sm,
.surface-analytical .text-body-lg,
.surface-analytical .text-display,
.surface-analytical .text-h1,
.surface-analytical .text-h2,
.surface-analytical .text-h3,
.surface-analytical .text-h4,
.surface-analytical .text-stat-value,
.surface-analytical .text-metadata,
.surface-analytical .text-caption {
  color: #ffffff;
}

/* !important needed here too, now that base.css's plain `.text-muted`
   carries `!important` (to beat Bootstrap's own same-named utility
   class, which is also `!important`) - any `!important` declaration
   always beats a non-important one regardless of specificity, so
   without this, that base rule would win here too and this surface's
   own intentional light-blue override would never apply. */
.surface-analytical .text-muted {
  color: var(--brand-light-blue) !important;
}

/* Same reason for `.text-body`: base.css's `.text-body` also carries `!important` (Bootstrap's same-named utility),
   so the plain white rule above never applied to it. In the light theme its --color-text-primary is navy, and the
   homepage Premium CTA's description rendered navy on navy - contrast 1:1, invisible (launch polish). */
.surface-analytical .text-body {
  color: #ffffff !important;
}

/* The surface is navy in BOTH themes, so its primary action is always Chag Yellow on navy. The plain .btn-primary
   follows --color-primary, which is navy itself in the light theme - the button disappeared into the surface
   (launch polish, the homepage "Go deeper with Premium" CTA). The focus ring switches to light blue, which reads on
   navy; the theme's slate ring does not. */
.surface-analytical .btn-primary {
  background: var(--color-accent);
  color: var(--color-accent-contrast);
}

.surface-analytical .btn-primary:hover:not(:disabled) {
  background: var(--color-accent-hover);
  color: var(--color-accent-contrast);
}

.surface-analytical .btn:focus-visible,
.surface-analytical a:focus-visible {
  outline-color: var(--color-intelligence);
}

/* ============ Section heading / divider ============ */
.section-heading {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-12);
  margin-bottom: var(--space-16);
}

.section-heading__title {
  font-size: var(--font-size-h3);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-primary);
  margin: 0;
}

.divider {
  border: none;
  border-top: 1px solid var(--color-border);
  margin: var(--space-24) 0;
}

/* ============ Form controls ============ */
.form-label {
  display: block;
  font-size: var(--font-size-body-sm);
  font-weight: var(--font-weight-medium);
  color: var(--color-text-secondary);
  margin-bottom: var(--space-4);
}

.form-control,
.form-select {
  width: 100%;
  padding: var(--space-8) var(--space-12);
  font-size: var(--font-size-body);
  color: var(--color-text-primary);
  background: var(--color-surface);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-input);
  transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

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

.form-control:focus,
.form-select:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 25%, transparent);
}

.form-check {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
}

.form-check-input {
  width: 1.125rem;
  height: 1.125rem;
  accent-color: var(--color-primary);
}

/* ============ Table wrapper ============ */
.table-wrapper {
  overflow-x: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

.table-wrapper table {
  width: 100%;
  border-collapse: collapse;
  min-width: 480px;
}

.table-wrapper th,
.table-wrapper td {
  padding: var(--space-12) var(--space-16);
  text-align: left;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--font-size-body-sm);
}

.table-wrapper th {
  color: var(--color-text-secondary);
  font-weight: var(--font-weight-semibold);
  background: var(--color-bg-secondary);
}

.table-wrapper tr:last-child td {
  border-bottom: none;
}

/* A table region that may scroll sideways is focusable (tabindex="0", role="region") so a keyboard can scroll it
   with the arrow keys; it shows the same focus ring as every other control. */
.table-wrapper[tabindex]:focus-visible {
  outline: 2px solid var(--color-focus-ring);
  outline-offset: 2px;
}

/* ============ Section heading with an end-of-row control ============
   A section's heading on the left and one small control at the opposite end of the same row (e.g. the standings
   table's right-arrow scroll affordance) - the heading keeps its own size and margins. */
.section-heading-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-12);
  margin-bottom: var(--space-12);
}

.section-heading-row > :first-child {
  min-width: 0;
  margin-bottom: 0;
}

/* ============ Horizontal-scroll affordance ============
   The right arrow beside a wide table's heading (static/js/scroll-affordance.js): hidden until the table really
   overflows; at the right edge it fades and reports aria-disabled (it stays focusable); scrolling back brings it
   back. An icon-only ghost button with the same border, radius and touch target as the filter controls. */
.scroll-affordance {
  flex: none;
  border: 1px solid var(--color-border-strong);
  box-shadow: none;
  color: var(--color-text-primary);
  transition: opacity var(--transition-base), background-color var(--transition-fast);
}

.scroll-affordance:hover:not([aria-disabled="true"]) {
  background: var(--color-bg-secondary);
}

.scroll-affordance:active:not([aria-disabled="true"]) {
  transform: translateX(2px);
}

.scroll-affordance.is-at-end,
.scroll-affordance[aria-disabled="true"] {
  opacity: 0.35;
  cursor: default;
}

.scroll-affordance[hidden] {
  display: none;
}

/* ============ Modal base ============ */
.modal-content {
  background: var(--color-surface);
  color: var(--color-text-primary);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-modal);
  box-shadow: var(--shadow-lg); /* Level 3 */
}

.modal-header,
.modal-footer {
  border-color: var(--color-border);
}

/* ============ Toast ============ */
.toast-container {
  position: fixed;
  bottom: var(--space-16);
  right: var(--space-16);
  z-index: 1080;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  max-width: 22rem;
}

.toast-cs {
  display: flex;
  align-items: flex-start;
  gap: var(--space-12);
  padding: var(--space-12) var(--space-16);
  background: var(--color-surface-elevated);
  color: var(--color-text-primary);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-primary);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.toast-cs[data-variant="success"] {
  border-left-color: var(--color-success);
}

.toast-cs[data-variant="warning"] {
  border-left-color: var(--color-warning);
}

.toast-cs[data-variant="error"] {
  border-left-color: var(--color-error);
}

/* ============ Skeleton loader ============ */
.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--color-bg-secondary);
  border-radius: var(--radius-md);
}

.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in srgb, var(--color-text-primary) 8%, transparent),
    transparent
  );
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .skeleton::after {
    animation: none;
  }
}

@keyframes skeleton-shimmer {
  100% {
    transform: translateX(100%);
  }
}

.skeleton-text {
  height: 0.9em;
  margin-bottom: var(--space-8);
  width: 100%;
}

.skeleton-text:last-child {
  width: 60%;
  margin-bottom: 0;
}

.skeleton-text--title {
  width: 40%;
  height: 1.5em;
}

.skeleton-block {
  height: 6rem;
  margin-top: var(--space-12);
}

/* ============ Empty / error states ============ */
.state-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-12);
  padding: var(--space-48) var(--space-16);
  color: var(--color-text-secondary);
}

.state-block__icon {
  width: 2.5rem;
  height: 2.5rem;
  color: var(--color-text-muted);
}

.state-block--error .state-block__icon {
  color: var(--color-error);
}

.state-block__title {
  font-size: var(--font-size-h4);
  font-weight: var(--font-weight-semibold);
  color: var(--color-text-primary);
  margin: 0;
}

/* Task 1.72 Group 1 originally added a narrow `.state-block__action
   .btn:focus-visible` override here (restoring focus visibility on the
   error-page recovery action alone) as a stopgap for the sitewide
   `.btn:focus-visible` defect that override's own comment explicitly
   flagged as a Group 2 repair. Task 1.72 Group 2's shared `.btn
   :focus-visible` rule (declared once, near the top of this file's
   "Buttons" section) is a strict superset of that narrow rule - the
   error-page recovery action is an ordinary `.btn` and is fully covered
   by the general rule now, so the page-specific override has been
   removed rather than left as a redundant, disconnected duplicate. */

/* ============ Breadcrumbs ============ */
.breadcrumbs {
  margin-bottom: var(--space-16);
}

.breadcrumbs__list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding: 0;
  margin: 0;
}

.breadcrumbs__item {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  color: var(--color-text-muted);
}

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

.breadcrumbs__item[aria-current="page"] {
  color: var(--color-text-primary);
}

/* ============ Layout utilities ============
   Small, genuinely-reusable helpers (used by the frontend preview page and
   available to any future page) - not a full utility-CSS framework, just
   enough to avoid inline style="" attributes in templates. */
.u-stack-gap {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-12);
  align-items: center;
}

.u-stack-gap--between {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-12);
  align-items: center;
  justify-content: space-between;
}

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

.u-flex-1 {
  flex: 1;
}

.u-m-0 {
  margin: 0;
}

.u-mx-12 {
  margin-inline: var(--space-12);
}

.u-mb-4 {
  margin-bottom: var(--space-4);
}

.u-grid-auto-fit {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-16);
}

.u-section-spacing {
  padding-block: var(--space-32);
}

.u-max-width-sm {
  max-width: 640px;
}

.u-mt-16 {
  margin-top: var(--space-16);
}

/* Color swatches - used by the frontend-preview page (core/frontend_preview.html)
   to demonstrate the token palette; not a product component. */
.u-swatch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-16);
}

.u-swatch {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-card);
  overflow: hidden;
  border: 1px solid var(--color-border);
}

.u-swatch__color {
  height: 4rem;
}

.u-swatch__label {
  padding: var(--space-8) var(--space-12);
  background: var(--color-surface);
}

.u-swatch__color--navy { background: var(--brand-navy); }
.u-swatch__color--light-blue { background: var(--brand-light-blue); }
.u-swatch__color--slate { background: var(--brand-slate); }
.u-swatch__color--yellow { background: var(--brand-yellow-green); }
.u-swatch__color--primary { background: var(--color-primary); }
.u-swatch__color--accent { background: var(--color-accent); }
.u-swatch__color--intelligence { background: var(--color-intelligence); }
.u-swatch__color--muted { background: var(--color-muted); }
.u-swatch__color--success { background: var(--color-success); }
.u-swatch__color--warning { background: var(--color-warning); }
.u-swatch__color--error { background: var(--color-error); }
.u-swatch__color--info { background: var(--color-info); }
.u-swatch__color--bg { background: var(--color-bg); }
.u-swatch__color--bg-secondary { background: var(--color-bg-secondary); }
.u-swatch__color--surface { background: var(--color-surface); }
.u-swatch__color--surface-elevated { background: var(--color-surface-elevated); }

/* ============ Site header / navigation ============ */
.site-header {
  position: sticky;
  top: 0;
  z-index: 1030;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}

.site-header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-16);
  padding-block: var(--space-12);
}

.site-header__logo {
  display: inline-flex;
  align-items: center;
}

.site-header__logo img {
  height: 2rem;
  width: auto;
}

/* Two logo variants (approved combination marks - navy ink for light
   backgrounds, light-blue ink for dark backgrounds), swapped with the same
   CSS-only show/hide pattern as the theme-toggle sun/moon icons below -
   no JS/flash involved. Mirrors that pattern's 3-state handling exactly
   (explicit [data-theme], falling back to prefers-color-scheme when JS
   never ran to set the attribute at all). */
.site-header__logo-img--dark {
  display: none;
}

[data-theme="dark"] .site-header__logo-img--light,
:root:not([data-theme="light"]) .site-header__logo-img--light {
  display: none;
}

[data-theme="dark"] .site-header__logo-img--dark,
:root:not([data-theme="light"]) .site-header__logo-img--dark {
  display: block;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .site-header__logo-img--light {
    display: block;
  }

  :root:not([data-theme="dark"]) .site-header__logo-img--dark {
    display: none;
  }
}

[data-theme="light"] .site-header__logo-img--light {
  display: block;
}

[data-theme="light"] .site-header__logo-img--dark {
  display: none;
}

.desktop-nav {
  display: none;
}

/* Desktop-only auth actions (My Account, or Log In + Sign Up) and the
   mobile menu-trigger button both switch alongside `.desktop-nav`
   itself, at the exact same 1024px+ breakpoint (responsive.css) -
   replaces the former Bootstrap `d-none d-md-inline-flex`/`d-md-none`
   utility-class pair, which was tied to Bootstrap's own separate,
   fixed 768px `md` breakpoint (see responsive.css's header comment for
   why that caused the tablet header-wrapping defect this replaces). */
.site-header__auth-action {
  display: none;
}

.desktop-nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-24);
  list-style: none;
  margin: 0;
  padding: 0;
}

.desktop-nav__link {
  color: var(--color-text-secondary);
  font-size: var(--font-size-nav);
  font-weight: var(--font-weight-medium);
}

.desktop-nav__link:hover,
.desktop-nav__link:focus-visible {
  color: var(--color-text-primary);
  text-decoration: none;
}

.desktop-nav__link[aria-current="page"] {
  color: var(--color-primary);
}

.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--space-8);
}

.mobile-nav__actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

/* Slip trigger count badge - the stable hook a future prediction-slip
   task updates directly (see CLAUDE.md "Frontend conventions"). */
.slip-trigger {
  position: relative;
}

.slip-trigger__count {
  position: absolute;
  top: -0.25rem;
  right: -0.25rem;
  min-width: 1.1rem;
  height: 1.1rem;
  padding: 0 0.25rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-accent-contrast);
  font-size: 0.65rem;
  font-weight: var(--font-weight-bold);
  line-height: 1;
}

.slip-trigger__count[hidden] {
  display: none;
}

/* Theme toggle */
.theme-toggle .icon-sun,
.theme-toggle .icon-moon {
  width: 1.25rem;
  height: 1.25rem;
}

[data-theme="dark"] .theme-toggle .icon-sun,
:root:not([data-theme="light"]) .theme-toggle .icon-sun {
  display: none;
}

.theme-toggle .icon-moon {
  display: none;
}

[data-theme="dark"] .theme-toggle .icon-moon,
:root:not([data-theme="light"]) .theme-toggle .icon-moon {
  display: block;
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle .icon-sun {
    display: block;
  }

  :root:not([data-theme="dark"]) .theme-toggle .icon-moon {
    display: none;
  }
}

[data-theme="light"] .theme-toggle .icon-sun {
  display: block;
}

[data-theme="light"] .theme-toggle .icon-moon {
  display: none;
}

/* ============ Mobile navigation ============ */
.mobile-nav {
  display: flex;
}

.mobile-nav__offcanvas {
  background: var(--color-surface);
  color: var(--color-text-primary);
}

.mobile-nav__offcanvas .offcanvas-header {
  border-bottom: 1px solid var(--color-border);
}

/* "Menu" itself is the dismiss control (no separate X icon) - looks like
   plain heading text, but is a real <button> so it keeps the global
   :focus-visible ring and native keyboard/click behavior. */
.mobile-nav__close {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.mobile-nav__offcanvas .offcanvas-body {
  display: flex;
  flex-direction: column;
  gap: var(--space-24);
}

.mobile-nav__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.mobile-nav__list a {
  display: block;
  padding: var(--space-12) var(--space-8);
  color: var(--color-text-primary);
  font-size: var(--font-size-nav);
  font-weight: var(--font-weight-medium);
  border-radius: var(--radius-md);
}

.mobile-nav__list a:hover,
.mobile-nav__list a:focus-visible {
  background: var(--color-bg-secondary);
  text-decoration: none;
}

/* Task 1.30: anonymous Log In/Sign Up or authenticated My Account/Log
   Out - full-width stacked, tighter gap than the offcanvas body's own
   section spacing above (nav vs. auth actions), since these two buttons
   are one related group. */
.mobile-nav__auth-actions {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.mobile-nav__auth-actions .btn {
  width: 100%;
}

.mobile-nav__auth-actions form {
  display: contents;
}

/* Prepared for a future bottom nav (Home | Predictions | Slip | Results |
   Account) - present in the DOM (see templates/base/mobile_navigation.html)
   but inert/hidden until that milestone builds real destinations. */
.mobile-bottom-nav {
  display: none;
}

/* ============ Footer ============ */
.site-footer {
  background: var(--color-bg-secondary);
  border-top: 1px solid var(--color-border);
  margin-top: var(--space-64);
}

/* Task 1.72A Phase 1 consolidation review: mobile must render exactly
   TWO link columns (CLAUDE.md "Four link columns on desktop. Two link
   columns on mobile."). This previously read `repeat(3, 1fr)` - a
   regression this redesign itself introduced (confirmed against
   `origin/master`, which had a single stacked column here instead -
   not "two columns" either, but `repeat(3, 1fr)` made it worse, not
   better). Two columns matches the 768px+ tablet rule below exactly
   (Product+Company share row one, Content+Legal alone in row two). */
.site-footer__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-32);
  padding-block: var(--space-48);
}

/* Brand/logo/tagline cell spans the full mobile-width row above the three
   link columns below it (Product/Company/Content) - see responsive.css
   for the desktop treatment, where it returns to sharing one column
   alongside the other three instead of spanning. */
.site-footer__brand {
  grid-column: 1 / -1;
}

.site-footer__heading {
  font-size: var(--font-size-caption);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  /* Task 1.72 Group 2: was --color-text-muted, which axe-core's
     color-contrast rule (WCAG 2 AA, 1.4.3) confirmed genuinely fails in
     light theme - #6b7191 on --color-bg-secondary's #f4f5f9 computes to
     4.38:1, just under the required 4.5:1 for this caption-sized text
     (dark theme's own muted value happens to clear 4.5:1, but only
     narrowly, at 4.65:1). --color-text-secondary is what
     .site-footer__list's own links already use successfully against
     this identical background (7.99:1 light / 8.32:1 dark) - reusing it
     here keeps every footer text element on one already-verified-safe
     token instead of two, in both themes. */
  color: var(--color-text-secondary);
  margin: 0 0 var(--space-12);
}

.site-footer__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.site-footer__list a {
  color: var(--color-text-secondary);
  font-size: var(--font-size-body-sm);
}

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

/* Task 1.72A Phase 1 final repair: the social icons now live in their
   own row, ABOVE the copyright row (see footer.html's own comment for
   why) - this row owns the divider between the link grid and the
   closing footer area; `.site-footer__bottom` below no longer needs
   one of its own. Left-aligned to match every other footer column's
   own alignment (Brand/Product/Company/Content are all left-aligned);
   `flex-wrap` is a defensive no-op at every supported width (4 small
   icons never actually need to wrap) rather than a real requirement. */
.site-footer__social-row {
  padding-top: var(--space-16);
  border-top: 1px solid var(--color-border);
}

.site-footer__social {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
}

.site-footer__bottom {
  padding-block: var(--space-16);
  font-size: var(--font-size-caption);
  /* Task 1.72 Group 2: same repair, same reason, as .site-footer__heading
     above - --color-text-muted fails WCAG 2 AA contrast (4.38:1 of the
     required 4.5:1) against this identical --color-bg-secondary footer
     background in light theme. */
  color: var(--color-text-secondary);
}

/* Below the 1024px breakpoint where the Prediction Slip's own fixed
   mobile trigger bar is present (responsive.css), this copyright row
   - now genuinely the LAST element on the page - carries extra bottom
   clearance so the trigger (its own `bottom: var(--space-16)` offset
   plus its `--touch-target-min` height) can never reach up far enough
   to cover the social row above it, even scrolled all the way to the
   bottom of the page. Never achieved via z-index/absolute positioning
   or by hiding the slip itself - plain flow padding only. */
@media (max-width: 1023px) {
  .site-footer__bottom {
    padding-bottom: calc(var(--touch-target-min) + var(--space-16) * 2);
  }
}

.site-footer__logo img {
  height: 1.75rem;
  width: auto;
}

/* Task 1.60 §7: the header's progressively-enhanced search trigger/panel
   - lives here (not a page-specific CSS file) since the header renders
   on every page. Tokens only, no new colours/spacing/radii. */

/* Phones (below 768px): the trigger sits left of the slip/theme/menu icons,
   so a panel anchored to it ran off the left edge. It anchors to the
   full-width sticky header instead (.site-header__search is no longer a
   containing block) and is centred with 16px side margins. From 768px the
   original trigger-anchored desktop layout applies unchanged. */
.site-header__search {
  position: static;
}

.search-panel {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 50;
  width: min(calc(100% - 2 * var(--space-16)), 32rem);
  margin: var(--space-8) auto 0;
  padding: var(--space-12);
  background: var(--color-surface-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}

@media (min-width: 768px) {
  .site-header__search {
    position: relative;
  }

  .search-panel {
    left: auto;
    width: min(24rem, 92vw);
    margin: var(--space-8) 0 0;
  }
}

.search-panel[hidden] {
  display: none;
}

.search-panel__input {
  width: 100%;
}

.search-panel__listbox {
  list-style: none;
  margin: var(--space-8) 0 0;
  padding: 0;
  max-height: 20rem;
  overflow-y: auto;
}

.search-panel__option {
  display: block;
  padding: var(--space-8);
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: inherit;
  text-decoration: none;
}

.search-panel__option:hover,
.search-panel [aria-selected="true"] {
  background: var(--color-bg-secondary);
}

.search-panel__status {
  margin-top: var(--space-8);
  font-size: var(--font-size-caption);
  color: var(--color-text-muted);
}
