/* ============================================================================
   Protecting Texas Children — embeddable county map
   Brand palette and type scale live here; everything else derives from them.
   ========================================================================= */

/*
  The brand faces are Klinsman (display) and Avenir (body). Neither can be
  pulled from a CDN because the Content-Security-Policy only permits fonts from
  this origin — which is deliberate. To use the real faces, drop the web font
  files into public/fonts/, add them to the manifest in src/assets.js, and
  uncomment the @font-face rules below.

  @font-face {
    font-family: 'Klinsman';
    src: url('/assets/fonts/klinsman.woff2') format('woff2');
    font-weight: 400 700;
    font-display: swap;
  }
  @font-face {
    font-family: 'Avenir Next';
    src: url('/assets/fonts/avenir-next.woff2') format('woff2');
    font-weight: 400 600;
    font-display: swap;
  }

  Until then the stacks below fall back to the closest geometric sans available
  on the viewer's system, which on macOS and iOS is Avenir itself.
*/

:root {
  --green: #3d3f1b;
  --cream: #f5f0de;
  --rust: #c2482f;
  --teal: #6ea3a2;

  /* Tints derived from the palette rather than introduced as new colours. */
  --county-fill: color-mix(in srgb, var(--green) 9%, var(--cream));
  --county-fill-hover: color-mix(in srgb, var(--green) 20%, var(--cream));
  --county-stroke: color-mix(in srgb, var(--green) 28%, var(--cream));

  --font-display: 'Klinsman', 'Avenir Next', 'Avenir', 'Futura', 'Century Gothic',
    system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body: 'Avenir Next', 'Avenir', 'Nunito Sans', system-ui, -apple-system,
    'Segoe UI', 'Helvetica Neue', sans-serif;

  --radius: 6px;
  --shadow: 0 2px 10px color-mix(in srgb, var(--green) 22%, transparent);
}

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

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--cream);
  color: var(--green);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.page {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1rem;
  max-width: 1100px;
  margin: 0 auto;
}

/* ------------------------------------------------------------------ map -- */

.map {
  position: relative;

  /* The map is a graphic, not a document. Without these, dragging across it
     paints a text selection over the counties and a tap on mobile flashes a
     grey box. Text elsewhere on the page stays selectable. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.map__svg {
  display: block;
  width: 100%;
  height: auto;
  /*
    Must clip: once zoomed, the part of the map outside the viewBox would
    otherwise paint over the legend below it.
  */
  overflow: hidden;
  /*
    At base zoom a one-finger drag must still scroll the embedding page —
    trapping the reader inside the iframe would be hostile. pan-y leaves
    vertical scrolling to the page while still allowing a two-finger pinch.
    The script swaps this to `none` once zoomed in, when panning the map is
    what a drag should do. Horizontal panning is handled by the script.
  */
  touch-action: pan-y;
}

.map__svg.is-zoomed {
  touch-action: none;
  cursor: grab;
}

.map__svg.is-panning {
  cursor: grabbing;
}

.county {
  fill: var(--county-fill);
  stroke: var(--county-stroke);
  stroke-width: 0.6;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  transition: fill 140ms ease-out;
}

.county--highlighted {
  fill: var(--green);
  stroke: var(--cream);
  stroke-width: 0.75;
  cursor: pointer;
}

/* Only highlighted counties are interactive, so only they react to the mouse. */
.county--highlighted:hover,
.county--highlighted.is-active {
  fill: var(--rust);
}

/* Keyboard focus gets a clear indicator. */
.county--highlighted:focus-visible {
  outline: none;
  fill: var(--rust);
  stroke: var(--green);
  stroke-width: 2;
}

/* A mouse click must not leave a ring behind. Some browsers still paint a
   default outline on a focused SVG element even though :focus-visible does
   not match, so it is cleared explicitly. */
.county--highlighted:focus:not(:focus-visible) {
  outline: none;
}

.map__outline {
  fill: none;
  stroke: var(--green);
  stroke-width: 1.4;
  stroke-linejoin: round;
  pointer-events: none;
  vector-effect: non-scaling-stroke;
}

.map__status {
  margin: 0.5rem 0 0;
  text-align: center;
  font-size: 0.85rem;
  color: color-mix(in srgb, var(--green) 65%, var(--cream));
}

.map__status:empty {
  display: none;
}

.map__status--error {
  color: var(--rust);
}

/* ------------------------------------------------------------ zoom UI -- */

.zoom {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 5;
}

.zoom__button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  color: var(--green);
  background: color-mix(in srgb, var(--cream) 88%, transparent);
  border: 1px solid var(--county-stroke);
  border-radius: var(--radius);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 120ms ease-out, color 120ms ease-out;
}

.zoom__button svg {
  width: 1rem;
  height: 1rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
}

.zoom__button:hover:not(:disabled) {
  background: var(--green);
  color: var(--cream);
}

.zoom__button:focus-visible {
  outline: 2px solid var(--rust);
  outline-offset: 2px;
}

.zoom__button:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Only shown after a plain scroll over the map, to explain why nothing zoomed. */
.map__hint {
  position: absolute;
  left: 50%;
  bottom: 0.75rem;
  transform: translateX(-50%);
  margin: 0;
  padding: 0.4rem 0.75rem;
  background: color-mix(in srgb, var(--green) 92%, transparent);
  color: var(--cream);
  font-size: 0.78rem;
  border-radius: var(--radius);
  pointer-events: none;
  opacity: 0;
  transition: opacity 160ms ease-out;
}

.map__hint.is-visible {
  opacity: 1;
}

/* -------------------------------------------------------------- tooltip -- */

.tooltip {
  position: absolute;
  z-index: 10;
  top: 0;
  left: 0;
  /*
    Sized against the map container, not the viewport. Inside a narrow iframe a
    vw-based width can still exceed the padded container, which is how the
    bubble ended up wider than the space available to it.
  */
  max-width: min(18rem, calc(100% - 1rem));
  padding: 0.5rem 0.7rem;
  background: var(--green);
  color: var(--cream);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  pointer-events: none;
  opacity: 0;
  transform: translate3d(-50%, calc(-100% - 10px), 0);
  transition: opacity 120ms ease-out;
}

.tooltip.is-visible {
  opacity: 1;
}

/* Little pointer under the bubble. */
.tooltip::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -5px;
  width: 10px;
  height: 10px;
  background: var(--green);
  transform: translateX(-50%) rotate(45deg);
  border-radius: 1px;
}

.tooltip__name {
  margin: 0;
  font-family: var(--font-display);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  line-height: 1.2;
  /*
    Must wrap. "Collingsworth County" is 205px at this size, which does not fit
    a phone-width bubble — with nowrap the text simply overflowed the bubble and
    was clipped by the iframe edge.
  */
  overflow-wrap: break-word;
}

/* Headshot beside the person's name; both are optional and hide when unset. */
.tooltip__person {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-top: 0.4rem;
}

/* Toggled from script — simpler and more portable than a :has() rule. */
.tooltip__person.is-empty {
  display: none;
}

.tooltip__photo {
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  object-fit: cover;
  background: color-mix(in srgb, var(--cream) 25%, var(--green));
  border: 1.5px solid color-mix(in srgb, var(--cream) 45%, transparent);
}

.tooltip__who {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
  line-height: 1.25;
  color: var(--cream);
  overflow-wrap: break-word;
}

.tooltip__who:empty {
  display: none;
}

.tooltip__bio {
  margin: 0.4rem 0 0;
  font-size: 0.78rem;
  line-height: 1.4;
  color: color-mix(in srgb, var(--cream) 80%, var(--green));
  overflow-wrap: break-word;
}

.tooltip__bio:empty {
  display: none;
}

/* --------------------------------------------------------------- legend -- */

.legend {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-size: 0.85rem;
  letter-spacing: 0.02em;
}

.legend__swatch {
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 3px;
  background: var(--green);
}

.legend__count {
  color: color-mix(in srgb, var(--green) 60%, var(--cream));
}

.legend__count:empty {
  display: none;
}

/* ---------------------------------------------------------------- utils -- */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.is-hidden {
  display: none !important;
}

@media (prefers-reduced-motion: reduce) {
  .county,
  .tooltip {
    transition: none;
  }
}

/*
  The embed is meant to sit on the charity's cream page, so it does not invert
  in dark mode. It does raise contrast a little where the OS asks for it.
*/
@media (prefers-contrast: more) {
  :root {
    --county-fill: color-mix(in srgb, var(--green) 16%, var(--cream));
    --county-stroke: var(--green);
  }
}
