:root {
  --color-yes: #2e7d32;
  --color-no: #9e9e9e;
  --color-partial: #f9a825;
  --color-empty: #e0e0e0;
  /* Deliberately NOT PALETTE[0] (#1565c0, "blue") -- that's the exact same
     color as the Procedure Separation Type key's Type 1 swatch (see
     PROCEDURE_SEPARATION_TYPE_COLORS in dashboard-data.js), which made the
     Coverage Checklist's "Other/free text" chip and a Type 1 row's colored
     accents read as the same category at a glance. #00838f is PALETTE[5]
     ("teal") -- part of the same fixed color set, but not used by any of
     the five Procedure Separation Types, so it stays visually distinct. */
  --color-other: #00838f;

  --border: #dde2e8;
  --bg-subtle: #f6f8fb;
  --bg-page: #fafbfd;
  --text-main: #1c2430;
  --text-muted: #5b6470;

  /* Primary brand accent: a professional deep teal/navy, used for the bulk
     of interactive chrome (buttons, links, focus rings, active tab). */
  --accent: #2f5f8a;
  --accent-dark: #244a6c;
  --accent-soft: #eaf1f7;

  /* Secondary accent: a soft, dusty rose. Used sparingly (a thin gradient
     line, a hint-banner border, a couple of legend/brand touches) as a
     subtle nod to this being a women's-health resource, without leaning on
     overt gender-coded styling anywhere else in the UI. */
  --accent-rose: #c2698a;
  --accent-rose-soft: #f7e9ee;

  --warn: #b06a00;
  --warn-bg: #fdf3e2;
  --warn-border: #eecb8c;

  --hover-bg: #eef3f8;
  --shadow-sm: 0 1px 2px rgba(20, 30, 45, 0.06), 0 1px 1px rgba(20, 30, 45, 0.04);
  --shadow-md: 0 4px 14px rgba(20, 30, 45, 0.08), 0 1px 3px rgba(20, 30, 45, 0.06);
  --radius: 8px;

  font-size: 15px;
}

* { box-sizing: border-box; }

body {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  margin: 0;
  color: var(--text-main);
  /* Soft color wash in the corners plus a faint dot-grid, evoking graph
     paper / a scientific data platform without competing with the actual
     data. All three layers are intentionally subtle -- this reads as
     "textured" rather than "decorated" at normal viewing distance. */
  background-color: var(--bg-page);
  background-image:
    radial-gradient(1100px 550px at 8% -12%, var(--accent-soft) 0%, transparent 60%),
    radial-gradient(900px 480px at 104% 0%, var(--accent-rose-soft) 0%, transparent 55%),
    radial-gradient(circle, rgba(47, 95, 138, 0.07) 1px, transparent 1px);
  background-size: auto, auto, 24px 24px;
  background-repeat: no-repeat, no-repeat, repeat;
  background-attachment: fixed, fixed, fixed;
}

/* ---------------------------------------------------------------------
   Header
   --------------------------------------------------------------------- */

header.app-header {
  padding: 1.5rem 1.75rem 0;
  background: linear-gradient(120deg, #ffffff 0%, #ffffff 55%, var(--accent-soft) 130%);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  position: relative;
}

/* A gradient line across the very top of the page: mostly the neutral
   accent, sweeping into a soft rose at the end -- a subtle, non-literal
   hint that this is a women's-health resource, without color-coding the
   whole interface. */
header.app-header::before {
  content: "";
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 5px;
  background: linear-gradient(90deg, var(--accent) 0%, var(--accent-dark) 45%, var(--accent) 70%, var(--accent-rose) 100%);
}

.header-top {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

/* Logo + <h1> both live inside this link now -- it, not ".header-top",
   carries the flex layout/gap between them, since ".header-top" itself
   only has this one child. A subtle opacity dip on hover is the only
   affordance (rather than an underline, which would look wrong drawn
   across a whole heading) so it still reads as branding at rest. */
.brand-home-link {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  text-decoration: none;
  color: inherit;
  transition: opacity 0.15s ease;
}

.brand-home-link:hover {
  opacity: 0.82;
}

/* Logo: one solid badge carrying the full wordmark, filled with the same
   gradient as the header's own top accent bar (see "header.app-header
   ::before" above) so the two read as drawn from a single design system
   rather than two unrelated brand touches. */
.brand-mark {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  flex: none;
  padding: 0.4rem 0.85rem 0.45rem;
  border-radius: 10px;
  background: linear-gradient(100deg, var(--accent) 0%, var(--accent-dark) 45%, var(--accent) 70%, var(--accent-rose) 100%);
  box-shadow: var(--shadow-sm);
}

.brand-wordmark {
  font-weight: 500;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  line-height: 1;
  color: rgba(255, 255, 255, 0.82);
  white-space: nowrap;
}

.brand-wordmark strong {
  font-weight: 800;
  font-size: 0.98rem;
  letter-spacing: 0.05em;
  color: #fff;
  margin-left: 0.12em;
}

/* Small heartbeat/data-line flourish under the wordmark -- the one purely
   graphic element in the mark, tying it back to the health-data subject
   matter without needing a separate icon glyph. */
.brand-flourish {
  display: block;
  margin-top: 0.2rem;
}

/* Wraps the <h1> and the domain line so they stack vertically beside the
   logo badge -- ".brand-home-link" itself is a horizontal flex row (logo
   badge next to this), so the title and domain need their own column to
   stack in rather than becoming a third item laid out side-by-side. */
.brand-text {
  display: flex;
  flex-direction: column;
}

header.app-header h1 {
  margin: 0 0 0.15rem;
  font-size: 1.4rem;
  letter-spacing: -0.01em;
}

/* Brand-owned domain, shown under the title -- plain text rather than a
   real link until the domain is actually pointed at this site (see the
   comment beside it in index.html). */
.brand-domain {
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--text-muted);
  letter-spacing: 0.01em;
}

header.app-header p {
  margin: 0.15rem 0 1rem;
  color: var(--text-muted);
  font-size: 0.9rem;
  max-width: 60em;
  line-height: 1.5;
}

/* Site-wide banner for surfacing data-source status (e.g. sample/mock data
   vs. the live spreadsheet). Hidden by default; shown by JS when relevant. */
.mock-data-banner {
  display: none;
  margin: 0 1.75rem 1rem;
  padding: 0.65rem 0.9rem;
  background: var(--warn-bg);
  border: 1px solid var(--warn-border);
  border-left: 3px solid var(--warn);
  border-radius: 6px;
  color: #6b4700;
  font-size: 0.85rem;
  line-height: 1.5;
}
.mock-data-banner.visible { display: block; }
.mock-data-banner strong { color: var(--warn); }

nav.tabs {
  display: flex;
  gap: 0.3rem;
  flex-wrap: wrap;
}

nav.tabs button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  border: 1px solid transparent;
  border-bottom: none;
  background: transparent;
  padding: 0.6rem 1.05rem;
  font-size: 0.9rem;
  cursor: pointer;
  border-radius: 8px 8px 0 0;
  color: var(--text-muted);
  font-weight: 500;
  transition: background 0.15s ease, color 0.15s ease;
}

/* Home icon on the Information tab -- inherits the button's current text
   color (muted / hover / active-accent) via `stroke: currentColor` on the
   inline SVG, so it never needs its own color rules. */
nav.tabs button .tab-icon {
  flex: none;
  display: block;
}

nav.tabs button:hover {
  background: var(--bg-subtle);
  color: var(--text-main);
}

nav.tabs button.active {
  background: var(--bg-page);
  font-weight: 700;
  color: var(--accent-dark);
  border-color: var(--border);
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
  position: relative;
}

/* Women's Health Data Inventory / Items Reference dropdown grouping (see
   the comment on ".nav-item-group" in index.html). The group itself is a
   flex item right alongside every plain top-level button, so it lines up
   in the same row without needing its own layout rules; its own
   `position: relative` is just this dropdown's positioning anchor. */
.nav-item-group {
  position: relative;
  display: flex;
}
.nav-dropdown {
  display: none;
  flex-direction: column;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 20;
  min-width: 14em;
  background: var(--bg-page);
  border: 1px solid var(--border);
  border-radius: 0 8px 8px 8px;
  box-shadow: var(--shadow-md);
  overflow: hidden;
  padding: 0.3rem;
}
/* ":hover" only on devices with a real hover (mouse/trackpad). Touch
   browsers leave the last-tapped element stuck in ":hover" until something
   else is tapped, which kept this dropdown open after picking an option on
   phones. Touch devices use the ".open" class instead, toggled in
   wireTabs() in dashboard.js. */
@media (hover: hover) {
  .nav-item-group:hover .nav-dropdown {
    display: flex;
  }
}
.nav-item-group:focus-within .nav-dropdown,
.nav-item-group.open .nav-dropdown {
  display: flex;
}
/* Landing on Items Reference (via the dropdown -- it has no top-level
   button of its own) leaves the visible "Women's Health Data Inventory"
   button not itself ".active" (see wireTabs() in dashboard.js), which
   would otherwise leave the whole nav bar looking like nothing is
   selected. A lighter tint -- not the full ".active" treatment below,
   which would wrongly claim this exact tab is showing -- reads as "you're
   somewhere in this group" instead. ":not(.active)" skips this when the
   button *is* the exact active tab, so the two indicators never stack. */
.nav-item-group.group-active > button:not(.active) {
  background: var(--bg-subtle);
  color: var(--accent-dark);
}
/* Overrides "nav.tabs button" above for buttons specifically inside the
   dropdown -- needs the extra "nav.tabs" ancestor prefix to out-specificity
   that rule (a plain ".nav-dropdown button" selector alone loses to it).
   Stacked full-width rows instead of that rule's inline-flex tab shape,
   and no bottom-border/negative-margin tab styling, which only made sense
   for a horizontal tab strip. */
nav.tabs .nav-dropdown button {
  display: block;
  width: 100%;
  text-align: left;
  border: none;
  border-radius: 6px;
  padding: 0.5rem 0.7rem;
  margin-bottom: -1px; /* cancel out the inherited rule's own margin-bottom on .active */
}
nav.tabs .nav-dropdown button:hover {
  background: var(--hover-bg);
}
/* A left accent bar instead of the horizontal tab bar's gradient
   underline (see "nav.tabs button.active::after" below, suppressed here
   via display:none) -- reads correctly for a vertical list of options
   rather than implying an underline beneath text that isn't there. */
nav.tabs .nav-dropdown button.active {
  background: var(--accent-soft);
  border-left: 3px solid var(--accent);
  padding-left: calc(0.7rem - 3px);
}
nav.tabs .nav-dropdown button.active::after {
  display: none;
}

/* Gradient underline on the active tab instead of a flat color, echoing
   the header's top accent bar. */
nav.tabs button.active::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 3px;
  border-radius: 2px 2px 0 0;
  background: linear-gradient(90deg, var(--accent) 0%, var(--accent-rose) 100%);
}

/* ---------------------------------------------------------------------
   Layout
   --------------------------------------------------------------------- */

main {
  padding: 1.5rem 1.75rem 3rem;
  /* Was a flat `100rem` (1500px, since the root font-size is 15px -- see
     :root above). `main` has no `margin: auto` centering it, so on any
     screen wider than that flat cap, the leftover width just sat unused
     as a dead strip on the right of the page instead of being usable by
     the Coverage Checklist's matrix table / the Procedure Separation Type
     key on every tab. `96vw` scales with the actual window instead of
     leaving a fixed-size gutter behind on wide screens, while still
     leaving a small breathing margin (4% of viewport width) on any size. */
  max-width: 96vw;
}

.tab-panel { display: none; }
.tab-panel.active { display: block; animation: fade-in 0.18s ease; }

@keyframes fade-in {
  from { opacity: 0; transform: translateY(2px); }
  to { opacity: 1; transform: translateY(0); }
}

.panel-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: flex-end;
  margin-bottom: 0.9rem;
}
/* Custom Filter's "Share a link for this custom filter set" button, its
   own explanatory note underneath, and the transient "Link copied!"
   feedback -- grouped as their own stack so the whole thing reads as one
   unit sitting to the left of "Export" in ".result-controls" (see
   ".result-controls" below), the same row the Export controls are already
   in, rather than living up in ".panel-toolbar" as a lone button the way
   it used to. `align-items: center` centers the note/feedback under the
   button itself, rather than under the whole (wider) stack's left edge. */
.table-share {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
}
.table-share button.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.table-share .btn-icon {
  flex: none;
  width: 1em;
  height: 1em;
}
/* Italic distinguishes this as an aside/caption on the button above it,
   the same role ".legend-note" plays for the compact type legend. */
.table-share-note {
  font-size: 0.72rem;
  font-style: italic;
  color: var(--text-muted);
  max-width: 22rem;
  text-align: center;
}
.share-feedback {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--accent-dark);
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.field label {
  font-size: 0.75rem;
  color: var(--text-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* ---------------------------------------------------------------------
   Hint / instruction banners
   --------------------------------------------------------------------- */

.hint-banner {
  display: flex;
  gap: 0.55rem;
  align-items: flex-start;
  background: linear-gradient(135deg, var(--accent-soft) 0%, var(--accent-rose-soft) 160%);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent-rose);
  border-radius: var(--radius);
  padding: 0.7rem 0.95rem;
  font-size: 0.85rem;
  color: #3a4451;
  line-height: 1.5;
  margin-bottom: 0.9rem;
}

.hint-banner .hint-label {
  flex: none;
  font-weight: 700;
  color: var(--accent-dark);
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  padding-top: 0.1rem;
}

.hint-banner p { margin: 0; }

/* ---------------------------------------------------------------------
   Inputs / buttons
   --------------------------------------------------------------------- */

/* Safari gives `type="search"` its own native "capsule" chrome (rounded
   shape, inset shadow, built-in margin for the magnifier/clear-button
   decoration) that's otherwise never fully overridden by a plain border/
   padding/border-radius reset -- left in place, it renders every search
   box here a few pixels narrower and shifted from where a `type="text"`
   sibling with identical CSS sits (e.g. the hint banner and table above/
   below it, or the "Cohorts"/"Checklist items" search boxes next to their
   own plain-text labels), throwing off what should be a shared left edge.
   Chrome/Firefox never apply that native chrome, so this was invisible
   there -- `appearance: none` (plus the `-webkit-` prefix Safari still
   needs) strips it so every search box measures exactly like a text
   input everywhere. */
input[type="search"] {
  -webkit-appearance: none;
  appearance: none;
}

input[type="text"], input[type="search"], input[type="number"], select {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.45rem 0.6rem;
  font-size: 0.9rem;
  min-width: 18em;
  background: #fff;
  color: var(--text-main);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

input[type="text"]:focus, input[type="search"]:focus, input[type="number"]:focus, select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Search boxes inside the narrower picker sidebars should fill the
   sidebar's width rather than using the wider default min-width above,
   which was pushing them past the sidebar's edge. */
.picker .field input {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

/* `a.btn` alongside `button.btn` covers the Contribute tab's "Open the
   Contributor Form" link -- a real external link (needs a genuine <a
   href>, not a <button>), styled identically to every other primary
   action on the site rather than as an ".inline-link"-style hyperlink. */
button.btn, a.btn {
  display: inline-block;
  border: 1px solid var(--accent);
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-dark) 100%);
  color: #fff;
  border-radius: 6px;
  padding: 0.5rem 0.9rem;
  font-size: 0.85rem;
  cursor: pointer;
  font-weight: 600;
  text-decoration: none;
  transition: background 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
}

button.btn:hover, a.btn:hover {
  background: linear-gradient(135deg, var(--accent-dark) 0%, var(--accent-rose) 145%);
  box-shadow: var(--shadow-sm);
  color: #fff;
}

button.btn:active, a.btn:active {
  transform: translateY(1px);
}

button.btn.secondary {
  background: #fff;
  color: var(--accent);
}

button.btn.secondary:hover {
  background: var(--accent-soft);
}

button.btn.small {
  padding: 0.28rem 0.6rem;
  font-size: 0.78rem;
}

button:focus-visible, a:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.result-count {
  color: var(--text-muted);
  font-size: 0.85rem;
  margin: 0.25rem 0 0.6rem;
}

/* Pairs a table's result-count text with its "Table size" zoom control on
   the same row -- see ".table-zoom" below and wireTableZoom() in
   dashboard.js. `.result-count`'s own margin is zeroed here since this
   wrapper carries the vertical spacing instead, keeping the two visually
   aligned on one baseline rather than the count sitting slightly above the
   control. */
.result-row {
  display: flex;
  flex-wrap: wrap;
  /* flex-start, not center: Custom Filter's ".result-controls" here can be
     taller than the plain one-line result-count text (its Share button
     stack -- see ".table-share" -- carries a note/feedback line below the
     button itself), so centering the two against each other's total
     height left the count sitting visibly lower than the buttons beside
     it instead of level with them. */
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  margin: 0.25rem 0 0.6rem;
}
.result-row .result-count {
  margin: 0;
}

/* Groups a table's Share/Export controls together on the right side of
   ".result-row" -- without this wrapper, ".result-row"'s own
   space-between would spread them apart from each other instead of
   keeping them together opposite the count. `align-items: flex-start`
   (rather than the default center) lines up the *top* of each group --
   Custom Filter's Share button (see ".table-share" above) sits above its
   own note/feedback line, so centering the whole taller stack next to
   Export's single-line row would leave the Share button itself sitting
   noticeably lower than the CSV/Excel/PDF buttons beside it. */
.result-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.9rem;
}

/* Custom Filter only: pairs the compact Hysterectomy Inference Types key
   (".procedure-type-legend", hidden unless that field is an active
   condition -- see refreshTypeLegend() in dashboard.js) with the Table
   size control on one row below Share/Export, rather than in
   ".result-controls" with them -- so Table size always lines up with the
   key's own row instead of the row above it, whether or not the key
   itself happens to be visible right now. `margin-left: auto` on
   ".table-zoom" here (rather than `justify-content: space-between` on
   this row) keeps it pinned to the right in both cases, including when
   the key is hidden and it's the row's only visible child. */
.legend-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin: 0.4rem 0 0.9rem;
}
.legend-row .procedure-type-legend {
  margin: 0;
}
.legend-row .table-zoom {
  margin-left: auto;
}

.table-export {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.table-export-label {
  font-weight: 600;
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.03em;
}
.table-export button.btn.small {
  padding: 0.22rem 0.55rem;
}

.table-zoom {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.table-zoom-label {
  font-weight: 600;
  text-transform: uppercase;
  font-size: 0.7rem;
  letter-spacing: 0.03em;
}
.table-zoom-out, .table-zoom-in {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #fff;
  color: var(--accent-dark);
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.table-zoom-out:hover, .table-zoom-in:hover {
  background: var(--accent-soft);
  border-color: var(--accent);
}
.table-zoom-out:disabled, .table-zoom-in:disabled {
  opacity: 0.4;
  cursor: default;
  background: #fff;
  border-color: var(--border);
}
.table-zoom-pct {
  min-width: 3em;
  text-align: center;
  font-weight: 700;
  color: var(--text-main);
  /* Clicking this resets zoom to 100% -- see wireTableZoom() in
     dashboard.js. */
  cursor: pointer;
}

/* ---------------------------------------------------------------------
   Tables
   --------------------------------------------------------------------- */

table.data-table {
  border-collapse: collapse;
  width: 100%;
  /* Default zoom level (100%) -- overridden inline by wireTableZoom() in
     dashboard.js as `style.setProperty("--table-zoom", ...)` directly on
     this <table> element when the user clicks its "Table size" +/- buttons
     (see ".table-zoom" below). Every table-relative size derived from this
     element's own font-size -- column widths/padding in "em" (see
     ".narrow-col-header" etc.), the matrix header's own font-size just
     below, chip sizing -- scales together from this one variable, so
     zooming out simultaneously shrinks the text *and* narrows columns,
     fitting more of them on screen at once (and vice versa zooming in). */
  --table-zoom: 1;
  font-size: calc(0.85rem * var(--table-zoom));
  background: #fff;
}

table.data-table th, table.data-table td {
  border: 1px solid var(--border);
  padding: 0.5rem 0.6rem;
  text-align: left;
  vertical-align: top;
}

/* Background is split into an opaque `background-color` plus a separate
   `background-image` gradient (rather than one `background: linear-
   gradient(...)` shorthand) specifically so the hover transition below
   only ever animates between two fully-opaque colors. Animating the
   shorthand directly used to interpolate an *implicit* `background-color:
   transparent` (since the base rule never set one of its own) up to the
   solid `:hover` color, and CSS animates "transparent" as a color with
   0 alpha -- so partway through that 0.12s transition the header's
   background was itself partly see-through, letting the rows scrolled
   underneath a sticky header flash briefly visible. Both states now have
   a real, opaque `background-color` at every point in the transition, so
   there's nothing left to show through. */
table.data-table thead th {
  background-color: var(--bg-subtle);
  background-image: linear-gradient(180deg, #fdfefe 0%, var(--bg-subtle) 100%);
  cursor: pointer;
  white-space: nowrap;
  position: sticky;
  top: 0;
  z-index: 2;
  font-weight: 700;
  color: var(--text-main);
  user-select: none;
  transition: background-color 0.12s ease;
}

table.data-table thead th:hover {
  background-color: var(--hover-bg);
  /* Swapped instantly (not part of the transition above) rather than
     animated, since a gradient can't be smoothly interpolated toward a
     flat color anyway -- it would otherwise still flip abruptly to `none`
     partway through the transition regardless. */
  background-image: none;
}

/* Sortable column headers -- Cohort Summary, Coverage Checklist, and
   Custom Filter all wire every header up via wireSortableHeader() in
   dashboard.js, which adds this ".sortable" class, a click handler, and
   the ".sort-icon" (built just below) to each one. table.matrix-table
   thead th (the Coverage Checklist) otherwise sets its own
   `cursor: default` further down, so that's overridden back to pointer
   here specifically for its sortable headers; its hover tint already
   comes from the plain `table.data-table thead th:hover` rule above,
   since every table.matrix-table is also a table.data-table. Every
   sortable `<th>` here is already `position: sticky` (see the base
   `table.data-table thead th` rule above), which -- like `relative` --
   establishes a containing block for the icon's `position: absolute`
   below, so no extra positioning context needs to be added. */
table.data-table thead th.sortable,
table.matrix-table thead th.sortable {
  cursor: pointer;
}

/* Wrapper for a sortable header's own label text (Cohort Summary, Custom
   Filter, and the Coverage Checklist's Cohort column -- its narrower
   columns use ".th-label" instead, styled further down). Doesn't need
   any layout properties of its own: it's a plain inline span that just
   gives the header text a stable boundary separate from the absolutely-
   positioned ".sort-icon" appended after it, so the two never overlap
   or get visually tangled up regardless of how long the label is. */
.th-text {
  display: inline;
}

/* The "this column is sortable" affordance: two arrows side by side (one
   up, one down), sitting dim/idle until this column becomes the active
   sort -- at which point the arrow matching the current direction lights
   up in the accent color and the other one dims further, rather than
   disappearing outright, so the icon's presence stays consistent whether
   or not a column is the active sort. Sized to be clearly readable at a
   glance without competing with the header text for attention. */
.sort-icon {
  position: absolute;
  display: inline-flex;
  align-items: center;
  gap: 0.18em;
  font-size: 1em;
  line-height: 1;
  color: var(--text-muted);
  opacity: 0.45;
  pointer-events: none;
  transition: opacity 0.12s ease;
}
.sort-icon .arrow-up,
.sort-icon .arrow-down {
  display: inline-block;
}
th.sortable:hover .sort-icon {
  opacity: 0.7;
}
th.sorted-asc .sort-icon,
th.sorted-desc .sort-icon {
  opacity: 1;
}
th.sorted-asc .sort-icon .arrow-up { color: var(--accent); opacity: 1; }
th.sorted-asc .sort-icon .arrow-down { opacity: 0.3; }
th.sorted-desc .sort-icon .arrow-down { color: var(--accent); opacity: 1; }
th.sorted-desc .sort-icon .arrow-up { opacity: 0.3; }

/* Placement, variant 1 -- "right side of the cell, label on the left":
   Table 1/Table 3's wide columns (just Cohort Name, now that their other
   columns are ".narrow-col-header" -- see variant 2 below) plus the
   Coverage Checklist's own Cohort column specifically (it's wide/
   horizontal like Cohort Name, unlike the narrow checklist-item columns
   below). The extra right padding reserves room so the label's text never
   renders underneath the icon.
   Note: the Coverage Checklist's <table> carries *both* "data-table" and
   "matrix-table" classes (see index.html), so the plain
   "table.data-table thead th.sortable" selector on its own would also
   match that table's narrow checklist-item columns -- not just Table 1/
   Table 3's wide header -- fighting with variant 2's bottom-of-cell rules
   below (both sets of top/right/bottom/left would apply at once,
   stretching the icon between them). ":not(.matrix-table)" keeps this
   variant scoped to Table 1/Table 3's plain data-tables specifically, and
   ":not(.narrow-col-header)" excludes their own narrow columns the same
   way; the Coverage Checklist's Cohort column is opted back in by name via
   its own ".cohort-col-header" selector alongside it.
   The reserved padding-right is generous (well beyond the icon's own
   ~1.4em rendered width) rather than a tight fit: this header uses
   `white-space: nowrap` with the default (non-fixed) table-layout, so the
   Cohort Name column's width auto-sizes to exactly fit its widest cell --
   and the header label itself is part of that, leaving little natural
   slack. A tight padding-right would size the column just barely around
   the text, right up against the icon with no room for font-metric
   variance; the wider padding-right here reserves real breathing room so
   the text and icon never crowd each other regardless of font rendering. */
table.data-table:not(.matrix-table) thead th.sortable:not(.narrow-col-header),
table.matrix-table thead th.cohort-col-header.sortable {
  padding-right: 2.75em;
}
table.data-table:not(.matrix-table) thead th.sortable:not(.narrow-col-header) .sort-icon,
table.matrix-table thead th.cohort-col-header.sortable .sort-icon {
  right: 0.75em;
  top: 50%;
  transform: translateY(-50%);
}

/* Placement, variant 2 -- "bottom of the cell, centered": the Coverage
   Checklist's narrower columns (every checklist item/rollup), plus Table
   1/Table 3's own narrow columns (".narrow-col-header" -- everything
   except Cohort Name; see appendColumnHeader() in dashboard.js). These
   headers are taller than they are wide and often wrap onto multiple
   lines, so the icon consistently sits centered below the label rather
   than sometimes sharing its line and sometimes not. The larger bottom
   padding (vs. the generic matrix header's own padding-bottom above)
   opens up clear breathing room between the label text and the icon
   underneath it. */
table.matrix-table thead th.sortable:not(.cohort-col-header),
table.data-table:not(.matrix-table) thead th.sortable.narrow-col-header {
  padding-bottom: 1.9rem;
}
table.matrix-table thead th.sortable:not(.cohort-col-header) .sort-icon,
table.data-table:not(.matrix-table) thead th.sortable.narrow-col-header .sort-icon {
  left: 50%;
  bottom: 0.5rem;
  transform: translateX(-50%);
}

/* Table 1/Table 3's narrow columns (everything but Cohort Name) --
   allowed to wrap onto multiple lines (their ".th-label" span handles the
   actual wrapping/hyphenation, generalized above) and capped at a shared
   width, the same way the Coverage Checklist's own narrow columns are,
   rather than forcing the column wide enough to fit one long unwrapped
   label -- see appendColumnHeader() in dashboard.js. 11em (rather than a
   tighter fit) so a long label like "Distinguishes hysterectomy type
   (supracervical / total / radical)?" wraps across a handful of lines
   instead of reading as a tall, cramped, one-word-per-line column; once
   there are enough of these columns to no longer all fit on screen at
   once, ".table-scroll"'s own `overflow: auto` (see above) just lets the
   table scroll horizontally rather than squeezing every column narrower
   to force them all into view. */
table.data-table:not(.matrix-table) thead th.narrow-col-header {
  width: 11em;
  max-width: 11em;
  text-align: center;
  vertical-align: middle;
}
table.data-table:not(.matrix-table) td.narrow-col-cell {
  text-align: center;
}
/* Table 1/Table 3 specifically (not ".matrix-table", which already sets
   this on its own): with the base "table.data-table" rule's plain
   `width: 100%` above, auto layout treats every column's `width` below as
   only a soft preference -- when honoring all of them literally would make
   the table wider than its 100%, the browser was quietly compressing them
   back down to fit instead of actually growing the table and letting
   ".table-scroll"'s `overflow: auto` (see above) kick in, no matter how
   wide a width was requested. Fixed layout instead takes each column's
   width as authoritative and sizes the table to their sum, overflowing
   its container -- exactly the horizontal-scrolling trade the widths
   below are deliberately making. */
table.data-table:not(.matrix-table) {
  table-layout: fixed;
}

/* Hysterectomy Inference Classification specifically: wider than Custom
   Filter's own narrow columns (11em above) -- with 13 columns total
   (vs. Custom Filter's default 5), this tab already needs horizontal
   scrolling regardless, so trading a bit more of it for headers/values
   that wrap across fewer, more legible lines is a better trade here. */
#t1-table thead th.narrow-col-header {
  width: 15em;
  max-width: 15em;
}
/* Hysterectomy Inference Classification's Cohort Name column, widened --
   deliberately at the cost of fewer of the 12 columns after it fitting on
   screen at once (this tab already scrolls horizontally regardless, see
   above), so long cohort names read on one or two lines instead of
   wrapping much further. */
#t1-table thead th.sortable:not(.narrow-col-header) {
  width: 22em;
  max-width: 22em;
}
/* Custom Filter specifically: Cohort Name has no width cap of its own
   elsewhere (unlike the Coverage Checklist matrix's sticky cohort column),
   so it was sizing to its single longest name and crowding out this tab's
   normally-short baseline columns beside it. Capped here -- not on
   Hysterectomy Inference Classification's own Cohort Name column, which
   wasn't reported as a problem -- so a long name just wraps onto a second
   line instead. */
#t3-table thead th.sortable:not(.narrow-col-header) {
  width: 14em;
  max-width: 14em;
}

/* Table 1/Table 3's one wide column (Cohort Name) -- centered the same
   way its narrow siblings above are, rather than the plain left/top
   alignment every other data-table cell gets by default (see "table.
   data-table th, table.data-table td" near the top of this file). Left
   uncentered, it visibly sat apart from its now-wrapped, vertically-
   centered neighbors: top-aligned against their multi-line labels, and
   left-aligned against their centered text. Scoped to the header only --
   its own body cells (long free-text cohort names) stay left-aligned,
   which is what that column's actual content wants. */
table.data-table:not(.matrix-table) thead th.sortable:not(.narrow-col-header) {
  text-align: center;
  vertical-align: middle;
}

table.data-table tbody tr {
  transition: background 0.1s ease;
}

table.data-table tbody tr:nth-child(even) { background: #fbfcfd; }

table.data-table tbody tr:hover td {
  background: var(--hover-bg);
}

.table-scroll {
  max-height: 65vh;
  overflow: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  /* Echoes the header's top gradient bar so each data panel reads as part
     of the same visual system, without repeating the exact same treatment
     everywhere. */
  border-top: 3px solid transparent;
  background:
    linear-gradient(#fff, #fff) padding-box,
    linear-gradient(90deg, var(--accent) 0%, var(--accent-rose) 100%) border-box;
}

/* The Coverage Checklist's table-scroll is left to its own natural height
   (no cap), so the table's box is always exactly as tall as its own rows
   need, never taller/shorter than that. The generic 65vh cap above would
   otherwise clip that natural height back down. The picker sidebar next
   to it is likewise left to its own natural height (see ".two-col"'s
   `align-items: start` and "#t2-sidebar" below) rather than being forced
   to match this table -- each side sizes independently now. */
#t2-table-scroll {
  max-height: none;
}

/* Row color-coding by Procedure Separation Type (Cohort Summary tab). The
   accent color is set per-row via a CSS custom property on the <tr> itself
   (see dashboard.js), so the whole row -- not just one cell -- can read it:
   a colored stripe on the first (Cohort Name) cell for a quick visual
   anchor, a light always-on tint across the entire row so the coding is
   visible at a glance without hovering, and a bolder full-row tint on
   hover. */
td.accent-cell {
  border-left: 4px solid var(--row-accent, transparent);
}
/* A single, fixed tint -- not varied by the row's even/odd position in the
   table -- so the same Type always reads as exactly the same shade
   wherever it appears, instead of quietly alternating between two
   different tints (13%/18% mix, to compensate for the plain zebra-
   striping background beneath it) depending on where a row happens to
   land. `--row-tint` is an optional hand-picked pastel override (see
   DD.PROCEDURE_SEPARATION_TYPE_ROW_TINTS in dashboard-data.js) for the
   couple of types -- currently the red/magenta pair -- whose automatic
   color-mix() tint reads too similar to each other once diluted this
   light; every other type falls back to the plain formula. */
table.data-table tbody tr.accent-row {
  background: var(--row-tint, color-mix(in srgb, var(--row-accent, transparent) 18%, #fff));
}
table.data-table tbody tr.accent-row:hover td {
  background: color-mix(in srgb, var(--row-accent, transparent) 32%, var(--hover-bg));
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.9rem;
  font-size: 0.8rem;
  margin: 0.4rem 0 0.9rem;
  color: var(--text-muted);
  align-items: center;
}
/* Without this, the plain `hidden` attribute (see Custom Filter's
   "#t3-type-legend" -- toggled by refreshTypeLegend() in dashboard.js)
   doesn't actually hide a ".legend": author stylesheets always win over
   the browser's own `[hidden] { display: none }` default regardless of
   specificity, so ".legend"'s own `display: flex` above was quietly
   overriding it and leaving the (still-populated) legend visible. The
   extra `[hidden]` here outranks the plain class on specificity, so this
   wins instead. */
.legend[hidden] {
  display: none;
}
.legend .legend-caption {
  font-weight: 600;
  color: var(--text-main);
  margin-right: 0.15rem;
}
.legend .legend-item { display: flex; align-items: center; gap: 0.35rem; }
.legend .swatch {
  width: 0.9em;
  height: 0.9em;
  border-radius: 3px;
  display: inline-block;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.08) inset;
}
/* Custom Filter's compact Hysterectomy Inference Types legend (see
   refreshTypeLegend() in dashboard.js) -- unlike the full key aside every
   other tab shows this information in, each swatch here carries its
   definition as a hover tooltip (attachTooltip()) rather than printing it
   inline, so a "help" cursor signals that up front. */
.procedure-type-legend .legend-item {
  cursor: help;
}
.legend .legend-note {
  font-style: italic;
}

/* ---------------------------------------------------------------------
   Per-panel layout: main content + a right-hand-side key sidebar
   --------------------------------------------------------------------- */

/* Every tab (Cohort Summary, Coverage Checklist, Custom Filter, Map) puts
   its main content in `.panel-main` and the Procedure Separation Type key
   in a `.panel-key` aside alongside it, so the key consistently lives in
   the negative space on the right rather than taking up a horizontal
   strip above the content. `align-items: stretch` (rather than `start`)
   makes `.panel-key` match `.panel-main`'s full rendered height every time,
   so the key's box bottom always lines up with the bottom edge of whatever
   is in `.panel-main` (the table-scroll or the map) instead of stopping
   short after its own, usually much shorter, content. Collapses to a
   single column on narrow viewports (see the media query below) so the key
   doesn't crowd out the table on small screens -- it just drops beneath
   the content instead (and each becomes its own row there, so `stretch`
   has no effect on that layout). */
.panel-body-with-key {
  display: grid;
  grid-template-columns: 1fr 17rem;
  gap: 1.1rem;
  align-items: stretch;
}
.panel-main {
  min-width: 0; /* allow the table-scroll/map to shrink inside the grid track */
}
.panel-key {
  position: sticky;
  top: 0.75rem;
}

/* Hysterectomy Inference Classification tab: `.table-scroll`'s generic
   65vh cap (see `.table-scroll` above) was too short for either of this
   tab's actual needs -- the key's own content (title plus all five Type
   1-5 entries, each with a full sentence of definition text) doesn't fit
   within it on most screens, and neither did ~10 rows of the table. Left
   alone, `align-items: stretch` sizes the shared row to fit the *taller*
   of the two (the key), which stretches `.panel-main`/the table up to
   match but doesn't make the table any taller (it's independently capped
   at 65vh), leaving the key's box to trail off below the table's bottom
   edge instead of ending flush with it. Pinning the row to a taller,
   guaranteed-minimum height instead -- `minmax()` rather than a flat
   value, so it still grows further on a tall viewport -- forces both
   boxes' bottoms (as well as their already-aligned tops) to land in
   exactly the same place, tall enough that the key's full five-entry
   content fits without its own scrollbar on a typical laptop-or-larger
   screen; `.panel-key` keeps a matching internal scrollbar as a fallback
   for shorter viewports where it still doesn't fit. */
#panel-summary .panel-body-with-key {
  grid-template-rows: minmax(48rem, 85vh);
}
#panel-summary .panel-key {
  overflow-y: auto;
}
/* ".table-scroll"'s own generic 65vh cap (see ".table-scroll" above) would
   otherwise still apply regardless of how tall the grid row above just
   became -- `height: 100%` instead fills exactly whatever height
   ".panel-main" (a grid item stretched by the row above, via the default
   `align-items: stretch`) actually resolves to, so the table's own visible
   height always tracks the row's -- the same guaranteed minimum, and the
   same further growth on a taller viewport -- keeping the table and key's
   bottom edges aligned exactly like every other tab's key pairing does. */
#panel-summary .table-scroll {
  max-height: none;
  height: 100%;
}
/* Custom Filter has no key aside anymore (see ".procedure-type-legend" --
   its own Hysterectomy Inference Types key, when relevant, is a compact
   legend above the table instead), so there's no sibling to stretch-match
   here. A flat `height` (this used to be `height: max(48rem, 85vh)`) forced
   the box to that height regardless of how many rows actually matched,
   leaving a block of empty space below a short, heavily-filtered result --
   `max-height` instead only ever caps the box, so it still shrinks to fit
   however many rows are actually there (down to a single row), and only
   grows up to this same generous cap (scrolling internally past that) once
   there's enough data to need it. */
#panel-filter .table-scroll {
  max-height: max(48rem, 85vh);
}

@media (max-width: 900px) {
  .panel-body-with-key {
    grid-template-columns: 1fr;
  }
  #panel-summary .panel-body-with-key {
    grid-template-rows: none;
  }
  .panel-key {
    position: static;
  }
  #panel-filter .table-scroll {
    max-height: 65vh;
  }
}

/* ---------------------------------------------------------------------
   Phone-width layout (<=640px)
   --------------------------------------------------------------------- */
/* Everything in this block only takes effect at roughly phone-portrait
   widths (most phones report a ~360-430px layout viewport) -- well
   inside the 900px breakpoint above, which already stacks each tab's
   Procedure Separation Type key underneath its main content regardless
   of screen size. Every rule here only overrides properties *within*
   this max-width guard, so nothing above ~640px -- tablets, laptops, the
   site's normal desktop layout -- is touched by any of it. */
@media (max-width: 640px) {
  header.app-header {
    padding: 1.1rem 1rem 0;
  }
  header.app-header h1 {
    font-size: 1.2rem;
  }

  /* At phone width the logo badge sits on the same row as the already-
     shrunk <h1> (see ".header-top" -- it doesn't wrap), so trim its
     padding/text down a notch to keep the row from crowding. */
  .brand-mark {
    padding: 0.3rem 0.6rem 0.35rem;
  }
  .brand-wordmark {
    font-size: 0.68rem;
  }
  .brand-wordmark strong {
    font-size: 0.85rem;
  }

  nav.tabs button {
    padding: 0.5rem 0.7rem;
    font-size: 0.82rem;
  }

  main {
    /* The desktop `96vw` cap (see "main" above) exists to stop the page
       from stretching edge-to-edge on very wide monitors -- moot at
       phone width, and `vw` units can also trigger a horizontal
       scrollbar of their own on some mobile browsers since they're
       measured including the scrollbar gutter. `100%` plus a smaller
       side padding gives the same "don't run flush to the screen edge"
       effect without that risk. */
    max-width: 100%;
    padding: 1rem 0.85rem 2rem;
  }

  /* Toolbar fields, search boxes, and Custom Filter's per-condition
     selects/inputs: the desktop `min-width: 18em` baseline (see the
     plain `input[type=...]` rule near the top of this file) is sized for
     roomy rows sitting side-by-side on a wide screen -- at phone widths
     it instead forces each field wider than the viewport itself. Letting
     every field shrink and fill its actual row width keeps each one
     fully visible and lets `.panel-toolbar`/`.filter-row`'s existing
     `flex-wrap` stack them cleanly instead of overflowing sideways. */
  input[type="text"], input[type="search"], input[type="number"], select {
    min-width: 0;
    width: 100%;
  }
  .panel-toolbar .field {
    width: 100%;
  }
  .filter-row {
    flex-direction: column;
    align-items: stretch;
  }
  .filter-row .remove-condition {
    align-self: flex-end;
  }

  /* Coverage Checklist: the Cohorts / Procedure Separation Type /
     Checklist items picker sidebar sits in a fixed 260px-wide column
     next to the matrix table at every other screen size (see ".two-col"
     above), which was never given its own responsive treatment. At
     phone widths that fixed column alone eats most of the viewport,
     squeezing the actual data table into an unusably thin sliver.
     Stacking the sidebar above the table instead -- each then using the
     phone's full width -- is the single biggest fix in this block; the
     sidebar's own existing internal scrolling (see "#t2-sidebar"/
     ".picker" above) keeps its three stacked pickers from taking over
     the whole page underneath it. */
  .two-col {
    grid-template-columns: 1fr;
  }
  /* Caps the *combined* stacked-pickers region to a single scrollable
     block, rather than leaving each individual `.picker` with its own
     nested 60vh scroll region (still fine at desktop, where the sidebar
     sits beside the table rather than above it). Nested scroll areas are
     a common touch-scrolling pitfall: a swipe started inside an inner
     picker's own scrollbar doesn't bubble out to scroll the page/sidebar
     around it, which reads as "stuck" on a phone. */
  #t2-sidebar {
    max-height: 46vh;
  }
  .picker {
    max-height: none;
  }

  /* Detail modal: the 40%/60% label-value split (see ".modal dl" above)
     is comfortable at desktop widths but cramped once the label column
     drops well under half of a phone's much narrower total width --
     stack each label above its value instead. */
  .modal dl {
    grid-template-columns: 1fr;
    gap: 0.15rem 0;
  }
  .modal dt {
    margin-top: 0.5rem;
  }

  /* Smoother momentum scrolling for the scrollable tables and picker
     lists on iOS Safari specifically -- has no effect on non-touch
     input, so it's safe to apply here unconditionally rather than
     needing a touch-specific guard of its own. */
  .table-scroll, .picker, #t2-sidebar {
    -webkit-overflow-scrolling: touch;
  }
}

/* ---------------------------------------------------------------------
   Landscape-phone / small-tablet width (641px-900px)
   --------------------------------------------------------------------- */
/* This range sits strictly between the two breakpoints above: wide enough
   that ".two-col" (the Women's Health Data Inventory's picker sidebar next
   to its table -- see ".two-col" further below) is still showing side by
   side rather than stacked (that only happens at <=640px, in the block
   above), but still well short of a real desktop window. At these widths
   the Cohorts / Checklist items picker stack naturally runs shorter than
   the table beside it, leaving its column trailing off above the table's
   actual bottom edge instead of lining up with it top-to-bottom.
   An earlier version of this block tried to fix that with CSS alone --
   `align-items: stretch` on ".two-col" plus `flex: 1 1 0; min-height: 0`
   on the Cohorts/Checklist-items pickers -- but that combination turned
   out to be unreliable across browsers: a grid item that's *itself* a
   flex container, stretched only via the grid's `align-items`, doesn't
   always get a definite height that its own flex-grow children can
   actually grow into, so instead of growing to fill the space, those two
   pickers collapsed toward zero. This just removes that CSS entirely and
   lets `syncLandscapeChecklistHeight()` in dashboard.js do the same job
   directly in pixels instead, which sidesteps the browser inconsistency.
   `.picker`'s desktop `max-height: 60vh` cap (see ".picker" further
   below) is lifted here so that JS-assigned height can actually take
   effect without being re-clamped back down. */
@media (min-width: 641px) and (max-width: 900px) {
  #t2-cohort-picker,
  #t2-column-picker {
    max-height: none;
  }

}

/* ---------------------------------------------------------------------
   Coverage Checklist: portrait-phone notice
   --------------------------------------------------------------------- */
/* ".portrait-notice" (see index.html) reuses ".hint-banner"'s own styling
   (gradient background, left accent border, etc. -- see ".hint-banner"
   above) so it visually matches every other banner on the site, but stays
   hidden everywhere by default; the media query below is the only thing
   that ever makes it visible. Unlike the rest of this stylesheet's mobile
   handling (which makes every tab genuinely usable on a phone), the
   Women's Health Data Inventory's dozens of narrow checklist-item columns
   don't fit a phone-portrait width at any font size without heavy
   horizontal scrolling no matter what CSS does here -- so rather than
   present a barely-usable sliver of that table, portrait phone users see
   this notice instead and are steered toward landscape or a larger screen.
   The other three tabs (Hysterectomy Inference Classification, Custom
   Filter, Cohort Map) have no such column-count problem and stay fully
   usable in portrait, so this is deliberately scoped to this tab
   specifically -- see "#panel-checklist" in every selector below -- rather
   than applied site-wide.
   `orientation: portrait` (alongside the same `max-width: 640px` used by
   the phone-width block above) specifically targets narrow *and* tall
   screens, rather than every screen <=640px -- a handful of small phones
   are still close to 640px wide even in landscape, and this notice should
   never show for those, only for phones held upright. Being pure CSS
   (no JS), this also re-evaluates live the instant a phone is rotated,
   without needing to listen for a resize/orientation event at all. */
#panel-checklist .portrait-notice {
  display: none;
}
@media (max-width: 640px) and (orientation: portrait) {
  #panel-checklist > .hint-banner:not(.portrait-notice),
  #panel-checklist > .result-count,
  #panel-checklist > .legend,
  #panel-checklist > .two-col {
    display: none;
  }
  #panel-checklist .portrait-notice {
    display: flex;
  }
}

/* Procedure Separation Type key -- the *only* legend for the "Type 1" ..
   "Type 5" color-coding used everywhere it shows up (Cohort Summary and
   Custom Filter row color, Coverage Checklist matrix row/column color, Map
   marker color): each entry pairs a fixed color swatch with its full
   definition text, so a viewer never has to cross-reference a separate
   flat swatch legend to know what a color means. See
   renderProcedureSeparationKey() / DD.PROCEDURE_SEPARATION_TYPE_DEFINITIONS
   + DD.PROCEDURE_SEPARATION_TYPE_COLORS in dashboard.js / dashboard-data.js. */
.procedure-key {
  background: var(--bg-subtle);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  font-size: 0.87rem;
  color: var(--text-muted);
  /* `.panel-key` (the same element -- see dashboard.js) is stretched by
     its grid parent to match `.panel-main`'s full height (see
     `.panel-body-with-key` above), which is usually taller than this box's
     five fixed entries need on their own. Laying this out as a column flex
     container -- with `.procedure-key-list` below set to `flex: 1` and
     `justify-content: space-between` -- spreads those five entries down
     the whole available height instead of leaving them all clumped at the
     top with a slab of dead space underneath. */
  display: flex;
  flex-direction: column;
}
.procedure-key .procedure-key-title {
  margin: 0 0 0.85rem;
  font-weight: 700;
  color: var(--text-main);
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  line-height: 1.4;
}
.procedure-key .procedure-key-list {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  gap: 1rem;
  justify-content: space-between;
}
.procedure-key .procedure-key-item {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
}
.procedure-key .procedure-key-swatch {
  width: 1em;
  height: 1em;
  flex: none;
  margin-top: 0.2em;
  border-radius: 3px;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.08) inset;
}
.procedure-key .procedure-key-type {
  display: block;
  font-weight: 700;
  color: var(--accent-dark);
}
.procedure-key .procedure-key-text {
  margin: 0.15rem 0 0;
  line-height: 1.4;
}
/* Mobile Safari (and some other mobile browsers) apply their own "text
   autosizing"/font-boosting heuristics to blocks of text, adjusting the
   rendered font size per-paragraph based on things like its perceived
   column width -- since every entry here shares the exact same markup and
   CSS, there's nothing in this stylesheet that explains one type's text
   (typically whichever entry's wording happens to be shortest) rendering
   smaller than the rest, but that per-browser autosizing behavior does.
   Explicitly opting out with `text-size-adjust: 100%` keeps every entry at
   the same, explicitly-set font-size regardless of that heuristic. */
.procedure-key .procedure-key-type,
.procedure-key .procedure-key-text {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* Checklist / matrix table specifics */
/* `table-layout: fixed` (instead of the default `auto`) is required for
   the header hyphenation below to actually work. Under `auto` layout, a
   column's width isn't "definite" until the browser has already measured
   all its content -- a chicken-and-egg problem that leads most browsers to
   silently skip hyphenation inside auto-layout table cells altogether, so
   long words were breaking wherever they ran out of room with no hyphen
   shown at all, rather than at a proper syllable boundary. Every column
   here already gets an explicit width below (8em checklist columns, 32em
   cohort, 8em type), so fixed layout doesn't lose anything -- it just
   makes those widths definite up front, which is exactly what hyphenation
   needs to kick in. */
table.matrix-table {
  table-layout: fixed;
}
table.matrix-table thead th {
  /* No height/max-height here on purpose -- table cell height is only ever
     a minimum per the CSS table spec, so leaving it unset lets the header
     row grow to fit whichever label is longest (now downward, by wrapping
     onto more lines, rather than sideways). A fixed max-height was
     clipping longer labels (they were cutting off mid-word). */
  white-space: normal;
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
  /* Explicit (rather than just inheriting "table.data-table"'s own
     zoom-scaled font-size) since this header is deliberately smaller than
     the body text -- still needs its own "* var(--table-zoom)" so it
     scales in step with everything else when the user zooms. */
  font-size: calc(0.72rem * var(--table-zoom, 1));
  cursor: default;
  vertical-align: bottom;
}
/* Checklist-item and rollup column headers (everything except the sticky
   Cohort Name column, which sets its own width below): capped at a shared
   ~8em width so no column -- however long its variable/domain name -- ever
   gets wider than that baseline. Long labels wrap onto multiple lines
   within that width instead. Centered both ways (vertical-align +
   text-align) rather than sitting bottom-aligned like the generic
   `table.matrix-table thead th` rule above sets. */
table.matrix-table thead th:not(.cohort-col-header) {
  width: 8em;
  max-width: 8em;
  text-align: center;
  vertical-align: middle;
}
/* Checklist item / rollup body cells (the Yes/No/To some extent/Other
   chips): center each chip within its column, matching the centered header
   above it, rather than the default left alignment table cells get. Cohort
   Name cells have their own (sticky-left) alignment already, so they're
   excluded here. */
table.matrix-table td:not(.cohort-cell) {
  text-align: center;
}
/* The label text used to live in its own inner span so a `transform:
   rotate(180deg)` (for vertical text) could be kept off the sticky/
   positioned `<th>` itself, avoiding a browser repaint bug. Headers are
   horizontal now, so that transform is gone, but the label is kept in its
   own span regardless -- harmless, and one less thing to change if a
   similar positioning need comes up again later.
   Plain "table.data-table" (not scoped to ".matrix-table") so Table 1 and
   Table 3's own narrow columns (see ".narrow-col-header" further below)
   get the same wrap/hyphenate treatment as the Coverage Checklist's --
   every ".matrix-table" is already a ".data-table" too, so this one rule
   covers both. */
table.data-table thead th .th-label {
  display: block;
  white-space: normal;
  overflow-wrap: break-word;
  line-height: 1.25;
  /* The visible hyphen at a long word's break point (e.g. "Endo-" /
     "metriosis") is produced by soft hyphen characters (U+00AD) inserted
     directly into the label text -- see softHyphenateLabel() in
     dashboard.js -- rather than by the CSS `hyphens: auto` dictionary-
     based approach, which turned out not to reliably show a hyphen in
     practice (likely losing out to the `overflow-wrap: break-word`
     fallback above before the hyphenation dictionary got a chance to run).
     `hyphens: manual` (the CSS-default value, set explicitly here for
     clarity) tells the browser to only break at those explicit soft-hyphen
     points rather than trying to find additional ones itself. */
  hyphens: manual;
  -webkit-hyphens: manual;
  -ms-hyphens: manual;
}
table.matrix-table thead th.cohort-col-header {
  height: auto;
  vertical-align: middle;
}
/* Rollup (domain-level) columns get a faint accent tint on their header so
   they read as a distinct kind of column from the plain-white specific-item
   headers beside them once a user drills into any -- without needing a
   second legend to explain the difference. */
table.matrix-table thead th.rollup-col-header {
  background-color: var(--accent-soft);
}
/* Capped so the very longest cohort names (e.g. the ~74-character full
   title of Add Health) don't dominate the sticky first column and push
   the checklist-item columns off-screen -- but wide enough that only that
   extreme tail ever truncates. Actual name lengths in the data range from
   ~10 to ~74 characters, with a median around 39 and about a quarter of
   them 52+; at this column's ~13.6px bold font plus its 2x0.6rem cell
   padding, 32em (~440px of text-rendering room) comfortably fits names up
   through and a bit past that 75th-percentile mark, so only a handful of
   the longest outliers are ever truncated. The table-scroll container
   this sits in scrolls horizontally as well as vertically, so a wider
   sticky column doesn't break the layout -- it just uses more of that
   scrollable width. Overflowing names are ellipsis-truncated -- white-
   space: nowrap (needed anyway for the sticky column) plus overflow:
   hidden + text-overflow: ellipsis is the standard combination required
   for single-line truncation to render at all; the full name is still
   available via this cell's custom tooltip (see attachTooltip() /
   renderTable2Body() in dashboard.js -- shown instantly on hover, unlike
   a plain `title` attribute) and by clicking the row for the full
   record. */
table.matrix-table td.cohort-cell, table.matrix-table th.cohort-col-header {
  position: sticky;
  left: 0;
  background: #fff;
  z-index: 1;
  border-right: 2px solid var(--border);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 32em;
  max-width: 32em;
  font-weight: 600;
  transition: background 0.1s ease, color 0.1s ease;
}
table.matrix-table thead th.cohort-col-header {
  z-index: 3;
  background: var(--bg-subtle);
}
table.matrix-table tbody tr:hover td.cohort-cell {
  background: var(--hover-bg);
  color: var(--accent-dark);
}
table.matrix-table td.cohort-cell {
  cursor: pointer;
}
table.matrix-table td.cohort-cell:hover {
  text-decoration: underline;
}

.chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.6em;
  height: 1.6em;
  border-radius: 4px;
  /* "em"-based min-width/height above scale automatically with this --
     inherited from the ".matrix-table" ancestor's own zoom-scaled
     font-size (see "table.data-table" above), same mechanism as every
     other zoom-scaled size in the table. */
  font-size: calc(0.75rem * var(--table-zoom, 1));
  font-weight: 700;
  color: #fff;
  transition: transform 0.12s ease, box-shadow 0.12s ease;
  cursor: default;
}
.chip:hover {
  transform: scale(1.18);
  box-shadow: var(--shadow-sm);
}
.chip.cat-yes { background: var(--color-yes); }
.chip.cat-no { background: var(--color-no); }
.chip.cat-partial { background: var(--color-partial); color: #4a3300; }
.chip.cat-empty { background: var(--color-empty); color: #7a828c; box-shadow: none; }
.chip.cat-empty:hover { transform: none; }
/* Solid filled square (matching .cat-yes/.cat-no/.cat-partial) with the
   fixed white "T" glyph set in chipSymbol() in dashboard.js -- previously
   an outlined/transparent chip showing either a bit of the actual free-
   text value or, once that got too long, just a small dot, which read as
   an odd, half-empty square next to the solid Y/N/~ ones beside it. A
   dashed inset border frames the "T", echoing the classic "text box"
   insertion icon (a dashed-border rectangle) so the glyph reads as
   "free text" rather than just a letter -- drawn via ::before so it
   overlays the solid fill without disturbing the centered text itself. */
.chip.cat-other {
  background: var(--color-other);
  position: relative;
}
.chip.cat-other::before {
  content: "";
  position: absolute;
  inset: 2px;
  border: 1px dashed rgba(255, 255, 255, 0.85);
  border-radius: 2px;
  pointer-events: none;
}

/* ---------------------------------------------------------------------
   Pickers (Coverage Checklist sidebar)
   --------------------------------------------------------------------- */

.two-col {
  display: grid;
  grid-template-columns: 260px 1fr;
  gap: 1rem;
  align-items: start;
}

/* #t2-sidebar (the Cohorts / Procedure Separation Type / Checklist items
   pickers, stacked) has its height set directly, in pixels, to match the
   table's own rendered height -- see syncChecklistHeight() in dashboard.js.
   The three pickers stacked together often add up to more height than
   that, so the sidebar scrolls internally as one unit rather than
   overflowing past the table's bottom edge. Each individual `.picker`
   below keeps its own max-height/overflow too, for when just one list
   (e.g. Cohorts) is long enough to need its own scroll independent of the
   other two. */
#t2-sidebar {
  overflow-y: auto;
}

.picker {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.65rem;
  max-height: 60vh;
  overflow: auto;
  background: #fff;
  box-shadow: var(--shadow-sm);
}
.picker h3 {
  margin: 0 0 0.5rem;
  padding-bottom: 0.35rem;
  font-size: 0.85rem;
  color: var(--accent-dark);
  border-bottom: 2px solid transparent;
  border-image: linear-gradient(90deg, var(--accent) 0%, var(--accent-rose-soft) 100%) 1;
}
/* The Cohorts/Checklist Items pickers' search box sat flush against the
   Select all/Select none row right below it (".field" carries no bottom
   margin of its own, and ".picker-actions" below carries no top margin),
   while the Domains picker -- which has no search box, so its title's own
   0.5rem margin-bottom above is the only gap before its buttons -- reads
   with real breathing room by comparison. Matching that same 0.5rem here
   makes the gap consistent whether or not a picker happens to have a
   search box in between. */
.picker .field {
  margin-bottom: 0.5rem;
}
.picker .picker-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.5rem;
}
/* Buttons in this row (Select all / Select none / Hide column, for pickers
   that have one) must never shrink below their natural, single-line width
   -- the 260px sidebar (see ".two-col" grid-template-columns) is just
   narrow enough that a 3-button row like the Procedure Separation Type
   picker's would otherwise get squeezed by flex-shrink and wrap its *text*
   onto two lines, making those buttons visibly taller/bulkier than the
   plain 2-button rows elsewhere (e.g. Checklist items). flex-wrap above
   lets a button that doesn't fit drop to its own new line instead, so
   every button keeps the same skinny size everywhere. */
.picker .picker-actions button {
  flex-shrink: 0;
}
.picker label.picker-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.82rem;
  padding: 0.25rem 0.35rem;
  cursor: pointer;
  border-radius: 4px;
  transition: background 0.1s ease;
}
/* A long label (e.g. a domain name like "Cognitive / Imaging / Biological
   / Health Record Linked Data") should wrap onto multiple lines within the
   sidebar rather than force the row wider than the picker -- flex items
   default to `min-width: auto`, which keeps them from shrinking below
   their text's un-wrapped natural width, so this label span needs both an
   explicit `min-width: 0` (to allow shrinking at all) and `overflow-wrap`
   (as a fallback in case a run of the label has no natural space to break
   at). The checkbox itself (`flex: none` implicitly, via its fixed
   intrinsic size) is unaffected. */
.picker label.picker-row span {
  min-width: 0;
  overflow-wrap: break-word;
}
.picker label.picker-row:hover {
  background: var(--hover-bg);
}

/* Women's Health Data Inventory item picker: distinguishes a
   section-header row (e.g. "Vasomotor symptoms") from its indented
   sub-items (e.g. "Hot flashes item", "Night sweats item") -- see
   renderChecklistItemPicker() in dashboard.js. Checking/unchecking a
   header row cascades to all of its children (the header's checkbox
   itself goes indeterminate when only some of its children are checked);
   each child can still be toggled on its own. */
.picker label.picker-row.picker-header {
  font-weight: 700;
  color: var(--accent-dark);
  background: var(--accent-soft);
  margin-top: 0.3rem;
}
.picker label.picker-row.picker-header:first-child {
  margin-top: 0;
}
.picker label.picker-row.picker-header:hover {
  background: color-mix(in srgb, var(--accent-soft) 70%, var(--hover-bg));
}
.picker label.picker-row.picker-child {
  font-size: 0.78rem;
  color: var(--text-muted);
}

/* Indentation is keyed off nesting depth rather than baked into
   .picker-header/.picker-child, since a header can itself be nested
   inside another header (e.g. "Vasomotor symptom items" inside
   "Menopause-related symptom items") -- see renderChecklistItemPicker()'s
   renderNode()/makeRow() in dashboard.js, which assigns "picker-depth-N"
   based on how many ancestor groups a row is nested under. */
.picker label.picker-row.picker-depth-1 {
  padding-left: 1.35rem;
}
.picker label.picker-row.picker-depth-2 {
  padding-left: 2.4rem;
}
.picker label.picker-row.picker-depth-3 {
  padding-left: 3.45rem;
}

/* ---------------------------------------------------------------------
   Custom Filter builder
   --------------------------------------------------------------------- */

.filter-row {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.5rem;
  flex-wrap: wrap;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.4rem 0.5rem;
}
.filter-row .remove-condition {
  border: none;
  background: none;
  color: #b3261e;
  cursor: pointer;
  font-size: 1rem;
  padding: 0.2rem 0.4rem;
  border-radius: 4px;
  transition: background 0.1s ease;
}
.filter-row .remove-condition:hover {
  background: #fbe9e7;
}
/* "is any of": a collapsible checklist of the field's existing values. */
.filter-multi {
  position: relative;
  min-width: 12rem;
}
.filter-multi summary {
  cursor: pointer;
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.3rem 0.5rem;
  background: #fff;
  list-style: none;
}
.filter-multi summary::-webkit-details-marker {
  display: none;
}
.filter-multi summary::after {
  content: " \25BE";
}
.filter-multi-list {
  position: absolute;
  z-index: 20;
  top: calc(100% + 2px);
  left: 0;
  min-width: 100%;
  max-width: min(28rem, 90vw);
  max-height: 16rem;
  overflow-y: auto;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  padding: 0.3rem 0;
}
.filter-multi-list label {
  display: block;
  padding: 0.2rem 0.6rem;
  cursor: pointer;
  white-space: nowrap;
}
.filter-multi-list label:hover {
  background: #f5f5f5;
}
/* "is between": two number boxes joined by "and". */
.filter-between {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.filter-between input[type="number"],
.filter-row > input[type="number"] {
  width: 7rem;
}

/* ---------------------------------------------------------------------
   Map
   --------------------------------------------------------------------- */

#map {
  height: 65vh;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  border-top: 3px solid transparent;
  background:
    linear-gradient(#fff, #fff) padding-box,
    linear-gradient(90deg, var(--accent) 0%, var(--accent-rose) 100%) border-box;
  box-shadow: var(--shadow-sm);
}

.leaflet-popup-content .cohort-tooltip strong { display: block; margin-bottom: 0.15em; }
.cohort-tooltip { font-size: 0.85rem; line-height: 1.4; }
.cohort-tooltip .tooltip-hint {
  display: block;
  margin-top: 0.3em;
  color: var(--text-muted);
  font-size: 0.78em;
  font-style: italic;
}

/* ---------------------------------------------------------------------
   Detail modal
   --------------------------------------------------------------------- */

.modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(20, 26, 33, 0.55);
  align-items: flex-start;
  justify-content: center;
  padding: 4vh 1rem;
  overflow-y: auto;
  z-index: 1000;
}
.modal-backdrop.open { display: flex; }
.modal {
  background: #fff;
  border-radius: 12px;
  max-width: 46em;
  width: 100%;
  padding: 1.5rem 1.6rem 1.4rem;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-md);
  position: relative;
}

.modal::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 5px;
  border-radius: 12px 12px 0 0;
  background: linear-gradient(90deg, var(--accent) 0%, var(--accent-dark) 45%, var(--accent-rose) 100%);
}
.modal h2 {
  margin-top: 0;
  color: var(--accent-dark);
}
.modal .close-btn {
  float: right;
  border: none;
  background: none;
  font-size: 1.3rem;
  cursor: pointer;
  color: var(--text-muted);
  border-radius: 6px;
  padding: 0.1rem 0.4rem;
  transition: background 0.1s ease, color 0.1s ease;
}
.modal .close-btn:hover {
  background: var(--bg-subtle);
  color: var(--text-main);
}
.modal dl { display: grid; grid-template-columns: 40% 1fr; gap: 0.35rem 0.8rem; margin: 0; }
.modal dt { font-weight: 600; color: var(--text-muted); font-size: 0.8rem; }
.modal dd { margin: 0 0 0.4rem; font-size: 0.88rem; }
/* Procedure Separation Type's value row -- colored inline via a `style`
   attribute set in renderDetailSection() (dashboard.js), matching that
   type's fixed color everywhere else it appears. `cursor: help` hints
   that hovering it (see attachTooltip() call in openCohortDetail())
   reveals that type's definition. */
.modal dd.detail-proc-type {
  cursor: help;
}
.modal h3 { font-size: 0.9rem; margin: 1.1rem 0 0.4rem; border-top: 1px solid var(--border); padding-top: 0.65rem; color: var(--accent-dark); }

.empty-state {
  color: var(--text-muted);
  padding: 2rem 1rem;
  text-align: center;
}

/* ---------------------------------------------------------------------
   Custom hover tooltips
   --------------------------------------------------------------------- */

/* One shared floating tooltip element, positioned next to the cursor and
   shown/hidden by attachTooltip() in dashboard.js -- used in place of the
   native `title` attribute wherever its fixed, browser-controlled show
   delay would be too slow (see attachTooltip()'s own comment for why).
   `pointer-events: none` keeps it from ever intercepting the hover/click
   it's describing. */
.custom-tooltip {
  position: fixed;
  z-index: 9999;
  left: 0;
  top: 0;
  background: rgba(20, 24, 32, 0.94);
  color: #fff;
  font-size: 0.78rem;
  line-height: 1.35;
  padding: 0.35rem 0.6rem;
  border-radius: 5px;
  box-shadow: var(--shadow-sm);
  pointer-events: none;
  max-width: 22rem;
  white-space: normal;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.08s ease;
}
.custom-tooltip.visible {
  opacity: 1;
  visibility: visible;
}

/* ---------------------------------------------------------------------
   Static content tabs (Information, Hysterectomy Procedure Separation
   Info)
   --------------------------------------------------------------------- */
/* Unlike the four data tabs, these have no table/map/data-table
   scaffolding of their own -- static content, styled to match the rest of
   the site (same card/border/radius/shadow language, same accent gradients
   as .hint-banner and the header's top bar) while reading distinctly as a
   home/intro-style page rather than another data view. ".info-hero"/
   ".info-card" aren't scoped to a specific panel's id since any static
   content tab can use them. */

/* Body text beside a couple of stat call-outs (see ".info-hero-stats") --
   a plain single column of fairly short paragraphs left a wide strip of
   empty space beside them on anything wider than a phone; splitting into
   two flex children fills that space with something purposeful instead of
   just stretching the text itself wider. */
.info-hero {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(2rem, 4vw, 4rem);
  background: linear-gradient(135deg, var(--accent-soft) 0%, var(--accent-rose-soft) 160%);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: 2rem 2.25rem;
  min-height: 250px;
  margin-bottom: 1rem;
}

.info-hero-body {
  position: relative;
  z-index: 1;
  flex: 1 1 26em;
}

.info-hero h2 {
  margin: 0 0 0.4rem;
  font-size: 1.3rem;
  color: var(--accent-dark);
}

.info-hero p {
  position: relative;
  margin: 0;
  color: #3a4451;
  line-height: 1.55;
}

.info-hero p + p {
  margin-top: 0.65rem;
}

/* Stat call-outs beside the body text -- both numbers are already stated
   in the paragraph itself (30 cohorts, 11 domains), just pulled out and
   presented the way a typical landing page highlights its headline
   figures. */
.info-hero-stats {
  position: relative;
  z-index: 1;
  display: flex;
  flex: 0 0 auto;
  gap: 1.25rem;
}

.info-hero-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.1rem 1.5rem;
  min-width: 8rem;
  min-height: 6.5rem;
}

.info-hero-stat-num {
  font-size: 1.8rem;
  font-weight: 800;
  color: var(--accent-dark);
  line-height: 1.1;
}

.info-hero-stat-label {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-top: 0.15rem;
}

/* Purely decorative echo of the logo's heartbeat/data-line flourish (see
   ".brand-flourish"), oversized and washed out in the hero's bottom-right
   corner -- gives the banner some landing-page presence without adding any
   new copy of its own. Sized/positioned so it sits behind the text rather
   than crowding it, and never causes the hero to scroll horizontally
   (".info-hero" clips it via `overflow: hidden`). z-index:0 (with
   ".info-hero-body"/".info-hero-stats" explicitly at z-index:1) keeps it
   from painting over the real content -- a plain absolutely-positioned
   element would otherwise stack above its static-position siblings
   regardless of DOM order. */
.info-hero-deco {
  position: absolute;
  z-index: 0;
  right: -6%;
  bottom: -18%;
  width: 48%;
  max-width: 460px;
  height: auto;
  color: var(--accent);
  opacity: 0.16;
  pointer-events: none;
}

/* The Procedure Separation Type explanation and the Citing sidebar sit
   side by side rather than stacking full-width -- a home page reads more
   like a laid-out page and less like a plain list of cards when at least
   one section pairs main content with a shorter aside. Falls back to a
   single stacked column at narrower widths (see the phone/tablet media
   queries below). */
.info-row {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr);
  gap: 1rem;
  /* Stretch (the grid default) rather than "start": the citation list is
     much shorter than the procedure-type explanation beside it, and
     leaving it top-aligned at its own natural height stranded a tall
     empty gap next to a short floating box. Matching both cards' heights
     reads as one deliberately roomy pairing instead. */
  align-items: stretch;
  margin-bottom: 1rem;
}

.info-card {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  padding: 1.1rem 1.3rem;
  margin-bottom: 1rem;
}

/* Cards inside ".info-row" are grid items -- ".info-row" itself already
   carries the bottom margin for the row as a whole, so a second margin
   here would just add uneven trailing space under whichever card happens
   to be shorter. */
.info-row > .info-card {
  margin-bottom: 0;
}

/* Sidebar card reads as a distinct "aside" next to the main explanation --
   a tinted fill (rather than the same plain white as every other card)
   makes that pairing look deliberate instead of like a mis-sized card. */
.info-card-aside {
  background: var(--bg-subtle);
}

/* See the decorative <svg> markup itself in index.html for what this is
   -- position/overflow here just set up the card as its clipping/
   positioning context, the same pattern as ".info-hero"/".info-hero-deco"
   above. */
.info-card-explore {
  position: relative;
  overflow: hidden;
}
/* Its own text/heading sit in normal flow after the decoration in the
   DOM, but a positioned element paints above static ones regardless of
   source order (see the equivalent note on ".info-hero-deco" above) --
   z-index: 0 here plus z-index: 1 on the card's real content keeps the
   decoration behind it. */
.info-card-explore h3,
.info-card-explore p {
  position: relative;
  z-index: 1;
}
/* Anchored to the bottom-right corner, sized to roughly the bottom-right
   quadrant of whatever empty space this shorter card is left with next to
   its taller sibling -- and washed out to the same low opacity/single
   color as ".info-hero-deco", so it reads as the same kind of quiet
   background motif, not a second competing focal point. */
.info-card-deco {
  position: absolute;
  z-index: 0;
  right: -6%;
  bottom: -6%;
  width: 62%;
  height: 62%;
  color: var(--accent);
  opacity: 0.16;
  pointer-events: none;
}

.info-card h3 {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0 0 0.5rem;
  font-size: 1rem;
  color: var(--accent-dark);
}

.info-card-icon {
  flex: none;
  width: 1.05em;
  height: 1.05em;
}

.info-card p {
  margin: 0;
  color: var(--text-main);
  line-height: 1.55;
}

.info-card p + p {
  margin-top: 0.65rem;
}

/* First mention of "hysterectomy"/"oophorectomy" in the Procedure
   Separation Type explanation -- bolded and colored with the site's rose
   accent to flag them as the key terms the rest of the paragraph builds
   on, without introducing a new color of its own. */
.term-highlight {
  font-weight: 700;
  color: var(--accent-rose);
}

/* Placeholder link to the Yoon et al. (2026) paper -- underlined (rather
   than styled like the plain body text around it) so it reads as a link,
   and `cursor: help` hints that hovering it reveals the full citation via
   its `title` attribute, matching the same hint used for the cohort detail
   modal's Procedure Separation Type value (see ".modal dd.detail-proc-
   type" under "Detail modal"). */
.paper-link {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-underline-offset: 0.15em;
  cursor: help;
}

.paper-link:hover {
  color: var(--accent-dark);
  text-decoration-color: currentColor;
}

/* In-page link to another tab (e.g. pointing from the Procedure Separation
   Type explanation over to its dedicated info tab) -- a <button> under the
   hood (see wireInPageTabLinks() in dashboard.js, which just clicks the
   matching nav.tabs button), styled to read as a plain inline hyperlink
   within the surrounding paragraph rather than a UI button. */
.tab-link {
  display: inline;
  border: none;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-underline-offset: 0.15em;
  cursor: pointer;
}

.tab-link:hover {
  color: var(--accent-dark);
  text-decoration-color: currentColor;
}

/* The embedded Procedure Separation Type key reuses ".procedure-key" as-is
   (see the "Procedure Separation Type key" comment above it) but, unlike
   its `.panel-key` use elsewhere, isn't stretched to match a sibling's
   height here -- so give it its own vertical rhythm between the
   surrounding paragraphs instead of relying on a grid parent for spacing. */
.info-card .procedure-key {
  margin: 0.9rem 0;
}

/* Citation format(s) -- a label/citation pair per format, matching the
   <dl>/<dt>/<dd> pattern already used for the cohort detail modal's field
   list (see ".modal dl"/"dt"/"dd" under "Detail modal" above) rather than
   inventing a second convention for the same shape of content. Only APA
   is shown now (MLA/Chicago were dropped to keep this to one canonical
   format), but the markup stays a <dl> rather than flattening to a plain
   paragraph in case more formats come back later. Centered (rather than
   left-aligned like every other card's text) now that it's down to a
   single short label-plus-citation pair -- reads as a deliberately
   centered citation block instead of a left-aligned list missing its
   other rows. */
.citation-list {
  margin: 0;
  text-align: center;
}

.citation-list dt {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--accent-dark);
  margin: 0.9rem 0 0.25rem;
}

.citation-list dt:first-child {
  margin-top: 0;
}

.citation-list dd {
  margin: 0;
  color: var(--text-main);
  line-height: 1.55;
}

/* Contribute call to action -- same stacked heading-then-paragraph shape
   as every other card, but with a soft rose-tinted wash (rather than the
   plain white the others use) that echoes the hero's gradient at the top
   of the page, bookending the tab with the same accent language a closing
   CTA band gets on a typical home page. */
.info-cta {
  background: linear-gradient(135deg, #fff 0%, var(--accent-rose-soft) 220%);
  border-left: 3px solid var(--accent-rose);
}

/* Spacing for the "Open the Contributor Form" button sitting under its own
   paragraph inside ".info-cta" -- ".info-card p + p"'s margin only applies
   between two <p> siblings, not before a following ".btn". */
.info-cta .btn {
  margin-top: 0.9rem;
}

/* Fine-print note under the "Open the Contributor Form" button -- the
   eligibility disclaimer that isn't part of the release agreement above
   it, so it reads as a caveat on the button itself rather than another
   numbered legal item. Selector specificity (class + type + class) beats
   ".info-card p"'s plain `margin: 0` above without needing !important. */
.info-card p.cta-subtext {
  margin-top: 0.7rem;
  font-size: 0.8rem;
  color: var(--text-muted);
  line-height: 1.5;
}

/* Numbered lists on the Contribute tab -- the "How to Contribute" steps
   and the release-form disclaimers below them. Decimal-numbered, unlike
   Items Reference's bullet-only ".reference-list", and each <li> may hold
   several <p>s (the withdrawal-notice disclaimer runs three paragraphs)
   rather than always being a single line. */
.ordered-list {
  margin: 0;
  padding-left: 1.4rem;
  color: var(--text-main);
}

.ordered-list > li {
  margin: 0 0 0.6rem;
  line-height: 1.55;
}

.ordered-list > li:last-child {
  margin-bottom: 0;
}

.ordered-list > li p {
  margin: 0 0 0.5rem;
}

.ordered-list > li p:last-child {
  margin-bottom: 0;
}

/* Bold, slightly larger lead sentence directly under the hero's <h2> --
   reads as a tagline/strapline, the way a typical landing-page hero leads
   with a punchy one-liner before the fuller body copy beneath it. */
.info-hero p.info-hero-lead {
  font-weight: 700;
  font-size: 1.02rem;
  color: var(--accent-dark);
  margin-bottom: 0.65rem;
}

/* Small version-tag pill for "Current Release" -- reads as a release badge
   (the kind a changelog or software release page would show) rather than
   plain body text. */
.info-card p.release-badge {
  display: inline-block;
  background: var(--accent-soft);
  color: var(--accent-dark);
  font-weight: 700;
  font-size: 0.85rem;
  padding: 0.25rem 0.7rem;
  border-radius: 999px;
  margin-bottom: 0.5rem;
}

/* Generic inline link within card body text (e.g. Contribute's "click
   here") -- same treatment as ".paper-link" minus `cursor: help`, since
   this one is a real navigable link rather than a hover-for-citation
   affordance. */
.inline-link {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-underline-offset: 0.15em;
}

.inline-link:hover {
  color: var(--accent-dark);
  text-decoration-color: currentColor;
}

@media (max-width: 900px) {
  /* The main-explanation/citing-sidebar pairing only has room to sit side
     by side above tablet width -- stack them like every other section
     below that. */
  .info-row {
    grid-template-columns: 1fr;
  }
}

@media (max-width: 640px) {
  .info-hero,
  .info-card {
    padding: 0.9rem 1rem;
  }

  .info-hero {
    min-height: auto;
  }

  .info-hero-deco {
    width: 70%;
  }

  .info-cta {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ---------------------------------------------------------------------
   Items Reference tab
   --------------------------------------------------------------------- */
/* Native <details>/<summary> per domain -- collapsed by default, no JS
   needed for the show/hide behavior. Styled to match the card language
   used elsewhere (border/radius/shadow) rather than the browser default
   disclosure widget. */
.reference-category {
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  margin-bottom: 0.75rem;
  overflow: hidden;
}

.reference-category summary {
  cursor: pointer;
  padding: 0.85rem 1.2rem;
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--accent-dark);
  list-style: none;
  transition: background 0.15s ease;
}

.reference-category summary::-webkit-details-marker {
  display: none;
}

.reference-category summary::before {
  content: "\25B8";
  display: inline-block;
  width: 1em;
  margin-right: 0.4rem;
  color: var(--accent);
  transition: transform 0.15s ease;
}

.reference-category[open] summary::before {
  transform: rotate(90deg);
}

.reference-category summary:hover {
  background: var(--bg-subtle);
}

.reference-category[open] summary {
  border-bottom: 1px solid var(--border);
}

.reference-list {
  margin: 0;
  padding: 0.9rem 1.2rem 0.9rem 2.6rem;
  list-style: disc;
}

.reference-list ul {
  margin: 0.3rem 0 0.3rem 0;
  padding-left: 1.2rem;
  list-style: circle;
}

.reference-list li {
  margin: 0.3rem 0;
  line-height: 1.5;
  color: var(--text-main);
}

/* Map embedded in the Hysterectomy Inference Classification tab */

#panel-summary #map {
  width: 100%;
  min-height: 32rem;
  margin-top: 1rem;
  border-radius: 0.5rem;
  overflow: hidden;
}

#panel-summary .classification-map-section {
  margin-top: 2.5rem;
  padding-top: 2rem;
  border-top: 1px solid #d9dee5;
}

@media (max-width: 900px) {
  #panel-summary #map {
    min-height: 26rem;
  }
}

@media (max-width: 640px) {
  #panel-summary #map {
    min-height: 22rem;
  }
}

/* Full-width classification table and embedded map */
#panel-summary .panel-body-with-key {
  width: 100%;
  max-width: none;
  grid-template-columns: minmax(0, 1fr) !important;
}

#panel-summary .panel-main,
#panel-summary .table-scroll,
#panel-summary #map {
  width: 100%;
  max-width: none;
  box-sizing: border-box;
}

/* The map should use its natural height, not the table's tall grid row. */
#panel-summary .classification-map-section .panel-body-with-key {
  grid-template-rows: auto;
}