/* =====================================================================
   Southern Nevada Sports Hall of Fame — vertical kiosk.

   Three constraints shaped every decision in this file:

   1. 9:16 PORTRAIT. The stage locks to that ratio and every size is expressed
      in container-query units against it, so the same CSS fills a 1080x1920
      wall panel, a 2160x3840 4K panel, and a phone, with no breakpoints. On a
      wider screen the stage is centred and framed — a laptop shows the kiosk as
      it will actually look rather than a stretched approximation.

   2. TOUCH ONLY. There is no cursor, so there is not one `:hover` rule in this
      file. Affordance comes from resting contrast, and feedback from `:active`.
      Every target is at least 88px on its short edge.

   3. MIXED SOURCE ART, SHOWN BIG ANYWAY. 37 inductees now carry the Hall's own
      high-resolution scans (up to 3200px); the other 99 have only the migrated
      copy, a median of ~250px. Portraits are painted at full size regardless.
      Sizing each frame to its source kept everything crisp but turned the oldest
      inductees into small medallions beside their modern peers, which is the
      wrong trade for a hall of fame — the 1997 class should have the same
      presence as the 2026 one. Old scans look old; that is honest.

   Palette and type are taken from the Hall's own website (www.snshf.com) so the
   kiosk and the site read as one brand:

     gold   #f9be4d   the accent, used throughout
     gold gradient #eed78b -> #c78918   its "premium band" fill
     darks  #1d1d1d / #151515   its dark section backgrounds
     Montserrat headings, Roboto body — exactly as the site sets them

   Gold is the single accent: induction years, honours, active states and
   interaction all use it. The site's coral #f36371 was tried in the interaction
   role and dropped — against a dark ground and beside the gold it read as a
   second, competing brand, and a hall of fame wants one metal. (An earlier pass
   used the magenta-to-blue gradient from the logo's colour mark, which fought the
   website's language entirely.)
   ===================================================================== */

/* Self-hosted (SIL OFL 1.1 / Apache 2.0). Regenerate with scripts/fetch-fonts.py. */
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url('../fonts/montserrat-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('../fonts/roboto-latin.woff2') format('woff2');
}

:root {
  /* Ground — the website's own dark bands (#1d1d1d / #151515), a shade deeper
     so a 4K panel in a lit room still reads as black rather than grey. */
  --night:        #151515;
  --night-2:      #1D1D1D;
  --night-3:      #262626;
  --surface:      rgba(255, 255, 255, 0.045);
  --surface-2:    rgba(255, 255, 255, 0.075);
  --hair:         rgba(255, 255, 255, 0.10);
  --hair-soft:    rgba(255, 255, 255, 0.06);

  /* Type */
  --paper:        #FFFFFF;
  --muted:        rgba(255, 255, 255, 0.72);
  --faint:        rgba(255, 255, 255, 0.45);

  /* The website's accents */
  --gold:         #F9BE4D;
  --gold-press:   #D99F2B;   /* pressed state for gold-filled controls */
  --gold-light:   #EED78B;
  --gold-deep:    #C78918;
  /* Its "bg-three" premium band, top-to-bottom exactly as the site sets it. */
  --gold-grad: linear-gradient(to bottom, var(--gold-light) 0%, var(--gold-deep) 100%);
  /* Interaction fill — the same metal, angled so it reads as a surface rather
     than a repeat of the vertical honours band. */
  --brand: linear-gradient(120deg, var(--gold-light) 0%, var(--gold) 45%, var(--gold-deep) 100%);

  --radius:       clamp(10px, 1.4cqw, 22px);
  --radius-lg:    clamp(16px, 2.4cqw, 38px);

  /* One easing for everything that moves. */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

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

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: #0C0C0C;
  color: var(--paper);
  font-family: 'Roboto', ui-sans-serif, system-ui, -apple-system, sans-serif;
  /* A kiosk has no scrollbar and no pinch-zoom. */
  overflow: hidden;
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

/* No text selection, no long-press menus, no tap flash — this is an appliance. */
body {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* ---------------------------------------------------------------------
   The stage: a 9:16 panel, centred and framed on anything wider.
   `container-type: size` is what makes cqh/cqw units below resolve
   against the panel rather than the viewport, so the design scales as one
   piece on any display.
   --------------------------------------------------------------------- */

.frame {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background:
    radial-gradient(120% 60% at 50% 0%, #242424 0%, #0C0C0C 70%);
}

.stage {
  position: relative;
  aspect-ratio: 9 / 16;
  height: 100vh;
  height: 100dvh;
  width: auto;
  max-width: 100vw;
  /* When the viewport is narrower than 9:16 (a tall phone), width leads. */
  max-height: 100dvh;
  container-type: size;
  container-name: stage;
  overflow: hidden;
  /* Three bands: chrome, screens, chrome. The screens live in their own
     positioned band rather than being absolute against the whole stage —
     otherwise the top bar and footer, being siblings, are painted *over* the
     active screen instead of beside it. `minmax(0, 1fr)` is what lets the
     middle band shrink so its scrolling children can bound themselves. */
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  background:
    radial-gradient(140% 45% at 50% 100%, rgba(249, 190, 77, 0.09) 0%, transparent 60%),
    radial-gradient(120% 40% at 20% 0%, rgba(249, 190, 77, 0.07) 0%, transparent 55%),
    linear-gradient(180deg, var(--night-2) 0%, var(--night) 45%, #101010 100%);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.06), 0 40px 120px rgba(0, 0, 0, 0.7);
  isolation: isolate;
}

/* On a true 9:16 display the frame should be invisible. */
@media (aspect-ratio: 9/16) {
  .stage { box-shadow: none; }
  .frame { background: var(--night); }
}
@media (max-aspect-ratio: 9/16) {
  .stage { width: 100vw; height: auto; }
}

/* ---------------------------------------------------------------------
   Screens. One is visible at a time; transitions are a lift-and-fade so
   navigation feels like an app rather than a page load.
   --------------------------------------------------------------------- */

/* The band the screens share.
   Rows are assigned explicitly. A `display: none` element is not a grid item at
   all, so when the chrome is hidden on the attract screen an auto-placed
   `.screens` would slide up into row 1 — which is `auto`, and every screen's
   children are absolutely positioned, so the band would measure zero and the
   stage would paint empty. Pinning each band to its row makes the hidden chrome
   simply collapse instead. */
.screens { grid-row: 2; position: relative; overflow: hidden; min-height: 0; }
.topbar { grid-row: 1; }
.footer { grid-row: 3; }

.screen {
  position: absolute;
  inset: 0;
  display: none;
  opacity: 0;
  transition: opacity 260ms var(--ease), transform 260ms var(--ease);
  transform: translateY(1.2cqh);
}
/* Hiding an inactive screen is stated with `:not([data-active])` — specificity
   0-2-0 — rather than relying on the `display: none` in the base rule above.
   Two screens carry a second class that sets their own grid
   (`.screen.browse`, `.screen.search`), and those rules appear later in this file
   at equal 0-1-0 specificity, so they were beating the base rule and leaving both
   screens permanently displayed. Being `opacity: 0` they stayed invisible — and
   absolutely positioned over the whole stage, they silently swallowed every tap
   and every scroll on the screen underneath. Nothing looked wrong; nothing
   worked.
   `pointer-events: none` is belt-and-braces: an invisible full-bleed element is
   exactly the trap worth stating twice. */
.screen:not([data-active]) { display: none; pointer-events: none; }
.screen[data-active] { display: grid; }
.screen[data-shown] { opacity: 1; transform: none; }
/* Without this a scrolling child resolves its height from its content and
   overflows the band instead of scrolling inside it. */
.screen > * { min-height: 0; }

/* An author `display` rule beats the UA stylesheet's `[hidden]`, so the flex
   containers below would keep painting while "hidden". Be explicit. */
[hidden] { display: none !important; }

/* ---------------------------------------------------------------------
   Type scale. Everything is cqh/cqw so it tracks the panel.
   --------------------------------------------------------------------- */

.display {
  font-family: 'Montserrat', 'Roboto', sans-serif;
  font-weight: 800;
  /* Montserrat is not condensed, so long names wrap. Give them room rather than
     shrinking the face: a wrapped name at full size reads from across a room,
     a shrunken one does not. */
  line-height: 1.02;
  letter-spacing: -0.015em;
  text-transform: uppercase;
  margin: 0;
}

.eyebrow {
  font-family: 'Montserrat', sans-serif;
  font-weight: 500;
  font-size: 1.35cqh;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0;
}

.gradient-text {
  background: var(--gold-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* A thin gradient rule used as a section divider and under the header. */
.rule {
  height: 2px;
  border: 0;
  margin: 0;
  background: var(--brand);
  opacity: 0.85;
}

/* ---------------------------------------------------------------------
   Header — a persistent bar with Back / Home. On a kiosk, a guaranteed
   way out of any screen matters more than a clean edge.
   --------------------------------------------------------------------- */

.topbar {
  display: flex;
  align-items: center;
  gap: 1.6cqw;
  padding: 2.4cqh 4cqw 1.8cqh;
  min-height: 9cqh;
  /* Nothing here may widen the stage; the title is what shrinks. */
  overflow: hidden;
}

/* Retained for markup compatibility; the title now does the growing. */
.topbar__spacer { flex: 0 0 0; }

.topbar__title {
  font-family: 'Montserrat', sans-serif;
  font-weight: 600;
  font-size: 1.75cqh;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* The title takes the bar's leftover width rather than a spacer taking it, so
     it only ellipsises when it genuinely cannot fit. `min-width: 0` is what lets
     a flex item shrink below its content at all — without it the bar, and with it
     the whole stage, is forced wider than the panel. */
  flex: 1 1 auto;
  min-width: 0;
}

/* Step the title down for long labels rather than ellipsising them. The bar has
   roughly 440px left after the buttons and the mark; "About the Hall of Fame"
   wants 563px at the base size, and a clipped wayfinding label is worse than a
   slightly smaller complete one. Two steps cover everything the Hall publishes.
   Set from JS off the string length — a container query cannot measure text. */
.topbar__title[data-len="long"]  { font-size: 1.5cqh;  letter-spacing: 0.08em; }
.topbar__title[data-len="xlong"] { font-size: 1.28cqh; letter-spacing: 0.05em; }

/* Round icon buttons, sized for a fingertip. */
.iconbtn {
  flex: none;
  display: grid;
  place-items: center;
  width: 10.5cqw;
  height: 10.5cqw;
  min-width: 72px;
  min-height: 72px;
  border-radius: 50%;
  border: 1.5px solid var(--hair);
  background: var(--surface);
  color: var(--paper);
  font-size: 3.4cqw;
  cursor: pointer;
  transition: transform 140ms var(--ease), background 140ms var(--ease), border-color 140ms var(--ease);
}
.iconbtn svg { width: 44%; height: 44%; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.iconbtn:active {
  transform: scale(0.92);
  background: var(--surface-2);
  border-color: rgba(255, 255, 255, 0.24);
}

/* The mark in the top bar. Belt and braces on the sizing: `clamp` keeps it
   sensible even if the container query unit fails to resolve in some engine (in
   which case `height: auto` would paint the source at its natural 600x425 and
   shove everything else off the panel), and `max-width` plus `object-fit` mean it
   can never be the thing that overflows the bar. */
.logo {
  height: clamp(44px, 4.6cqh, 96px);
  width: auto;
  max-width: 20cqw;
  object-fit: contain;
  object-position: right center;
  display: block;
  flex: 0 0 auto;
}

/* ---------------------------------------------------------------------
   Screen 1 — Attract. Runs unattended; a drifting wall of faces behind
   the mark, and a pulse inviting a touch. Any tap anywhere enters.
   --------------------------------------------------------------------- */

#screen-attract { cursor: pointer; }

.attract {
  position: relative;
  display: grid;
  place-items: center;
  height: 100%;
  overflow: hidden;
}

/* Three columns of portraits creeping in alternating directions. Slow
   enough to read as ambient rather than as a slideshow demanding attention. */
.attract__wall {
  position: absolute;
  inset: -12% -2%;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2cqw;
  opacity: 0.30;
  filter: saturate(0.5) contrast(1.05);
  z-index: 0;
}
.attract__col {
  display: flex;
  flex-direction: column;
  gap: 2cqw;
  will-change: transform;
}
.attract__col--a { animation: drift-up 96s linear infinite; }
.attract__col--b { animation: drift-down 112s linear infinite; }
.attract__col--c { animation: drift-up 128s linear infinite; }

@keyframes drift-up   { from { transform: translateY(0); }    to { transform: translateY(-50%); } }
@keyframes drift-down { from { transform: translateY(-50%); } to { transform: translateY(0); } }

.attract__wall img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--radius);
  display: block;
  background: var(--night-3);
}

/* The wall is texture, not content — keep it well behind the type. */
.attract__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    radial-gradient(70% 42% at 50% 46%, rgba(15, 15, 15, 0.95) 0%, rgba(15, 15, 15, 0.74) 55%, rgba(15, 15, 15, 0.42) 100%),
    linear-gradient(180deg, rgba(15, 15, 15, 0.9) 0%, transparent 22%, transparent 74%, rgba(15, 15, 15, 0.95) 100%);
}

.attract__body {
  position: relative;
  z-index: 2;
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 2.2cqh;
  padding: 0 8cqw;
}

.attract__logo { width: 46cqw; height: auto; display: block; margin-bottom: 1cqh; }

/* The tagline. Long enough to need three or four lines at this size, which is
   what gives the attract screen its presence from across a room. */
.attract__headline {
  font-size: 6.4cqh;
  font-weight: 700;
  letter-spacing: 0.005em;
  text-wrap: balance;
  max-width: 84cqw;
}

.attract__sub {
  font-size: 2.05cqh;
  line-height: 1.5;
  color: var(--muted);
  max-width: 66cqw;
  margin: 0;
}

.attract__count {
  font-family: 'Montserrat', sans-serif;
  font-size: 1.5cqh;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: rgba(249, 190, 77, 0.62);
}

/* The touch invitation. A slow breathing pill — legible from across a
   room, and unmistakably an instruction. */
.attract__cta {
  margin-top: 1.5cqh;
  display: inline-flex;
  align-items: center;
  gap: 2.4cqw;
  padding: 2.5cqh 7cqw;
  border-radius: 999px;
  border: 1.5px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.05);
  font-family: 'Montserrat', sans-serif;
  font-size: 2.1cqh;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  animation: breathe 3.4s ease-in-out infinite;
}
@keyframes breathe {
  0%, 100% { transform: scale(1);    box-shadow: 0 0 0 0 rgba(249, 190, 77, 0.35); }
  50%      { transform: scale(1.03); box-shadow: 0 0 0 2.2cqh rgba(249, 190, 77, 0); }
}
.attract__cta::before {
  content: "";
  width: 1.5cqh;
  height: 1.5cqh;
  border-radius: 50%;
  background: var(--brand);
}

/* ---------------------------------------------------------------------
   Screen 2 — Hub. The four standing sections as full-width slabs, plus
   the way into the inductees.
   --------------------------------------------------------------------- */

.hub {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 0 4cqw 4cqh;
}
.hub::-webkit-scrollbar { display: none; }

/* The primary action: browse the inductees. Deliberately the only element
   on the screen carrying the brand gradient as a fill. */
.hero-card {
  position: relative;
  display: block;
  width: 100%;
  border: 0;
  border-radius: var(--radius-lg);
  padding: 4cqh 6cqw;
  margin-bottom: 3cqh;
  text-align: left;
  color: var(--paper);
  background: var(--brand);
  cursor: pointer;
  overflow: hidden;
  transition: transform 160ms var(--ease);
}
.hero-card:active { transform: scale(0.985); }

.hero-card__faces {
  position: absolute;
  right: -2cqw;
  bottom: -1cqh;
  display: flex;
  opacity: 0.9;
}
.hero-card__faces img {
  width: 13cqw;
  height: 13cqw;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid rgba(255, 255, 255, 0.55);
  margin-left: -4.2cqw;
  background: var(--night-3);
}

/* These are <span>s inside a <button> — a button's children stay inline unless
   told otherwise, which stacks the label, title and meta on top of each other. */
.hero-card__label,
.hero-card__title,
.hero-card__meta { display: block; }

.hero-card__label {
  font-family: 'Montserrat', sans-serif;
  font-size: 1.4cqh;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  opacity: 0.85;
}
.hero-card__title {
  font-family: 'Montserrat', sans-serif;
  font-weight: 700;
  font-size: 5.4cqh;
  line-height: 0.95;
  text-transform: uppercase;
  margin: 1cqh 0 0.6cqh;
}
/* Keep the line clear of the overlapping face stack in the bottom-right. */
.hero-card__meta { font-size: 1.85cqh; opacity: 0.92; max-width: 52cqw; }

/* The standing sections. */
.slab {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 4cqw;
  width: 100%;
  min-height: 15cqh;
  padding: 3cqh 5cqw;
  margin-bottom: 2cqh;
  border: 1.5px solid var(--hair-soft);
  border-radius: var(--radius-lg);
  background: var(--surface);
  color: var(--paper);
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  transition: transform 160ms var(--ease), background 160ms var(--ease);
}
.slab:active { transform: scale(0.985); background: var(--surface-2); }

/* A gradient edge marks each slab without adding another colour field. */
.slab::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 4px;
  background: var(--brand);
}

.slab__title {
  font-family: 'Montserrat', sans-serif;
  font-weight: 500;
  font-size: 3.1cqh;
  line-height: 1.04;
  text-transform: uppercase;
  letter-spacing: 0.01em;
}
.slab__sub {
  font-size: 1.7cqh;
  color: var(--faint);
  margin-top: 0.7cqh;
  /* Two lines of teaser, then stop — a slab is a door, not the room. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.slab__chev { color: var(--faint); font-size: 3cqh; flex: none; }
.slab__thumb {
  width: 15cqw;
  height: 15cqw;
  border-radius: var(--radius);
  object-fit: cover;
  flex: none;
  background: var(--night-3);
}

/* ---------------------------------------------------------------------
   Screen 3 — Inductees. A class rail plus a portrait grid.
   --------------------------------------------------------------------- */

.screen.browse[data-active] { grid-template-rows: auto auto 1fr; min-height: 0; }

/* Mode switch: by class / A–Z. A segmented control, because on a touch
   screen two visible options beat a dropdown that needs two taps. */
.segmented {
  display: grid;
  grid-auto-flow: column;
  gap: 1cqw;
  padding: 0.8cqh;
  margin: 0 4cqw 2cqh;
  background: var(--surface);
  border: 1.5px solid var(--hair-soft);
  border-radius: 999px;
}
.segmented button {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-family: 'Montserrat', sans-serif;
  font-size: 1.85cqh;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 1.9cqh 0;
  min-height: 66px;
  border-radius: 999px;
  cursor: pointer;
  transition: color 160ms var(--ease), background 160ms var(--ease);
}
.segmented button[aria-selected="true"] {
  color: #0A0A0F;
  background: var(--paper);
  font-weight: 600;
}
.segmented button:active { background: var(--surface-2); }

/* The class rail — horizontally scrollable year chips, snapped. */
.rail {
  display: flex;
  gap: 2cqw;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 0 4cqw 2.4cqh;
}
.rail::-webkit-scrollbar { display: none; }

.chip {
  flex: none;
  scroll-snap-align: center;
  min-width: 20cqw;
  min-height: 88px;
  padding: 1.6cqh 3.2cqw;
  border-radius: var(--radius);
  border: 1.5px solid var(--hair);
  background: var(--surface);
  color: var(--paper);
  font-family: 'Montserrat', sans-serif;
  cursor: pointer;
  display: grid;
  gap: 0.3cqh;
  place-items: center;
  transition: transform 140ms var(--ease), border-color 140ms var(--ease), background 140ms var(--ease);
}
.chip:active { transform: scale(0.94); }
.chip__year { font-size: 2.7cqh; font-weight: 600; letter-spacing: 0.02em; }
.chip__count { font-size: 1.2cqh; letter-spacing: 0.18em; color: var(--faint); text-transform: uppercase; }

.chip[aria-selected="true"] {
  background: var(--brand);
  border-color: transparent;
  color: #1A1207;
}
.chip[aria-selected="true"] .chip__count { color: rgba(26, 18, 7, 0.72); }

/* A class with nobody in it — 2003, 2020 and 2021 in this data — is shown
   but visibly unavailable, so the rail stays a truthful timeline instead of
   hiding the gap or offering a button that leads nowhere. */
.chip[data-empty] { opacity: 0.34; }
.chip[data-empty] .chip__count { color: var(--faint); }

/* The portrait grid. Two across rather than three: at ~44cqw each face is about
   470px on a 1080 panel, which is a presence rather than a thumbnail. Old scans
   are softer at that size and that is accepted — see .detail__portrait. */
.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 3.4cqw 3.4cqw;
  align-content: start;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 0 4cqw 5cqh;
  min-height: 0;
}
.grid::-webkit-scrollbar { display: none; }

.person {
  display: grid;
  gap: 1.1cqh;
  justify-items: center;
  border: 0;
  background: none;
  padding: 0;
  color: var(--paper);
  cursor: pointer;
  transition: transform 160ms var(--ease);
}
.person:active { transform: scale(0.94); }

/* The face frame. A gradient ring does the decorative work so the image
   itself is never scaled past what it can carry. */
.person__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  padding: 2px;
  background: linear-gradient(150deg, rgba(238, 215, 139, 0.7), rgba(199, 137, 24, 0.7));
}
.person__frame img {
  width: 100%;
  height: 100%;
  border-radius: calc(var(--radius) - 2px);
  object-fit: cover;
  object-position: center 28%;
  display: block;
  background: var(--night-3);
  /* Faces were shot decades apart on wildly different stock. A whisper of
     contrast normalisation makes a grid of them look like one collection. */
  filter: contrast(1.04) saturate(0.92);
}

.person__name {
  font-family: 'Montserrat', sans-serif;
  font-weight: 600;
  font-size: 1.95cqh;
  line-height: 1.15;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  /* Names run from "Al Bernstein" to "1976-77 UNLV Men's Basketball Team";
     three lines is enough for the longest without wrecking the grid rhythm. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.person__year {
  font-family: 'Montserrat', sans-serif;
  font-size: 1.4cqh;
  letter-spacing: 0.2em;
  color: rgba(249, 190, 77, 0.62);
}

/* Alphabetical mode gets a sticky letter heading. */
.grid__letter {
  grid-column: 1 / -1;
  font-family: 'Montserrat', sans-serif;
  font-size: 3.4cqh;
  font-weight: 700;
  color: var(--night-3);
  -webkit-text-stroke: 1.5px rgba(245, 243, 238, 0.22);
  text-transform: uppercase;
  padding: 1.6cqh 0 0.4cqh;
  position: sticky;
  top: 0;
  background: linear-gradient(180deg, var(--night) 60%, transparent);
  z-index: 2;
}

.empty-note {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--faint);
  font-size: 1.9cqh;
  line-height: 1.6;
  padding: 8cqh 6cqw;
}

/* ---------------------------------------------------------------------
   Screen 3b — the induction classes index.

   The Hall's own site navigates primarily by year (/hall-of-famers/2026/, with a
   year list in its main menu), so the kiosk gets the same front door: every class
   on one scrollable screen rather than only a horizontal chip rail, which hides
   most of thirty years off the edge of the panel.
   --------------------------------------------------------------------- */

.years {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2.6cqw;
  align-content: start;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 0 4cqw 5cqh;
  min-height: 0;
}
.years::-webkit-scrollbar { display: none; }

.yearcard {
  position: relative;
  display: grid;
  gap: 0.5cqh;
  justify-items: center;
  align-content: center;
  padding: 2.6cqh 1cqw;
  min-height: 96px;
  border: 1.5px solid var(--hair);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--paper);
  cursor: pointer;
  overflow: hidden;
  transition: transform 150ms var(--ease), background 150ms var(--ease);
}
.yearcard:active { transform: scale(0.95); background: var(--surface-2); }

/* The most recent class is the one visitors ask about, so it leads with the
   gold band the Hall uses for its own premium surfaces. */
.yearcard--latest {
  border-color: transparent;
  background: var(--gold-grad);
  color: #1A1207;
}
.yearcard--latest .yearcard__count { color: rgba(26, 18, 7, 0.72); }

.yearcard__year {
  font-family: 'Montserrat', sans-serif;
  font-weight: 800;
  font-size: 3.4cqh;
  letter-spacing: -0.02em;
  line-height: 1;
  text-align: center;
}
.yearcard__count {
  font-family: 'Montserrat', sans-serif;
  font-size: 1.2cqh;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  text-align: center;
}

/* A class with nobody in it — 2003, 2020 and 2021 here — stays on the timeline
   but is visibly unavailable, so the run of years remains truthful instead of
   silently skipping three. */
.yearcard[data-empty] { opacity: 0.32; }

.years__note {
  grid-column: 1 / -1;
  font-size: 1.5cqh;
  line-height: 1.55;
  color: var(--faint);
  padding: 0 1cqw 2cqh;
}

/* ---------------------------------------------------------------------
   Screen 4 — one inductee.
   --------------------------------------------------------------------- */

.detail {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 0 5.5cqw 4cqh;
  min-height: 0;
}
.detail::-webkit-scrollbar { display: none; }

.detail__head { display: grid; justify-items: center; text-align: center; gap: 1.6cqh; }

/* Big, always.
   Portraits are shown at full width regardless of source resolution: an old scan
   is presented at the same scale as a new one and simply looks like what it is.
   An earlier pass shrank the frame to the source's pixel width to avoid
   upscaling, which kept every picture crisp but made the oldest inductees appear
   as small medallions beside their modern peers — the wrong trade for a hall of
   fame, where the 1997 class deserves the same presence as the 2026 one.
   37 of the 136 now have the Hall's own high-resolution scan behind them. */
.detail__portrait {
  position: relative;
  width: 78cqw;
  /* 4:5 — a portrait crop suits sports photography and uses a tall panel well.
     `object-fit: cover` centres whatever aspect the source happens to be. */
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-lg);
  padding: 3px;
  background: var(--gold-grad);
  box-shadow: 0 3cqh 9cqh rgba(0, 0, 0, 0.6);
}
.detail__portrait img {
  width: 100%;
  height: 100%;
  border-radius: calc(var(--radius-lg) - 3px);
  object-fit: cover;
  object-position: center 28%;   /* faces sit above centre in most of these shots */
  display: block;
  background: var(--night-3);
  filter: contrast(1.04) saturate(0.94);
}

/* Champagne, and only here: the induction class. */
.year-badge {
  display: inline-flex;
  align-items: baseline;
  gap: 1.4cqw;
  padding: 1.1cqh 3.4cqw;
  border-radius: 999px;
  border: 0;
  background: var(--gold-grad);
  color: #1A1207;
  font-family: 'Montserrat', sans-serif;
  text-transform: uppercase;
}
.year-badge__label { font-size: 1.3cqh; letter-spacing: 0.26em; }
.year-badge__year { font-size: 2.3cqh; font-weight: 600; letter-spacing: 0.04em; }

.detail__name {
  font-size: 5.6cqh;
  font-weight: 700;
  line-height: 0.96;
  text-align: center;
  /* Team and family names are long; let them wrap rather than shrink. */
  text-wrap: balance;
}

.detail__credit {
  font-size: 1.25cqh;
  color: var(--faint);
  letter-spacing: 0.08em;
}

/* "The File" — born / claim to fame, lifted out of the bio prose during
   migration so it can be a real card instead of a run of bold paragraphs. */
.file-card {
  margin: 3cqh 0;
  border: 1.5px solid var(--hair-soft);
  border-radius: var(--radius-lg);
  background: var(--surface);
  overflow: hidden;
}
.file-card__head {
  display: flex;
  align-items: center;
  gap: 2.4cqw;
  padding: 2cqh 4.5cqw;
  border-bottom: 1.5px solid var(--hair-soft);
  font-family: 'Montserrat', sans-serif;
  font-size: 1.45cqh;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--faint);
}
.file-card__head::before {
  content: "";
  width: 2.4cqw;
  height: 2px;
  background: var(--brand);
}
.file-row { padding: 2.2cqh 4.5cqw; }
.file-row + .file-row { border-top: 1px solid var(--hair-soft); }
.file-row__label {
  font-family: 'Montserrat', sans-serif;
  font-size: 1.3cqh;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(249, 190, 77, 0.62);
  margin-bottom: 0.7cqh;
}
.file-row__value { font-size: 1.95cqh; line-height: 1.45; color: var(--paper); }

/* The bio. Generous measure and leading — this is read standing up. */
.prose { font-size: 2.0cqh; line-height: 1.66; color: rgba(245, 243, 238, 0.86); }
.prose p { margin: 0 0 1.9cqh; }
.prose p:last-child { margin-bottom: 0; }
.prose strong { color: var(--paper); font-weight: 600; }
.prose em { font-style: italic; }
.prose a { color: var(--brand-cyan); text-decoration: none; border-bottom: 1px solid rgba(53, 182, 232, 0.4); }
.prose h2, .prose h3 {
  font-family: 'Montserrat', sans-serif;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 3cqh 0 1.2cqh;
}
.prose h2 { font-size: 2.7cqh; }
.prose h3 { font-size: 2.2cqh; }
.prose ul, .prose ol { margin: 0 0 1.9cqh; padding-left: 5cqw; }
.prose li { margin-bottom: 0.8cqh; }

/* Move through a class without going back to the grid. */
.pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2.4cqw;
  margin-top: 4cqh;
}
.pager button {
  display: grid;
  gap: 0.5cqh;
  min-height: 96px;
  padding: 2.2cqh 3.4cqw;
  border: 1.5px solid var(--hair-soft);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--paper);
  text-align: left;
  cursor: pointer;
  transition: transform 150ms var(--ease), background 150ms var(--ease);
}
.pager button:active { transform: scale(0.97); background: var(--surface-2); }
.pager button:disabled { opacity: 0.28; }
.pager button:last-child { text-align: right; }
.pager__dir {
  font-family: 'Montserrat', sans-serif;
  font-size: 1.2cqh;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--faint);
}
.pager__name {
  font-family: 'Montserrat', sans-serif;
  font-size: 1.85cqh;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------------------------------------------------------------------
   Screen 5 — a content page (About, Board, Events, Get Involved).
   --------------------------------------------------------------------- */

.page {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 0 5.5cqw 5cqh;
  min-height: 0;
}
.page::-webkit-scrollbar { display: none; }

.page__hero {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius-lg);
  display: block;
  margin-bottom: 3cqh;
  background: var(--night-3);
}

.page__title {
  font-size: 5.0cqh;
  font-weight: 700;
  margin-bottom: 1cqh;
  text-wrap: balance;
}

.page__rule { width: 14cqw; margin: 2cqh 0 3cqh; }

/* The board screen is a heading-and-names roster: seventy-odd names in short
   centred paragraphs.
   NOT multi-column. `column-count` inside a vertically scrolling box lays the
   columns out over the *full scroll height*, so column two starts level with the
   top of column one — far above the visible area — and the reading order jumps
   from the bottom of the screen back up off-screen. It looked tidy in a
   screenshot and was unusable in practice. A single column keeps document order
   and simply scrolls; tight leading is what stops it becoming a ribbon. */
.page[data-slug="board"] .prose { font-size: 1.85cqh; line-height: 1.34; }
.page[data-slug="board"] .prose p { margin-bottom: 0.55cqh; }
/* Each group label was authored as a bold run at the head of its paragraph. */
.page[data-slug="board"] .prose strong {
  display: block;
  font-family: 'Montserrat', sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  font-size: 1.5cqh;
  color: var(--gold);
  margin: 3cqh 0 1cqh;
}
.page[data-slug="board"] .prose p:first-child strong { margin-top: 0; }

/* Get Involved: the QR is the point of the screen, so it gets the space. */
.qr-card {
  display: grid;
  justify-items: center;
  gap: 2.4cqh;
  text-align: center;
  margin: 3cqh 0;
  padding: 4cqh 6cqw;
  border-radius: var(--radius-lg);
  background: var(--paper);
  color: #0A0A0F;
}
.qr-card__code { width: 52cqw; height: auto; color: #0A0A0F; display: block; }
.qr-card__caption {
  font-family: 'Montserrat', sans-serif;
  font-size: 1.9cqh;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.qr-card__url { font-size: 1.6cqh; color: rgba(10, 10, 15, 0.6); letter-spacing: 0.04em; }

/* ---------------------------------------------------------------------
   Screen 6 — Search. A kiosk has no keyboard, so it brings its own.
   --------------------------------------------------------------------- */

.screen.search[data-active] { grid-template-rows: auto 1fr auto; min-height: 0; }

.search__field {
  display: flex;
  align-items: center;
  gap: 3cqw;
  margin: 0 4cqw 2cqh;
  padding: 2.4cqh 4.5cqw;
  border: 1.5px solid var(--hair);
  border-radius: var(--radius);
  background: var(--surface);
  min-height: 88px;
}
.search__value {
  flex: 1;
  font-family: 'Montserrat', sans-serif;
  font-size: 3cqh;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  min-height: 3.4cqh;
}
.search__value:empty::before { content: attr(data-placeholder); color: var(--faint); }
.search__caret {
  width: 2px;
  height: 3.2cqh;
  background: var(--gold);
  animation: blink 1.1s step-end infinite;
}
@keyframes blink { 50% { opacity: 0; } }

.keyboard {
  display: grid;
  gap: 1.4cqh;
  padding: 2.4cqh 3cqw 3cqh;
  background: rgba(0, 0, 0, 0.30);
  border-top: 1.5px solid var(--hair-soft);
}
.keyboard__row { display: grid; grid-auto-flow: column; gap: 1.4cqw; }
.key {
  min-height: 82px;
  border: 1.5px solid var(--hair-soft);
  border-radius: var(--radius);
  background: var(--surface-2);
  color: var(--paper);
  font-family: 'Montserrat', sans-serif;
  font-size: 2.5cqh;
  text-transform: uppercase;
  cursor: pointer;
  transition: transform 90ms var(--ease), background 90ms var(--ease);
}
.key:active { transform: scale(0.9); background: var(--gold-press); color: #1A1207; }
.key--wide { grid-column: span 2; font-size: 1.7cqh; letter-spacing: 0.14em; }

/* ---------------------------------------------------------------------
   Footer — a persistent home affordance and the Hall's own web address,
   so a visitor can carry the visit home.
   --------------------------------------------------------------------- */

.footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 3cqw;
  padding: 2cqh 5cqw 2.6cqh;
  border-top: 1px solid var(--hair-soft);
  font-size: 1.35cqh;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
  font-family: 'Montserrat', sans-serif;
}

.footer__home {
  display: inline-flex;
  align-items: center;
  gap: 2cqw;
  min-height: 72px;
  padding: 1.4cqh 4cqw;
  border-radius: 999px;
  border: 1.5px solid var(--hair);
  background: var(--surface);
  color: var(--paper);
  font-family: 'Montserrat', sans-serif;
  font-size: 1.5cqh;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  cursor: pointer;
}
.footer__home:active { transform: scale(0.95); background: var(--surface-2); }

/* ---------------------------------------------------------------------
   States
   --------------------------------------------------------------------- */

.loading, .fatal {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 0 10cqw;
  z-index: 20;
  background: var(--night);
  gap: 2cqh;
}
.loading[hidden], .fatal[hidden] { display: none; }

.spinner {
  width: 9cqw;
  height: 9cqw;
  border-radius: 50%;
  border: 3px solid var(--hair);
  border-top-color: var(--gold);
  animation: spin 900ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.fatal__title {
  font-family: 'Montserrat', sans-serif;
  font-size: 3cqh;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.fatal__body { font-size: 1.9cqh; color: var(--muted); line-height: 1.6; }

/* A visitor who leaves mid-screen shouldn't strand the next one, so the
   kiosk returns itself to the attract loop. This is the warning shown just
   before that happens — without it the screen appears to reset on its own. */
.idle-toast {
  position: absolute;
  left: 50%;
  bottom: 14cqh;
  transform: translateX(-50%) translateY(2cqh);
  z-index: 15;
  display: flex;
  align-items: center;
  gap: 2.4cqw;
  padding: 2cqh 5cqw;
  border-radius: 999px;
  background: rgba(10, 12, 20, 0.94);
  border: 1.5px solid var(--hair);
  font-family: 'Montserrat', sans-serif;
  font-size: 1.7cqh;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0;
  pointer-events: none;
  transition: opacity 240ms var(--ease), transform 240ms var(--ease);
}
.idle-toast[data-shown] { opacity: 1; transform: translateX(-50%); }

/* Respect a reduced-motion preference: the drifting wall and the breathing
   pill are the only decorative motion, and both can simply stop. */
@media (prefers-reduced-motion: reduce) {
  .attract__col, .attract__cta, .spinner, .search__caret { animation: none; }
  .screen { transition: opacity 120ms linear; transform: none; }
}
