@import url('/assets/css/bomforge-brand.css?v=20260731b');

/* ============================================================
   BOMAtlas Atlas Shell — the education-atlas app chrome.
   Light, clean, map-first. Used by / and the education shells
   (/trade-schools/, /universities/, /community-colleges/,
   /private-colleges/, /education/).

   THE PALETTE IS NOT DEFINED HERE. bomforge-brand.css?v=20260731b, imported above,
   declares --ink, --ink-2, --ink-3, --line, --line-strong, --paper,
   --paper-2, --accent, --accent-2, --seg-*, --font-sans, --glass and
   --paper-rgb on `:root:root` — a doubled selector that outranks any
   `:root` block in this file. This file used to carry its own full copy
   of all sixteen, and every one of them was inert. Reading them and
   picking a hardcoded neighbour that "matches" is how a hover fill of
   #eef1f5 got shipped against a near-white --ink: 1.03:1 in dark mode.
   Change a colour in bomforge-brand.css?v=20260731b. Only add a token here if brand
   does not define it, and say why, as the two below do.

   Type identity is never colour-alone: list rows, the legend, and the
   selection card all carry the type label as text.
   ============================================================ */

:root {
  /* Elevation. Every shadow here used to be rgba(15, 23, 42, …), a navy
     LIGHTER than the dark paper (#0A0A0A), so on dark the floating panel,
     the nav dropdown and the map tooltip cast a glow instead of a shadow
     and read as flat against the basemap. The hue flips and the alpha is
     scaled, so each surface keeps its relative weight in both themes. */
  --shadow-rgb: 15, 23, 42;
  --shadow-boost: 1;
  /* Not a colour: bomforge-brand.css?v=20260731b sets no color-scheme, so without this
     the native scrollbars and form controls stay light on the dark theme. */
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --shadow-rgb: 0, 0, 0;
    --shadow-boost: 3;
    color-scheme: dark;
  }
}
/* Manual toggle overrides — [data-theme] beats the OS media query by specificity. */
:root[data-theme='dark'] {
  --shadow-rgb: 0, 0, 0;
  --shadow-boost: 3;
  color-scheme: dark;
}
:root[data-theme='light'] {
  --shadow-rgb: 15, 23, 42;
  --shadow-boost: 1;
  color-scheme: light;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html {
  background: var(--paper);
}
body {
  font-family: var(--font-sans);
  background: var(--paper);
  color: var(--ink);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* ── Header ─────────────────────────────────────────────────── */
.hp-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 3000;
  background: rgba(var(--glass), 0.9);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border-bottom: 1px solid var(--line);
  height: 56px;
}
.hp-header-inner {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 16px;
  display: flex;
  align-items: center;
  height: 56px;
}
.hp-brand {
  position: relative;
  display: flex;
  align-items: center;
  gap: 2px;
  margin-right: auto;
}
.hp-logo {
  color: var(--ink);
  text-decoration: none;
  font-weight: 800;
  font-size: 18px;
  letter-spacing: -0.03em;
  /* Tap area without moving the layout: the wordmark is 29px tall. */
  display: inline-flex;
  align-items: center;
  padding: 8px 6px;
  margin: -8px -6px;
}
.hp-logo span {
  color: var(--accent);
}
.hp-switcher-btn {
  appearance: none;
  background: none;
  border: none;
  cursor: pointer;
  width: 26px;
  height: 34px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-2);
  transition:
    background 0.15s,
    color 0.15s;
}
.hp-switcher-btn[aria-expanded='true'] {
  background: var(--paper-2);
  color: var(--ink);
}
@media (hover: hover) {
  .hp-switcher-btn:hover {
    background: var(--paper-2);
    color: var(--ink);
  }
}
.hp-switcher-btn svg {
  width: 14px;
  height: 14px;
  stroke: currentColor;
  stroke-width: 2.25;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
.hp-switcher-panel {
  position: absolute;
  top: 44px;
  left: 0;
  min-width: 300px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 18px 44px rgba(var(--shadow-rgb), calc(0.16 * var(--shadow-boost)));
  padding: 6px;
  z-index: 4000;
}
.hp-switch-item {
  display: block;
  padding: 10px 12px;
  border-radius: 8px;
  text-decoration: none;
  transition: background 0.15s;
}
@media (hover: hover) {
  .hp-switch-item:hover {
    background: var(--paper-2);
  }
}
.hp-switch-item.is-current {
  background: var(--paper-2);
}
.hp-switch-name {
  display: block;
  color: var(--ink);
  font-weight: 800;
  font-size: 14.5px;
  letter-spacing: -0.02em;
}
.hp-switch-name b {
  color: var(--accent);
  font-weight: 800;
}
.hp-switch-item.is-current .hp-switch-name::after {
  content: 'Current';
  margin-left: 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  vertical-align: 1px;
}
.hp-switch-desc {
  display: block;
  color: var(--ink-2);
  font-size: 12.5px;
  margin-top: 1px;
}
.hp-nav {
  display: flex;
  align-items: center;
  gap: 22px;
}
.hp-nav a {
  color: var(--ink-2);
  text-decoration: none;
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -0.01em;
  transition: color 0.15s;
  display: inline-flex;
  align-items: center;
  padding: 8px 4px;
  margin: -8px -4px;
}
.hp-nav a[aria-current='page'] {
  color: var(--ink);
}
@media (hover: hover) {
  .hp-nav a:hover {
    color: var(--ink);
  }
}
.hp-nav-group {
  position: relative;
}
.hp-nav-trigger {
  appearance: none;
  background: none;
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 8px 6px;
  margin: -8px -6px;
  color: var(--ink-2);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -0.01em;
  transition: color 0.15s;
}
.hp-nav-trigger svg {
  width: 12px;
  height: 12px;
  stroke: currentColor;
  stroke-width: 2.25;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
  transition: transform 0.15s;
}
.hp-nav-group.open .hp-nav-trigger,
.hp-nav-trigger:focus-visible {
  color: var(--ink);
}
.hp-nav-group.open .hp-nav-trigger svg {
  transform: rotate(180deg);
}
@media (hover: hover) {
  .hp-nav-group:hover .hp-nav-trigger {
    color: var(--ink);
  }
  .hp-nav-group:hover .hp-nav-trigger svg {
    transform: rotate(180deg);
  }
}
.hp-nav-panel {
  display: none;
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  padding-top: 14px;
  z-index: 4000;
}
.hp-nav-panel::before {
  content: '';
  position: absolute;
  inset: 14px 0 0 0;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 18px 44px rgba(var(--shadow-rgb), calc(0.16 * var(--shadow-boost)));
}
.hp-nav-group.open .hp-nav-panel,
.hp-nav-group:focus-within .hp-nav-panel {
  display: block;
}
@media (hover: hover) {
  .hp-nav-group:hover .hp-nav-panel {
    display: block;
  }
}
.hp-nav-panel a {
  position: relative;
  display: block;
  min-width: 250px;
  padding: 9px 14px;
  margin: 0;
  border-radius: 8px;
  transition: background 0.15s;
}
.hp-nav-panel a:first-child {
  margin-top: 6px;
}
.hp-nav-panel a:last-child {
  margin-bottom: 6px;
}
@media (hover: hover) {
  .hp-nav-panel a:hover {
    background: var(--paper-2);
  }
}
.hp-nav-panel a b {
  display: block;
  color: var(--ink);
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.hp-nav-panel a span {
  display: block;
  color: var(--ink-2);
  font-size: 12px;
  margin-top: 1px;
}
.hp-nav-panel a {
  margin-left: 6px;
  margin-right: 6px;
}
/* Panels lead with one destination, then shortcuts.
   These were flat lists in which "All states" carried the same visual rank as
   "Pennsylvania", so six unexplained states read as the coverage rather than
   as a sample of it. The primary row now answers "where does this menu go",
   and the grid is labelled a shortcut so nobody reads six states as the set.
   Measured at 1440px the States panel is 273px and Countries 312px, with the
   two columns paying for the primary row rather than adding to it. */
.hp-nav-panel .hp-nav-primary {
  border-bottom: 1px solid var(--line);
  border-radius: 8px 8px 0 0;
  padding-bottom: 11px;
  margin-bottom: 2px;
}
.hp-nav-eyebrow {
  margin: 8px 14px 4px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.hp-nav-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 2px;
}
.hp-nav-grid a {
  min-width: 0;
}

/* Auth is not site navigation. Sign in sat in the same row and rhythm as
   Government and States, so the eye read seven peers instead of five
   destinations and two account actions. */
.hp-nav-divider {
  width: 1px;
  height: 20px;
  background: var(--line-strong);
  margin: 0 2px;
  flex-shrink: 0;
}

/* Keyboard users had no visible focus anywhere in the header: the only
   affordance was hover, which a keyboard cannot produce. */
.hp-nav a:focus-visible,
.hp-nav-trigger:focus-visible,
.hp-nav-panel a:focus-visible,
.hp-logo:focus-visible,
.hp-switcher-btn:focus-visible,
.hp-switch-item:focus-visible,
.hp-menu-btn:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: 6px;
}

.hp-nav-cta {
  background: var(--accent);
  color: var(--paper) !important;
  padding: 7px 14px;
  border-radius: 8px;
  font-weight: 600;
}
@media (hover: hover) {
  .hp-nav-cta:hover {
    background: var(--accent-2);
  }
}
.hp-menu-btn {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 10px;
  margin: -10px -10px -10px 0;
}
.hp-menu-btn svg {
  width: 22px;
  height: 22px;
  stroke: var(--ink);
  stroke-width: 2;
  stroke-linecap: round;
  fill: none;
}
@media (max-width: 768px) {
  .hp-menu-btn {
    display: block;
  }
  .hp-nav {
    display: none;
    position: absolute;
    top: 56px;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: var(--paper);
    border-bottom: 1px solid var(--line);
    box-shadow: 0 16px 40px rgba(var(--shadow-rgb), calc(0.12 * var(--shadow-boost)));
  }
  .hp-nav.active {
    display: flex;
    max-height: calc(100vh - 56px);
    overflow-y: auto;
  }
  .hp-nav a {
    padding: 13px 20px;
    font-size: 15px;
    border-top: 1px solid var(--line);
  }
  .hp-nav-cta {
    border-radius: 0;
    text-align: center;
  }
  /* Dropdown groups flatten into always-open sections on mobile. */
  .hp-nav-group {
    border-top: 1px solid var(--line);
  }
  .hp-nav-trigger {
    width: 100%;
    padding: 13px 20px 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-2);
    pointer-events: none;
  }
  .hp-nav-trigger svg {
    display: none;
  }
  .hp-nav-panel {
    display: block;
    position: static;
    transform: none;
    padding-top: 0;
  }
  .hp-nav-panel::before {
    display: none;
  }
  .hp-nav-panel a {
    min-width: 0;
    margin: 0;
    border-radius: 0;
    border-top: 0;
    padding: 9px 20px;
  }
  .hp-nav-panel a b {
    font-size: 15px;
    font-weight: 500;
  }
  .hp-nav-panel a span {
    display: none;
  }
  .hp-switcher-panel {
    position: fixed;
    top: 52px;
    left: 12px;
    right: 12px;
    min-width: 0;
  }
}

/* ── App shell: the map IS the page ─────────────────────────── */
html.atlas-app,
body.ts-app {
  height: 100%;
}
body.ts-app {
  overflow: hidden;
}
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--paper);
  padding: 10px 16px;
  border-radius: 0 0 8px 0;
  z-index: 4000;
}
.skip-link:focus {
  left: 0;
}
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.app-main {
  position: fixed;
  top: 56px;
  left: 0;
  right: 0;
  bottom: 0;
}
#map {
  position: absolute;
  inset: 0;
  background: var(--paper-2);
}
.map-dead {
  display: grid;
  place-content: center;
  height: 100%;
  color: var(--ink-2);
  font-size: 13px;
  text-align: center;
  padding: 24px;
}
.map-attrib {
  position: absolute;
  bottom: 8px;
  left: 10px;
  z-index: 5;
  width: 18px;
  height: 18px;
  overflow: hidden;
  white-space: nowrap;
  font-size: 0;
  line-height: 18px;
  color: var(--ink-3);
  background: rgba(var(--glass), 0.75);
  border-radius: 9px;
  cursor: default;
}
/* License text (OSM ODbL / CARTO) collapses to an i-dot; hover or focus expands it. */
.map-attrib::before {
  content: 'i';
  display: inline-block;
  width: 18px;
  text-align: center;
  font-size: 11px;
  font-style: italic;
  font-family: Georgia, serif;
}
.map-attrib:focus-within {
  width: auto;
  font-size: 11px;
  padding: 0 8px;
}
.map-attrib:focus-within::before {
  content: '';
  width: 0;
}
@media (hover: hover) {
  .map-attrib:hover {
    width: auto;
    font-size: 11px;
    padding: 0 8px;
  }
  .map-attrib:hover::before {
    content: '';
    width: 0;
  }
}

/* ── Floating directory panel ───────────────────────────────── */
.panel {
  position: absolute;
  top: 14px;
  right: 14px;
  bottom: 14px;
  /* Responsive: 400px on wide screens, shrinks on narrow desktops so the
     map keeps the majority of the viewport. */
  width: clamp(320px, 33vw, 400px);
  display: flex;
  flex-direction: column;
  background: rgba(var(--glass), 0.96);
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow:
    0 2px 8px rgba(var(--shadow-rgb), calc(0.08 * var(--shadow-boost))),
    0 24px 64px rgba(var(--shadow-rgb), calc(0.16 * var(--shadow-boost)));
  overflow: hidden;
  z-index: 10;
}
.panel-head {
  padding: 16px 16px 12px;
  border-bottom: 1px solid var(--line);
}
.panel-head h1 {
  font-size: 23px;
  font-weight: 800;
  letter-spacing: -0.03em;
  line-height: 1.12;
  color: var(--ink);
  margin: 0 0 10px;
}
.panel-head h1 em {
  font-style: normal;
  color: var(--accent);
}
.panel-sub {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ink-2);
  margin: 0 0 10px;
}

/* Segment tabs: one dot per institution type */
/* These wrap. They were a single-line scroller to save panel head, but the
   type filter is the primary control on this page and the scroller hid it:
   the first pill was clipped at the left edge, the last faded out under the
   mask, and nothing on screen said the row moved. A filter you cannot see is
   worse than a taller panel, and the Focus chips below already wrap, so the
   scroller was also the odd one out. Pills keep white-space: nowrap so a
   label like "Community colleges" never breaks mid-phrase. */
.ts-segments {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 12px;
}
.ts-segments button {
  white-space: nowrap;
}
.ts-segments button {
  flex-shrink: 0;
}
.ts-segments button .lg-n {
  margin-left: 5px;
  font-size: 11px;
  font-weight: 600;
  opacity: 0.55;
  font-variant-numeric: tabular-nums;
}
.ts-segments button {
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 12px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--paper);
  color: var(--ink-2);
  font-size: 12.5px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition:
    background 0.15s,
    color 0.15s,
    border-color 0.15s,
    transform 0.08s;
}
@media (hover: hover) {
  .ts-segments button:hover {
    border-color: var(--ink-3);
    color: var(--ink);
  }
}
.ts-segments button:active {
  transform: scale(0.96);
}
.ts-segments button.active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.seg-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

.panel-stats {
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-2);
  margin: 0 0 10px;
  font-variant-numeric: tabular-nums;
}
.panel-stats strong {
  color: var(--ink);
  font-weight: 700;
}

.search-row {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}
.search-row input[type='text'] {
  flex: 1 1 auto;
  min-width: 0;
  padding: 9px 12px;
  background: var(--paper-2);
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  color: var(--ink);
  font-size: 16px;
  font-family: inherit;
  transition:
    border-color 0.15s,
    background 0.15s;
}
.search-row input[type='text']:focus {
  border-color: var(--accent);
  background: var(--paper);
}
.search-row input[type='text']::placeholder {
  color: var(--ink-3);
}
.search-row select {
  /* 0 1, not 0 0: at large accessibility text sizes (or any UA that widens
     form controls) a rigid 132px basis pushed the select's tail past the
     panel edge. It may shrink to 96px; the search input still leads. */
  flex: 0 1 132px;
  min-width: 96px;
  /* A flex item's automatic minimum size is its MIN-CONTENT size, and for a
     <select> that is its widest option. The state list is worldwide now, so
     the widest option is "Provence-Alpes-Cote d'Azur (1)" and this select
     floored at 273px of a 362px row, squeezing the search input, the primary
     action of the whole surface, to 66px: the placeholder rendered "Sear". */
  min-width: 0;
  text-overflow: ellipsis;
  appearance: none;
  -webkit-appearance: none;
  padding: 9px 28px 9px 12px;
  background: var(--paper-2)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%230f172a' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E")
    no-repeat right 10px center / 12px;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  color: var(--ink);
  font-size: 16px;
  font-family: inherit;
  cursor: pointer;
}

.chips-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-3);
  margin: 0 0 4px;
}
.ts-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.ts-chips button {
  padding: 5px 11px;
  min-height: 34px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: transparent;
  color: var(--ink-2);
  /* Matches .ts-legend button — Focus and Type sit one above the other and
     read as one control surface, so they cannot be a half-pixel apart. */
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition:
    background 0.15s,
    color 0.15s,
    border-color 0.15s,
    transform 0.08s;
}
@media (hover: hover) {
  .ts-chips button:hover {
    border-color: var(--accent);
    color: var(--accent-2);
  }
}
.ts-chips button:active {
  transform: scale(0.96);
}
.ts-chips button.active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--paper);
}

/* Type legend (Everything view) */
.ts-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-bottom: 10px;
}
.ts-legend button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  min-height: 34px;
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--paper);
  color: var(--ink-2);
  /* 12px, not 11px. These chips carry the type key AND the cohort size —
     the densest information in the panel — and 11px is under the floor
     where a five-word label stays scannable at arm's length. */
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition:
    border-color 0.15s,
    color 0.15s;
}
@media (hover: hover) {
  .ts-legend button:hover {
    border-color: var(--ink-3);
    color: var(--ink);
  }
}
.lg-dot {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex-shrink: 0;
}
.lg-n {
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
/* Focus chips carry counts too now, and an active chip is a dark pill:
   --ink-3 on --accent is 1.4:1, so the count would vanish exactly when the
   filter it describes is the one in force. */
.ts-chips button .lg-n {
  margin-left: 5px;
  font-weight: 600;
}
.ts-chips button.active .lg-n {
  color: var(--paper);
  opacity: 0.62;
}
.sg-type .lg-dot {
  width: 7px;
  height: 7px;
  margin-right: 3px;
  vertical-align: 0;
  display: inline-block;
}

/* Selected school card */
.selected-school-panel {
  display: none;
  padding: 12px 16px;
  background: var(--paper);
  border-bottom: 2px solid var(--accent);
}
.selected-school-panel.visible {
  display: block;
}
.selected-school-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 8px;
}
.selected-school-title {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--ink);
  line-height: 1.35;
  padding-top: 4px;
}
.selected-school-close {
  background: none;
  border: none;
  color: var(--ink-2);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  min-width: 44px;
  min-height: 44px;
  display: grid;
  place-items: center;
  margin: -8px -10px 0 0;
  border-radius: 8px;
}
@media (hover: hover) {
  .selected-school-close:hover {
    color: var(--ink);
    background: var(--paper-2);
  }
}
.selected-school-info {
  margin-top: 4px;
}
.sel-meta {
  color: var(--ink-2);
  font-size: 12.5px;
}
.sel-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 8px;
}
.sel-chip {
  font-size: 11px;
  padding: 2px 8px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  color: var(--ink-2);
}
.selected-school-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
.selected-school-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--accent);
  color: var(--paper);
  border: none;
  border-radius: 8px;
  padding: 8px 14px;
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  text-decoration: none;
}
.selected-school-btn.secondary {
  background: var(--paper-2);
  color: var(--ink-2);
  border: 1px solid var(--line-strong);
}
/* Hover only where a pointer can actually hover. On a touch screen :hover
   sticks after the tap, so a hardcoded light hover fill (was #eef1f5) stayed
   painted under near-white --ink text in dark mode: 1.03:1, unreadable. The
   fill is now mixed off the theme tokens so it tracks light and dark. */
@media (hover: hover) {
  .selected-school-btn:hover {
    background: var(--accent-2);
  }
  .selected-school-btn.secondary:hover {
    background: color-mix(in srgb, var(--ink) 8%, var(--paper-2));
    color: var(--ink);
  }
}

/* Directory list */
.school-list {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* The last row must clear the rounded panel edge at scroll end. */
  padding-bottom: 12px;
}
/* Scroll hint: fade the clipped row at the panel's bottom edge while more
   rows remain below (filter-panel.js toggles fp-more on scroll). */
.panel::after {
  content: '';
  position: absolute;
  left: 1px;
  right: 1px;
  bottom: 1px;
  height: 44px;
  border-radius: 0 0 15px 15px;
  background: linear-gradient(to bottom, transparent, rgba(var(--glass), 0.94));
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
}
.panel.fp-more::after {
  opacity: 1;
}
.school-list::-webkit-scrollbar {
  width: 8px;
}
.school-list::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: 4px;
}
.loading {
  padding: 24px;
  text-align: center;
  color: var(--ink-2);
  font-size: 13px;
}
.sg-count {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 16px 4px;
  font-size: 11px;
  color: var(--ink-2);
}
.sg-hide {
  border: none;
  background: none;
  /* Negative margin keeps the 11px label visually where it was while giving
     the hit area the 24px WCAG 2.2 (2.5.8) floor. */
  padding: 7px 6px;
  margin: -7px -6px;
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-2);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}
@media (hover: hover) {
  .sg-hide:hover {
    color: var(--ink);
  }
}

/* Collapsed directory: the list is one click away, not a wall of rows. */
.school-list.is-collapsed {
  flex: 0 0 auto;
  /* The 180px floor below exists so a live directory is never squeezed to
     one row. A collapsed directory is one row on purpose. */
  min-height: 0;
  overflow: visible;
  padding-bottom: 0;
}
/* On the phone sheet the preview must still SHRINK: flex-basis auto at a
   fixed sheet height meant the 3-row preview kept its full content height
   and the flex overflow spilled the foot (and its capture CTA) past the
   viewport edge — the sheet looked sheared off. It scrolls instead. */
@media (max-width: 768px) {
  .school-list.is-collapsed {
    flex: 0 1 auto;
    /* At least one full row stays visible on the shortest phones; the
       rest of the preview scrolls. */
    min-height: 72px;
    overflow-y: auto;
    overflow-x: hidden;
  }
}
.sg-browse {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  min-height: 56px;
  padding: 12px 16px;
  border: none;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: none;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
  transition:
    background 0.12s,
    box-shadow 0.12s;
}
/* A persistent rail, not the accent bar: touch has no hover to discover the
   row with, but .sg-row:hover already means "accent bar = you are on this
   row", and two meanings for one mark is one too many. The foot draws its own
   top border, so this row must not draw the same line again. */
.sg-browse {
  box-shadow: inset 3px 0 0 var(--line-strong);
  border-bottom: none;
}
.sg-browse:focus-visible {
  background: var(--paper-2);
  box-shadow: inset 3px 0 0 var(--accent);
}
@media (hover: hover) {
  .sg-browse:hover {
    background: var(--paper-2);
    box-shadow: inset 3px 0 0 var(--accent);
  }
}
.sg-browse-body {
  flex: 1;
  min-width: 0;
}
.sg-browse-label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.sg-browse-hint {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--ink-2);
}
.sg-browse-arrow {
  font-size: 15px;
  color: var(--ink-2);
  flex-shrink: 0;
}
.sg-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 10px 16px;
  cursor: pointer;
  border: none;
  border-bottom: 1px solid var(--line);
  background: none;
  font-family: inherit;
  text-align: left;
  transition:
    background 0.12s,
    box-shadow 0.12s;
}
@media (hover: hover) {
  .sg-row:hover {
    background: var(--paper-2);
    box-shadow: inset 3px 0 0 var(--accent);
  }
}
.sg-fav {
  width: 20px;
  height: 20px;
  border-radius: 4px;
  flex-shrink: 0;
}
.sg-mono {
  width: 20px;
  height: 20px;
  border-radius: 4px;
  background: var(--ink);
  color: var(--paper);
  font-size: 11px;
  font-weight: 700;
  display: grid;
  place-items: center;
  flex-shrink: 0;
}
.sg-body {
  min-width: 0;
  flex: 1;
  display: block;
}
.sg-name {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sg-meta {
  display: block;
  font-size: 11.5px;
  color: var(--ink-2);
  margin-top: 1px;
}
.sg-empty {
  min-height: 60%;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 6px;
  text-align: center;
  padding: 24px;
}
.sg-empty-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}
.sg-empty-hint {
  font-size: 12px;
  color: var(--ink-2);
  margin-bottom: 6px;
}

/* Panel foot: state links (SEO) + cross-links */
.panel-foot {
  border-top: 1px solid var(--line);
  padding: 8px 16px 12px;
  /* The CTA card inside this foot is var(--paper-2). Sharing that value gave
     the card no surface of its own: it read as flatter than the list rows
     above it. The foot is the recessive chrome; the card sits on it. */
  background: var(--paper);
}
.panel-foot details summary {
  font-size: 12px;
  font-weight: 700;
  color: var(--ink-2);
  cursor: pointer;
  padding: 10px 0;
  display: flex;
  align-items: center;
  min-height: 32px;
}
.ts-state-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px 10px;
  margin: 6px 0 4px;
  max-height: 180px;
  overflow-y: auto;
}
.ts-state-grid a {
  font-size: 12px;
  color: var(--ink-2);
  text-decoration: none;
  padding: 2px 0;
  display: flex;
  align-items: center;
  min-height: 32px;
}
@media (hover: hover) {
  .ts-state-grid a:hover {
    color: var(--accent);
  }
}
.foot-note {
  font-size: 11.5px;
  margin-top: 4px;
  line-height: 1.5;
}
.foot-link {
  color: var(--ink-2);
  font-weight: 600;
  text-decoration: none;
  display: inline-block;
  padding: 9px 0;
  margin: -5px 0;
}
@media (hover: hover) {
  .foot-link:hover {
    color: var(--accent);
  }
}
.foot-tools {
  font-size: 11px;
  color: var(--ink-3);
  margin-top: 4px;
}
.foot-tools a {
  display: inline-block;
  padding: 10px 2px;
  margin: -7px 0;
}
.foot-tools a {
  color: var(--ink-2);
  text-decoration: none;
  font-weight: 500;
}
@media (hover: hover) {
  .foot-tools a:hover {
    color: var(--accent);
  }
}

/* Embed mode: bare map */
.embed-mode .hp-header,
.embed-mode .panel {
  display: none !important;
}
.embed-mode .app-main {
  top: 0;
}

/* Sheet handle: mobile-only affordance */
.sheet-handle {
  display: none;
}

/* ── Mobile: map on top, panel as bottom sheet ──────────────── */
@media (max-width: 768px) {
  .app-main {
    display: flex;
    flex-direction: column;
  }
  #map {
    position: relative;
    inset: auto;
    flex: 1 1 auto;
    min-height: 90px;
  }
  .sheet-handle {
    display: grid;
    place-items: center;
    width: 100%;
    /* Was 19px tall in total: below the 24px WCAG floor and far below the
       44px thumb target, on the one control that owns the whole sheet. The
       bar still reads as a 5px grip; the button around it is now 44. */
    min-height: 44px;
    padding: 14px 0 10px;
    background: none;
    border: none;
    cursor: grab;
    flex-shrink: 0;
    /* Without this the browser can claim the vertical gesture as a scroll
       and fire pointercancel mid-drag, which reads as a dead handle. */
    touch-action: none;
  }
  .sheet-handle:active::before {
    background: var(--ink-2);
  }
  .sheet-handle::before {
    content: '';
    width: 44px;
    height: 5px;
    border-radius: 999px;
    /* --line-strong is a HAIRLINE token (0.16 alpha): 1.44:1 light and
       1.57:1 dark, under the 3:1 floor for a graphical control. */
    background: var(--ink-3);
  }
  .panel {
    position: relative;
    inset: auto;
    width: auto;
    flex: 0 0 56%;
    max-height: 56%;
    border-radius: 14px 14px 0 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
    transition:
      flex-basis 0.28s ease,
      max-height 0.28s ease;
  }
  .panel.sheet-expanded {
    flex: 0 0 88%;
    max-height: 88%;
  }
  .panel.sheet-peek {
    flex: 0 0 22%;
    max-height: 22%;
  }
  .panel.sheet-peek .panel-head,
  .panel.sheet-peek .panel-foot,
  .panel.sheet-peek .selected-school-panel {
    display: none;
  }
  .panel-head {
    padding: 10px 14px 8px;
  }
  .panel-head h1 {
    font-size: 19px;
    margin-bottom: 8px;
  }
  .panel-sub {
    display: none;
  }
  /* One swipeable line on phones. See the head-budget note further down for
     why it stopped wrapping here and nowhere else. */
  .ts-segments {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    scroll-padding-inline: 14px;
    margin-bottom: 8px;
  }
  .ts-segments button {
    white-space: nowrap;
    padding: 9px 13px;
    min-height: 38px;
    scroll-snap-align: start;
  }
  .panel-stats {
    margin-bottom: 8px;
  }
  /* 16px inputs: iOS Safari zooms the page on focus below 16px */
  .search-row input[type='text'],
  .search-row select {
    font-size: 16px;
  }
  /* One-line scroll rows on narrow screens, but never a chip sliced through
     the middle of a word with no explanation. Two things make a cut edge
     legible instead of broken:

       mask-image  the row fades out at whichever end has more to scroll, so
                   a half-visible chip reads as "there is more this way"
       scroll-snap the row cannot come to REST mid-chip; it settles on a
                   chip boundary, so at rest every visible label is whole

     The hidden scrollbar was the whole problem: it removed the only signal
     that the row scrolled at all, leaving a clipped word that looked like a
     layout bug. Keep it hidden, and pay for it with the fade. */
  .ts-chips,
  .ts-legend {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    scroll-snap-type: x proximity;
    -webkit-mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 14px,
      #000 calc(100% - 22px),
      transparent 100%
    );
    mask-image: linear-gradient(
      to right,
      transparent 0,
      #000 14px,
      #000 calc(100% - 22px),
      transparent 100%
    );
    /* The left fade is dead space at scrollLeft 0, so pad it back. */
    padding-left: 14px;
    margin-left: -14px;
  }
  .ts-chips::-webkit-scrollbar,
  .ts-legend::-webkit-scrollbar {
    display: none;
  }
  .ts-chips button,
  .ts-legend button {
    white-space: nowrap;
    scroll-snap-align: start;
  }
  .ts-chips button {
    padding: 7px 11px;
  }
  .ts-legend {
    margin-bottom: 8px;
  }
  /* When a school is selected, hand its card the head's space */
  .panel.has-selection .panel-stats,
  .panel.has-selection .chips-label,
  .panel.has-selection .ts-chips,
  .panel.has-selection .ts-legend,
  .panel.has-selection .panel-sub {
    display: none;
  }
  .panel-foot {
    padding: 6px 14px 8px;
  }
  .map-attrib {
    display: none;
  }
}

/* Short phones. 56% of a 667px screen is 341px, and the head, the collapsed
   three-row directory and the foot need 366: the sheet arrived at its
   DEFAULT stop already scrolling, so the access CTA and the browse links
   were cut off before the visitor had touched anything. Give the rest stop
   the height its own contents ask for. */
@media (max-width: 768px) and (max-height: 720px) {
  .panel {
    flex: 0 0 64%;
    max-height: 64%;
  }
}

/* Landscape phones: the sheet itself scrolls */
@media (max-height: 500px) {
  .panel {
    overflow-y: auto;
    flex: 0 0 62%;
    max-height: 62%;
  }
  .panel-head {
    flex-shrink: 0;
  }
  .school-list {
    flex: none;
    min-height: 160px;
  }
}

/* ── Map inspect HUD + density scale (kepler/Tufte overlays) ── */
.map-inspect {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 20;
  pointer-events: none;
  min-width: 150px;
  /* Five type rows plus a place line; 240px ellipsised "community colleges"
     on the widest row. */
  max-width: 264px;
  padding: 9px 11px;
  background: rgba(var(--glass), 0.96);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  box-shadow: 0 8px 28px rgba(var(--shadow-rgb), calc(0.14 * var(--shadow-boost)));
  font-variant-numeric: tabular-nums;
  will-change: transform;
}
.map-inspect .mi-n {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink);
  letter-spacing: -0.02em;
  line-height: 1.25;
}
.map-inspect .mi-mix {
  margin-top: 4px;
  font-size: 11px;
  color: var(--ink-2);
  line-height: 1.4;
}
.map-inspect .mi-act {
  margin-top: 5px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-3);
}

/* Cluster tip: place, count, composition. The chip on the map can only
   carry a number; everything that makes that number ACTIONABLE — which
   town it covers, and what mix of institutions it is — lives here. */
.map-inspect .mi-place {
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-3);
  letter-spacing: 0.01em;
  line-height: 1.3;
  margin-bottom: 1px;
  /* One line, ellipsised: a two-line place name would shove the count off
     the cursor and the tip would jump as it resolves in. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.map-inspect .mi-bar {
  display: flex;
  gap: 1px;
  height: 5px;
  margin: 7px 0 6px;
  border-radius: 3px;
  overflow: hidden;
}
.map-inspect .mi-bar i {
  /* flex-basis 0 so the flex-grow the JS writes is a pure proportion of the
     cluster; with the default `auto` basis a segment's share would depend on
     nothing at all, since these have no content. */
  flex-basis: 0;
  min-width: 2px;
}
.map-inspect .mi-row {
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.map-inspect .mi-row b {
  font-weight: 700;
  color: var(--ink);
}
.map-inspect .mi-sw {
  flex-shrink: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
}
/* ── Cluster chooser: small clusters list their members on click ────── */
/* "2 in this cluster · click to expand" used to zoom one step and often
   land you on the same cluster. For clusters small enough to enumerate,
   the click now opens the actual schools so one more tap gets you THERE. */
.cluster-pop .maplibregl-popup-content {
  padding: 6px;
  min-width: 216px;
  max-width: 280px;
  background: rgba(var(--glass), 0.97);
  border: 1px solid var(--line-strong);
  border-radius: 12px;
  box-shadow: 0 10px 34px rgba(var(--shadow-rgb), calc(0.2 * var(--shadow-boost)));
}
.cluster-pop .maplibregl-popup-tip {
  border-top-color: var(--line-strong);
  border-bottom-color: var(--line-strong);
}
.cluster-pop .maplibregl-popup-close-button {
  width: 26px;
  height: 26px;
  top: 2px;
  right: 2px;
  border-radius: 7px;
  font-size: 16px;
  color: var(--ink-2);
}
.cluster-pop .maplibregl-popup-close-button:hover {
  background: var(--paper-2);
  color: var(--ink);
}
.cp-title {
  padding: 5px 30px 4px 8px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-3);
}
.cp-list {
  max-height: 230px;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.cp-row {
  display: block;
  width: 100%;
  min-height: 44px;
  padding: 6px 8px;
  border: none;
  border-radius: 8px;
  background: none;
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
@media (hover: hover) {
  .cp-row:hover {
    background: var(--paper-2);
  }
}
.cp-row .cp-city {
  display: block;
  margin-top: 1px;
  font-size: 11.5px;
  font-weight: 400;
  color: var(--ink-2);
}

.map-density-scale {
  position: absolute;
  left: 12px;
  bottom: 36px;
  z-index: 8;
  width: 148px;
  padding: 8px 10px;
  background: rgba(var(--glass), 0.88);
  backdrop-filter: blur(10px);
  border: 1px solid var(--line);
  border-radius: 10px;
  pointer-events: none;
}
.panel-cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  margin-bottom: 10px;
  background: var(--ink);
  border-radius: 12px;
  color: var(--paper);
}
.panel-cta-copy {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.panel-cta-copy strong {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.panel-cta-copy span {
  font-size: 11.5px;
  color: var(--paper);
  opacity: 0.75;
  line-height: 1.35;
}
.panel-cta-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}
.panel-cta-signin {
  font-size: 12px;
  color: var(--paper);
  opacity: 0.8;
  text-decoration: none;
}
@media (hover: hover) {
  .panel-cta-signin:hover {
    opacity: 1;
  }
}

.map-density-scale .mds-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 5px;
}
.map-density-scale .mds-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--ink-2);
}
.map-density-scale .mds-3d {
  pointer-events: auto;
  cursor: pointer;
  font: inherit;
  min-height: 32px;
  min-width: 32px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  padding: 2px 8px;
  border-radius: 6px;
  border: 1px solid var(--line);
  background: var(--paper);
  color: var(--ink-2);
  transition:
    background 0.15s,
    color 0.15s;
}
@media (hover: hover) {
  .map-density-scale .mds-3d:hover {
    border-color: var(--ink-3);
    color: var(--ink);
  }
}
.map-density-scale .mds-3d.active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
.map-density-scale .mds-bar {
  height: 6px;
  border-radius: 999px;
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--accent) 8%, transparent),
    var(--accent)
  );
}
.map-density-scale .mds-hint {
  margin-top: 5px;
  font-size: 11px;
  color: var(--ink-3);
  line-height: 1.35;
}
@media (max-width: 768px) {
  .map-density-scale {
    display: none;
  }
  .map-inspect {
    max-width: 180px;
    font-size: 12px;
  }
}
body.embed-mode .map-density-scale {
  display: none;
}

/* ── Lead CTA (panel foot on map shells) ────────────────────── */
.foot-cta {
  display: block;
  text-align: center;
  background: var(--accent);
  color: var(--paper);
  border-radius: 9px;
  padding: 10px 14px;
  margin: 4px 0 8px;
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  transition: background 0.15s;
}
@media (hover: hover) {
  .foot-cta:hover {
    background: var(--accent-2);
  }
}

/* ── Program-domain chips (trade segment) ────────────────────────────── */
/* Separates the industrial training pipeline from the beauty/health cohorts.
   Visually distinct from the program chips below them: these scope the
   cohort, those filter within it. */
.ts-domains {
  margin-bottom: 10px;
}
.ts-domains button {
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  background: var(--paper);
  color: var(--ink-2);
  font-weight: 600;
}
@media (hover: hover) {
  .ts-domains button:hover {
    border-color: var(--ink-3);
    color: var(--ink);
  }
}
.ts-domains button.active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}

/* ══ Mobile sheet budget ══════════════════════════════════════════════
   The sheet is a fixed-height flex column (56% of the viewport). Head and
   foot are auto-sized, and the list is the only flexible child — so when
   head + foot exceed the sheet, flexbox crushes the list to ZERO and the
   directory silently disappears. Measured at 390x844: head 216 + foot 212
   + handle 19 = 447 against a 441 budget, list = 0px.

   Three rules keep that from recurring: the list gets a hard floor, the
   foot stays compact, and the chip rows scroll horizontally instead of
   wrapping into extra lines (or clipping off-screen, which is what they
   did — the segment row measured 667px wide inside a 362px panel). */
@media (max-width: 768px) {
  /* Non-negotiable: the directory is the product. It can shrink, not vanish.
     96px = two preview rows; 132 forced the flex math over budget on
     375x667 once the Type tabs joined the default stop, and the foot (with
     no floor of its own) was what silently paid. */
  .school-list {
    flex: 1 1 auto;
    min-height: 96px;
  }
  /* The foot is links + capture; if the budget is short the LIST shrinks
     to its floor, the foot never collapses to an unclickable sliver. */
  .panel-foot {
    flex-shrink: 0;
  }

  /* Open-ended chip rows become single-line swipeable scrollers. Wrapping
     cost two lines each and the program list has no fixed length.

     .ts-segments is in this group ON PHONES ONLY (it still wraps on desktop).
     Six pills wrapped to three lines at 375px and five at 320px: 124px and
     210px of a sheet whose default stop is ~340px total, which is what put
     the directory under its own 96px floor and pushed the capture band off
     the bottom edge at 375x667. It was excluded originally because a rail
     "clipped the first pill and nothing said the row had moved" — the
     measured [data-ovf] fades and scroll-snap below are the answer to
     exactly that, and updateSegmentChrome() scrolls the active pill in. */
  .ts-segments,
  .ts-legend,
  .ts-chips {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 2px;
  }
  .ts-segments::-webkit-scrollbar,
  .ts-legend::-webkit-scrollbar,
  .ts-chips::-webkit-scrollbar {
    display: none;
  }
  .ts-segments button,
  .ts-chips button,
  .ts-legend > * {
    flex-shrink: 0;
  }

  /* Readability floor is 11px; letting the line wrap on narrow phones
     costs less than text nobody can read. */
  .panel-stats {
    font-size: 11px;
    letter-spacing: 0.03em;
    margin-bottom: 8px;
  }

  /* The conversion band collapses to a single row: the supporting sentence
     costs ~50px of directory on a phone and the buttons carry the message. */
  .panel-cta {
    padding: 8px 10px;
    margin-bottom: 8px;
  }
  .panel-cta-copy span {
    display: none;
  }
  .panel-cta-copy strong {
    font-size: 12.5px;
  }

  /* Duplicated by the header nav (Education / Suppliers / Government /
     States) and by "Browse by state" directly above them. */
  .foot-note,
  .foot-tools {
    display: none;
  }
}

/* The trade segment adds two more chip rows (Focus + Programs), which pushed
   the sheet's children to 526px inside a 441px box — the conversion band
   overflowed off-screen entirely. Chips get compact on phones, and the sheet
   itself scrolls as a safety valve so no segment can ever strand the foot
   below the fold no matter how much chrome it adds. */
@media (max-width: 768px) {
  .panel {
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .ts-segments,
  .ts-domains,
  .ts-chips {
    gap: 5px;
    margin-bottom: 6px;
  }
  .ts-segments button,
  .ts-domains button,
  .ts-chips button {
    padding: 5px 10px;
    font-size: 11.5px;
  }
  .chips-label {
    margin-bottom: 3px;
    font-size: 11px;
  }
  .panel-head h1 {
    margin-bottom: 7px;
  }

  /* Scroll affordance: without it a chip cut mid-word reads as a layout bug
     rather than "swipe for more". .ts-segments wraps, so it gets no mask. */
  .ts-domains,
  .ts-chips,
  .ts-legend {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 22px), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 22px), transparent);
  }
}

/* ══ Panel budget at every width ══════════════════════════════════════
   Adding the Focus row pushed the desktop head to 539px inside an 816px
   panel, leaving the directory 60px — one row. The chip rows wrap into
   three lines at a 400px panel width, so the program row scrolls
   horizontally instead, the list gets a floor it cannot be squeezed
   below, and the panel scrolls if a segment's chrome still overruns. */
.panel {
  overflow-y: auto;
  overscroll-behavior: contain;
}
/* Desktop floor only. This rule sits AFTER the mobile block, so unscoped it
   silently out-voted the phone's smaller floor (equal specificity, later
   source order) and the sheet's flex math could never resolve: children
   totalled ~500px inside a 441px sheet and the foot rendered clipped below
   the viewport edge. */
@media (min-width: 769px) {
  .school-list {
    min-height: 180px;
  }
}
.ts-chips {
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
}
.ts-chips::-webkit-scrollbar {
  display: none;
}
.ts-chips button {
  flex-shrink: 0;
}

/* ── Country scope ───────────────────────────────────────────────────── */
.country-select {
  width: 100%;
  padding: 8px 11px;
  margin-bottom: 8px;
  background: var(--paper-2);
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  color: var(--ink);
  font-size: 13px;
  font-family: inherit;
}
/* Says what is missing outside the US instead of letting an empty filter row
   imply the data is merely hidden. */
.country-note {
  margin: 0 0 10px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--ink-2);
  background: var(--paper-2);
  border-left: 2px solid var(--line-strong);
  padding: 7px 9px;
  border-radius: 0 6px 6px 0;
}

/* ── Atlas access capture ────────────────────────────────────────────── */
/* Not a gate. The atlas stays fully crawlable; gating an SEO surface behind
   a form trades the traffic for the capture. */
.panel-cta {
  display: block;
  position: relative;
  background: var(--paper-2);
  border: 1px solid var(--line-strong);
  color: var(--ink);
  padding: 10px 34px 10px 12px;
}
.panel-foot .panel-cta-copy span {
  color: var(--ink);
  opacity: 0.65;
}
.panel-foot .panel-cta-form input {
  border-color: var(--line-strong);
  background: var(--paper);
  color: var(--ink);
}
.panel-foot .panel-cta-form input::placeholder {
  color: var(--ink);
  opacity: 0.45;
}
.panel-foot .panel-cta-msg {
  color: var(--ink);
  opacity: 0.7;
}
.panel-cta[hidden] {
  display: none;
}
.panel-cta-close {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: none;
  border-radius: 6px;
  background: transparent;
  cursor: pointer;
}
.panel-cta-close svg {
  width: 13px;
  height: 13px;
  fill: none;
  stroke: var(--ink);
  opacity: 0.55;
  stroke-width: 2;
  stroke-linecap: round;
}
@media (hover: hover) {
  .panel-cta-close:hover {
    background: var(--line-strong);
  }
  .panel-cta-close:hover svg {
    opacity: 1;
  }
}
.panel-cta-form {
  display: flex;
  gap: 6px;
  margin-top: 8px;
}
.panel-cta-form input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 8px 11px;
  font-size: 16px;
  font-family: inherit;
  border: 1px solid rgba(var(--paper-rgb), 0.22);
  background: rgba(var(--paper-rgb), 0.08);
  color: var(--paper);
  border-radius: 8px;
}
.panel-cta-form input::placeholder {
  color: rgba(var(--paper-rgb), 0.55);
}
.panel-cta-form input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
.panel-cta-form button {
  flex-shrink: 0;
  padding: 8px 14px;
  font-size: 12.5px;
  font-weight: 700;
  font-family: inherit;
  color: var(--paper);
  background: var(--accent);
  border: none;
  border-radius: 8px;
  cursor: pointer;
}
@media (hover: hover) {
  .panel-cta-form button:hover {
    background: var(--accent-2);
  }
}
.panel-cta-msg {
  margin: 7px 0 0;
  font-size: 11.5px;
  color: var(--ink-2);
  min-height: 0;
}

/* ══ Mobile sheet, corrected ═══════════════════════════════════════════
   Three faults shipped together and made the sheet unusable on a phone:
   the panel AND the list were both overflow:auto, so every touch drag was
   ambiguous between two nested scrollers; the head had grown to 327px of a
   441px sheet (country selector + focus chips + note + signup); and the
   handle was a click toggle with no drag.

   One scroll surface only: the list. The panel never scrolls. */
@media (max-width: 768px) {
  .panel {
    overflow: hidden;
    touch-action: pan-y;
  }
  /* The EXPANDED stop shows every control plus the capture form and the
     state browser; on a 667px phone that stack exceeds the 88% sheet and
     the foot used to shear off past the viewport with no way to reach it.
     At this stop the map is already surrendered, so the sheet may scroll. */
  .panel.sheet-expanded {
    /* Block flow, not flex: shrink-to-fit flex children under-report the
       scroll extent, which left the last rows unreachable at max scroll. */
    display: block;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .panel.sheet-expanded .school-list {
    /* Content height; the sheet is the one scroll surface at this stop. */
    flex: none;
    min-height: 0;
    overflow: visible;
  }
  .panel.sheet-expanded .panel-foot {
    /* Clearance at the scroll tail: the last row must fully clear the
       viewport edge at max scroll. */
    padding-bottom: 20px;
  }
  .school-list {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }

  /* Head must leave the list room without being squeezed out of existence.
     max-height + flex-shrink collapsed it to the h1 and took the segment
     tabs, country selector, search and Focus chips off the phone entirely.
     flex-shrink:0 keeps the controls; the trimming rules below keep it small. */
  .panel-head {
    flex-shrink: 0;
    padding: 10px 14px 8px;
  }

  /* THE HEAD BUDGET.
     .panel-head is flex-shrink:0 at a fixed 332px inside a 340px sheet, and
     the panel deliberately never scrolls, so at the default stop the
     directory had a visible height of ZERO: not below the fold, unreachable.
     Measured 0 of 3 preview rows on 375x667 and 390x664. Someone typing
     "welding" watched fourteen matching rows render where they could not be
     seen, and no gesture revealed them.

     So the sheet earns its filters. At the default stop it keeps the three
     things the first decision needs: the title, the TYPE tabs, and the
     search row. Country, Focus and Programs are one tap up, at the stop
     that has room for them.

     The Type tabs used to be in this hide list and the stats line was not —
     which inverted the priority: the one control that says what the atlas
     HOLDS (universities / colleges / trade schools) was invisible until you
     found the drag handle, while a passive counter kept its row. The counts
     come back at the expanded stop; the list itself already answers "how
     many are in view" at the default one.

     The h1 is SHRUNK, never hidden: it is this page's only h1, and this site
     has already paid once for shipping crawl content as hidden text.

     The state select joined this list when search was promoted out of the
     Where section: search used to share a row with it, so the two cost 49px
     together. Full-width search plus a full-width select costs 99px, which
     is the whole directory on a 375x667 phone. Search stays; state goes up
     one stop with country, focus and programs — and "Browse by state" in
     the foot is at that same stop. */
  .panel:not(.sheet-expanded) #fpSecWhere .search-row,
  .panel:not(.sheet-expanded) .panel-stats,
  .panel:not(.sheet-expanded) #programChipsLabel,
  .panel:not(.sheet-expanded) #programChips,
  .panel:not(.sheet-expanded) #countryLabel,
  /* #countryPicker, not #countryFilter. #countryFilter is the native <select>,
     already aria-hidden and tabindex=-1; the control the user actually sees is
     #countryPickerBtn inside #countryPicker. Hiding the wrong one left a 40px
     combobox in a 151px head budget, stripped of the "Country" label the rule
     above it did hide, reading only "All countries" with no context. */
  .panel:not(.sheet-expanded) #countryPicker,
  .panel:not(.sheet-expanded) #countryNote,
  .panel:not(.sheet-expanded) #domainChipsLabel,
  .panel:not(.sheet-expanded) #domainChips {
    display: none;
  }
  .panel:not(.sheet-expanded) .panel-head h1 {
    font-size: 15px;
    margin-bottom: 4px;
  }

  .ts-segments,
  .ts-domains,
  .ts-chips {
    margin-bottom: 5px;
  }
  .search-row {
    margin-bottom: 5px;
  }
  .panel-stats {
    margin-bottom: 5px;
  }
  .country-note {
    display: none;
  }
  .panel-head h1 {
    font-size: 18px;
    margin-bottom: 6px;
  }
  .panel-sub {
    display: none;
  }
  .country-select {
    padding: 6px 10px;
    margin-bottom: 6px;
  }
  /* The access ask belongs in the foot on a phone, not above the directory. */
  .panel-cta-form,
  .panel-cta-copy span {
    display: none;
  }
  .panel.sheet-expanded .panel-cta-form {
    display: flex;
  }
  /* "Browse by state" duplicates the state filter one tap away; at the
     default stop its row is what pushed the foot past the 375x667 budget.
     It returns at the expanded stop. */
  .panel:not(.sheet-expanded) .panel-foot details {
    display: none;
  }

  /* A real drag target: 44px, the iOS minimum. */
  .sheet-handle {
    padding: 14px 0 8px;
    touch-action: none;
    cursor: grab;
  }
  .sheet-handle:active {
    cursor: grabbing;
  }
  .panel.sheet-dragging {
    transition: none;
  }
}

/* Basemap attribution is a license requirement (OSM ODbL + CARTO), so it
   stays — but it whispers until you ask for it. */
.maplibregl-ctrl-attrib,
.leaflet-control-attribution {
  opacity: 0.35;
  font-size: 11px;
  transition: opacity 0.15s;
}
@media (hover: hover) {
  .maplibregl-ctrl-attrib:hover,
  .leaflet-control-attribution:hover {
    opacity: 1;
  }
}

/* Theme toggle (hp shell): quiet ghost icon button, no enclosing ring. */
.ba-theme-toggle {
  appearance: none;
  border: none;
  background: transparent;
  color: var(--ink-2);
  line-height: 0;
  width: 34px;
  height: 34px;
  border-radius: 8px;
  cursor: pointer;
  margin: 0 6px 0 2px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition:
    color 0.15s,
    background 0.15s;
}
.ba-theme-toggle svg {
  width: 17px;
  height: 17px;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
@media (hover: hover) {
  .ba-theme-toggle:hover {
    color: var(--ink);
    background: var(--paper-2);
  }
}

/* ══ Chip-rail scroll affordance, both edges ═══════════════════════════
   The rails already faded their RIGHT edge so "a chip cut mid-word reads
   as a layout bug rather than swipe for more". The left edge never got the
   same treatment, and setSegment() scrolls the rail (scrollLeft =
   activeSeg.offsetLeft - 12) to bring the route's own tab into view — so on
   /universities/ and /education/ the rail loads already scrolled and the
   preceding chip is sliced against a hard edge with no fade at all. That is
   the exact bug the right-edge mask exists to prevent, on the other side.

   Fading both edges unconditionally is not the fix either: at scrollLeft 0
   a permanently dimmed first chip reads as disabled. So only the edge that
   actually has content behind it fades. syncRailEdges() in map-globe.js
   writes data-ovf on scroll and resize; these rules are the whole vocabulary
   of that attribute. Selectors carry the class so they outrank the
   single-edge masks earlier in this file at every breakpoint. */
.ts-segments[data-ovf],
.ts-domains[data-ovf],
.ts-chips[data-ovf],
.ts-legend[data-ovf] {
  /* The active chip must not land flush against a fading edge. */
  scroll-padding-inline: 14px;
  -webkit-mask-image: none;
  mask-image: none;
}
.ts-segments[data-ovf='start'],
.ts-domains[data-ovf='start'],
.ts-chips[data-ovf='start'],
.ts-legend[data-ovf='start'] {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 22px);
  mask-image: linear-gradient(to right, transparent 0, #000 22px);
}
.ts-segments[data-ovf='end'],
.ts-domains[data-ovf='end'],
.ts-chips[data-ovf='end'],
.ts-legend[data-ovf='end'] {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 22px), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 22px), transparent);
}
.ts-segments[data-ovf='both'],
.ts-domains[data-ovf='both'],
.ts-chips[data-ovf='both'],
.ts-legend[data-ovf='both'] {
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 22px,
    #000 calc(100% - 22px),
    transparent
  );
  mask-image: linear-gradient(
    to right,
    transparent 0,
    #000 22px,
    #000 calc(100% - 22px),
    transparent
  );
}

@media (max-width: 768px) {
  /* ── The "See all N" row must never be the thing that gets clipped ──
     The collapsed preview is a scroller with a shrinking flex basis, and
     .sg-browse is its last child. Measured on a 390x844 phone the list box
     is 193px against a 217px scrollHeight, so the browse row (58px, the one
     row whose entire job is to open the directory) rendered 35px tall with
     its hint line "Or click a pin on the map" sliced through the middle.
     Pinning it to the bottom of the scroller keeps the whole control on
     screen and turns the preview rows into what scrolls under it. */
  .school-list.is-collapsed .sg-browse {
    position: sticky;
    bottom: 0;
    z-index: 2;
    background: var(--paper);
    box-shadow:
      inset 3px 0 0 var(--line-strong),
      0 -6px 12px -8px rgba(0, 0, 0, 0.45);
  }
  .school-list.is-collapsed .sg-browse:focus-visible {
    box-shadow:
      inset 3px 0 0 var(--accent),
      0 -6px 12px -8px rgba(0, 0, 0, 0.45);
  }

  /* The sheet's own bottom edge is the viewport's bottom edge, so on any
     phone with a home indicator the capture CTA sat underneath it. */
  .panel-foot {
    padding-bottom: max(12px, env(safe-area-inset-bottom));
  }
}
