/* Rep Maps — small layer over Pico classless. Mobile-first.
   Brand: teal wordmark + coral accent (sampled from the Fórsa logo).
   Buttons/links use an accessible darker teal (#2c7a83, ~5:1 on white); the
   raw logo teal (#3a8b94) and coral (#f05562) are reserved for non-text
   accents (header rule, banner border, badges). */

:root {
  --forsa-teal: #3a8b94;   /* logo wordmark — decorative accents only */
  --forsa-coral: #f05562;  /* logo accent mark — highlights/badges */
}

/* Selectors mirror Pico's own (:root:not([data-theme=dark]) etc.) — plain
   :root loses the specificity contest. */
:root:not([data-theme=dark]),
[data-theme=light] {
  --pico-primary: #2c7a83;
  --pico-primary-background: #2c7a83;
  --pico-primary-border: #2c7a83;
  --pico-primary-hover: #24626a;
  --pico-primary-hover-background: #24626a;
  --pico-primary-hover-border: #24626a;
  --pico-primary-underline: rgba(44, 122, 131, .5);
  --pico-primary-focus: rgba(44, 122, 131, .35);
  --pico-primary-inverse: #fff;
}

@media only screen and (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --pico-primary: #67b8c0;
    --pico-primary-background: #2c7a83;
    --pico-primary-border: #2c7a83;
    --pico-primary-hover: #3a9aa4;
    --pico-primary-hover-background: #24626a;
    --pico-primary-hover-border: #24626a;
    --pico-primary-underline: rgba(103, 184, 192, .5);
    --pico-primary-focus: rgba(103, 184, 192, .35);
    --pico-primary-inverse: #fff;
  }
}

[data-theme=dark] {
  --pico-primary: #67b8c0;
  --pico-primary-background: #2c7a83;
  --pico-primary-border: #2c7a83;
  --pico-primary-hover: #3a9aa4;
  --pico-primary-hover-background: #24626a;
  --pico-primary-hover-border: #24626a;
  --pico-primary-underline: rgba(103, 184, 192, .5);
  --pico-primary-focus: rgba(103, 184, 192, .35);
  --pico-primary-inverse: #fff;
}

body > header.site-header,
body > main,
body > footer.site-footer {
  max-width: 900px;
  margin-inline: auto;
  padding-inline: 1rem;
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  padding-block: .75rem;
  border-bottom: 3px solid var(--forsa-teal);
  margin-bottom: 1rem;
}
.site-header .brand { display: flex; align-items: center; gap: .75rem; }
.brand-logo { height: 46px; width: auto; }
.brand-text { display: flex; flex-direction: column; }
.brand-text small { color: var(--pico-muted-color); }
.site-nav { display: flex; align-items: center; gap: 1rem; }
.logout-form { display: inline; margin: 0; }
.link-button {
  background: none; border: none; color: var(--pico-primary);
  text-decoration: underline; cursor: pointer; padding: 0; margin: 0;
  width: auto; font-size: 1rem;
}

.flash { padding: .6rem .9rem; border-radius: .3rem; }
.flash-success { background: #e6f4ea; color: #1e4620; }
.flash-error, .flash-warning { background: #fdecea; color: #611a15; }

/* Rep portal member list: stacked rows so the tap target stays on screen on a
   phone even with the mobile number shown. */
.member-list { list-style: none; padding: 0; margin: 0; }
.member-row {
  display: flex; align-items: center; gap: .75rem;
  padding: .55rem 0;
  border-bottom: 1px solid var(--pico-muted-border-color);
}
.member-info { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.member-name { font-weight: 600; }
/* The member's mobile, as a tel: link — a rep on a phone dials with one tap.
   Deliberately not the muted grey the email used: this is meant to be pressed,
   so it keeps link colour and enough padding to be a real target. Tabular
   figures so the numbers line up down the column. */
.member-mobile {
  align-self: flex-start;
  font-size: .95rem;
  font-variant-numeric: tabular-nums;
  padding: .1rem 0;
  white-space: nowrap;
}
.tick-btn {
  flex: 0 0 auto;
  min-height: 44px;
  min-width: 7.5rem;
  margin: 0;
  padding: .5rem 1rem;
}
.member-search { margin-bottom: .75rem; }
.progress-banner {
  background: #e8f3f4;
  border-left: 4px solid var(--forsa-coral);
  padding: .6rem .9rem;
  border-radius: .3rem;
  margin-bottom: 1rem;
}

/* Participation (ballot turnout) — votes only; replaces the old contacted banner. */
.participation {
  background: #e8f3f4;
  border-left: 4px solid var(--forsa-coral);
  padding: .6rem .9rem;
  border-radius: .3rem;
  margin-bottom: 1rem;
}
.participation-label { margin: 0 0 .4rem; }
.participation-bar { width: 100%; height: .55rem; margin: 0; }
.still-to-contact { margin: .35rem 0 0; color: var(--pico-muted-color); }
.contact-note { margin: .25rem 0 .75rem; }

/* "Spoken to" confirmation modal (Pico renders <dialog> as a centered modal). */
.confirm-dialog article { max-width: 24rem; }
.confirm-dialog h3 { margin-bottom: .2rem; }
.confirm-name { font-size: 1.15rem; margin: 0 0 .6rem; }
.confirm-actions { display: flex; gap: .5rem; justify-content: flex-end; flex-wrap: wrap; margin-bottom: 0; }
.confirm-actions button { width: auto; margin-bottom: 0; }
/* The "send their voting code" action is an <a href="mailto:">, not a button:
   htmx would preventDefault a button's click and the mail client would never
   open. Match the buttons beside it, and beat Pico's a[role=button] display
   rule so [hidden] still hides it when there is nothing to send. */
.confirm-actions a[role="button"] { width: auto; margin-bottom: 0; }
.confirm-actions [hidden] { display: none; }
/* Pico's classless build ignores .secondary, so the understatement has to be
   spelled out: declining is the solid button, sending someone their voting code
   is the outlined one beside it. Deliberately still obvious enough to find —
   quiet, not hidden. */
#uvc-send {
  background: transparent;
  color: var(--forsa-teal);
  border-color: var(--forsa-teal);
}
#uvc-send:hover, #uvc-send:focus-visible { background: var(--forsa-teal); color: #fff; }

/* Branch committee overview: one card per workplace in the branch. */
.branch-grid {
  display: grid; gap: .75rem;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  margin-top: 1rem;
}
.wp-card {
  display: flex; flex-direction: column; gap: .3rem;
  padding: .8rem 1rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: .4rem;
  color: inherit; text-decoration: none;
  transition: border-color .15s ease, transform .15s ease;
}
.wp-card:hover, .wp-card:focus-visible { border-color: var(--forsa-teal); transform: translateY(-1px); }
.wp-card-name { line-height: 1.3; }
.wp-card-stat { font-size: .9rem; }
.wp-card-bar { height: .45rem; margin: 0; }
.wp-card-meta { color: var(--pico-muted-color); font-size: .8rem; }
.wp-card-unmapped { font-style: italic; }
.branch-back { margin-bottom: .25rem; }
.branch-back a { text-decoration: none; }

/* Dashboard */
.table-scroll { overflow-x: auto; }
.stat-row { display: flex; flex-wrap: wrap; gap: .75rem; margin-bottom: 1.5rem; }
.stat-tile {
  flex: 1 1 8rem;
  display: flex; flex-direction: column;
  padding: .8rem 1rem;
  border: 1px solid var(--pico-muted-border-color);
  border-radius: .4rem;
}
.stat-value { font-size: 1.7rem; font-weight: 700; line-height: 1.2; }
.stat-label { color: var(--pico-muted-color); font-size: .85rem; }
.dash-table { font-size: .95rem; }
.dash-table th, .dash-table td { padding: .4rem .6rem; }
.scope-crumbs { margin-bottom: .5rem; }
.panel-grid { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 900px) { .panel-grid { grid-template-columns: 1fr 1fr; } }
.chart-wrap { position: relative; height: 320px; }

/* Scope filter bar (staff national workplace list) */
.filter-bar {
  display: flex; flex-wrap: wrap; align-items: end; gap: .75rem;
  padding: .75rem 1rem; margin-bottom: 1.5rem;
  background: #e8f3f4; border-radius: .4rem;
}
.filter-bar .field { display: flex; flex-direction: column; gap: .2rem; margin: 0; }
.filter-bar label { font-size: .8rem; color: var(--pico-muted-color); }
.filter-bar select, .filter-bar input { margin: 0; min-width: 12rem; }
.filter-bar button { margin: 0; }
.filter-clear { align-self: center; font-size: .9rem; }
.filter-note { margin: .25rem 0 .75rem; }
.grid-summary { margin: .5rem 0 0; color: var(--pico-muted-color); font-size: .9rem; }
.pager {
  display: flex; flex-wrap: wrap; gap: .75rem;
  align-items: center; justify-content: center; margin-top: 1.25rem;
}
.pager-status { color: var(--pico-muted-color); font-size: .9rem; }

/* Rep leaderboard */
.leaderboard td, .leaderboard th { padding: .4rem .6rem; vertical-align: middle; }
.leaderboard .rank {
  width: 2.5rem; text-align: center;
  color: var(--pico-muted-color); font-variant-numeric: tabular-nums;
}
.leaderboard .count { font-weight: 700; font-size: 1.05rem; }
.leaderboard tr.is-top { background: rgba(240, 85, 98, .08); }
.tier-badge {
  display: inline-block; padding: .05rem .5rem; border-radius: 999px;
  font-size: .75rem; font-weight: 700; white-space: nowrap;
  background: #fde7e9; color: #b3323d;
}
.tier-badge.tier-none { background: var(--pico-muted-border-color); color: var(--pico-muted-color); }

/* Member-row flags. Lighter than .tier-badge: they sit beside a name and must
   not out-shout it, so they read as an aside rather than a status light. */
.member-flag {
  display: inline-block; padding: .05rem .45rem; border-radius: 999px;
  font-size: .7rem; font-weight: 600; white-space: nowrap;
  vertical-align: middle; margin-left: .35rem;
}
.flag-pledged, .flag-role { background: #e4f1f2; color: #2a656c; }
/* A rep whose own branch is not the one in the path — labelled rather than
   hidden, so "who speaks for these members" stays readable at a glance. */
.flag-other-branch { background: #eef0f2; color: #55606a; font-weight: 500; }
.flag-premob  { background: #eae6f6; color: #4b3b8f; }
.flag-new     { background: #fdf0dc; color: #8a5a10; }

/* Dark-scheme variants for the hardcoded tints above. */
@media only screen and (prefers-color-scheme: dark) {
  .progress-banner { background: #17282a; }
  .participation { background: #17282a; }
  .filter-bar { background: #17282a; }
  .flash-success { background: #16281a; color: #a8d5ae; }
  .flash-error, .flash-warning { background: #331d1a; color: #e8b3ac; }
  .leaderboard tr.is-top { background: rgba(240, 85, 98, .14); }
  .flag-pledged, .flag-role { background: #17323a; color: #8fd0d8; }
  .flag-other-branch { background: #232a30; color: #9aa7b2; }
  .flag-premob  { background: #241f3a; color: #b9adea; }
  .flag-new     { background: #33260f; color: #e5bd7a; }
  .tier-badge { background: #3a1f22; color: #f2a3aa; }
}

/* Data protection: member lists must not be printable. */
.print-notice { display: none; }
@media print {
  /* .people-panel is a member list like any other — without it the page would
     print reps' names, mobiles and emails directly under a notice saying it
     must not be printed. */
  .member-list-area, .people-panel, .site-nav { display: none !important; }
  .print-notice { display: block; font-size: 1.1rem; }
}

.site-footer { margin-top: 3rem; padding-block: 1rem; border-top: 1px solid var(--pico-muted-border-color); }
/* Each footer line on its own row; the product attribution reads as a signature
   above the usage notice rather than running into it. */
.site-footer small { display: block; }
.site-footer .attribution { color: var(--pico-muted-color); margin-bottom: .2rem; }

/* Keyboard focus + motion preferences */
.tick-btn:focus-visible, .wp-card:focus-visible, .link-button:focus-visible {
  outline: 3px solid var(--pico-primary-focus);
  outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .wp-card { transition: none; }
  .wp-card:hover, .wp-card:focus-visible { transform: none; }
}

/* ─── Mobile pass (phones ≤600px) ───────────────────────────────────────────
   This portal is used primarily on phones by workplace reps. The rules below
   only tighten the app's own custom components for small screens; every rule
   above (desktop, dark, print, ≥900px) is left untouched. Pico already handles
   the rest for us: form inputs and submit buttons are full-width and 16px at
   the mobile root font size (no iOS focus-zoom), and <dialog> is centred with a
   width:100% article that already fits a ~375px viewport. */
@media (max-width: 600px) {
  /* Header — keep the 120×65 logo modest and let the nav drop onto its own
     full-width row with tap-friendly links instead of crowding the brand. */
  .brand-logo { height: 40px; }
  .site-nav { width: 100%; gap: 1.25rem; }
  .site-nav a,
  .site-nav .link-button { display: inline-block; padding-block: .4rem; }

  /* Rep list rows — name + mobile take the full width and the "Spoken to"
     button drops below as a full-width ≥44px tap target. The desktop min-width
     is cleared so it can shrink to the row; extra padding gives thumb spacing. */
  .member-row { flex-wrap: wrap; gap: .5rem .75rem; padding: .65rem 0; }
  .member-info { flex-basis: 100%; }
  .tick-btn { flex-basis: 100%; width: 100%; min-width: 0; }

  /* Committee overview — one tappable workplace card per row. */
  .branch-grid { grid-template-columns: 1fr; }

  /* Staff filters: eight stacked fields ran to ~785px, so the first workplace
     started two screens down. Two columns halves that. Search and the buttons
     span the full width — a truncated "Workplace name or code" is useless, and
     Apply stays a comfortable target. */
  .filter-bar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .45rem .6rem;
    padding: .6rem .7rem;
    align-items: end;
  }
  .filter-bar .field { min-width: 0; }
  .filter-bar select,
  .filter-bar input { min-width: 0; width: 100%; }
  .filter-bar .field:first-of-type,
  .filter-bar button,
  .filter-clear { grid-column: 1 / -1; }
  .filter-bar button { min-height: 44px; margin-top: .2rem; }
  .filter-clear { text-align: center; padding-block: .4rem; }

  /* Pagination: thumb-sized, and centred rather than strung across the row. */
  .pager { gap: .5rem 1rem; }
  .pager a { padding-block: .4rem; }

  /* Confirmation modal — stack Cancel/Yes as full-width tap targets so neither
     crowds nor overflows on a narrow screen. */
  .confirm-actions { flex-direction: column; align-items: stretch; }
  .confirm-actions button,
  .confirm-actions a[role="button"] { width: 100%; min-height: 44px; }
  /* Stacked, the safe choice takes the top slot — the same reason it is
     rightmost and focused on a wide screen. */
  #uvc-confirm .confirm-actions { flex-direction: column-reverse; }
}

/* Project Ochtó work list. One block per workplace so the batch reads as
   "this site, then that site" rather than a flat ten names. */
.worklist-count { margin: 0 0 .75rem; }
.worklist-block { margin-bottom: 1.25rem; }
.worklist-workplace { font-size: 1.05rem; margin: 0; }
.worklist-remaining { margin: .1rem 0 .35rem; }
.worklist-done { margin: 1rem 0; }
.worklist-refresh { margin: 1rem 0 0; }

/* "Missing details" campaign (engage/missing_details.py): the drill-down rows
   and the member rows with their two actions. Bars are <progress>, like the
   participation ones — style-src is 'self', so no inline widths. */
/* Both are <nav>, which Pico lays out space-between; these read left to right. */
.detail-crumbs {
  display: flex; flex-wrap: wrap; justify-content: flex-start; gap: .4rem;
  margin-bottom: .25rem; font-size: .9rem; color: var(--pico-muted-color);
}
.detail-crumbs a { text-decoration: none; }
.detail-filter {
  display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 1.25rem;
  margin: 0 0 1rem; font-size: .95rem;
}
.detail-filter a[aria-current="page"] { font-weight: 700; text-decoration: none; }
.detail-rows { list-style: none; padding: 0; margin: 1rem 0 0; }
.detail-row {
  display: grid; grid-template-columns: minmax(10rem, 1fr) 3.5rem 2fr; gap: .2rem .75rem;
  align-items: center; padding: .55rem 0; border-bottom: 1px solid var(--pico-muted-border-color);
}
.detail-row-name { font-weight: 600; text-decoration: none; }
.detail-row-none .detail-row-name { font-style: italic; }
.detail-row-pct { text-align: right; font-variant-numeric: tabular-nums; font-size: .9rem; }
.detail-bar { width: 100%; height: .55rem; margin: 0; }
/* A two-segment bar without an inline width: teal for done or marked, Fórsa
   coral for members somebody has in hand. Two stacked <progress> elements,
   coral reaching further and teal painted over it, because one progress paints
   one colour and style-src is 'self'. The wrapper owns the track and the
   rounding, so the coral layer is simply absent when nobody holds anyone. */
.bar-stack {
  position: relative; display: block; overflow: hidden;
  border-radius: var(--pico-border-radius);
  background: var(--pico-progress-background-color);
}
.bar-layer { position: absolute; left: 0; top: 0; width: 100%; height: 100%; margin: 0; background: transparent; }
/* One rule per vendor, never a comma list: a browser that does not know one of
   these selectors drops the whole rule and the segment vanishes silently.
   The webkit one is qualified with `progress` to outweigh Pico's own
   `progress[value]::-webkit-progress-value`, which otherwise repaints the
   segment teal and the coral is invisible in Chrome while Firefox looks fine. */
.bar-layer::-webkit-progress-bar { background: transparent; }
progress.bar-in-hand::-webkit-progress-value { background-color: var(--forsa-coral); }
.bar-in-hand::-moz-progress-bar { background-color: var(--forsa-coral); }
.detail-row-stat { grid-column: 1 / -1; color: var(--pico-muted-color); font-size: .85rem; }
/* Units whose every open detail is marked: out of the working list until the
   next sync judges the marks, one click away rather than gone. */
.detail-waiting { margin-top: 1.25rem; }
.detail-waiting summary { color: var(--pico-muted-color); font-size: .9rem; cursor: pointer; }
.detail-rows-waiting .detail-row-name { color: var(--pico-muted-color); }
.member-number { color: var(--pico-muted-color); font-size: .8rem; font-variant-numeric: tabular-nums; }
/* On "members you have in hand", where the rows come from everywhere: the
   site each one sits at, linking to the list that can mark them resolved. */
.member-where { font-size: .85rem; align-self: flex-start; }
.mine-link { margin: .25rem 0 1rem; }
/* The email beside (or instead of) the mobile on the staff-only campaign rows;
   a long address wraps rather than widening the row on a phone. */
.member-email { align-self: flex-start; font-size: .95rem; padding: .1rem 0; overflow-wrap: anywhere; }
.detail-actions { display: flex; gap: .5rem; align-items: center; flex: 0 0 auto; }
.detail-actions a[role="button"],
.detail-actions button { margin: 0; width: auto; min-height: 44px; padding: .5rem 1rem; white-space: nowrap; }
/* Pico's classless build ignores .secondary (see #uvc-send), so the three
   actions are styled here: outlined teal = the link out to APT, solid = the
   mark we keep, outlined muted = taking a mark back. Hover uses the darker
   primary so the white label keeps its contrast in dark mode too. */
.wrap-link { background: transparent; color: var(--pico-primary); border: 1px solid var(--pico-primary); }
.wrap-link:hover, .wrap-link:focus-visible {
  background: var(--pico-primary-background); border-color: var(--pico-primary-background); color: var(--pico-primary-inverse);
}
.resolve-btn.secondary { background: transparent; color: var(--pico-muted-color); border: 1px solid var(--pico-muted-border-color); }
.resolve-btn.secondary:hover, .resolve-btn.secondary:focus-visible { border-color: var(--pico-muted-color); color: var(--pico-color); }
.member-row-marked .member-name { color: var(--pico-muted-color); }
/* A member a colleague has in hand: the whole row steps back, and it is sorted
   to the end of the list. Muted, never hidden — the same rule as a mark. */
.member-row-held .member-info { color: var(--pico-muted-color); }
.member-marked-by { color: var(--pico-muted-color); font-size: .8rem; }
/* Coral for what is missing — it is the point of the page; teal once marked;
   coral again once a sync has judged the mark and the detail is still blank. */
.flag-missing  { background: #fde7e9; color: #b3323d; }
.flag-resolved { background: #e4f1f2; color: #2a656c; }
.flag-resolved.flag-lapsed { background: #fde7e9; color: #b3323d; }
/* The hold badge: neutral for a colleague's, teal when it is yours, coral once
   it has sat a week (ENGAGEMENT_STALE_AFTER — holds do not expire, so being
   visibly old is the only thing that gets a forgotten one released). For
   whoever may let go of it the badge is a <button>, which Pico styles as a
   full button — reset to the pill the rest of the row already reads. */
.flag-engaged { background: #eef0f2; color: #55606a; }
.flag-engaged.flag-mine { background: #e4f1f2; color: #2a656c; }
.flag-engaged.flag-stale { background: #fde7e9; color: #b3323d; }
button.flag-engaged {
  border: 0; font-family: inherit; font-size: .7rem; line-height: inherit;
  margin: 0 0 0 .35rem; padding: .05rem .45rem; width: auto; min-height: 0;
  cursor: pointer;
}
/* What the headline figures are and are not. Its own rule rather than reusing
   .sync-note, which carries a coral .sync-stale variant and a different
   meaning; this line never escalates. */
.figures-note { margin: .35rem 0 0; color: var(--pico-muted-color); }
/* Reps at a site / the branch committee: staff-only contact panels, collapsed
   so the work list keeps the top of the page. The rows inside are the people
   rows, so a number behaves the same wherever it appears. */
.people-panel { margin: 0 0 1rem; }
.people-panel > summary { color: var(--pico-muted-color); font-size: .9rem; cursor: pointer; }
.people-panel .member-list { margin-top: .5rem; }
/* When marks were last judged; coral once the nightly sync is overdue. */
.sync-note { margin: .35rem 0 0; color: var(--pico-muted-color); }
.sync-note.sync-stale { color: #b3323d; font-weight: 600; }

@media only screen and (prefers-color-scheme: dark) {
  .flag-missing  { background: #3a1f22; color: #f2a3aa; }
  .flag-resolved { background: #17323a; color: #8fd0d8; }
  .flag-resolved.flag-lapsed { background: #3a1f22; color: #f2a3aa; }
  .flag-engaged { background: #232a30; color: #9aa7b2; }
  .flag-engaged.flag-mine { background: #17323a; color: #8fd0d8; }
  .flag-engaged.flag-stale { background: #3a1f22; color: #f2a3aa; }
  .sync-note.sync-stale { color: #f2a3aa; }
}
@media (max-width: 600px) {
  .detail-row { grid-template-columns: 1fr 3.5rem; }
  .detail-bar { grid-column: 1 / -1; }
  /* Two full-width buttons stacked, or "Resolve in APT" + "Mark as resolved
     again" overflow a 360px screen and drag the page sideways. */
  .detail-actions { flex-basis: 100%; flex-direction: column; align-items: stretch; }
  .detail-actions a[role="button"],
  .detail-actions button { flex: 1 1 auto; width: 100%; }
  /* The hold badge is a button for whoever may let go: on a phone it wraps to
     its own line anyway, so give it something worth tapping. */
  button.flag-engaged { padding: .4rem .7rem; min-height: 2.25rem; }
}

/* Hub: a card with nothing behind it greys out rather than disappearing — a
   missing button reads as a broken page, a greyed one with a reason reads as
   "not for you". Not a link, so it is not focusable either. */
.wp-card-empty {
  opacity: 0.55;
  cursor: default;
}
.ballot-line {
  margin-top: -0.5rem;
}
