/* components.css — Wushu Scoring shared component primitives.
 *
 * Everything here consumes tokens from tokens.css. No raw hex, no raw px under
 * 64. Class names use the `ws-` prefix to avoid collisions with page-local CSS
 * still living inside individual HTML files.
 */

/* ── Reset (light-touch — only what every page needs) ─────────────────────── */
.ws-reset *,
.ws-reset *::before,
.ws-reset *::after {
  box-sizing: border-box;
}

/* ── Wordmark / brand lockup ──────────────────────────────────────────────── */
.ws-wordmark {
  color: var(--color-text-primary);
  white-space: nowrap;
  display: inline-flex;
  align-items: baseline;
  text-decoration: none;
}
/* Legacy ornament — kept for any caller still rendering Wushu ✤ Scoring,
 * but the current motifs.js wordmark replaces this with the Wushu Precision
 * type-only lockup. */
.ws-wordmark__ornament {
  color: var(--color-brand-red);
  font-size: 0.9em;
}

/* Brand mark + banner lockups — rendered as <img> elements pointing at
 * per-theme assets in /img/brand/. motifs.js hot-swaps `src` on
 * `wushu:theme-change` so they stay in sync with the active colourway. */
.wp-mark, .wp-banner {
  display: inline-block;
  line-height: 0;
  vertical-align: middle;
  max-width: 100%;
}

/* ── Brush hairline rule ──────────────────────────────────────────────────── */
/* Section divider that fades from transparent → rule-strong → transparent,
 * evoking an ink-brush stroke. Use as <div class="ws-brush-rule"></div>. */
.ws-brush-rule {
  height: 1px;
  border: 0;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--color-surface-rule-strong) 15%,
    var(--color-surface-rule-strong) 85%,
    transparent 100%
  );
}

/* ── Buttons ──────────────────────────────────────────────────────────────── */
.ws-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  line-height: 1;
  padding: 0 var(--space-5);
  height: 44px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  background: transparent;
  color: var(--color-text-primary);
  cursor: pointer;
  text-decoration: none;
  transition:
    background-color var(--motion-quick) var(--motion-ease-standard),
    border-color     var(--motion-quick) var(--motion-ease-standard),
    color            var(--motion-quick) var(--motion-ease-standard),
    transform        80ms var(--motion-ease-exit),
    opacity          var(--motion-quick) var(--motion-ease-standard);
}
.ws-btn:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}
.ws-btn:active { transform: scale(0.97); }
.ws-btn:disabled,
.ws-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

.ws-btn--primary {
  background: var(--color-action-primary);
  color: var(--color-text-on-accent);
}
.ws-btn--primary:hover:not(:disabled) {
  background: var(--color-action-primary-hover);
}

.ws-btn--brand {
  background: var(--color-brand-red);
  color: var(--color-text-on-accent);
}
.ws-btn--brand:hover:not(:disabled) {
  background: var(--color-brand-red-hover);
}

.ws-btn--ghost {
  background: var(--color-surface-card);
  border-color: var(--color-surface-rule);
  color: var(--color-text-primary);
}
.ws-btn--ghost:hover:not(:disabled) {
  background: var(--color-surface-sunken);
  border-color: var(--color-surface-rule-strong);
}

.ws-btn--sm { height: 36px; padding: 0 var(--space-4); font-size: var(--font-size-sm); }
.ws-btn--lg { height: 56px; padding: 0 var(--space-6); font-size: var(--font-size-md); }

/* ── Inputs ───────────────────────────────────────────────────────────────── */
.ws-input,
.ws-textarea,
.ws-select {
  width: 100%;
  height: 44px;
  padding: 0 var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--font-size-base);
  color: var(--color-text-primary);
  background: var(--color-surface-card);
  border: 1px solid var(--color-surface-rule-strong);
  border-radius: var(--radius-md);
  transition: border-color var(--motion-quick) var(--motion-ease-standard),
              box-shadow   var(--motion-quick) var(--motion-ease-standard);
}
.ws-textarea {
  height: auto;
  padding: var(--space-3) var(--space-4);
  min-height: 96px;
  line-height: 1.5;
  resize: vertical;
}
.ws-input:focus,
.ws-textarea:focus,
.ws-select:focus {
  outline: none;
  border-color: var(--color-action-primary);
  box-shadow: var(--shadow-focus);
}
.ws-input::placeholder,
.ws-textarea::placeholder { color: var(--color-text-muted); }

.ws-label {
  display: block;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--font-tracking-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-1);
}

/* ── Card / surface ───────────────────────────────────────────────────────── */
.ws-card {
  background: var(--color-surface-card);
  border: 1px solid var(--color-surface-rule);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-sm);
}

/* ── Status pill / badge ──────────────────────────────────────────────────── */
.ws-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-family: var(--font-sans);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  letter-spacing: var(--font-tracking-wide);
  line-height: 1;
  background: var(--color-neutral-soft-bg);
  color: var(--color-neutral-soft-text);
  border: 1px solid transparent;
}
.ws-pill__dot {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: currentColor;
}
.ws-pill--success { background: var(--color-success-soft-bg); color: var(--color-success-soft-text); }
.ws-pill--success .ws-pill__dot { background: var(--color-success-strong); }
.ws-pill--warning { background: var(--color-warning-soft-bg); color: var(--color-warning-soft-text); }
.ws-pill--warning .ws-pill__dot { background: var(--color-warning-strong); }
.ws-pill--danger  { background: var(--color-danger-soft-bg);  color: var(--color-danger-soft-text);  }
.ws-pill--danger  .ws-pill__dot { background: var(--color-danger-strong); }
.ws-pill--info    { background: var(--color-action-subtle-bg); color: var(--color-action-subtle-fg); }
.ws-pill--info    .ws-pill__dot { background: var(--color-action-primary); }
.ws-pill--beta    { background: var(--color-action-subtle-bg); color: var(--color-action-subtle-fg); }
.ws-pill--beta    .ws-pill__dot { background: var(--color-action-primary); }
.ws-pill--alpha   { background: var(--color-warning-soft-bg);  color: var(--color-warning-soft-text); }
.ws-pill--alpha   .ws-pill__dot { background: var(--color-warning-strong); }
.ws-pill--soon    { background: var(--color-neutral-soft-bg);  color: var(--color-neutral-soft-text); }
.ws-pill--soon    .ws-pill__dot { background: var(--color-text-muted); }

.ws-pill--live .ws-pill__dot {
  animation: ws-live-pulse 2s ease-in-out infinite;
}

/* ── Role badge — outlined pill, accent-only ──────────────────────────────── */
.ws-role-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-family: var(--font-sans);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--font-tracking-caps);
  text-transform: uppercase;
  line-height: 1;
  background: transparent;
  border: 1px solid currentColor;
}
.ws-role-badge__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.ws-role-badge--ahj   { color: var(--color-role-ahj); }
.ws-role-badge--dd    { color: var(--color-role-dd); }
.ws-role-badge--op    { color: var(--color-role-op); }
.ws-role-badge--qom   { color: var(--color-role-qom); }
.ws-role-badge--hj    { color: var(--color-role-hj); }
.ws-role-badge--quick { color: var(--color-role-quick); }

/* ── Role accent strip — 2px coloured line under topbars ──────────────────── */
.ws-role-strip { height: 2px; width: 100%; }
.ws-role-strip--ahj   { background: var(--color-role-ahj); }
.ws-role-strip--dd    { background: var(--color-role-dd); }
.ws-role-strip--op    { background: var(--color-role-op); }
.ws-role-strip--qom   { background: var(--color-role-qom); }
.ws-role-strip--hj    { background: var(--color-role-hj); }
.ws-role-strip--quick { background: var(--color-role-quick); }

/* ── Taiji disc — spin when scoring is live ───────────────────────────────── */
.ws-taiji {
  display: inline-block;
  vertical-align: middle;
}
.ws-taiji--spin { animation: ws-taiji-spin 12s linear infinite; }
@media (prefers-reduced-motion: reduce) {
  .ws-taiji--spin { animation: none; }
}

/* ── Animations ───────────────────────────────────────────────────────────── */
@keyframes ws-taiji-spin { to { transform: rotate(360deg); } }
@keyframes ws-live-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@keyframes ws-shake {
  0%, 100% { transform: translateX(0); }
  20%      { transform: translateX(-4px); }
  40%      { transform: translateX(4px); }
  60%      { transform: translateX(-3px); }
  80%      { transform: translateX(3px); }
}
.ws-shake { animation: ws-shake 240ms var(--motion-ease-exit); }

@media (prefers-reduced-motion: reduce) {
  .ws-shake,
  .ws-pill--live .ws-pill__dot { animation: none; }
}

/* ── Tabs ──────────────────────────────────────────────────────────────────
 * Underline-style tab strip. Pair `<button class="ws-tab" data-tab="basics">`
 * triggers with `<div class="ws-tab-panel" data-tab-panel="basics">` panes,
 * inside a `<div class="ws-tabs">` row. Wire interaction with
 * `wireTabs(root)` from /js/ws-tabs.js.
 */
.ws-tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--color-surface-rule-strong);
  margin-bottom: var(--space-5);
  overflow-x: auto;
  scrollbar-width: none;
}
.ws-tabs::-webkit-scrollbar { display: none; }
.ws-tab {
  padding: 10px 14px;
  font-family: inherit;
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--motion-quick) var(--motion-ease-standard),
              border-color var(--motion-quick) var(--motion-ease-standard);
}
.ws-tab:hover { color: var(--color-text-primary); }
.ws-tab:focus-visible {
  outline: none;
  color: var(--color-text-primary);
  box-shadow: var(--shadow-focus);
  border-radius: var(--radius-sm);
}
.ws-tab[aria-selected="true"],
.ws-tab.active {
  color: var(--color-action-primary);
  border-bottom-color: var(--color-action-primary);
}
.ws-tab-panel { display: none; }
.ws-tab-panel[aria-hidden="false"],
.ws-tab-panel.active { display: block; }

/* ── Stepper ───────────────────────────────────────────────────────────────
 * Numbered step indicators for multi-step wizards. Place numbered badges in
 * a row; each step's badge gets the `done` / `current` / `inactive` state
 * driven by `wireStepper(root, { onStepChange })`.
 */
.ws-stepper {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  flex-wrap: wrap;
}
.ws-step {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}
.ws-step:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
  border-radius: var(--radius-sm);
}
.ws-step__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--color-surface-rule-strong);
  color: var(--color-text-on-accent);
  font-size: 11px;
  font-weight: var(--font-weight-semibold);
  flex-shrink: 0;
}
.ws-step--current { color: var(--color-text-primary); }
.ws-step--current .ws-step__badge { background: var(--color-action-primary); }
.ws-step--done    .ws-step__badge { background: var(--color-success-strong); }
.ws-step__divider {
  flex: 1;
  min-width: 12px;
  height: 1px;
  background: var(--color-surface-rule-strong);
}

/* ── Modal footer rail ─────────────────────────────────────────────────────
 * Sticky action rail for long modals (tabbed or otherwise). Keeps Save /
 * Cancel pinned to the bottom of the dialog so they don't disappear off
 * the end of a long tab pane.
 */
.ws-modal-footer {
  position: sticky;
  bottom: 0;
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  margin: 0 calc(-1 * var(--space-5)) calc(-1 * var(--space-5));
  background: var(--color-surface-card);
  border-top: 1px solid var(--color-surface-rule);
}

/* ── Visually-hidden utility (for accessible-only labels) ─────────────────── */
.ws-sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}

/* ── Touch-mode toggle ──────────────────────────────────────────────────────
 * Small "Touch ✓" pill that lives in panel headers. Matches the visual
 * language of #bbm-toggle in the QoM judge. State driven by
 * public/js/touch-mode.js (wired via wireTouchToggle()).
 */
.ws-touch-toggle {
  height: 28px;
  padding: 0 10px;
  border: 0.5px solid var(--color-surface-rule-strong);
  border-radius: 6px;
  background: var(--color-surface-card);
  font-size: 11px;
  font-weight: 500;
  color: var(--color-text-muted);
  cursor: pointer;
  letter-spacing: 0.04em;
  flex-shrink: 0;
  transition: background 0.1s, color 0.1s, border-color 0.1s;
}
.ws-touch-toggle.touch-on {
  background: var(--color-success-strong);
  border-color: var(--color-success-strong);
  color: #fff;
}
.ws-touch-toggle:focus-visible {
  outline: 2px solid var(--color-action-primary);
  outline-offset: 2px;
}

/* Wrapper that clusters the panel tally + touch toggle on the right side of
 * a .choreo-panel-title (which uses justify-content:space-between). */
.choreo-panel-actions {
  display: inline-flex;
  gap: 10px;
  align-items: center;
}

/* ── Choreo picker: big-button flat grid ───────────────────────────────────
 * In touch mode the two-step category → codes flow collapses into one grid
 * of every valid choreography code. Each tile shows the code (huge), the
 * deduction value, and the category as a small label.
 */
.choreo-big-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
}
.choreo-big-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 96px;
  padding: 12px 8px;
  border: 1px solid var(--color-surface-rule-strong);
  border-radius: 12px;
  background: var(--color-surface-card);
  color: var(--color-text-primary);
  cursor: pointer;
  text-align: center;
  line-height: 1.2;
  touch-action: manipulation;
  -webkit-tap-highlight-color: rgba(0,0,0,0.05);
  transition: background 0.08s, border-color 0.08s, transform 0.06s;
}
.choreo-big-chip:hover { background: var(--color-danger-soft-bg); border-color: var(--color-danger-soft-border); }
.choreo-big-chip:active { transform: scale(0.97); }
.choreo-big-chip.restricted { opacity: 0.35; cursor: not-allowed; }
.choreo-big-chip.restricted:hover { background: var(--color-surface-card); border-color: var(--color-surface-rule-strong); }
.choreo-big-code { font-size: clamp(1.5rem, 5.5vw, 2.25rem); font-weight: 700; font-family: var(--font-mono); line-height: 1; }
.choreo-big-val  { font-size: 13px; color: var(--color-role-ahj); font-family: var(--font-mono); font-weight: 600; }
.choreo-big-cat  { font-size: 10px; color: var(--color-text-muted); letter-spacing: 0.06em; text-transform: uppercase; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Required-movements widget: big-card mode ──────────────────────────────
 * `.rm-grid.touch` swaps the wrap flex layout for a 2-col card grid. Each
 * card grows tall enough to host a prominent count + name with a sizable
 * minus button. Compact mode (default) is unchanged.
 */
.rm-grid.touch {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
}
.rm-grid.touch .rm-chip {
  display: grid;
  grid-template-columns: 1fr 44px;
  align-items: stretch;
  min-height: 88px;
  border-radius: 12px;
  overflow: hidden;
}
.rm-grid.touch .rm-chip > .rm-btn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 4px;
  padding: 10px 12px;
  font-size: 13px;
  text-align: left;
  touch-action: manipulation;
}
.rm-grid.touch .rm-btn-count { font-size: clamp(1.6rem, 5vw, 2.25rem); font-weight: 700; font-family: var(--font-mono); line-height: 1; }
.rm-grid.touch .rm-btn-name { font-size: 13px; opacity: 0.95; white-space: normal; max-width: 100%; overflow: visible; text-overflow: clip; line-height: 1.15; }
.rm-grid.touch .rm-btn-req  { font-size: 11px; opacity: 0.7; }
.rm-grid.touch .rm-advisory-badge { font-size: 10px; margin-left: 0; align-self: flex-start; }
.rm-grid.touch .rm-minus-btn {
  width: 44px;
  min-width: 44px;
  font-size: 22px;
  border-left: 1px solid var(--color-surface-rule-strong);
  touch-action: manipulation;
}

/* ── Big-button mode: floating exit pill ──────────────────────────────────
 * Mounted by js/touch-mode.js (and the DD pages' view-mode toggles) whenever
 * big-button mode is on. Sits at the bottom-right corner above page content
 * (z-index 80) but below the `#exit-warning` modal (z-index 999), so users
 * always have a visible escape from any scroll position.
 */
.big-button-exit-pill {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 80;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 12px 20px;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: var(--font-weight-medium);
  line-height: 1;
  color: var(--color-text-on-accent);
  background: var(--color-action-primary);
  border: 0;
  border-radius: 999px;
  box-shadow: var(--shadow-md);
  cursor: pointer;
  touch-action: manipulation;
}
.big-button-exit-pill:hover { background: var(--color-action-primary-hover); }
.big-button-exit-pill:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.big-button-exit-pill:active { transform: scale(0.97); }

/* ── Big-button mode: enlarge topbar dashboard escape ─────────────────────
 * The default `.topbar-back` is a 12px / 5–10px target — fine for mouse,
 * undersized for a finger. While big-button mode is on (touch-mode for
 * AHJ/HJ/Quick, dd-touch-mode for DD-bearing pages), grow it to a real
 * touch target so users can reliably leave the page.
 */
body.touch-mode .topbar-back,
body.dd-touch-mode .topbar-back {
  font-size: 14px;
  padding: 10px 16px;
  border-radius: 8px;
}