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

html, body {
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: contain;
  width: 100%;
  max-width: 100vw;
  overflow-x: hidden;
}

button, input, textarea {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

body {
  background-color: var(--bg);
  background-image: radial-gradient(
    ellipse at 50% 0%,
    rgba(232, 68, 46, 0.10) 0%,
    transparent 50%
  );
  background-repeat: no-repeat;
  background-size: 100% 600px;
  color: var(--text);
  font-family: 'Barlow Condensed', sans-serif;
  min-height: 100vh;
  min-height: 100dvh;
  padding:
    calc(env(safe-area-inset-top) + 8px)
    calc(env(safe-area-inset-right) + 8px)
    calc(env(safe-area-inset-bottom) + 12px)
    calc(env(safe-area-inset-left) + 8px);
}

header {
  text-align: center;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

header h1 {
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* Smash-logo lean - brand moment, more pronounced than the functional labels. */
  transform: skewX(-10deg);
}

header p {
  font-size: 0.65rem;
  color: var(--dim);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-top: 2px;
}

.panels {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-bottom: 10px;
  width: 100%;
  max-width: 100%;
}

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 9px;
  padding: 9px 8px;
  min-width: 0;
  overflow: hidden;
}

/* Per-category panel accents. Each panel sets its own --accent triple and
   the shared block below applies the gradient overlay, tinted border, and
   top-edge highlight using rgba(var(--accent), ...). Tweak opacities here
   to dial the effect up or down for all three at once. */
.panel.exp-panel       { --accent: 77, 159, 255; }
.panel.inexp-panel     { --accent: 46, 232, 160; }
.panel.set-teams-panel { --accent: 255, 201, 77; }

.panel.exp-panel,
.panel.inexp-panel,
.panel.set-teams-panel {
  background-image: linear-gradient(
    180deg,
    rgba(var(--accent), 0.08) 0%,
    rgba(var(--accent), 0) 40%
  );
  border-color: rgba(var(--accent), 0.4);
  box-shadow:
    inset 0 1px 0 rgba(var(--accent), 0.25),
    0 0 14px rgba(var(--accent), 0.05);
}

.panel.exp-panel .panel-header,
.panel.inexp-panel .panel-header,
.panel.set-teams-panel .panel-header {
  border-bottom-color: rgba(var(--accent), 0.2);
}

.panel-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px;
  margin-bottom: 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
}

.panel-label {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Subtle slash on functional labels. inline-block lets the transform apply. */
  display: inline-block;
  transform: skewX(-6deg);
}

.panel-label.exp   { color: var(--exp); }
.panel-label.inexp { color: var(--inexp); }
.panel-label.set   { color: var(--set); }

.panel-count {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.65rem;
  color: var(--dim);
  flex-shrink: 0;
}

.panel-header-right {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.btn-bulk {
  background: transparent;
  color: var(--dim);
  border: 1px solid var(--border);
  border-radius: 5px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
}

.btn-bulk:active {
  background: var(--surface);
  color: var(--text);
}

.input-row {
  display: flex;
  gap: 4px;
  margin-bottom: 8px;
  min-width: 0;
}

input[type="text"] {
  flex: 1 1 0;
  min-width: 0;
  max-width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 8px;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 16px; /* prevents iOS auto-zoom on focus */
  outline: none;
  transition: border-color 0.15s;
  -webkit-appearance: none;
  appearance: none;
}

input[type="text"]:focus { border-color: #4f6ef7; }
input[type="text"]::placeholder { color: var(--muted); }

.btn-add {
  background: #1f2a4a;
  color: var(--exp);
  border: 1px solid #2a3d6a;
  border-radius: 6px;
  padding: 0;
  width: 40px;
  min-height: 40px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 1.2rem;
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.btn-add:active { background: #263660; }

.btn-add.inexp {
  background: #0a2420;
  color: var(--inexp);
  border-color: #12483a;
}

.btn-add.inexp:active { background: #123028; }

.btn-add.set {
  background: #2a2010;
  color: var(--set);
  border-color: #4a3a18;
}

.btn-add.set:active { background: #382a14; }

.set-teams-panel { margin-bottom: 10px; }

.set-teams-panel .panel-header { margin-bottom: 8px; }

/* The whole panel header is a button now so the entire row is tappable
   with keyboard + screen-reader support. Strip native button defaults so
   it still looks identical to the original <div class="panel-header">. */
.set-teams-toggle {
  width: 100%;
  background: transparent;
  border: none;
  padding-left: 0;
  padding-right: 0;
  cursor: pointer;
  color: inherit;
  font: inherit;
  text-align: left;
  -webkit-tap-highlight-color: transparent;
}

.set-teams-meta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.set-teams-chevron {
  color: var(--dim);
  transition: transform 0.15s, color 0.15s;
}

/* Collapsed: hide the body region, rotate the chevron up. Also drop the
   header's bottom margin + border so the collapsed row reads as one slim
   strip rather than a header with an invisible body below it. */
.set-teams-panel.collapsed .set-teams-body  { display: none; }
.set-teams-panel.collapsed .set-teams-toggle { margin-bottom: 0; }
.set-teams-panel.collapsed .panel-header     { margin-bottom: 0; padding-bottom: 0; border-bottom: none; }
.set-teams-panel.collapsed .set-teams-chevron { transform: rotate(180deg); }

/* Populated panel gets a gold-tinted count so a collapsed-but-non-empty
   panel is visually distinct from a collapsed-and-empty one. */
.set-teams-panel.has-pairs .panel-count    { color: var(--set); font-weight: 700; }
.set-teams-panel.has-pairs .set-teams-chevron { color: var(--set); }

/* Tap feedback */
.set-teams-toggle:active .panel-label { opacity: 0.7; }

.fp-add { align-items: center; }

.fp-add .fp-amp {
  flex-shrink: 0;
  color: var(--dim);
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  padding: 0 2px;
}

.fixed-pair-row {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--bg);
  border-radius: 5px;
  padding: 7px 8px 7px 12px;
  min-height: 34px;
  min-width: 0;
  position: relative;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    inset 0 -1px 0 rgba(0, 0, 0, 0.3);
}

.fixed-pair-row::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  background: var(--set);
}

.fixed-pair-row .name-edit { flex: 1 1 0; }

.fixed-pair-row .fp-amp {
  color: var(--dim);
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  flex-shrink: 0;
}

.results-add {
  margin-top: 0;
  margin-bottom: 18px;
}

.results-add .section-label {
  margin-bottom: 8px;
}

.name-list {
  display: flex;
  flex-direction: column;
  gap: 3px;
  max-height: 44dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.name-list::-webkit-scrollbar { width: 3px; }
.name-list::-webkit-scrollbar-track { background: transparent; }
.name-list::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }

/* Player rows are styled as name tags: 3px category-color bar on the left,
   a faint top-edge highlight + slight bottom-edge shadow inset for the
   "slightly raised card" feel, and medium-weight (500) text. The bar color
   comes from --bar-color set per-panel below; .fixed-pair-row (set teams)
   hardcodes gold via its own rule further down. */
.name-tag {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--bg);
  border-radius: 5px;
  padding: 7px 8px 7px 12px;
  min-height: 40px;
  min-width: 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  font-weight: 500;
  gap: 4px;
  position: relative;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    inset 0 -1px 0 rgba(0, 0, 0, 0.3);
}

.name-tag::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  background: var(--bar-color, var(--border));
}

.exp-panel   .name-tag { --bar-color: var(--exp); }
.inexp-panel .name-tag { --bar-color: var(--inexp); }

.name-tag > span {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.btn-remove {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 -4px 0 0;
  border-radius: 5px;
}

.btn-remove:active { color: #ef4444; background: rgba(239,68,68,0.08); }

.name-edit {
  flex: 1;
  min-width: 0;
  width: 100%;
  background: transparent;
  border: none;
  border-bottom: 1px solid transparent;
  border-radius: 0;
  padding: 0;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 16px; /* prevents iOS auto-zoom on focus */
  font-weight: 500; /* match the name-tag's bolder weight */
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: border-bottom-color 0.15s;
}

.name-edit:focus { border-bottom-color: #4f6ef7; }

.list-empty {
  font-size: 0.68rem;
  color: var(--dim);
  text-align: center;
  padding: 10px 0;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.btn-generate {
  width: 100%;
  padding: 13px;
  background: var(--surface);
  color: var(--dim);
  border: 1px solid var(--border);
  border-radius: 9px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.95rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: not-allowed;
  margin-bottom: 6px;
  transition: all 0.15s;
}

.btn-generate.active {
  background: linear-gradient(120deg,
    #e8442e 0%,
    #c93820 35%,
    #b03018 60%,
    #d4882a 85%,
    #ffd76e 100%);
  border-color: rgba(255, 201, 77, 0.6);
  color: #fffcef;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  cursor: pointer;
  /* Slow breathing pulse: a gold halo ring expands outward and fades while
     the drop shadow swells slightly. The single most-tapped surface in the
     app now visibly invites the tap. */
  animation: btn-generate-pulse 1.8s ease-in-out infinite;
}

@keyframes btn-generate-pulse {
  0%, 100% {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.18),
      0 0 0 0 rgba(255, 201, 77, 0.5),
      0 4px 12px rgba(232, 68, 46, 0.28);
  }
  50% {
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.25),
      0 0 0 6px rgba(255, 201, 77, 0),
      0 6px 16px rgba(232, 68, 46, 0.45);
  }
}

.btn-generate.active:active {
  animation: none;
  opacity: 0.88;
  transform: scale(0.985) skewX(-6deg);
}

@media (prefers-reduced-motion: reduce) {
  .btn-generate.active { animation: none; }
}

#results { display: none; }
#results.show { display: block; }

.section-label {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--dim);
  margin-bottom: 10px;
  transform: skewX(-6deg);
  transform-origin: left center;
}

/* Team cards (stacked members, not versus) */
.team-card {
  background: var(--surface);
  border: 2px solid var(--border);
  border-radius: 11px;
  padding: 13px 14px;
  margin-bottom: 11px;
  display: flex;
  align-items: stretch;
  gap: 12px;
  /* Smash fighter-card feel: top edge highlight inside + lifted drop shadow
     outside. The combination of slightly thicker border + inner highlight +
     outer shadow is what reads as "card object" rather than "list row" at
     mobile scale. */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.07),
    0 5px 16px rgba(0, 0, 0, 0.38);
}

.team-card.fixed {
  border-left-width: 4px;
  border-left-color: var(--set);
  padding-left: 12px;
  /* Gold-tinted top highlight + faint outer gold halo so the Set Teams card
     subtly glows in its category color, on top of the base drop shadow. */
  box-shadow:
    inset 0 1px 0 rgba(255, 201, 77, 0.22),
    0 5px 16px rgba(0, 0, 0, 0.38),
    0 0 20px rgba(255, 201, 77, 0.08);
}

.team-card.fixed .team-num { color: var(--set); }

/* Big stencil-style team number. Chunky Barlow Condensed 800 in dim grey
   (or gold on fixed cards), skewed -10deg to match the brand-tier headers
   from PR #25, with a horizontal stencil cut drawn by a ::after pseudo
   filled in the card's own background color. */
.team-num {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--dim);
  min-width: 34px;
  letter-spacing: -0.02em;
  line-height: 1;
  align-self: center;
  position: relative;
  transform: skewX(-6deg);
  transform-origin: center;
}

.team-num::after {
  content: '';
  position: absolute;
  left: -3px;
  right: -3px;
  top: calc(50% - 0.75px);
  height: 1.5px;
  background: var(--surface);
  pointer-events: none;
}

.team-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.team-name-input {
  width: 100%;
  background: transparent;
  border: none;
  border-bottom: 1px dashed var(--border);
  border-radius: 0;
  padding: 0 0 8px 0;
  margin-bottom: 8px;
  color: var(--text);
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.05rem; /* >=16px so iOS does not auto-zoom; up from 16px flat */
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}

.team-name-input::placeholder {
  color: var(--dim);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0.04em;
}

.team-name-input:focus { border-bottom-color: var(--text); border-bottom-style: solid; }

/* While the team has no name and the user is not actively editing the input,
   pulse the dashed underline in gold so it visibly invites a tap. The
   :placeholder-shown:not(:focus) compound matches "input is empty AND not
   being edited", so the animation stops the moment the user taps in OR
   types a character. */
.team-name-input:placeholder-shown:not(:focus) {
  animation: team-name-hint 1.8s ease-in-out infinite;
}

@keyframes team-name-hint {
  0%, 100% { border-bottom-color: rgba(255, 201, 77, 0.25); }
  50%      { border-bottom-color: rgba(255, 201, 77, 0.9); }
}

@media (prefers-reduced-motion: reduce) {
  .team-name-input:placeholder-shown:not(:focus) {
    animation: none;
    border-bottom-color: rgba(255, 201, 77, 0.55);
  }
}

.team-member {
  display: flex;
  align-items: center;
  gap: 9px;
  cursor: pointer;
  padding: 4px 6px;
  border-radius: 6px;
  transition: background-color 0.15s, box-shadow 0.15s;
}

.team-member:active { background: rgba(255, 255, 255, 0.04); }

.team-member:focus-visible {
  outline: 2px solid var(--exp);
  outline-offset: 2px;
}
.team-member.inexp:focus-visible { outline-color: var(--inexp); }

/* Selected swap player breathes so it is obviously the "active" pick. Pulse
   matches the 1.8s rhythm used by the Generate button and the team-name
   underline pulse - one design language across the app. Background alpha
   and outline width/alpha both animate, so the selection both brightens
   and tightens at peak. */
.team-member.selected.exp {
  background: rgba(77, 159, 255, 0.08);
  box-shadow: inset 0 0 0 1.5px rgba(77, 159, 255, 0.55);
  animation: swap-pulse-exp 1.8s ease-in-out infinite;
}

.team-member.selected.inexp {
  background: rgba(46, 232, 160, 0.08);
  box-shadow: inset 0 0 0 1.5px rgba(46, 232, 160, 0.55);
  animation: swap-pulse-inexp 1.8s ease-in-out infinite;
}

@keyframes swap-pulse-exp {
  0%, 100% {
    background: rgba(77, 159, 255, 0.08);
    box-shadow: inset 0 0 0 1.5px rgba(77, 159, 255, 0.55);
  }
  50% {
    background: rgba(77, 159, 255, 0.22);
    box-shadow: inset 0 0 0 2px rgba(77, 159, 255, 1);
  }
}

@keyframes swap-pulse-inexp {
  0%, 100% {
    background: rgba(46, 232, 160, 0.08);
    box-shadow: inset 0 0 0 1.5px rgba(46, 232, 160, 0.55);
  }
  50% {
    background: rgba(46, 232, 160, 0.22);
    box-shadow: inset 0 0 0 2px rgba(46, 232, 160, 1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .team-member.selected.exp,
  .team-member.selected.inexp { animation: none; }
  .team-member.selected.exp {
    background: rgba(77, 159, 255, 0.18);
    box-shadow: inset 0 0 0 2px var(--exp);
  }
  .team-member.selected.inexp {
    background: rgba(46, 232, 160, 0.18);
    box-shadow: inset 0 0 0 2px var(--inexp);
  }
}

.swap-hint {
  display: none;
  padding: 8px 12px;
  border-radius: 7px;
  margin-bottom: 10px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-align: center;
}

.swap-hint.show { display: block; }

.swap-hint.exp {
  background: rgba(77, 159, 255, 0.10);
  border: 1px solid rgba(77, 159, 255, 0.35);
  color: var(--exp);
}

.swap-hint.inexp {
  background: rgba(46, 232, 160, 0.10);
  border: 1px solid rgba(46, 232, 160, 0.35);
  color: var(--inexp);
}

.member-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}

.member-dot.exp   { background: var(--exp); }
.member-dot.inexp { background: var(--inexp); }
.member-dot.set   { background: var(--set); }

.member-name {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.95rem;
  font-weight: 500;
  word-break: break-word;
}

.member-name.exp   { color: var(--exp); }
.member-name.inexp { color: var(--inexp); }
.member-name.set   { color: var(--set); }

.team-hr {
  border: none;
  border-top: 1px solid var(--border);
}

.unpaired-block { margin-top: 18px; }

.unpaired-label {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  margin-bottom: 9px;
  transform: skewX(-6deg);
  transform-origin: left center;
}

.unpaired-label.exp   { color: var(--exp); }
.unpaired-label.inexp { color: var(--inexp); }

.unpaired-tag {
  display: inline-flex;
  align-items: center;
  position: relative;
  background: var(--bg);
  border-radius: 5px;
  padding: 6px 10px 6px 12px;
  margin: 0 5px 5px 0;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  font-weight: 500;
  min-height: 30px;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    inset 0 -1px 0 rgba(0, 0, 0, 0.3);
}

.unpaired-tag::before {
  content: '';
  position: absolute;
  left: 0;
  top: 4px;
  bottom: 4px;
  width: 3px;
  background: var(--bar-color, var(--border));
}

.unpaired-tag.exp   { --bar-color: var(--exp);   color: var(--text); }
.unpaired-tag.inexp { --bar-color: var(--inexp); color: var(--text); }

/* Inline hint that appears under an unpaired bucket when the pairing leaves
   a strand the user can't resolve in the current mode. Muted info tone
   (dim text + small info-circle icon) - explains WHY those players are
   still sitting in the bucket, doesn't shout. */
.unpaired-hint {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  letter-spacing: 0.03em;
  color: var(--dim);
}

.unpaired-hint .hint-icon {
  flex-shrink: 0;
  width: 12px;
  height: 12px;
}

.btn-back {
  width: 100%;
  margin-top: 20px;
  padding: 15px;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 9px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background-color 0.18s, border-color 0.18s, color 0.18s;
}

.btn-back:active { background: var(--surface); }

.btn-back.armed {
  position: relative;
  overflow: hidden;
  background: rgba(239, 68, 68, 0.12);
  border-color: rgba(239, 68, 68, 0.55);
  color: var(--danger);
}

.btn-back.armed:active { background: rgba(239, 68, 68, 0.22); }

/* Hairline countdown bar that depletes left-to-right during the 3s arm
   window. Bound to .armed so it mounts/unmounts with the JS timer
   (app.js:402-430) automatically - no extra wiring needed. */
.btn-back.armed::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: var(--danger);
  transform-origin: left;
  animation: reset-timer 3s linear forwards;
}

@keyframes reset-timer { to { transform: scaleX(0); } }

@media (prefers-reduced-motion: reduce) {
  .btn-back.armed::after { display: none; }
}

.btn-pair-unpaired {
  width: 100%;
  margin-top: 14px;
  padding: 14px;
  background: linear-gradient(120deg, #122c54 0%, #112a1a 100%);
  color: var(--text);
  border: 1px solid #1e3a55;
  border-radius: 9px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
}

.btn-pair-unpaired:active { opacity: 0.75; }

.btn-challonge {
  width: 100%;
  margin-top: 12px;
  padding: 16px;
  background: linear-gradient(120deg, #1a3a20 0%, #112a1a 100%);
  color: var(--inexp);
  border: 1px solid #1e4a28;
  border-radius: 9px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
}

.btn-challonge:active { opacity: 0.75; }

/* Export modal - bottom sheet */
.modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.7);
  z-index: 100;
}

.modal-backdrop.open { display: flex; align-items: flex-end; }

.modal-sheet {
  width: 100%;
  background: #161820;
  border: 1px solid var(--border);
  border-bottom: none;
  border-radius: 16px 16px 0 0;
  padding:
    22px
    calc(env(safe-area-inset-right) + 18px)
    calc(env(safe-area-inset-bottom) + 24px)
    calc(env(safe-area-inset-left) + 18px);
  max-height: 85dvh;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.modal-handle {
  width: 44px;
  height: 4px;
  background: var(--border);
  border-radius: 2px;
  margin: 0 auto 2px;
}

.modal-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}

/* Role-based accent variants on modal-sheet. The cascade sets CSS custom
   properties consumed by .modal-title and the primary buttons. is-input and
   is-list resolve to the same colors today but stay semantically distinct so
   future styling can diverge without renaming HTML. */
.modal-sheet.is-input,
.modal-sheet.is-list,
.modal-sheet.is-info {
  --modal-accent:        var(--exp);
  --modal-accent-grad:   var(--exp-grad);
  --modal-accent-border: var(--exp-border);
}

.modal-sheet.is-outbound {
  --modal-accent:        var(--inexp);
  --modal-accent-grad:   var(--inexp-grad);
  --modal-accent-border: var(--inexp-border);
}

.modal-sheet.is-danger {
  --modal-accent:        var(--danger);
  --modal-accent-grad:   var(--danger-grad);
  --modal-accent-border: var(--danger-border);
}

/* Side-drawer variant. :has() scopes off the base display-toggle (which
   kills CSS transitions) only for the drawer; other modals stay as-is. */
.modal-backdrop:has(> .modal-sheet.is-drawer) {
  display: flex;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  align-items: stretch;
  justify-content: flex-start;
  transition: opacity 250ms ease-out, visibility 0s linear 250ms;
}
.modal-backdrop.open:has(> .modal-sheet.is-drawer) {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
  transition: opacity 250ms ease-out, visibility 0s linear 0s;
}

.modal-sheet.is-drawer {
  width: 85vw;
  max-width: 320px;
  height: 100dvh;
  max-height: 100dvh;
  border-radius: 0 16px 16px 0;
  border-bottom: 1px solid var(--border);
  border-left: none;
  box-shadow: 4px 0 24px rgba(0, 0, 0, 0.4);
  /* Clear the floating × button at top:8px + 40px = 48px + safe-area-inset-top,
     plus 16px breathing room. Overrides the base sheet's 22px padding-top. */
  padding-top: calc(env(safe-area-inset-top) + 64px);
  transform: translateX(-100%);
  transition: transform 250ms cubic-bezier(0.2, 0, 0, 1);
}
.modal-backdrop.open:has(> .modal-sheet.is-drawer) .modal-sheet.is-drawer {
  transform: translateX(0);
}
/* JS toggles .is-dragging during touch follow so the sheet tracks the finger. */
.modal-sheet.is-drawer.is-dragging { transition: none; }

/* The horizontal drag-grabber bar belongs to bottom sheets, not drawers. */
.modal-sheet.is-drawer .modal-handle { display: none; }

/* Hamburger morphs to × via :has() so all close paths (×, backdrop, Done,
   swipe) stay in sync. z-index bump puts the × above the backdrop (z=100). */
body:has(#menu-modal.open) #menu-btn { z-index: 110; }

#menu-btn line {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform 250ms cubic-bezier(0.2, 0, 0, 1), opacity 200ms ease-out;
}
body:has(#menu-modal.open) #menu-btn line:nth-child(1) {
  transform: translateY(5px) rotate(45deg);
}
body:has(#menu-modal.open) #menu-btn line:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}
body:has(#menu-modal.open) #menu-btn line:nth-child(3) {
  transform: translateY(-5px) rotate(-45deg);
}

/* `backwards` fill mode is mandatory — without it items flash at final state
   for one frame before the delay starts. */
@keyframes drawer-stagger-in {
  from { opacity: 0; transform: translateX(-12px); }
  to   { opacity: 1; transform: translateX(0); }
}
@keyframes drawer-divider-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Stagger fires per-view via .is-entering so sub-view nav replays the animation.
   Delays overlap the 250ms slide tail so total view-entry settles ~400ms. */
.drawer-view.is-entering .menu-option {
  animation: drawer-stagger-in 250ms cubic-bezier(0.2, 0, 0, 1) backwards;
}
.drawer-view.is-entering .menu-option:nth-child(1) { animation-delay: 150ms; }
.drawer-view.is-entering .menu-option:nth-child(2) { animation-delay: 180ms; }
.drawer-view.is-entering .menu-option:nth-child(3) { animation-delay: 210ms; }
.drawer-view.is-entering .menu-option:nth-child(4) { animation-delay: 240ms; }
.drawer-view.is-entering .menu-option:nth-child(5) { animation-delay: 270ms; }
.drawer-view.is-entering .menu-divider {
  animation: drawer-divider-in 150ms ease-out 200ms backwards;
}

/* Drawer viewport clips off-screen views during sub-view navigation. */
.drawer-viewport {
  flex: 1;
  position: relative;
  overflow: hidden;
}

.drawer-view {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
  transition: transform 250ms cubic-bezier(0.2, 0, 0, 1),
              opacity   250ms cubic-bezier(0.2, 0, 0, 1);
}

/* Active view: on-screen. */
.modal-sheet[data-view="main"]            .drawer-view-main,
.modal-sheet[data-view="team-creation"]   .drawer-view-team-creation,
.modal-sheet[data-view="scheduling"]      .drawer-view-scheduling,
.modal-sheet[data-view="options"]         .drawer-view-options {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}

/* Going forward into a sub-view: main slides off LEFT. */
.modal-sheet[data-view="team-creation"][data-nav-direction="forward"] .drawer-view-main,
.modal-sheet[data-view="scheduling"][data-nav-direction="forward"]    .drawer-view-main,
.modal-sheet[data-view="options"][data-nav-direction="forward"]       .drawer-view-main {
  transform: translateX(-100%);
  opacity: 0;
}

/* Back-nav: sub-views fall back to the default translateX(100%), so they
   exit to the right while the main view animates back to translateX(0). */

/* Sub-view header: back arrow + title in a flex row, overriding the base
   .modal-header at line 948 which is baseline/space-between. */
.modal-header.sub-header {
  align-items: center;
  justify-content: flex-start;
  gap: 6px;
}
.drawer-back-btn {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  color: var(--text);
  cursor: pointer;
  border-radius: 8px;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}
.drawer-back-btn:active { background: rgba(255, 255, 255, 0.04); }
.drawer-back-btn svg { width: 22px; height: 22px; }

/* Top-level menu buttons get a leading icon column. Sub-view buttons keep
   the existing column layout (no icons) since they're not category buttons. */
.drawer-view-main .menu-option {
  flex-direction: row;
  align-items: center;
  gap: 12px;
}
.menu-option-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  color: var(--text);
}
.menu-option-icon svg { width: 22px; height: 22px; }
.menu-option-text {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
  flex: 1;
  min-width: 0;
}

/* Scheduling sub-view placeholder. Quiet copy until the feature ships. */
.scheduling-placeholder {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 14px;
}
.scheduling-placeholder .placeholder-title {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
  margin: 0;
}
.scheduling-placeholder .placeholder-body {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  color: var(--muted);
  line-height: 1.5;
  margin: 0;
}

/* Profiles screen: full-page overlay that slides in from the right. Sits at
   z-index 80 — below modal backdrops (100) so showConfirm can overlay it. */
#profiles {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: var(--bg);
  transform: translateX(100%);
  transition: transform 250ms cubic-bezier(0.2, 0, 0, 1);
  display: flex;
  flex-direction: column;
  padding-top: env(safe-area-inset-top);
}
body[data-screen="profiles"] #profiles { transform: translateX(0); }
body[data-screen="profiles"] header,
body[data-screen="profiles"] #menu-btn { display: none !important; }

/* Hide the + button when the form sub-view is open. Two-attribute trigger
   so we don't have to rely on sibling selectors that depend on DOM order. */
body[data-profiles-subview="form"] .profiles-add-btn,
body[data-profiles-subview="main-picker"] .profiles-add-btn { display: none; }

/* Stats strip below the header - hidden when not on the list subview
   so the form/picker get the full viewport height. */
.profiles-stats {
  display: flex;
  gap: 8px;
  padding: 10px 16px;
  flex-shrink: 0;
  border-bottom: 1px solid var(--border);
}
body[data-profiles-subview="form"] .profiles-stats,
body[data-profiles-subview="main-picker"] .profiles-stats { display: none; }

.profile-stat-chip {
  flex: 1;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 6px;
  padding: 6px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface);
}
.profile-stat-num {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
}
.profile-stat-label {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.85;
}
.profile-stat-chip.exp {
  color: var(--exp);
  background: rgba(77, 159, 255, 0.10);
  border-color: rgba(77, 159, 255, 0.28);
}
.profile-stat-chip.inexp {
  color: var(--inexp);
  background: rgba(46, 232, 160, 0.10);
  border-color: rgba(46, 232, 160, 0.28);
}
.profile-stat-chip.total {
  color: var(--dim);
}

/* Dedicated Profiles header bar - not .modal-header to avoid baseline-rule leak. */
.profiles-header {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.profiles-back-btn,
.profiles-add-btn {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: none;
  color: var(--text);
  cursor: pointer;
  border-radius: 8px;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}
.profiles-back-btn:active,
.profiles-add-btn:active { background: rgba(255, 255, 255, 0.04); }
.profiles-back-btn svg,
.profiles-add-btn svg { width: 22px; height: 22px; }
.profiles-title {
  flex: 1;
  text-align: center;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text);
  transform: skewX(-6deg);
}

/* List ↔ form sub-view sliding (mirrors the drawer pattern). */
.profiles-viewport {
  flex: 1;
  position: relative;
  overflow: hidden;
  /* min-height: 0 is needed so this flex item can shrink below its
     intrinsic content size on iOS Safari - otherwise the viewport
     can grow past the screen and the absolute child has no clipped
     box to scroll inside of. */
  min-height: 0;
}
.profiles-view {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px;
  padding-bottom: calc(env(safe-area-inset-bottom) + 16px);
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
  transition: transform 250ms cubic-bezier(0.2, 0, 0, 1),
              opacity 250ms cubic-bezier(0.2, 0, 0, 1);
}
.profiles-viewport[data-subview="list"] .profiles-view-list {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}
.profiles-viewport[data-subview="form"] .profiles-view-form {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}
.profiles-viewport[data-subview="form"] .profiles-view-list {
  transform: translateX(-100%);
  opacity: 0;
}
.profiles-viewport[data-subview="main-picker"] .profiles-view-main-picker {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}
.profiles-viewport[data-subview="main-picker"] .profiles-view-form,
.profiles-viewport[data-subview="main-picker"] .profiles-view-list {
  transform: translateX(-100%);
  opacity: 0;
}

/* Profile form: Main-character trigger button looks like a sibling input
   field so it slots into the form visually, but it's a button that opens
   the roster picker sub-view instead of a text input. */
.profile-main-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  min-height: 42px;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 16px;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.profile-main-trigger:active { background: var(--surface); }
.profile-main-trigger svg {
  width: 16px;
  height: 16px;
  color: var(--dim);
  flex-shrink: 0;
}
.profile-main-trigger .is-placeholder { color: var(--muted); }

/* Main-picker sub-view: search row + grid of fighter tiles. */
.main-picker-search-row { margin-bottom: 12px; }
.main-picker-search-row input[type="search"] {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 16px; /* prevents iOS auto-zoom on focus */
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.main-picker-search-row input[type="search"]:focus { border-color: #4f6ef7; }

/* Wrapper for the profile-picker search so a custom clear button can sit
   inside the input on the right. -webkit-appearance:none above hides the
   native search cancel icon on iOS, so we draw our own. Input gets
   extra right padding to keep the text from sliding under the button. */
.schedule-picker-search-row { position: relative; }
.schedule-picker-search-row input[type="search"] { padding-right: 38px; }
.schedule-picker-search-row input[type="search"]::-webkit-search-cancel-button { display: none; }
.picker-search-clear {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--dim);
  border: none;
  border-radius: 50%;
  color: var(--bg);
  cursor: pointer;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}
.picker-search-clear[hidden] { display: none; }
.picker-search-clear:active { background: var(--text); }

.main-picker-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
}
@media (min-width: 480px) {
  .main-picker-grid { grid-template-columns: repeat(4, 1fr); }
}

.main-tile {
  padding: 8px 4px;
  min-height: 92px;
  background: rgba(77, 159, 255, 0.06);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  overflow-wrap: break-word;
  word-break: break-word;
  hyphens: auto;
}
.main-tile:active { background: rgba(77, 159, 255, 0.14); }
.main-tile[hidden] { display: none; }
.main-tile.is-selected {
  background: rgba(255, 201, 77, 0.18);
  border-color: rgba(255, 201, 77, 0.6);
  color: var(--set);
}
.main-tile-icon {
  width: 48px;
  height: 48px;
  object-fit: contain;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}
.main-tile-name {
  display: block;
  width: 100%;
}
.main-tile-clear {
  grid-column: 1 / -1;
  background: var(--surface);
  font-size: 0.85rem;
  min-height: 48px;
  flex-direction: row;
  gap: 0;
}

.main-picker-empty {
  text-align: center;
  margin-top: 24px;
  color: var(--dim);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.8rem;
}

/* Profile cards in the list view. */
.profiles-list {
  display: flex;
  flex-direction: column;
}
.profile-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 8px;
  width: 100%;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  flex-shrink: 0;
  /* Override the global `button { touch-action: manipulation }` -
     manipulation can swallow vertical pan on iOS when the touch
     starts on a button, blocking scroll of the parent list. */
  touch-action: pan-y;
}
.profile-card:active { background: var(--surface); }
/* Needed because .profile-card sets display:flex, which beats the
   implicit display:none the `hidden` attribute relies on. Without this
   the picker search filter ran but no rows visibly disappeared. */
.profile-card[hidden] { display: none; }
.profile-avatar {
  width: 40px;
  height: 40px;
  flex-shrink: 0;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.3rem;
  font-weight: 800;
}
.profile-avatar.exp   { background: rgba(77, 159, 255, 0.18); color: var(--exp); }
.profile-avatar.inexp { background: rgba(46, 232, 160, 0.18); color: var(--inexp); }
/* Icon variant: profile.main matches a known SSBU fighter id, so we
   render their stock icon in place of the letter. The skill-tinted
   background still shows through the icon's transparent corners,
   preserving the at-a-glance skill cue. */
.profile-avatar.profile-avatar-icon {
  padding: 0;
  overflow: hidden;
}
.profile-avatar.profile-avatar-icon img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  -webkit-user-drag: none;
}
.profile-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.profile-name {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--text);
}
.profile-skill-chip {
  display: inline-block;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 2px 6px;
  border-radius: 4px;
  align-self: flex-start;
}
.profile-skill-chip.exp   { color: var(--exp);   background: rgba(77, 159, 255, 0.12); }
.profile-skill-chip.inexp { color: var(--inexp); background: rgba(46, 232, 160, 0.12); }
.profile-main {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: var(--dim);
}

/* Add/edit form. */
.profile-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.form-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.form-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--dim);
}
/* Secondary helper text under a .form-label, used by the Attendance
   slider to explain what the 0-10 value means. Smaller and dimmer than
   the label, no uppercase. */
.form-helper {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: var(--dim);
  opacity: 0.7;
  margin-top: -2px;
}

/* Attendance slider row: range input takes the available width, the
   numeric value sits to the right in a fixed-width monospace box so it
   doesn't jitter as the digit count changes. */
.profile-attendance-row {
  display: flex;
  align-items: center;
  gap: 12px;
}
.profile-attendance-row input[type="range"] {
  flex: 1;
  min-width: 0;
  accent-color: var(--set);
  background: transparent;
  padding: 0;
  border: none;
}
.profile-attendance-out {
  min-width: 28px;
  text-align: center;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 8px;
}
.profile-form input,
.profile-form textarea {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  color: var(--text);
  font-family: inherit;
  font-size: 0.95rem;
  width: 100%;
}
.profile-form textarea {
  resize: vertical;
  min-height: 72px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.85rem;
}
.profile-skill-segmented {
  display: flex;
  gap: 8px;
}
.skill-seg-btn {
  flex: 1;
  padding: 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}
.skill-seg-btn[aria-checked="true"].skill-seg-exp {
  border-color: var(--exp);
  background: rgba(77, 159, 255, 0.12);
  color: var(--exp);
}
.skill-seg-btn[aria-checked="true"].skill-seg-inexp {
  border-color: var(--inexp);
  background: rgba(46, 232, 160, 0.12);
  color: var(--inexp);
}
.profile-form-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 8px;
}
.btn-profile-save {
  padding: 14px;
  background: var(--exp-grad);
  border: 1px solid var(--exp-border);
  border-radius: 8px;
  color: var(--text);
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}
.btn-profile-delete {
  padding: 14px;
  background: var(--danger-grad);
  border: 1px solid var(--danger-border);
  border-radius: 8px;
  color: var(--danger);
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}

/* ===== Scheduling screen ===========================================
   Full-page overlay parallel to #profiles. Same slide-in mechanism,
   same header styling (reuses .profiles-header / .profiles-back-btn /
   .profiles-title). The .schedule-viewport sub-view machine mirrors
   .profiles-viewport: heatmap, profile-picker, profile-shifts, shift-form. */
#schedule {
  position: fixed;
  inset: 0;
  z-index: 80;
  background: var(--bg);
  transform: translateX(100%);
  transition: transform 250ms cubic-bezier(0.2, 0, 0, 1);
  display: flex;
  flex-direction: column;
  padding-top: env(safe-area-inset-top);
}
body[data-screen="schedule"] #schedule { transform: translateX(0); }
body[data-screen="schedule"] header { display: none; }
/* render() sets menu-btn's display inline ('flex' when no tournament is
   paired), which beats a non-!important rule and lets the fixed-position
   hamburger float over the back chevron in the Scheduling header. */
body[data-screen="schedule"] #menu-btn { display: none !important; }

/* Empty 40x40 cell on the header's right edge so the title stays
   visually centered opposite the back chevron on the left. */
.schedule-header-spacer { width: 40px; height: 40px; }

/* Edit-availability button lives inside the heatmap content (top of the
   date-range row) since the header now carries only the back chevron.
   Icon-only, sized to match the inputs' visual height. */
.schedule-edit-availability-btn {
  align-self: flex-end;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  cursor: pointer;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 0.15s, border-color 0.15s;
}
.schedule-edit-availability-btn:active {
  background: var(--bg);
  border-color: rgba(255, 201, 77, 0.4);
}
.schedule-edit-availability-btn svg { width: 20px; height: 20px; }

.schedule-viewport {
  flex: 1;
  position: relative;
  overflow: hidden;
  min-height: 0;
}
.schedule-view {
  position: absolute;
  inset: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px;
  padding-bottom: calc(env(safe-area-inset-bottom) + 16px);
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
  transition: transform 250ms cubic-bezier(0.2, 0, 0, 1),
              opacity 250ms cubic-bezier(0.2, 0, 0, 1);
}
.schedule-viewport[data-subview="heatmap"] .schedule-view-heatmap,
.schedule-viewport[data-subview="profile-picker"]  .schedule-view-profile-picker,
.schedule-viewport[data-subview="profile-shifts"]  .schedule-view-profile-shifts,
.schedule-viewport[data-subview="shift-form"]      .schedule-view-shift-form {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}
/* Anything to the left of the current subview slides off-left to match
   the right-side slide-in (parallels the .profiles-viewport rules). */
.schedule-viewport[data-subview="profile-picker"] .schedule-view-heatmap,
.schedule-viewport[data-subview="profile-shifts"] .schedule-view-heatmap,
.schedule-viewport[data-subview="profile-shifts"] .schedule-view-profile-picker,
.schedule-viewport[data-subview="shift-form"]     .schedule-view-heatmap,
.schedule-viewport[data-subview="shift-form"]     .schedule-view-profile-picker,
.schedule-viewport[data-subview="shift-form"]     .schedule-view-profile-shifts {
  transform: translateX(-100%);
  opacity: 0;
}

/* Date-range picker row at the top of the heatmap subview. */
.schedule-range-row {
  display: flex;
  gap: 10px;
  margin-bottom: 12px;
}
.schedule-range-field {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.schedule-range-field input[type="date"] {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 16px; /* prevents iOS auto-zoom */
  width: 100%;
  -webkit-appearance: none;
  appearance: none;
}

/* Top-3 best-slots callout strip. Cards are tappable - tapping snap-scrolls
   the heatmap to the target cell. */
.schedule-best-slots {
  display: flex;
  gap: 8px;
  margin-bottom: 12px;
  overflow-x: auto;
  scrollbar-width: none;
}
.schedule-best-slots::-webkit-scrollbar { display: none; }
.schedule-best-slot-card {
  flex: 1 0 auto;
  min-width: 100px;
  padding: 8px 10px;
  background: rgba(46, 232, 160, 0.10);
  border: 1px solid rgba(46, 232, 160, 0.30);
  border-radius: 8px;
  color: var(--text);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: left;
  font-family: inherit;
}
.schedule-best-slot-card:active { background: rgba(46, 232, 160, 0.18); }
.schedule-best-slot-rank {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.65rem;
  color: var(--inexp);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.schedule-best-slot-when {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.schedule-best-slot-free {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: var(--dim);
}
.schedule-best-slot-card.is-empty {
  background: var(--surface);
  border-color: var(--border);
  color: var(--dim);
  cursor: default;
}

/* Share row sits between the best-slot cards and the heatmap. Pair of
   pill buttons - left primary "Share image", right secondary "Copy text". */
.schedule-share-row {
  display: flex;
  gap: 8px;
  margin: 4px 0 14px;
}
.schedule-share-btn {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 11px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.schedule-share-btn:active { transform: translateY(1px); filter: brightness(1.15); }
.schedule-share-btn svg { flex-shrink: 0; }
.schedule-share-btn-secondary {
  background: transparent;
  color: var(--dim);
}

/* Heatmap: CSS grid with sticky hour labels (left) and sticky date headers
   (top). overflow-x: auto on the wrapper lets date columns scroll. */
.schedule-heatmap-scroll {
  /* Both axes scroll inside this bounded container so sticky hour labels
     (left axis) and date headers (top axis) anchor to the same scroll
     parent. Without overflow on both axes, sticky-top stops working. */
  overflow: auto;
  max-height: 60vh;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  -webkit-overflow-scrolling: touch;
}
.schedule-heatmap {
  display: grid;
  grid-auto-rows: 26px;
  grid-template-rows: 32px repeat(24, 26px);
  /* grid-template-columns is set inline by JS as the date count varies. */
}
.schedule-heatmap-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  color: var(--text);
}
.schedule-heatmap-cell.is-hour-label,
.schedule-heatmap-cell.is-date-header {
  background: var(--surface);
  color: var(--dim);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.schedule-heatmap-cell.is-hour-label {
  position: sticky;
  left: 0;
  z-index: 2;
}
.schedule-heatmap-cell.is-date-header {
  position: sticky;
  top: 0;
  z-index: 2;
  flex-direction: column;
  gap: 0;
  padding: 2px 0;
  line-height: 1.1;
}
.schedule-heatmap-cell.is-corner {
  position: sticky;
  top: 0;
  left: 0;
  z-index: 3;
  background: var(--surface);
}
.schedule-heatmap-cell .date-dow {
  font-size: 0.6rem;
  color: var(--muted);
  font-weight: 600;
}
.schedule-heatmap-cell .date-md {
  font-size: 0.7rem;
  color: var(--text);
  font-weight: 700;
}
/* 5-bucket color scale: red-tinted -> neutral -> green-tinted. Discrete
   buckets read more legibly on a dense 24x N grid than a continuous ramp. */
.schedule-heatmap-cell[data-bucket="0"] { background: rgba(239, 68, 68, 0.18);  color: rgba(239, 68, 68, 0.85); }
.schedule-heatmap-cell[data-bucket="1"] { background: rgba(239, 68, 68, 0.08);  color: var(--dim); }
.schedule-heatmap-cell[data-bucket="2"] { background: rgba(255, 201, 77, 0.10); color: var(--set); }
.schedule-heatmap-cell[data-bucket="3"] { background: rgba(46, 232, 160, 0.14); color: var(--inexp); }
.schedule-heatmap-cell[data-bucket="4"] { background: rgba(46, 232, 160, 0.30); color: var(--inexp); font-weight: 700; }
.schedule-heatmap-cell.is-target-flash {
  outline: 2px solid var(--set);
  outline-offset: -2px;
}
/* Body cells are tappable - opens the availability modal. Reset native
   button styling and give a brief tap feedback. */
.schedule-heatmap-cell.is-tappable { cursor: pointer; }
.schedule-heatmap-cell.is-tappable:active { filter: brightness(1.25); }

/* Heatmap cell-detail modal: two side-by-side lists of profile names
   - "Available" in green, "Busy" in red. Stacks on narrow widths. */
.schedule-cell-modal-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  max-height: 55vh;
  overflow-y: auto;
  padding: 4px 2px;
}
.schedule-cell-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.schedule-cell-section-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 4px 8px;
  border-radius: 6px;
}
.schedule-cell-available .schedule-cell-section-title {
  background: rgba(46, 232, 160, 0.14);
  color: var(--inexp);
}
.schedule-cell-busy .schedule-cell-section-title {
  background: rgba(239, 68, 68, 0.14);
  color: rgba(239, 99, 99, 0.95);
}
.schedule-cell-section-count {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  opacity: 0.85;
}
.schedule-cell-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.schedule-cell-name {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.85rem;
  color: var(--text);
}
.schedule-cell-empty {
  color: var(--dim);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  padding: 4px 10px;
}
.schedule-heatmap-empty {
  text-align: center;
  margin-top: 24px;
  color: var(--dim);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.8rem;
}

/* Profile-picker subview - reuses .profiles-list / .profile-card. */
.schedule-picker-hint {
  text-align: center;
  margin-bottom: 12px;
  color: var(--dim);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
}

/* Profile-shifts subview: header + list of shift rows + Add button. */
.schedule-shifts-header { margin-bottom: 12px; }
.schedule-shifts-name {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--text);
  margin: 0;
}
.schedule-shifts-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 16px;
}
.schedule-shifts-actions { display: flex; }
.schedule-shifts-actions .btn-profile-save { flex: 1; }
.schedule-shifts-date-group {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.schedule-shifts-date-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--dim);
  margin-top: 8px;
}
.schedule-shifts-date-group:first-child .schedule-shifts-date-label { margin-top: 0; }
.schedule-shift-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  width: 100%;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  color: var(--text);
}
.schedule-shift-row:active { background: var(--surface); }
.schedule-shift-row .shift-time {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.95rem;
  font-weight: 700;
  flex: 1;
}
.schedule-shift-row .shift-chev {
  width: 16px;
  height: 16px;
  color: var(--dim);
  flex-shrink: 0;
}
.schedule-shifts-empty {
  text-align: center;
  padding: 20px 10px;
  color: var(--dim);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
}

/* Shift-form subview. */
.schedule-recent-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 12px;
}
.schedule-recent-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.schedule-recent-chip {
  padding: 6px 10px;
  background: rgba(77, 159, 255, 0.10);
  border: 1px solid rgba(77, 159, 255, 0.30);
  border-radius: 999px;
  color: var(--exp);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.75rem;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.schedule-recent-chip:active { background: rgba(77, 159, 255, 0.20); }

/* Unavailable-all-day toggle: shifts the form between "specific hours" and
   "block the whole day" modes. ON state hides the hour selects so the
   user doesn't get confused about why those values are ignored. */
.schedule-allday-row { margin-bottom: 4px; }
.schedule-allday-btn {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.schedule-allday-btn:active { background: var(--surface); }
.schedule-allday-btn.is-on {
  background: rgba(255, 201, 77, 0.10);
  border-color: rgba(255, 201, 77, 0.50);
}
.allday-check {
  width: 20px;
  height: 20px;
  border-radius: 5px;
  border: 2px solid var(--dim);
  flex-shrink: 0;
  position: relative;
}
.schedule-allday-btn.is-on .allday-check {
  border-color: var(--set);
  background: var(--set);
}
.schedule-allday-btn.is-on .allday-check::after {
  content: '';
  position: absolute;
  left: 4px;
  top: 0;
  width: 6px;
  height: 11px;
  border: solid var(--bg);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.allday-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.allday-title {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.schedule-allday-btn.is-on .allday-title { color: var(--set); }
.allday-sub {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.7rem;
  color: var(--dim);
}

.schedule-time-row {
  display: flex;
  gap: 10px;
}
.schedule-time-row .form-field { flex: 1; }
.schedule-time-row select {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 16px;
  width: 100%;
  -webkit-appearance: none;
  appearance: none;
}
.schedule-hour-hint {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: var(--dim);
  margin: 0;
}
.schedule-hour-hint.is-error { color: var(--danger); }

.schedule-date-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.schedule-date-pill {
  padding: 6px 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.75rem;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  line-height: 1.2;
  text-align: center;
}
.schedule-date-pill .pill-dow {
  display: block;
  font-size: 0.6rem;
  color: var(--muted);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.schedule-date-pill.is-on {
  background: rgba(255, 201, 77, 0.18);
  border-color: rgba(255, 201, 77, 0.60);
  color: var(--set);
}
.schedule-date-pill.is-on .pill-dow { color: var(--set); }

.modal-title {
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--modal-accent, var(--text));
  /* Slash on modal headers. inline-block enables the transform. */
  display: inline-block;
  transform: skewX(-6deg);
}

.modal-hint {
  font-size: 0.72rem;
  color: var(--dim);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.export-textarea,
.bulk-textarea {
  flex: 1;
  min-height: 200px;
  max-height: 360px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  color: var(--text);
  font-family: 'IBM Plex Mono', monospace;
  font-size: 16px; /* prevents iOS auto-zoom on focus */
  line-height: 1.7;
  resize: none;
  outline: none;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.bulk-textarea:focus { border-color: #4f6ef7; }
.bulk-textarea::placeholder { color: var(--muted); }

.btn-bulk-add {
  flex: 1;
  padding: 15px;
  background: var(--modal-accent-grad);
  color: var(--text);
  border: 1px solid var(--modal-accent-border);
  border-radius: 9px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}

.btn-bulk-add:active { opacity: 0.75; }

.btn-bulk-add:disabled {
  background: var(--surface);
  border-color: var(--border);
  color: var(--dim);
  cursor: not-allowed;
  opacity: 0.6;
}

.home-actions {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 8px;
}

/* Hamburger menu button, fixed in the top-left corner. Hidden from JS in
   render() while state.hasPaired is true so it disappears with the rest of
   the Home UI during a tournament. */
.menu-btn {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 8px);
  left: calc(env(safe-area-inset-left) + 8px);
  z-index: 90;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 0.15s, border-color 0.15s;
}

.menu-btn:active {
  background: var(--bg);
  border-color: rgba(255, 201, 77, 0.4);
}

/* Menu modal option rows */
.menu-options {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.menu-option {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 12px 14px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.15s, border-color 0.15s;
}

.menu-option:active { background: var(--surface); }

.menu-option.active {
  border-color: var(--set);
  background: rgba(255, 201, 77, 0.08);
  box-shadow: inset 0 1px 0 rgba(255, 201, 77, 0.2);
}

.menu-option-title {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
}

.menu-option.active .menu-option-title { color: var(--set); }

.menu-option-desc {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: var(--dim);
  line-height: 1.4;
}

.menu-divider {
  border: none;
  border-top: 1px solid var(--border);
  margin: 4px 0;
  width: 100%;
}

.menu-option.destructive .menu-option-title { color: var(--danger); }
.menu-option.destructive:active {
  background: rgba(239, 68, 68, 0.08);
  border-color: var(--danger);
}

.info-body {
  padding: 0 4px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text);
}
.info-body p { margin: 0 0 10px; }
.info-body p:last-child { margin-bottom: 0; }
.info-body a {
  color: var(--modal-accent);
  text-decoration: underline;
  word-break: break-all;
}
.info-title {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
}
.info-meta { color: var(--dim); font-size: 0.78rem; }
.info-footnote { color: var(--dim); font-size: 0.78rem; }
.info-bullets {
  padding-left: 20px;
  margin: 0;
}
.info-bullets li { margin-bottom: 10px; }
.info-bullets li:last-child { margin-bottom: 0; }
.info-bullets strong { color: var(--text); font-weight: 700; }

.btn-presets {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 6px 12px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}

.btn-presets:active { background: var(--surface); }

.btn-presets svg { color: var(--exp); }

.preset-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 50dvh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.preset-empty {
  font-size: 0.78rem;
  color: var(--dim);
  text-align: center;
  padding: 16px 8px;
  letter-spacing: 0.04em;
  border: 1px dashed var(--border);
  border-radius: 8px;
}

.preset-row {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.preset-info {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
}

.preset-name {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}

/* Inline-rename input shown when a preset row is in edit mode. Inherits the
   global input[type="text"] base; this rule just makes it span the .preset-info
   column instead of flex-growing alongside .preset-counts. */
.preset-name-input {
  width: 100%;
  margin: 0;
}

.preset-counts {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.72rem;
  color: var(--dim);
  flex-shrink: 0;
}

.preset-actions {
  display: flex;
  gap: 6px;
}

.preset-actions button {
  flex: 1;
  padding: 8px 0;
  border-radius: 6px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  min-height: 36px;
}

.btn-preset-load {
  background: linear-gradient(120deg, #1a2e5c 0%, #1e1040 100%);
  color: var(--text);
  border: 1px solid #2a4080;
}
.btn-preset-load:active { opacity: 0.75; }

.btn-preset-rename {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
}
.btn-preset-rename:active { background: var(--surface); }

.btn-preset-delete {
  background: transparent;
  color: #ef4444;
  border: 1px solid #4a1a1a;
}
.btn-preset-delete:active { background: rgba(239,68,68,0.1); }

.preset-save-row {
  display: flex;
  gap: 6px;
  margin-top: 4px;
}

#preset-save-name {
  flex: 1 1 0;
  min-width: 0;
}

.btn-preset-save {
  flex-shrink: 0;
  padding: 0 16px;
  min-height: 38px;
  background: var(--modal-accent-grad);
  color: var(--modal-accent);
  border: 1px solid var(--modal-accent-border);
  border-radius: 6px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
}
.btn-preset-save:active { opacity: 0.75; }

.modal-actions {
  display: flex;
  gap: 10px;
}

.btn-copy {
  flex: 1;
  padding: 15px;
  background: var(--modal-accent-grad);
  color: var(--modal-accent);
  border: 1px solid var(--modal-accent-border);
  border-radius: 9px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}

.btn-copy:active { opacity: 0.75; }

.btn-close-modal {
  padding: 15px 20px;
  background: transparent;
  color: var(--dim);
  border: 1px solid var(--border);
  border-radius: 9px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}

.btn-close-modal:active { background: var(--surface); }

/* Share - tertiary, matches Close visually. Hidden by default; revealed by JS
   when navigator.share is available. white-space:nowrap keeps the ellipsis
   from wrapping when all 3 buttons share a tight row. */
.btn-share {
  padding: 15px 16px;
  background: transparent;
  color: var(--dim);
  border: 1px solid var(--border);
  border-radius: 9px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
}

.btn-share:active { background: var(--surface); }

/* Confirm sheet - sits above other modals (z:150) but below the toast (z:200).
   Cancel reuses .btn-close-modal but stretches flex:1 to balance the OK button. */
#confirm-modal { z-index: 150; }

.modal-body {
  font-size: 0.95rem;
  color: var(--dim);
  line-height: 1.4;
  margin: 0;
}

.btn-confirm-ok {
  flex: 1;
  padding: 15px;
  background: var(--modal-accent-grad);
  color: var(--modal-accent);
  border: 1px solid var(--modal-accent-border);
  border-radius: 9px;
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
}

.btn-confirm-ok:active { opacity: 0.75; }

#confirm-modal .btn-close-modal { flex: 1; }

/* Toast - single shared pill pinned top-center, z above modal (modal is z:100).
   Lives outside the modal in the DOM so it persists across modal hide. Tap to
   dismiss early. Variants tint border + bg + color. */
.toast {
  position: fixed;
  top: calc(env(safe-area-inset-top) + 16px);
  left: 50%;
  transform: translate(-50%, -16px);
  z-index: 200;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 18px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--dim);
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms ease, transform 180ms ease;
  cursor: pointer;
  max-width: calc(100vw - 32px);
}

.toast.show {
  opacity: 1;
  transform: translate(-50%, 0);
  pointer-events: auto;
}

.toast.success {
  color: #22c55e;
  border-color: #166534;
  background: #0a1d12;
}

.toast.error {
  color: #ef4444;
  border-color: #7f1d1d;
  background: #2a0a0a;
}

.toast-icon {
  display: inline-flex;
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* One-shot confetti burst on Generate. Container is added to <body> by
   fireConfetti() and removed ~2.5s later. Each .confetti-piece reads its
   color/position/drift/rotation/size/timing from CSS custom properties set
   inline by the JS, so the animation logic stays here and only the random
   numbers come from JS. */
.confetti-burst {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 50;
}

.confetti-piece {
  position: absolute;
  top: -20px;
  left: var(--x);
  width: var(--size);
  height: var(--size);
  background: var(--c);
  border-radius: 1px;
  animation: confetti-fall var(--duration) ease-in var(--delay) forwards;
}

/* Every third piece is a vertical streamer rather than a square - gives the
   burst a mix of confetti and party-streamer shapes. */
.confetti-piece:nth-child(3n) {
  width: calc(var(--size) * 0.45);
  height: calc(var(--size) * 1.6);
  border-radius: 0;
}

@keyframes confetti-fall {
  0% {
    transform: translate(0, 0) rotate(0deg);
    opacity: 1;
  }
  85% { opacity: 1; }
  100% {
    transform: translate(var(--dx), 110vh) rotate(var(--rot));
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .confetti-burst { display: none; }
}

/* Reduced-motion: drawer fades, hamburger snaps, stagger off. Swipe still
   works (snap-back is just instant). */
@media (prefers-reduced-motion: reduce) {
  .modal-sheet.is-drawer {
    transition: opacity 150ms ease-out;
    transform: none;
  }
  .modal-backdrop.open:has(> .modal-sheet.is-drawer) .modal-sheet.is-drawer {
    transform: none;
  }
  #menu-btn line { transition: none; }
  body:has(#menu-modal.open) #menu-btn line:nth-child(2) {
    transform: scaleX(0);
  }
  .drawer-view { transition: none; }
  .drawer-view.is-entering .menu-option,
  .drawer-view.is-entering .menu-divider {
    animation: none;
  }
  #profiles,
  .profiles-view,
  #schedule,
  .schedule-view {
    transition: none;
  }
}

/* While the splash is showing, paint body in the splash-gradient edge
   color so any iOS-Safari viewport-mismatch gap doesn't reveal the
   app's dark --bg as a "black bar at the bottom." Cleared from the
   <html> element as soon as the splash element is removed. */
html.splash-showing body { background-color: #3a0606; }

/* Skip-mode set by the inline head script when the splash has already
   played this session - hide the element from the very first paint. */
html.splash-skip #splash { display: none; }

#splash {
  position: fixed;
  inset: 0;
  /* Explicit dynamic-viewport sizing as belt+braces alongside inset:0,
     because iOS Safari's position:fixed historically binds to the
     layout viewport and doesn't always cover the full visible area. */
  width: 100vw;
  width: 100dvw;
  height: 100vh;
  height: 100dvh;
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(ellipse at 50% 40%, #e8442e 0%, #a01818 55%, #3a0606 100%);
  padding:
    env(safe-area-inset-top)
    env(safe-area-inset-right)
    env(safe-area-inset-bottom)
    env(safe-area-inset-left);
  /* Master fade is part of the same 2.5s timeline that drives the
     Kirby bounce - 92% of the duration is opacity 1, the last 8% is
     the fade-out. JS listens for animationend on this element to
     remove it from the DOM. forwards keeps the end state (opacity 0)
     so there's no flash before the JS removal fires. */
  animation: splash-master 2.5s ease forwards;
}

.splash-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
}

/* Kirby drops in from above, lands with a squash-and-stretch, then
   settles to a gentle hold. transform-origin near the feet keeps the
   bottom of Kirby planted during the impact compress. */
.splash-kirby {
  width: 180px;
  height: 180px;
  filter: drop-shadow(0 12px 24px rgba(0, 0, 0, 0.35));
  transform-origin: 50% 75%;
  animation: splash-kirby-bounce 2.5s linear forwards;
}

.splash-title {
  font-family: 'Barlow Condensed', sans-serif;
  font-size: 1.9rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #fffcef;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  margin-top: 4px;
  opacity: 0;
  transform: skewX(-10deg) translateY(20px);
  animation: splash-title-in 2.5s ease-out forwards;
}

.splash-tagline {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 0.78rem;
  color: rgba(255, 252, 239, 0.7);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  margin-top: -8px;
  opacity: 0;
  animation: splash-tagline-in 2.5s ease-out forwards;
}

@keyframes splash-master {
  0%, 92% { opacity: 1; }
  100%    { opacity: 0; }
}

@keyframes splash-kirby-bounce {
  /* Off-screen above */
  0%   { transform: translateY(-110vh) scale(1, 1); }
  /* Drop accelerates - lands at 28% (700ms) */
  28%  { transform: translateY(0)      scale(1, 1); }
  /* Impact squash */
  32%  { transform: translateY(0)      scale(1.35, 0.65); }
  /* Rebound stretch up */
  38%  { transform: translateY(-32px)  scale(0.82, 1.22); }
  /* Falls back to ground */
  44%  { transform: translateY(0)      scale(1, 1); }
  /* Second smaller squash */
  47%  { transform: translateY(0)      scale(1.18, 0.85); }
  /* Second smaller rebound */
  52%  { transform: translateY(-14px)  scale(0.93, 1.08); }
  /* Settles */
  56%  { transform: translateY(0)      scale(1, 1); }
  /* Gentle hold-breath float */
  72%  { transform: translateY(-6px)   scale(1, 1); }
  88%  { transform: translateY(0)      scale(1, 1); }
  100% { transform: translateY(0)      scale(1, 1); }
}

@keyframes splash-title-in {
  0%, 40%  { opacity: 0; transform: skewX(-10deg) translateY(20px); }
  56%      { opacity: 1; transform: skewX(-10deg) translateY(0); }
  88%      { opacity: 1; transform: skewX(-10deg) translateY(0); }
  100%     { opacity: 0; transform: skewX(-10deg) translateY(0); }
}

@keyframes splash-tagline-in {
  0%, 56%  { opacity: 0; }
  68%      { opacity: 1; }
  88%      { opacity: 1; }
  100%     { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  /* Drop the choreography - show the splash composed and still, fade
     it out at the same 2.5s mark. */
  .splash-kirby {
    animation: none;
    transform: none;
  }
  .splash-title {
    animation: none;
    opacity: 1;
    transform: skewX(-10deg);
  }
  .splash-tagline {
    animation: none;
    opacity: 1;
  }
}

/* Smash slant — every Barlow Condensed uppercase element gets the -6deg
   functional-tier skew. Brand-tier titles (header h1, splash-title) keep
   their -10deg at their own definition sites. The functional-tier
   elements that already had -6deg (panel-label, section-label, team-num,
   unpaired-label, modal-title) are likewise declared in place. Toasts
   are intentionally excluded — functional feedback, not chrome. */
.btn-generate,
.btn-back,
.btn-bulk-add,
.btn-challonge,
.btn-close-modal,
.btn-confirm-ok,
.btn-copy,
.btn-pair-unpaired,
.btn-presets,
.btn-preset-save,
.btn-share,
.info-title,
.menu-option-title,
.preset-actions button,
.team-name-input {
  transform: skewX(-6deg);
}

/* ---- Login gate ---- */

/* Full-viewport overlay above everything except the splash. When visible the
   underlying app must not be focusable; login-gate.is-active toggles that on
   <html> so the rest of the page becomes inert. */
/* The [hidden] override is mandatory: without it our `display: flex` below
   beats the browser default `[hidden] { display: none }` and the gate is
   permanently visible even after login. */
.login-gate[hidden] { display: none !important; }
.login-gate {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding:
    calc(env(safe-area-inset-top) + 24px)
    calc(env(safe-area-inset-right) + 18px)
    calc(env(safe-area-inset-bottom) + 24px)
    calc(env(safe-area-inset-left) + 18px);
  background:
    radial-gradient(ellipse at 30% 0%,  rgba(77, 159, 255, 0.18) 0%, transparent 55%),
    radial-gradient(ellipse at 80% 100%, rgba(46, 232, 160, 0.12) 0%, transparent 55%),
    var(--bg);
  overflow-y: auto;
}

html.login-locked,
html.login-locked body {
  overflow: hidden;
}

.login-card {
  width: 100%;
  max-width: 380px;
  border: 1px solid var(--border);
  border-radius: 18px;
  padding: 28px 22px 24px;
  /* Match the home hero's gradient treatment so the login feels like a
     pre-home version of the same card. */
  background:
    radial-gradient(ellipse at 25% 30%, rgba(77, 159, 255, 0.18), transparent 60%),
    radial-gradient(ellipse at 80% 80%, rgba(46, 232, 160, 0.12), transparent 55%),
    linear-gradient(180deg, var(--surface) 0%, var(--bg) 100%);
  box-shadow:
    0 30px 60px -20px rgba(0, 0, 0, 0.6),
    0 0 0 1px rgba(255, 255, 255, 0.02) inset;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.login-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 6px;
}

.login-brand-mark {
  width: 96px;
  height: 96px;
  filter: drop-shadow(0 8px 20px rgba(255, 94, 133, 0.35));
  margin-bottom: 4px;
}

.login-brand-title {
  font-family: 'Barlow Condensed', sans-serif;
  font-weight: 800;
  font-size: 26px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text);
  line-height: 1;
}

.login-brand-tagline {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--exp);
  letter-spacing: 1.5px;
  text-transform: uppercase;
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.login-field { display: flex; flex-direction: column; gap: 6px; }

.login-field-label {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--dim);
}

.login-field input {
  background: #0f1118;
  border: 1px solid var(--border);
  border-radius: 9px;
  padding: 11px 13px;
  color: var(--text);
  font-size: 15px;
  font-family: 'IBM Plex Mono', monospace;
  outline: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.login-field input:focus {
  border-color: var(--exp);
  box-shadow: 0 0 0 3px rgba(77, 159, 255, 0.18);
}

.login-field input:invalid:not(:placeholder-shown) {
  border-color: var(--danger);
}

.login-error {
  min-height: 18px;
  font-size: 13px;
  color: var(--danger);
  line-height: 1.35;
}

.login-error:empty { display: none; }

.login-submit {
  position: relative;
  background: linear-gradient(120deg, #1a2e5c 0%, #1e1040 100%);
  border: 1px solid var(--exp-border);
  color: var(--text);
  font-family: 'Barlow Condensed', sans-serif;
  font-weight: 700;
  font-size: 16px;
  letter-spacing: 1px;
  text-transform: uppercase;
  padding: 12px 16px;
  border-radius: 10px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: skewX(-6deg);
  transition: filter 0.15s ease;
}

.login-submit > * { transform: skewX(6deg); }

.login-submit:hover { filter: brightness(1.15); }

.login-submit:disabled {
  opacity: 0.6;
  cursor: not-allowed;
  filter: none;
}

.login-submit.is-loading .login-submit-label { visibility: hidden; }

.login-submit-spinner {
  display: none;
  position: absolute;
  width: 18px;
  height: 18px;
  border: 2px solid rgba(220, 227, 240, 0.25);
  border-top-color: var(--text);
  border-radius: 50%;
  animation: login-spin 0.7s linear infinite;
}

.login-submit.is-loading .login-submit-spinner { display: block; }

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

.login-hint {
  margin: 0;
  font-size: 12px;
  color: var(--dim);
  line-height: 1.5;
  text-align: center;
}

/* ============================================================
   Home hero + hub
   Renders only on the home screen. Hides on profiles/schedule
   (via body[data-screen]) and on the results view (via #home
   being display:none when state.hasPaired).
   ============================================================ */

.home-hero {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 24px 22px 20px;
  min-height: 170px;
  margin: 0 auto 14px;
  max-width: 640px;
  background:
    radial-gradient(ellipse at 25% 30%, rgba(77, 159, 255, 0.28), transparent 60%),
    radial-gradient(ellipse at 80% 80%, rgba(46, 232, 160, 0.18), transparent 55%),
    linear-gradient(180deg, #13151c 0%, #0c0d10 100%);
}

.home-hero-eyebrow {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 10px;
  letter-spacing: 2px;
  color: var(--exp);
  text-transform: uppercase;
  margin-bottom: 6px;
  position: relative;
  z-index: 2;
}

.home-hero-headline {
  font-family: 'Barlow Condensed', sans-serif;
  font-weight: 800;
  font-size: 26px;
  line-height: 1;
  color: var(--text);
  margin: 0 0 10px;
  text-transform: uppercase;
  letter-spacing: 1px;
  max-width: 65%;
  position: relative;
  z-index: 2;
}

.home-hero-stats {
  font-size: 12px;
  color: #7a8090;
  letter-spacing: 0.3px;
  position: relative;
  z-index: 2;
  max-width: 65%;
}

.home-hero-stats strong {
  color: var(--inexp);
  font-weight: 600;
}

.home-hero-mascot {
  position: absolute;
  right: -8px;
  bottom: -10px;
  width: 130px;
  height: 130px;
  z-index: 1;
  filter: drop-shadow(0 6px 20px rgba(255, 94, 133, 0.3));
  pointer-events: none;
}

/* Hub button strip ----------------------------------------- */

.home-hub {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin: 0 auto 16px;
  max-width: 640px;
}

.home-hub-btn {
  background: #13151c;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 52px;
  cursor: pointer;
  color: var(--text);
  text-align: left;
  font: inherit;
  transition:
    transform 0.2s cubic-bezier(.34, 1.56, .64, 1),
    border-color 0.2s ease,
    box-shadow 0.2s ease;
}

.home-hub-btn:hover {
  transform: translateY(-2px) scale(1.02);
  border-color: var(--exp);
  box-shadow: 0 8px 20px -8px rgba(77, 159, 255, 0.5);
}

.home-hub-btn.is-green:hover {
  border-color: var(--inexp);
  box-shadow: 0 8px 20px -8px rgba(46, 232, 160, 0.5);
}

.home-hub-btn:active {
  transform: translateY(0) scale(0.98);
  transition-duration: 120ms;
}

.home-hub-icon {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  background: var(--exp-grad);
  border: 1px solid var(--exp-border);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--exp);
  flex-shrink: 0;
}

.home-hub-btn.is-green .home-hub-icon {
  background: var(--inexp-grad);
  border-color: var(--inexp-border);
  color: var(--inexp);
}

.home-hub-label {
  font-family: 'Barlow Condensed', sans-serif;
  font-weight: 700;
  font-size: 14px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}

/* ============================================================
   Home hero + hub: animations
   Entrance plays only when the .is-first-load class is on
   <html> (set by the JS first-load gate). Ambient animations
   (glow pulse, Kirby bob) always run while the home is visible.
   ============================================================ */

@keyframes home-hero-enter {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes home-hero-glow {
  0%, 100% { box-shadow: inset 0 0 0 0 rgba(77, 159, 255, 0); }
  50%      { box-shadow: inset 0 0 40px 0 rgba(77, 159, 255, 0.08); }
}

@keyframes home-hero-bob {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50%      { transform: translateY(-6px) rotate(2deg); }
}

/* Ambient: always on while home is visible. */
.home-hero {
  animation: home-hero-glow 6s ease-in-out infinite;
}

.home-hero-mascot {
  animation: home-hero-bob 4s ease-in-out infinite;
  transform-origin: center bottom;
}

/* Entrance: only when html.is-first-load is set. The JS removes
   that class after the first home render so re-entry from
   Profiles/Schedule doesn't re-animate. */
html.is-first-load .home-hero-eyebrow,
html.is-first-load .home-hero-headline,
html.is-first-load .home-hero-stats,
html.is-first-load .home-hero-mascot,
html.is-first-load .home-hub-btn {
  animation-fill-mode: both;
  animation-timing-function: cubic-bezier(.34, 1.56, .64, 1);
  animation-duration: 450ms;
  animation-name: home-hero-enter;
}

html.is-first-load .home-hero-eyebrow  { animation-delay: 50ms;  }
html.is-first-load .home-hero-headline { animation-delay: 150ms; }
html.is-first-load .home-hero-stats    { animation-delay: 250ms; }

/* Mascot: entrance, then ambient bob starts AFTER entrance completes
   (450ms duration + 350ms delay = 800ms, bob starts at 900ms). */
html.is-first-load .home-hero-mascot {
  animation:
    home-hero-enter 450ms 350ms cubic-bezier(.34, 1.56, .64, 1) both,
    home-hero-bob 4s 900ms ease-in-out infinite;
}

html.is-first-load .home-hub-btn:nth-child(1) { animation-delay: 400ms; }
html.is-first-load .home-hub-btn:nth-child(2) { animation-delay: 500ms; }

/* Reduced motion: strip everything. Hover keeps its color/border/
   shadow change but loses the spring. */
@media (prefers-reduced-motion: reduce) {
  .home-hero,
  .home-hero-mascot,
  html.is-first-load .home-hero-eyebrow,
  html.is-first-load .home-hero-headline,
  html.is-first-load .home-hero-stats,
  html.is-first-load .home-hero-mascot,
  html.is-first-load .home-hub-btn {
    animation: none !important;
  }
  .home-hub-btn {
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
  }
  .home-hub-btn:hover {
    transform: none;
  }
  .home-hub-btn:active {
    transform: none;
  }
}
