/**************************************************************
Base
***************************************************************/
:root {
  --colorPrimary: #0f5e9c;
  --colorPrimaryDark: #0b4775;
  --colorOnPrimary: #ffffff;
  --colorText: #1f2937;
  --colorTextMuted: #4b5563;
  --colorBackground: #f8fafc;
  --colorSurface: #ffffff;
  --colorBorder: #e2e8f0;
  --colorInputBorder: #6b7280;
  --colorFocus: #1f2937;
  --colorError: #b91c1c;
  --colorErrorSurface: #fef2f2;
  --colorWarning: #d97706;
  --colorWarningText: #92400e; /* 6.4:1 on the warning surface */
  --colorWarningSurface: #fef3c7;

  /* Hero band text and outlines, checked for contrast against the brand blue */
  --colorOnPrimaryMuted: #d7e5f2;
  --colorOnPrimaryBorder: #9fc3e3;
  --colorOnPrimaryHover: #e6f0f9;

  /* Decorative only (heading bars, the highlighted map dot), never text */
  --colorAccent: #f59e0b;

  /* Chips and badges; each text color clears 4.5:1 on its surface */
  --colorChipSurface: #f1f5f9;
  --colorStatuteText: #0b4775;
  --colorStatuteSurface: #e6f0f9;
  --colorReleasedText: #115e59;
  --colorReleasedSurface: #ccfbf1;
  --colorFlaggedText: #92400e;
  --colorFlaggedSurface: #fef3c7;

  /* Charts. Single-series bars use the brand blue. The officer groups use a categorical
     palette checked for colorblind separation; each group keeps its color in every range. */
  --colorChartBar: var(--colorPrimary);
  --colorChartBarHover: var(--colorPrimaryDark);
  --colorChartBarPartial: #9fc3e3; /* A bar still filling up, like today's */
  --colorGridline: #e1e0d9;
  --colorSeriesPacificIslander: #2a78d6;
  --colorSeriesWhite: #eb6834;
  --colorSeriesAsian: #1baf7a;
  --colorSeriesBlack: #eda100;
  --colorSeriesHispanic: #e87ba4;
  --colorSeriesNativeAmerican: #008300;
  --colorSeriesOther: #898781;

  --fontBody: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --fontSizeXs: 0.8125rem;
  --fontSizeSm: 0.875rem;
  --fontSizeBase: 1rem;
  --fontSizeLg: 1.25rem;
  --fontSizeH2: 1.5rem;
  --fontSizeH1: 2rem;
  --fontSizeStat: 1.75rem;
  --fontSizeHero: 3rem;
  --lineHeightBody: 1.6;
  --lineHeightHeading: 1.2;

  --spaceXs: 0.25rem;
  --spaceSm: 0.5rem;
  --spaceMd: 1rem;
  --spaceLg: 2rem;

  --contentWidth: 60rem;
  --radiusSm: 0.25rem;
  --radiusMd: 0.5rem;
  --radiusLg: 0.75rem;
  --radiusPill: 999px;
  --radiusBar: 4px;
  --radiusUnit: 2px;
  --borderWidth: 1px;
  --focusWidth: 3px;

  --barThickness: 1rem;
  --columnWidth: 1.5rem;
  --columnPlotHeight: 12rem;
  --trendHeight: 3rem;
  --unitSize: 0.75rem;
  --gapSurface: 2px;

  --shadowCard: 0 1px 2px rgba(15, 23, 42, 0.06), 0 4px 16px rgba(15, 23, 42, 0.08);
  --bandOverlap: 3rem; /* How far the first block below the page band rises into it */
  --bandArtHeight: clamp(9rem, 16vw, 13rem); /* The skyline along the band's bottom */
  --accentBarWidth: 2.5rem;
  --accentBarHeight: 4px;

  /* Motion: chart bars growing in, and arrest cards fading up as they scroll into view */
  --easeOut: cubic-bezier(0.22, 1, 0.36, 1);
  --growDuration: 700ms;
  --revealDuration: 400ms;
  --revealRise: 1rem;
}

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

/* Column layout keeps the footer at the bottom on short pages */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  margin: 0;
  font-family: var(--fontBody);
  font-size: var(--fontSizeBase);
  line-height: var(--lineHeightBody);
  color: var(--colorText);
  background-color: var(--colorBackground);
}

main {
  flex: 1;
}

h1,
h2 {
  margin: 0 0 var(--spaceMd);
  line-height: var(--lineHeightHeading);
}

h1 {
  font-size: var(--fontSizeH1);
}

h2 {
  font-size: var(--fontSizeH2);
}

p {
  margin: 0 0 var(--spaceMd);
}

a {
  color: var(--colorPrimary);
  text-underline-offset: 0.15em;
}

a:hover {
  color: var(--colorPrimaryDark);
}

:focus-visible {
  outline: var(--focusWidth) solid var(--colorFocus);
  outline-offset: 2px;
}

/**************************************************************
Utilities
***************************************************************/
.pageWidth {
  width: 100%;
  max-width: var(--contentWidth);
  margin-inline: auto;
  padding-inline: var(--spaceMd);
}

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

/**************************************************************
Components
***************************************************************/
/* Hidden until a keyboard user tabs to it */
.skipLink {
  position: absolute;
  top: -100%;
  left: var(--spaceMd);
  z-index: 10;
  padding: var(--spaceSm) var(--spaceMd);
  color: var(--colorText);
  background-color: var(--colorSurface);
  border-radius: var(--radiusMd);
}

.skipLink:focus {
  top: var(--spaceMd);
}

.card {
  padding: var(--spaceMd);
  background-color: var(--colorSurface);
  border: var(--borderWidth) solid var(--colorBorder);
  border-radius: var(--radiusLg);
  box-shadow: var(--shadowCard);
}

.card > :last-child {
  margin-bottom: 0;
}

/* Works on <button> and on links styled as buttons */
.button {
  display: inline-block;
  padding: var(--spaceSm) var(--spaceMd);
  font: inherit;
  font-weight: 600;
  color: var(--colorOnPrimary);
  text-decoration: none;
  background-color: var(--colorPrimary);
  border: 0;
  border-radius: var(--radiusMd);
  cursor: pointer;
}

.button:hover {
  color: var(--colorOnPrimary);
  background-color: var(--colorPrimaryDark);
}

/* A white button for use on the blue page band */
.buttonInverse {
  color: var(--colorPrimary);
  background-color: var(--colorOnPrimary);
}

.buttonInverse:hover {
  color: var(--colorPrimaryDark);
  background-color: var(--colorOnPrimaryHover);
}

/* Chips: small rounded labels for facts about a person (age, sex, ethnicity) */
.chipList {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spaceXs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.chip {
  padding: 0.125rem var(--spaceSm);
  font-size: var(--fontSizeSm);
  background-color: var(--colorChipSurface);
  border: var(--borderWidth) solid var(--colorBorder);
  border-radius: var(--radiusPill);
}

/* Badges: short, bold labels. The text always says what the color means. */
.badge {
  display: inline-block;
  padding: 0.125rem var(--spaceSm);
  font-size: var(--fontSizeXs);
  font-weight: 600;
  line-height: var(--lineHeightBody);
  white-space: nowrap;
  border-radius: var(--radiusSm);
}

.badgeStatute {
  color: var(--colorStatuteText);
  background-color: var(--colorStatuteSurface);
}

.badgeReleased {
  color: var(--colorReleasedText);
  background-color: var(--colorReleasedSurface);
}

.badgeFlagged {
  color: var(--colorFlaggedText);
  background-color: var(--colorFlaggedSurface);
}

.formErrors {
  margin-bottom: var(--spaceMd);
  padding: var(--spaceMd);
  color: var(--colorError);
  background-color: var(--colorErrorSurface);
  border: var(--borderWidth) solid var(--colorError);
  border-radius: var(--radiusMd);
}

.formErrors > :last-child {
  margin-bottom: 0;
}

.flashMessage {
  margin-bottom: var(--spaceMd);
  padding: var(--spaceSm) var(--spaceMd);
  color: var(--colorText); /* It can sit on the blue band, which sets white text */
  background-color: var(--colorSurface);
  border: var(--borderWidth) solid var(--colorBorder);
  border-left: var(--focusWidth) solid var(--colorPrimary);
  border-radius: var(--radiusMd);
}

/* Data that should have been refreshed by now. It can sit on the blue band, too. */
.staleNotice {
  margin-bottom: var(--spaceMd);
  padding: var(--spaceSm) var(--spaceMd);
  color: var(--colorWarningText);
  background-color: var(--colorWarningSurface);
  border: var(--borderWidth) solid var(--colorWarning);
  border-left: var(--focusWidth) solid var(--colorWarning);
  border-radius: var(--radiusMd);
}

/* A label stacked above its input */
.formField {
  display: flex;
  flex-direction: column;
  gap: var(--spaceXs);
}

.formField input {
  padding: var(--spaceSm);
  font: inherit;
  border: var(--borderWidth) solid var(--colorInputBorder);
  border-radius: var(--radiusMd);
}

/* Stat tiles: a label, a big number, and a note */
.statRow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: var(--spaceMd);
  margin: 0;
}

.statTile {
  padding: var(--spaceMd);
  background-color: var(--colorSurface);
  border: var(--borderWidth) solid var(--colorBorder);
  border-radius: var(--radiusLg);
  box-shadow: var(--shadowCard);
}

.statLabel {
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
}

.statValue {
  margin: 0;
  font-size: var(--fontSizeStat);
  font-weight: 600;
  line-height: var(--lineHeightHeading);
}

.statTileHero .statValue {
  font-size: var(--fontSizeHero);
}

/* Home: on phones the Arrests tile takes the full row, above the other three in a row;
   wider screens give it two of five columns (see Media queries) */
.statRowHome {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.statRowHome .statTileHero {
  grid-column: 1 / -1;
}

/* The chart sits beside the number, with the notes below both */
.statTileHero.hasTrend {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--spaceLg);
  align-items: end;
}

.statTileHero.hasTrend > :not(.statValue, .trendCell) {
  grid-column: 1 / -1;
}

.trendCell {
  margin: 0 0 var(--spaceSm);
}

/* The Arrests tile's chart over time: thin columns on a hairline, each with a tooltip.
   dashboardCharts.js sets --barShare, as for the other charts. */
.trendBars {
  display: flex;
  gap: var(--gapSurface);
  align-items: flex-end;
  height: var(--trendHeight);
  border-bottom: var(--borderWidth) solid var(--colorGridline);
}

/* The full height is the hover target, so even a short bar is easy to point at */
.trendSlot {
  position: relative;
  display: flex;
  flex: 1;
  align-items: flex-end;
  max-width: var(--columnWidth);
  height: 100%;
}

.trendBar {
  width: 100%;
  height: calc(100% * var(--barShare, 0) / 100);
  background-color: var(--colorChartBar);
  border-radius: var(--radiusUnit) var(--radiusUnit) 0 0;
  transition: height var(--growDuration) var(--easeOut);
}

.trendBarPartial {
  background-color: var(--colorChartBarPartial);
}

.trendSlot:hover .trendBar {
  background-color: var(--colorChartBarHover);
}

.trendSlot:hover::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + var(--spaceXs));
  left: 50%;
  z-index: 1;
  padding: 0 var(--spaceSm);
  font-size: var(--fontSizeXs);
  white-space: nowrap;
  color: var(--colorOnPrimary);
  pointer-events: none;
  background-color: var(--colorText);
  border-radius: var(--radiusSm);
  transform: translateX(-50%);
}

/* Tooltips at the ends line up with the chart's edge instead of hanging past it */
.trendSlot:nth-child(-n + 3):hover::after {
  left: 0;
  transform: none;
}

.trendSlot:nth-last-child(-n + 3):hover::after {
  right: 0;
  left: auto;
  transform: none;
}

.statNote {
  margin: 0;
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
}

/* Charts */
.chartCaption,
.chartCaveat {
  color: var(--colorTextMuted);
}

.chartCaveat {
  margin-top: var(--spaceMd);
  font-size: var(--fontSizeSm);
}

/* Horizontal bars grow from a hairline baseline, with the value at the tip.
   dashboardCharts.js sets --barShare (0–100) from each bar's data-share when the chart
   scrolls into view, and the bars grow to it (see --growDuration). */
.barList {
  display: grid;
  gap: var(--spaceSm);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Labels get at most 30% of the row (and never over 12rem), so bars keep room on phones */
.barRow {
  display: grid;
  grid-template-columns: minmax(5rem, min(30%, 12rem)) 1fr;
  gap: var(--spaceMd);
  align-items: center;
}

.barLabel {
  font-size: var(--fontSizeSm);
  overflow-wrap: anywhere;
}

.barArea {
  display: flex;
  gap: var(--spaceSm);
  align-items: center;
  border-left: var(--borderWidth) solid var(--colorGridline);
}

/* The 4rem left over is room for the value label */
.barFill {
  flex: none;
  width: calc((100% - 4rem) * var(--barShare, 0) / 100);
  height: var(--barThickness);
  background-color: var(--colorChartBar);
  border-radius: 0 var(--radiusBar) var(--radiusBar) 0;
  transition: width var(--growDuration) var(--easeOut);
}

.barValue,
.columnValue {
  font-size: var(--fontSizeSm);
  font-variant-numeric: tabular-nums;
}

/* Columns stand on a hairline baseline, with the value on top */
.columnPlot {
  display: flex;
  gap: var(--spaceSm);
  height: var(--columnPlotHeight);
  border-bottom: var(--borderWidth) solid var(--colorGridline);
}

.columnItem {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--spaceXs);
  align-items: center;
  justify-content: flex-end;
}

/* The 1.75rem left over is room for the value above the tallest column */
.columnBar {
  width: min(100%, var(--columnWidth));
  height: calc((100% - 1.75rem) * var(--barShare, 0) / 100);
  background-color: var(--colorChartBar);
  border-radius: var(--radiusBar) var(--radiusBar) 0 0;
  transition: height var(--growDuration) var(--easeOut);
}

.barFill:hover,
.columnBar:hover {
  background-color: var(--colorChartBarHover);
}

.columnAxis {
  display: flex;
  gap: var(--spaceSm);
  padding-top: var(--spaceXs);
}

.columnLabel {
  flex: 1;
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
  text-align: center;
}

/* Legend: a color swatch beside plain text, so color is never the only label */
.chartLegend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spaceSm) var(--spaceMd);
  margin: 0 0 var(--spaceMd);
  padding: 0;
  font-size: var(--fontSizeSm);
  list-style: none;
}

.legendItem {
  display: flex;
  gap: var(--spaceXs);
  align-items: center;
}

.legendCount {
  font-weight: 600;
}

.legendSwatch,
.officerUnit {
  width: var(--unitSize);
  height: var(--unitSize);
  border-radius: var(--radiusUnit);
}

.seriesPacificIslander {
  background-color: var(--colorSeriesPacificIslander);
}

.seriesWhite {
  background-color: var(--colorSeriesWhite);
}

.seriesAsian {
  background-color: var(--colorSeriesAsian);
}

.seriesBlack {
  background-color: var(--colorSeriesBlack);
}

.seriesHispanic {
  background-color: var(--colorSeriesHispanic);
}

.seriesNativeAmerican {
  background-color: var(--colorSeriesNativeAmerican);
}

.seriesOther {
  background-color: var(--colorSeriesOther);
}

/* The table twin of each chart */
.tableView {
  margin-top: var(--spaceMd);
  font-size: var(--fontSizeSm);
}

.tableView summary {
  font-weight: 600;
  color: var(--colorPrimary);
  cursor: pointer;
}

.tableView table {
  margin-top: var(--spaceSm);
  border-collapse: collapse;
}

.tableView th,
.tableView td {
  padding: var(--spaceXs) var(--spaceMd);
  text-align: left;
  border-bottom: var(--borderWidth) solid var(--colorBorder);
}

.tableView td {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

/**************************************************************
Header
***************************************************************/
.siteHeader {
  color: var(--colorOnPrimary);
  background-color: var(--colorPrimary);
}

/* The dark focus ring would disappear on the blue header */
.siteHeader :focus-visible {
  outline-color: var(--colorOnPrimary);
}

.headerInner {
  display: flex;
  flex-direction: column;
  gap: var(--spaceSm);
  padding-block: var(--spaceMd);
}

.siteTitle {
  font-size: var(--fontSizeLg);
  font-weight: 700;
  color: var(--colorOnPrimary);
  text-decoration: none;
}

.siteTitle:hover {
  color: var(--colorOnPrimary);
  text-decoration: underline;
}

/**************************************************************
Nav
***************************************************************/
.navList {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spaceMd);
  align-items: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

.navLink {
  padding-block: var(--spaceXs);
  color: var(--colorOnPrimary);
  text-decoration: none;
  border-bottom: 2px solid transparent;
}

.navLink:hover {
  color: var(--colorOnPrimary);
  border-bottom-color: currentColor;
}

.navLink[aria-current='page'] {
  font-weight: 600;
  border-bottom-color: var(--colorOnPrimary);
}

.navAccount {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spaceSm);
  align-items: center;
}

.navUser {
  font-size: var(--fontSizeSm);
  overflow-wrap: anywhere;
}

/* The log out button looks like the nav links beside it */
.navButton {
  padding: var(--spaceXs) 0;
  font: inherit;
  color: var(--colorOnPrimary);
  cursor: pointer;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
}

.navButton:hover {
  border-bottom-color: currentColor;
}

/**************************************************************
Page band
***************************************************************/
/* The band starts right under the header, so main has no top padding */
.bandMain {
  padding-bottom: var(--spaceLg);
}

/* The blue band at the top of every page, with the skyline along its bottom */
.pageBand {
  color: var(--colorOnPrimary);
  background:
    url('/img/bandSkyline.svg') right bottom / auto var(--bandArtHeight) no-repeat,
    linear-gradient(160deg, var(--colorPrimary), var(--colorPrimaryDark));
}

/* The dark focus ring would disappear on the blue band */
.pageBand :focus-visible {
  outline-color: var(--colorOnPrimary);
}

/* Extra room at the bottom for the first block below, which rises into the band */
.bandInner {
  padding-block: var(--spaceLg) calc(var(--bandOverlap) + var(--spaceLg));
}

.bandInner > :last-child {
  margin-bottom: 0;
}

.bandLead {
  max-width: 40rem;
  font-size: var(--fontSizeLg);
  color: var(--colorOnPrimaryMuted);
}

.bandLead a {
  color: var(--colorOnPrimary);
}

.bandLead a:hover {
  color: var(--colorOnPrimaryHover);
}

.bandNote {
  font-size: var(--fontSizeSm);
  color: var(--colorOnPrimaryMuted);
}

/* The first block below the band (stat tiles, a card, or a message) rises into it */
.bandContent > :first-child {
  position: relative;
  margin-top: calc(-1 * var(--bandOverlap));
}

/**************************************************************
Date filter
***************************************************************/
/* The filter sits on the blue page band, so its colors are the white-on-blue versions */
.dateFilter {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spaceMd);
  align-items: flex-end;
}

.presetList {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spaceSm);
  margin: 0;
  padding: 0;
  list-style: none;
}

.presetLink {
  display: inline-block;
  padding: var(--spaceXs) var(--spaceMd);
  color: var(--colorOnPrimary);
  text-decoration: none;
  background-color: transparent;
  border: var(--borderWidth) solid var(--colorOnPrimaryBorder);
  border-radius: var(--radiusPill);
}

.presetLink:hover {
  color: var(--colorOnPrimary);
  border-color: var(--colorOnPrimary);
}

.presetLink[aria-current='true'] {
  font-weight: 600;
  color: var(--colorPrimary);
  background-color: var(--colorOnPrimary);
  border-color: var(--colorOnPrimary);
}

.dateField {
  display: flex;
  flex-direction: column;
  gap: var(--spaceXs);
  font-size: var(--fontSizeSm);
}

.dateField input {
  padding: var(--spaceXs) var(--spaceSm);
  font: inherit;
  color: var(--colorText);
  background-color: var(--colorSurface);
  border: var(--borderWidth) solid var(--colorOnPrimaryBorder);
  border-radius: var(--radiusMd);
}

/**************************************************************
Dashboard
***************************************************************/
.summarySection,
.chartCard {
  margin-bottom: var(--spaceLg);
}

/* A short accent bar under each section heading */
.chartCard > h2::after,
.sectionHeader > h2::after,
.aboutData > h2::after {
  content: '';
  display: block;
  width: var(--accentBarWidth);
  height: var(--accentBarHeight);
  margin-top: var(--spaceSm);
  background-color: var(--colorAccent);
  border-radius: var(--radiusPill);
}

.aboutData {
  max-width: 45rem;
  color: var(--colorTextMuted);
}

.aboutData > h2 {
  font-size: var(--fontSizeLg);
  color: var(--colorText);
}

.dataNote {
  margin: var(--spaceMd) 0 0;
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
}

.officerUnits {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gapSurface);
}

/**************************************************************
News
***************************************************************/
.newsSection,
.xSection {
  margin-bottom: var(--spaceLg);
}

/* The heading, with a link to HPD's news page at the other end */
.sectionHeader {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spaceSm) var(--spaceMd);
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: var(--spaceMd);
}

.sectionHeader > h2 {
  margin-bottom: 0;
}

/* Stacked on phones; the featured release beside the list on wide screens (see Media queries) */
.newsLayout {
  display: grid;
  gap: var(--spaceMd);
}

/* Each card is one link: the title's link stretches over the whole card */
.newsFeatured,
.newsItem {
  position: relative;
}

.newsTitle {
  margin: 0 0 var(--spaceXs);
  font-size: var(--fontSizeLg);
  line-height: var(--lineHeightHeading);
}

.newsTitle a {
  color: var(--colorText);
  text-decoration: none;
}

.newsTitle a::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--radiusLg);
}

.newsFeatured:hover .newsTitle a,
.newsItem:hover .newsTitle a {
  text-decoration: underline;
}

/* The focus ring goes around the whole card, not just the title */
.newsTitle a:focus-visible {
  outline: none;
}

.newsTitle a:focus-visible::after {
  outline: var(--focusWidth) solid var(--colorFocus);
  outline-offset: 2px;
}

.newsDate {
  margin: 0 0 var(--spaceSm);
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
}

/* A release without a photo shows the page band's gradient and skyline */
.newsPlaceholder {
  background:
    url('/img/bandSkyline.svg') right bottom / auto 70% no-repeat,
    linear-gradient(160deg, var(--colorPrimary), var(--colorPrimaryDark));
}

/* The featured card's photo runs edge to edge, so the padding moves to the text below it */
.card.newsFeatured {
  padding: 0;
}

.newsImage {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: calc(var(--radiusLg) - var(--borderWidth)) calc(var(--radiusLg) - var(--borderWidth)) 0 0;
}

.newsBody {
  padding: var(--spaceMd);
}

.newsBody > :last-child {
  margin-bottom: 0;
}

.newsReadMore {
  font-weight: 600;
  color: var(--colorPrimary);
}

.newsList {
  display: grid;
  gap: var(--spaceMd);
  margin: 0;
  padding: 0;
  list-style: none;
}

.card.newsItem {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr);
  gap: var(--spaceMd);
  align-items: center;
  padding: var(--spaceSm);
}

.newsItem .newsTitle {
  font-size: var(--fontSizeBase);
  overflow-wrap: anywhere;
}

.newsItem .newsDate {
  margin: 0;
}

.newsThumb {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radiusMd);
}

/* HPD's X posts: one column on phones, two on wider screens (see Media queries) */
.xPostList {
  display: grid;
  gap: var(--spaceMd);
  margin: 0;
  padding: 0;
  list-style: none;
}

.card.xPost {
  display: flex;
  flex-direction: column;
  gap: var(--spaceSm);
  padding: var(--spaceMd);
}

/* X's display requirements: the avatar to the left of the name and @handle */
.xPostHeader {
  display: flex;
  gap: var(--spaceSm);
  align-items: center;
}

.xAvatar {
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
}

.xAuthor {
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--spaceXs);
  min-width: 0;
  margin: 0;
  font-size: var(--fontSizeSm);
  line-height: var(--lineHeightHeading);
}

.xName {
  font-weight: 600;
}

.xHandle {
  color: var(--colorTextMuted);
}

.xTime {
  flex: none;
  margin-left: auto;
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
}

/* The post's own line breaks are kept; long links wrap instead of widening the card */
.xText {
  margin: 0;
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.xMedia {
  position: relative;
  display: block;
  margin-top: auto;
}

.xMedia img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radiusMd);
}

.xMediaBadge {
  position: absolute;
  right: var(--spaceSm);
  bottom: var(--spaceSm);
  padding: 0 var(--spaceSm);
  font-size: var(--fontSizeXs);
  font-weight: 600;
  color: var(--colorOnPrimary);
  background-color: var(--colorText);
  border-radius: var(--radiusPill);
}

/**************************************************************
Arrests
***************************************************************/
.arrestSearch {
  margin-bottom: var(--spaceLg);
}

.arrestSearch label {
  display: block;
  margin-bottom: var(--spaceXs);
  font-weight: 600;
}

.searchRow {
  display: flex;
  gap: var(--spaceSm);
}

/* min-width: 0 lets the field shrink beside the button on narrow phones */
.searchRow input {
  flex: 1;
  min-width: 0;
  padding: var(--spaceSm);
  font: inherit;
  color: var(--colorText);
  background-color: var(--colorSurface);
  border: var(--borderWidth) solid var(--colorInputBorder);
  border-radius: var(--radiusMd);
}

.searchHint {
  margin: var(--spaceXs) 0 0;
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
}

.searchSummary {
  margin-bottom: var(--spaceMd);
  font-weight: 600;
}

.arrestFeed {
  display: grid;
  gap: var(--spaceMd);
}

/* arrestFeed.js hides cards below the fold until they scroll into view, then fades them up.
   Without JavaScript, no card is ever hidden. */
.arrestCard {
  transition:
    opacity var(--revealDuration) var(--easeOut),
    transform var(--revealDuration) var(--easeOut);
}

.arrestCard.beforeReveal {
  opacity: 0;
  transform: translateY(var(--revealRise));
}

.arrestHeader > * {
  margin-bottom: var(--spaceSm);
}

/* The time, with a "Flagged for review" badge beside it when there is one */
.arrestHeading,
.offenseHeading {
  display: flex;
  flex-wrap: wrap;
  gap: var(--spaceSm);
  align-items: center;
}

.arrestHeading {
  justify-content: space-between;
}

.arrestTime {
  margin: 0;
  font-size: var(--fontSizeLg);
}

.arrestName {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.arrestNameMissing {
  font-weight: 400;
  font-style: italic;
  color: var(--colorTextMuted);
}

.offenseList {
  display: grid;
  gap: var(--spaceMd);
  margin: var(--spaceMd) 0;
  padding: 0;
  list-style: none;
}

.offenseItem {
  padding-top: var(--spaceMd);
  border-top: var(--borderWidth) solid var(--colorBorder);
}

/* The charge name, followed by its statute badge and a "Released" badge */
.offenseHeading {
  margin-bottom: var(--spaceSm);
}

.offenseTitle {
  margin: 0;
  font-size: var(--fontSizeBase);
}

.offenseDetails {
  display: grid;
  gap: var(--spaceSm);
  margin: 0;
  font-size: var(--fontSizeSm);
}

/* Label above value on phones; side by side on wider screens (see Media queries) */
.offenseField {
  display: grid;
}

.offenseField dt {
  color: var(--colorTextMuted);
}

.offenseField dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.arrestFooter {
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
}

.arrestFooter p {
  margin-bottom: var(--spaceXs);
}

.ocrNote {
  display: block;
}

.arrestWarnings {
  margin-top: var(--spaceSm);
}

.arrestWarnings summary {
  cursor: pointer;
}

.arrestWarnings ul {
  margin: var(--spaceXs) 0 0;
}

.feedStatus {
  margin: var(--spaceLg) 0 var(--spaceMd);
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
}

.feedEnd {
  font-weight: 600;
}

/**************************************************************
Dispatches
***************************************************************/
.dispatchActive {
  margin-bottom: var(--spaceLg);
}

/* Leaflet needs the map box to have a height */
.callMap {
  height: 20rem;
  margin-bottom: var(--spaceSm);
  border: var(--borderWidth) solid var(--colorBorder);
  border-radius: var(--radiusMd);
}

.mapNote {
  margin-bottom: var(--spaceMd);
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
}

.mapPopupType,
.mapPopupLine,
.mapPopupNote {
  display: block;
}

.mapPopupNote {
  margin-top: var(--spaceXs);
  font-style: italic;
  color: var(--colorTextMuted);
}

.callList {
  margin: 0;
  padding: 0;
  list-style: none;
}

.callItem {
  font-size: var(--fontSizeSm);
  border-top: var(--borderWidth) solid var(--colorBorder);
}

.callItem:first-child {
  border-top: 0;
}

/* One call's details: stacked on phones; four columns on wider screens (see Media queries) */
.callRow {
  display: grid;
  gap: 0 var(--spaceMd);
  padding-block: var(--spaceSm);
}

/* A row whose call is on the map. It reaches a little past the text on each side, so its
   highlight doesn't sit tight against the words. */
.callButton {
  width: calc(100% + 2 * var(--spaceSm));
  margin-inline: calc(-1 * var(--spaceSm));
  padding-inline: var(--spaceSm);
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  background: none;
  border: 0;
  border-radius: var(--radiusMd);
}

/* Set by the map script while the row or its dot is pointed at, focused, or open */
.callButton.isHighlighted {
  background-color: var(--colorStatuteSurface);
}

.callTime,
.callDistrict {
  color: var(--colorTextMuted);
}

.callTime {
  font-variant-numeric: tabular-nums;
}

.callType {
  font-weight: 600;
}

.callPlace {
  overflow-wrap: anywhere;
}

/* A call type or district name is longer than a number, so it's set smaller */
.statValueText {
  font-size: var(--fontSizeLg);
  overflow-wrap: anywhere;
}

/**************************************************************
Login
***************************************************************/
/* The heading sits centered above the centered card */
.loginIntro {
  text-align: center;
}

.loginIntro .bandLead {
  margin-inline: auto;
}

.loginCard {
  max-width: 28rem;
  margin-inline: auto;
}

.loginForm {
  display: grid;
  gap: var(--spaceMd);
}

.loginForm .button {
  justify-self: start;
}

/**************************************************************
Footer
***************************************************************/
.siteFooter {
  font-size: var(--fontSizeSm);
  color: var(--colorTextMuted);
  background-color: var(--colorSurface);
  border-top: var(--borderWidth) solid var(--colorBorder);
}

.footerInner {
  padding-block: var(--spaceLg);
}

.footerInner > :last-child {
  margin-bottom: 0;
}

/**************************************************************
Media queries
***************************************************************/
@media (min-width: 40rem) {
  :root {
    --fontSizeH1: 2.5rem;
  }

  .headerInner {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }

  .card {
    padding: var(--spaceLg);
  }

  .statRowHome {
    grid-template-columns: 2fr repeat(3, minmax(0, 1fr));
  }

  .statRowHome .statTileHero {
    grid-column: auto;
  }

  .newsBody {
    padding: var(--spaceLg);
  }

  .xPostList {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .offenseField {
    grid-template-columns: 9rem 1fr;
    gap: var(--spaceSm);
  }

  .callRow {
    grid-template-columns: 8rem minmax(8rem, 13rem) 1fr 5.5rem;
    align-items: baseline;
  }

  .callMap {
    height: 26rem;
  }
}

/* The featured release beside the list, which shares its height among the list's cards */
@media (min-width: 56rem) {
  .newsLayout {
    grid-template-columns: 3fr 2fr;
  }

  .newsList {
    grid-auto-rows: 1fr;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important; /* Chart bars would otherwise pop in one at a time */
    scroll-behavior: auto !important;
  }
}

/* A printout shows every card, including ones never scrolled to */
@media print {
  .arrestCard.beforeReveal {
    opacity: 1;
    transform: none;
  }
}
