/* Self-hosted, replacing the fonts.googleapis.com/fonts.gstatic.com link -
   that round trip (DNS + TLS + a second fetch for the actual font files)
   was measured as the single largest contributor to render-blocking delay
   on this page (~78% of a 3.2s LCP in a real Lighthouse run). Fraunces and
   IBM Plex Sans are variable fonts - one file covers a weight range, so
   each gets a single ranged font-weight declaration rather than repeating
   the same file behind separate weight-specific @font-face blocks. */
@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 500 600;
  font-display: swap;
  src: url("/static/fonts/fraunces-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 500 600;
  font-display: swap;
  src: url("/static/fonts/fraunces-italic.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-sans-variable.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-500.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-600.woff2") format("woff2");
}

:root {
  color-scheme: light;

  --ink: #17241c;
  --ink-soft: #223327;

  --turf: #3b6e4f;
  --turf-deep: #2a4f38;

  --chalk: #f6f2e7;
  --paper: #fffdf8;

  --stone: #e2d9c4;
  --stone-strong: #c9bd9c;
  --moss: #5b6b5a;

  --ochre: #a8801f;
  --ochre-soft: #f1e3bd;
  --ochre-deep: #7e6017;

  --flag: #8b3a2b;

  --ink-text: #1c2620;
  /* A tight, low-blur drop rather than an ambient glow - paper resting on a
     desk casts a shadow close to its own edge, not a soft halo floating
     16px above the surface. */
  --shadow: 0 1px 0 rgba(23, 36, 28, 0.08), 0 3px 8px rgba(23, 36, 28, 0.16);
  /* A flat sheet of paper has square-ish corners, not a rounded app card;
     this is just enough radius to keep sharp corners from looking like a
     rendering glitch at small sizes. */
  --radius: 3px;

  --font-display: "Fraunces", "Iowan Old Style", Georgia, serif;
  --font-body:
    "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
    sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--chalk);
  color: var(--ink-text);
  font-family: var(--font-body);
  font-size: 15px;
}

button,
input,
select {
  font: inherit;
}

.app-shell {
  display: grid;
  grid-template-columns: minmax(320px, 380px) minmax(0, 1fr);
  min-height: 100vh;
}

/* ---------- Sidebar: fitting intake ---------- */

/* Pinned to the viewport so a long results list doesn't stretch the panel
   into a column of empty ink below the form. */
.profile-panel {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  background: var(--ink);
  border-right: 1px solid rgba(0, 0, 0, 0.3);
  color: var(--chalk);
  padding: 32px 28px;
  overflow-y: auto;
}

.brand-lockup {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 30px;
}

/* 44px, not the 30px the old vector mark used: the artwork is an illustration
   with thin shafts and separate club heads, and below ~40px those details
   collapse into an unreadable smudge. */
.brand-mark {
  flex: none;
  width: 44px;
  height: 44px;
}

.brand-lockup h1 {
  margin: 0;
  color: var(--chalk);
  font-family: var(--font-display);
  font-weight: 600;
  font-style: italic;
  font-size: 1.9rem;
  line-height: 1;
  letter-spacing: 0.01em;
}

.brand-tagline {
  margin: 4px 0 0;
  color: rgba(246, 242, 231, 0.6);
  font-size: 0.72rem;
  font-style: italic;
}

.empty-state p,
.list-toolbar p {
  margin: 0;
  color: var(--moss);
}

.profile-form {
  display: grid;
  gap: 22px;
}

.form-section {
  display: grid;
  gap: 12px;
  padding-top: 20px;
  border-top: 1px dashed rgba(246, 242, 231, 0.22);
}

.form-section:first-of-type {
  padding-top: 0;
  border-top: none;
}

.section-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--chalk);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.field {
  display: grid;
  gap: 7px;
}

.field span,
.budget-control span {
  color: inherit;
  font-size: 0.8rem;
  font-weight: 500;
}

.profile-panel .field span {
  color: rgba(246, 242, 231, 0.7);
}

.field input,
.field select {
  width: 100%;
  height: 42px;
  border: 1px solid rgba(246, 242, 231, 0.24);
  border-radius: 7px;
  background: var(--ink-soft);
  color: var(--chalk);
  padding: 0 12px;
  outline: none;
}

.field input:focus,
.field select:focus {
  border-color: var(--ochre);
  box-shadow: 0 0 0 3px rgba(168, 128, 31, 0.28);
}

/* Custom typeahead dropdown for "current driver"/"current irons" (see
   app.js) - a real, positioned list the page controls, not a native
   <datalist> popup. Native datalist rendering is entirely up to the
   browser/OS and unstylable; several mobile browsers show it as an autofill
   strip glued to the keyboard instead of a list anchored to the field.

   position: fixed with JS-computed top/left/width (see app.js), not
   position: absolute inside .typeahead-field - the field lives inside
   .profile-panel, which scrolls (overflow-y: auto) and would clip an
   absolutely-positioned dropdown whenever the field sits near its edge,
   which is exactly the "current driver" field's usual position at the top
   of a long scrollable form. app.js moves this element to a direct child of
   <body> so no scrolling ancestor can clip it. */
.typeahead-suggestions {
  position: fixed;
  z-index: 1000;
  margin: 4px 0 0;
  padding: 4px;
  list-style: none;
  max-height: 220px;
  overflow-y: auto;
  border: 1px solid rgba(246, 242, 231, 0.24);
  border-radius: 7px;
  background: var(--ink-soft);
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.35);
}

.typeahead-suggestions[hidden] {
  display: none;
}

.typeahead-suggestions li {
  padding: 9px 10px;
  border-radius: 5px;
  color: var(--chalk);
  font-size: 0.88rem;
  cursor: pointer;
}

.typeahead-suggestions li[aria-selected="true"] {
  background: var(--turf);
}

/* Four independent toggles, unlike .segmented's mutually-exclusive pair, so
   real checkboxes rather than a segmented control - each one reads on its own. */
.shopping-for {
  display: grid;
  gap: 7px;
  padding: 0;
  border: none;
  margin: 0;
}

.shopping-for legend {
  display: block;
  padding: 0;
  margin: 0 0 2px;
  color: rgba(246, 242, 231, 0.7);
  font-size: 0.8rem;
  font-weight: 500;
}

.checkbox-option {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 34px;
  padding: 0 10px;
  border: 1px solid rgba(246, 242, 231, 0.24);
  border-radius: 7px;
  background: var(--ink-soft);
  cursor: pointer;
}

.checkbox-option input {
  width: 15px;
  height: 15px;
  accent-color: var(--turf);
}

.checkbox-option span {
  color: rgba(246, 242, 231, 0.85);
  font-size: 0.84rem;
  font-weight: 500;
}

.checkbox-option input:focus-visible {
  outline: 2px solid var(--ochre);
  outline-offset: 2px;
}

.segmented {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding: 4px;
  border: 1px solid rgba(246, 242, 231, 0.2);
  border-radius: 7px;
  background: rgba(0, 0, 0, 0.16);
}

.segmented label {
  cursor: pointer;
}

.segmented input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.segmented span {
  display: grid;
  min-height: 34px;
  place-items: center;
  border-radius: 5px;
  color: rgba(246, 242, 231, 0.65);
  font-size: 0.84rem;
  font-weight: 500;
}

.segmented input:checked + span {
  background: var(--chalk);
  color: var(--ink);
  font-weight: 600;
}

/* The real radio is visually hidden, so the focus ring has to live on the
   rendered span or keyboard users get no indication at all. */
.segmented input:focus-visible + span {
  outline: 2px solid var(--ochre);
  outline-offset: 2px;
}

/* The intake form is taller than a laptop viewport, so on wide screens the
   submit button used to sit ~330px below the fold inside the panel's own
   scroll area - the app's primary action was invisible until you scrolled.
   Pinning it to the bottom of the panel keeps it reachable at any scroll
   position. Only above the sidebar breakpoint: below it the panel is static
   and a sticky bar would ride the viewport over the results. */
@media (min-width: 921px) {
  /* A sticky child cannot be pushed past its containing block, so the panel's
     bottom padding would hold the bar 32px short of the floor. The bar's own
     padding replaces it. */
  .profile-panel {
    padding-bottom: 0;
  }

  .form-actions {
    position: sticky;
    bottom: 0;
    z-index: 2;
    /* Bleed over the panel's 28px side padding so the shelf reaches its
       edges and no field scrolls through beside the button. */
    margin: 0 -28px;
    padding: 14px 28px 20px;
    background: var(--ink);
    border-top: 1px dashed rgba(246, 242, 231, 0.22);
  }
}

.primary-action {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 48px;
  border: 1px solid var(--turf);
  border-radius: 7px;
  background: var(--turf);
  color: var(--chalk);
  padding: 0 16px;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.28);
}

.primary-action:hover {
  background: var(--turf-deep);
  border-color: var(--turf-deep);
}

.primary-action:focus-visible {
  outline: 2px solid var(--ochre);
  outline-offset: 2px;
}

.primary-action:disabled {
  cursor: wait;
  opacity: 0.65;
}

/* ---------- Results: the fitting sheet ---------- */

.results-panel {
  padding: 36px 40px;
}

.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 24px;
}

.page-header h2 {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: clamp(1.5rem, 2.4vw, 2.15rem);
  line-height: 1.15;
  letter-spacing: 0.01em;
  max-width: 640px;
}

.page-subtitle {
  margin: 8px 0 0;
  max-width: 480px;
  color: var(--moss);
  font-size: 0.85rem;
  line-height: 1.4;
}

.page-disclaimer {
  margin: 10px 0 20px;
  color: var(--moss);
  font-size: 0.66rem;
  font-style: italic;
  line-height: 1.4;
}

/* Spec grid rendered as a single fitting-sheet ticket, torn along the top */
.spec-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow);
  margin-bottom: 20px;
  overflow: hidden;
}

.spec-grid::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 4px;
  background-image: repeating-linear-gradient(
    90deg,
    var(--stone-strong) 0,
    var(--stone-strong) 8px,
    transparent 8px,
    transparent 16px
  );
}

.spec-tile {
  min-height: 92px;
  padding: 18px 18px 16px;
  border-left: 1px dashed var(--stone);
}

.spec-tile:first-child {
  border-left: none;
}

.spec-tile span {
  display: block;
  color: var(--moss);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.spec-tile strong {
  display: block;
  margin-top: 12px;
  color: var(--ink);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 1.5rem;
  line-height: 1.1;
}

/* align-content defaults to stretch, which grew the three auto rows to fill
   340px and left the heading marooned between two bands of empty paper.
   Centring the block and spacing it explicitly keeps it compact. */
.empty-state {
  display: grid;
  place-items: center;
  align-content: center;
  gap: 10px;
  min-height: 260px;
  padding: 32px 24px;
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  background: var(--paper);
  text-align: center;
  box-shadow: var(--shadow);
}

.empty-state h3 {
  margin: 0;
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.2rem;
}

.empty-line {
  width: min(360px, 55%);
  height: 2px;
  border-radius: 999px;
  background: repeating-linear-gradient(
    90deg,
    var(--stone-strong) 0,
    var(--stone-strong) 6px,
    transparent 6px,
    transparent 12px
  );
}

.recommendations {
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.tabs {
  display: flex;
  gap: 6px;
  padding: 10px;
  border-bottom: 1px solid var(--stone);
  background: var(--chalk);
}

.tab-button {
  min-height: 34px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  color: var(--moss);
  padding: 0 14px;
  font-weight: 600;
  cursor: pointer;
}

.tab-button.active {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--chalk);
}

.tab-button:focus-visible {
  outline: 2px solid var(--turf);
  outline-offset: 2px;
}

.tab-panel {
  display: none;
  padding: 18px;
}

.tab-panel.active {
  display: block;
}

.list-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 14px;
}

.list-toolbar h3 {
  margin: 0 0 3px;
  color: var(--ink);
  font-family: var(--font-display);
  font-weight: 500;
  font-size: 1.1rem;
}

.toolbar-controls {
  display: flex;
  align-items: end;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 14px;
}

.condition-control {
  display: grid;
  gap: 6px;
  color: var(--moss);
}

.control-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.82rem;
  font-weight: 500;
}

.condition-control select {
  height: 34px;
  min-width: 92px;
  border: 1px solid var(--stone-strong);
  border-radius: 7px;
  background: var(--chalk);
  color: var(--ink);
  padding: 0 10px;
  font-weight: 500;
  outline: none;
}

.condition-control select:focus {
  border-color: var(--turf);
  box-shadow: 0 0 0 3px rgba(59, 110, 79, 0.16);
}

.info-icon {
  position: relative;
  display: inline-grid;
  width: 16px;
  height: 16px;
  place-items: center;
  border: 1px solid var(--moss);
  border-radius: 999px;
  color: var(--moss);
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 1;
  cursor: help;
}

.info-icon::after {
  position: absolute;
  right: -10px;
  bottom: calc(100% + 8px);
  z-index: 5;
  width: 230px;
  border: 1px solid var(--ink-soft);
  border-radius: 7px;
  background: var(--ink);
  color: var(--chalk);
  content: attr(data-tooltip);
  opacity: 0;
  padding: 9px 10px;
  pointer-events: none;
  transform: translateY(4px);
  transition:
    opacity 0.12s ease,
    transform 0.12s ease;
  font-size: 0.76rem;
  font-weight: 400;
  line-height: 1.35;
  text-transform: none;
  letter-spacing: 0;
}

.info-icon:hover::after,
.info-icon:focus::after {
  opacity: 1;
  transform: translateY(0);
}

.info-icon:focus-visible {
  outline: 2px solid var(--turf);
  outline-offset: 2px;
}

/* Label above the control, matching .condition-control - the two sat beside
   each other with their labels on different lines, which read as ragged. */
.budget-control {
  display: grid;
  grid-template-columns: minmax(140px, 220px) minmax(64px, auto);
  grid-template-areas:
    "label label"
    "slider value";
  align-items: center;
  gap: 6px 10px;
  color: var(--moss);
  font-size: 0.82rem;
  font-weight: 500;
}

.budget-control > span {
  grid-area: label;
}

.budget-control input {
  grid-area: slider;
}

.budget-control strong {
  grid-area: value;
}

.budget-control input {
  accent-color: var(--turf);
}

.budget-control strong {
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.92rem;
  text-align: right;
}

.club-list {
  display: grid;
  gap: 9px;
}

.club-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px 16px;
  border: 1px solid var(--stone);
  border-radius: var(--radius);
  background: var(--chalk);
  padding: 13px 14px;
}

.club-title {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}

.club-title h4 {
  margin: 0;
  color: var(--ink);
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.2;
}

.club-meta {
  color: var(--moss);
  font-family: var(--font-mono);
  font-size: 0.78rem;
}

/* The badge spans both card rows rather than sharing row 1 with the title.
   A 52px badge next to a 19px title forced row 1 to badge height, leaving a
   ~49px hole between the club name and the reasons below it. Spanning lets
   the title and reasons stack at their own heights in column 1, so the gap
   can't reopen no matter how tall the badge gets. */
.score-block {
  grid-column: 2;
  grid-row: 1 / span 2;
  align-self: center;
  display: grid;
  justify-items: center;
  gap: 6px;
}

.score-label {
  color: var(--moss);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: center;
  line-height: 1.15;
  white-space: nowrap;
}

.score-badge {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 999px;
  border: 2px solid var(--stone-strong);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.9rem;
  font-weight: 600;
}

.score-badge.band-high {
  border-color: var(--turf);
  background: var(--turf);
  color: var(--chalk);
}

.score-badge.band-mid {
  border-color: var(--ochre);
  background: var(--ochre-soft);
  color: var(--ochre-deep);
}

.score-badge.band-low {
  border-color: var(--stone-strong);
  background: var(--paper);
  color: var(--moss);
}

/* Full-sentence reasons read poorly crammed into rounded chips, so this is
   a plain list with a small marker instead of a row of pill badges. */
.reason-list {
  grid-column: 1;
  display: grid;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.reason-list li {
  position: relative;
  padding-left: 16px;
  color: var(--ink);
  font-size: 0.84rem;
  line-height: 1.4;
}

.reason-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--turf);
}

.notice {
  border: 1px dashed var(--stone-strong);
  border-radius: var(--radius);
  padding: 18px;
  color: var(--moss);
  text-align: center;
}

.tie-note {
  border: 1px solid var(--ochre);
  border-radius: var(--radius);
  background: var(--ochre-soft);
  padding: 10px 14px;
  color: var(--ochre-deep);
  font-size: 0.9em;
}

.hidden {
  display: none !important;
}

/* Announced to screen readers only; results render silently otherwise. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

.form-error {
  display: grid;
  justify-items: start;
  gap: 10px;
  border: 1px solid var(--flag);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink-text);
  padding: 16px 18px;
  margin-bottom: 20px;
  box-shadow: var(--shadow);
}

.form-error strong {
  color: var(--flag);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
}

.form-error p {
  margin: 0;
  color: var(--moss);
}

.retry-action {
  min-height: 36px;
  border: 1px solid var(--flag);
  border-radius: 7px;
  background: transparent;
  color: var(--flag);
  padding: 0 14px;
  font-weight: 600;
  cursor: pointer;
}

.retry-action:hover {
  background: var(--flag);
  color: var(--paper);
}

.retry-action:focus-visible {
  outline: 2px solid var(--flag);
  outline-offset: 2px;
}

.loading-state {
  gap: 14px;
}

.spinner {
  width: 28px;
  height: 28px;
  border: 3px solid var(--stone);
  border-top-color: var(--turf);
  border-radius: 999px;
  animation: spin 0.8s linear infinite;
}

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

/* Touch pointers need 44px targets; leave mouse sizing untouched. */
@media (pointer: coarse) {
  .tab-button,
  .retry-action {
    min-height: 44px;
  }

  .condition-control select {
    height: 44px;
  }

  .segmented span {
    min-height: 44px;
  }
}

/* ---------- Mobile chrome: the profile drawer ---------- */

/* Inert on desktop, where the profile panel is a permanent sidebar and none of
   this chrome has anything to do. Everything that drives the drawer lives in
   the 920px block below - the same breakpoint at which the panel stops being
   a column of the grid. */
.mobile-bar,
.drawer-scrim,
.drawer-close {
  display: none;
}

@media (max-width: 920px) {
  .app-shell {
    /* minmax(0, 1fr), not plain 1fr - a bare 1fr track still defaults to
       min-width: auto on its grid item, so non-shrinkable content inside
       (e.g. a results row that doesn't wrap) forces the track past the
       viewport instead of respecting it. The explicit 0 minimum is what
       makes the desktop second column (line 55) safe; this mobile
       single-column override needs the same guard. */
    grid-template-columns: minmax(0, 1fr);
  }

  /* Five tiles at full width divide evenly with no wrap, but the same five
     columns get cramped once the panel itself narrows below ~920px - "Super
     Game Improvement" has nowhere to go. Three columns wrap to two rows
     instead (3 then 2); the nth-child rule below drops the divider on
     whichever tile now starts the second row, since :first-child alone only
     ever catches the very first tile, not the first of each wrapped row. */
  .spec-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .spec-tile:nth-child(3n + 1) {
    border-left: none;
  }

  /* The bar is sticky, so anything scrolled to the top of the viewport comes
     to rest underneath it - which is where revealResults() puts the results
     panel after a submit made from the drawer. */
  html {
    scroll-padding-top: 68px;
  }

  /* The panel stops being a column of the page and becomes an overlay: fixed,
     so opening it slides over the results instead of pushing them a screenful
     further down, and off-canvas until body.drawer-open. Holding the
     visibility change until the slide finishes keeps the shut panel out of the
     tab order without cutting the transition short. dvh over vh because this
     is a long form that scrolls inside a fixed box, which is exactly where a
     retracting URL bar strands the last field. */
  .profile-panel {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 40;
    width: min(370px, 86vw);
    height: 100vh;
    height: 100dvh;
    padding-bottom: 0;
    border-bottom: 0;
    box-shadow: 16px 0 44px rgba(0, 0, 0, 0.38);
    overscroll-behavior: contain;
    transform: translateX(-100%);
    visibility: hidden;
    transition:
      transform 0.24s ease,
      visibility 0s linear 0.24s;
  }

  body.drawer-open .profile-panel {
    transform: translateX(0);
    visibility: visible;
    transition:
      transform 0.24s ease,
      visibility 0s;
  }

  /* Without this the results scroll under the open drawer, which reads as the
     drawer itself drifting. */
  body.drawer-open {
    overflow: hidden;
  }

  .drawer-scrim {
    position: fixed;
    inset: 0;
    z-index: 35;
    display: block;
    background: rgba(12, 15, 13, 0.55);
    opacity: 0;
    visibility: hidden;
    transition:
      opacity 0.24s ease,
      visibility 0s linear 0.24s;
  }

  body.drawer-open .drawer-scrim {
    opacity: 1;
    visibility: visible;
    transition:
      opacity 0.24s ease,
      visibility 0s;
  }

  /* Three tracks so the brand centers over the whole bar regardless of the
     toggle button's width, rather than being pushed off-center by it (a
     plain two-item flex row puts the brand wherever space-between leaves it,
     which reads as lopsided once the toggle moved to the left edge). The
     third track has nothing in it - its only job is mirroring the toggle's
     column so the middle one is genuinely centered, not just left-shifted. */
  .mobile-bar {
    position: sticky;
    top: 0;
    z-index: 30;
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 12px;
    background: var(--ink);
    border-bottom: 1px solid rgba(0, 0, 0, 0.3);
    padding: 10px 20px;
  }

  .mobile-bar-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    grid-column: 2;
  }

  .mobile-bar h1 {
    margin: 0;
    color: var(--chalk);
    font-family: var(--font-display);
    font-weight: 600;
    font-style: italic;
    font-size: 1.45rem;
    line-height: 1;
    letter-spacing: 0.01em;
  }

  .mobile-bar .brand-mark {
    width: 34px;
    height: 34px;
  }

  /* The bar carries the brand at this width, so the lockup inside the drawer
     would only ever be a second copy of it one swipe away. */
  .profile-panel .brand-lockup {
    display: none;
  }

  /* Matches .drawer-close below - same circle, same border, same size - so
     the button that opens the drawer and the one that closes it read as a
     pair. It used to be a wide bordered pill with a text label, which had
     more visual weight than the plain "x" it opens onto and read as a
     generic dashboard control next to the illustrated brand mark. */
  .drawer-toggle {
    grid-column: 1;
    justify-self: start;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border: 1px solid rgba(246, 242, 231, 0.3);
    border-radius: 999px;
    background: transparent;
    color: var(--chalk);
    cursor: pointer;
  }

  .drawer-toggle:focus-visible,
  .drawer-close:focus-visible {
    outline: 2px solid var(--ochre);
    outline-offset: 2px;
  }

  .drawer-toggle-icon {
    position: relative;
    width: 15px;
    height: 11px;
    border-top: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
  }

  .drawer-toggle-icon::before {
    position: absolute;
    top: 2px;
    left: 0;
    right: 0;
    border-top: 2px solid currentColor;
    content: "";
  }

  /* The toggle that opened the drawer is underneath it once it's open, so the
     drawer carries its own way out rather than relying on the sliver of bar
     left beside it. */
  .drawer-close {
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    margin: 0 0 12px auto;
    border: 1px solid rgba(246, 242, 231, 0.3);
    border-radius: 999px;
    background: transparent;
    color: var(--chalk);
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
  }

  /* The panel used to run the full width here, which left room to pair fields
     two-up inside each section; at ~370px of drawer that pairing is back to
     squeezing every field to ~150px, so the base single-column layout is what
     applies now and the rules that arranged the two columns are gone with it.

     The shelf matches the desktop one: the drawer scrolls internally and the
     form is long, so the submit button stays on the floor instead of at the
     end of a scroll. The negative margin bleeds it over the panel's 28px side
     padding so it reaches the drawer's edges. */
  .form-actions {
    position: sticky;
    bottom: 0;
    z-index: 2;
    margin: 0 -28px;
    padding: 14px 28px 20px;
    background: var(--ink);
    border-top: 1px dashed rgba(246, 242, 231, 0.22);
  }
}

@media (max-width: 680px) {
  .results-panel {
    padding: 20px;
  }

  /* No bottom padding, for the same reason as the desktop panel: it would hold
     the sticky submit shelf short of the drawer's floor. */
  .profile-panel {
    padding: 20px 20px 0;
  }

  /* Re-bled to match the 20px padding above - the 28px bleed inherited from
     the wider drawer would overhang the edges by 8px on each side. */
  .form-actions {
    margin: 0 -20px;
    padding: 14px 20px 20px;
  }

  .spec-grid {
    grid-template-columns: 1fr;
  }

  .spec-tile {
    border-left: none;
    border-top: 1px dashed var(--stone);
  }

  .spec-tile:first-child {
    border-top: none;
  }

  .page-header,
  .list-toolbar,
  .club-card {
    display: grid;
  }

  /* A phone can't spare a whole column for badge plus caption - the reasons
     were wrapping inside 173px. The badge alone holds the title's row and the
     reasons take the full width underneath; the caption is desktop-only. */
  .score-block {
    grid-row: 1;
    align-self: start;
  }

  .score-label {
    display: none;
  }

  .reason-list {
    grid-column: 1 / -1;
  }

  .budget-control {
    grid-template-columns: 1fr;
    grid-template-areas:
      "label"
      "slider"
      "value";
    width: 100%;
  }

  .toolbar-controls {
    justify-content: stretch;
    width: 100%;
  }

  .condition-control,
  .condition-control select {
    width: 100%;
  }

  .budget-control strong {
    text-align: left;
  }

  /* The tooltip hung off the 16px icon and grew sideways, so whichever edge it
     grew toward landed wherever the icon happened to sit. On a full-width
     mobile control that put it past the left of the card, where
     .recommendations' overflow:hidden chopped the text off. Anchoring to
     .condition-control instead pins both edges to a box that is already the
     width of the toolbar, so the tooltip cannot reach either edge regardless
     of where the icon sits or how narrow the phone is - no viewport
     arithmetic to keep in sync with the icon's position. Absolute positioning
     resolves against the nearest positioned ancestor, so taking .info-icon
     out of that role is what hands the job to the control. */
  .condition-control {
    position: relative;
  }

  .info-icon {
    position: static;
  }

  .info-icon::after {
    left: 0;
    right: 0;
    width: auto;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition-duration: 0.001ms !important;
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
  }

  /* Reduced motion should remove movement, not feedback. A small spinner in a
     fixed spot signals a live request without the large-area motion this
     setting guards against, so it keeps spinning; everything else stays still. */
  .spinner {
    animation: spin 0.8s linear infinite !important;
    animation-duration: 0.8s !important;
    animation-iteration-count: infinite !important;
  }
}
