/* main.css — shared styles for GTRRR Portal */
/* Mobile-first, low-bandwidth, high-contrast. No external fonts. */

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

/* The `hidden` attribute must actually hide (2026-08-21).
   `[hidden]{display:none}` is only a USER-AGENT rule, so ANY author rule that
   sets `display` on the element beats it — e.g. `.btn{display:inline-flex}`
   left `<a class="btn" hidden>` permanently visible. That bug shipped twice in
   two days (the revision banner, then the Corriger button), each time looking
   like a state-management bug when it was pure CSS precedence. This reset makes
   `hidden` authoritative everywhere, so `el.hidden = true/false` is a safe
   toggle on any element regardless of its classes.
   Verified before adding: no element in the codebase is shown via
   `style.display` while still carrying `hidden`, so nothing relies on the
   old (broken) behaviour. */
[hidden] { display: none !important; }

:root {
  --clr-primary:   #1d6fa4;   /* neutral blue — not UNDP brand blue */
  --clr-primary-d: #155a85;
  --clr-accent:    #f59e0b;   /* amber for warnings */
  --clr-danger:    #dc2626;
  --clr-success:   #16a34a;
  --clr-bg:        #f8fafc;
  --clr-surface:   #ffffff;
  --clr-border:    #d1d5db;
  --clr-text:      #1f2937;
  --clr-text-muted:#6b7280;
  --radius:        6px;
  --shadow-sm:     0 1px 3px rgba(0,0,0,.10);
  --shadow-md:     0 4px 12px rgba(0,0,0,.12);
  --font:          system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

  /* ── Shared type scale (punch list #8, 2026-08-20) ──────────────
     22 distinct font-size values had accumulated across the pages,
     clustered into near-identical steps (.78/.8, .82/.85, .88/.9 …) that
     read as drift between blocks. These six steps are the whole UI-text
     scale; headings (h1-h3) and the geo-map SVG label sizes in
     admin-analysis.html keep their own calibrated values. */
  --fs-xxs:  .75rem;
  --fs-xs:   .8rem;
  --fs-sm:   .85rem;
  --fs-md:   .9rem;
  --fs-lg:   .95rem;
  --fs-base: 1rem;
}

/* Auth gate (punch list #11) — guarded pages hide their main content until
   the auth + role check has decided, so a reviewer never sees a flash of the
   submission form and a non-admin never sees a flash of an admin page. The
   topbar stays visible so the page doesn't read as broken while checking.
   `visibility` (not `display`) keeps layout stable, so nothing reflows when
   the content is revealed. A failsafe timer in each page's head script
   removes the class even if the check never completes — a blank page must
   never be the permanent end state. */
html.auth-gate main { visibility: hidden; }

html, body {
  font-family: var(--font);
  font-size: 16px;
  color: var(--clr-text);
  background: var(--clr-bg);
  min-height: 100vh;
}

/* ── Typography ─────────────────────────────────── */
h1 { font-size: 1.5rem; font-weight: 700; }
h2 { font-size: 1.25rem; font-weight: 600; }
h3 { font-size: 1.05rem; font-weight: 600; }
p  { line-height: 1.6; }

/* ── Layout helpers ─────────────────────────────── */
.container {
  max-width: 960px;
  margin: 0 auto;
  padding: 0 1rem;
}

.card {
  background: var(--clr-surface);
  border: 1px solid var(--clr-border);
  border-radius: var(--radius);
  padding: 1.5rem;
  box-shadow: var(--shadow-sm);
}

.sr-only {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip: rect(0,0,0,0);
}

/* ── Top navigation bar ──────────────────────────── */
.topbar {
  background: var(--clr-primary);
  color: #fff;
  padding: .75rem 1rem;
  /* installed/standalone iOS PWA: apple-mobile-web-app-status-bar-style
     is black-translucent, so content draws under the status bar (clock/
     Wi-Fi/battery icons) unless padded clear of it. env(safe-area-inset-*)
     is 0 on browsers/devices without a notch/status-bar overlay, so this
     is a no-op there — max() keeps the normal .75rem padding in that case. */
  padding-top: max(.75rem, env(safe-area-inset-top));
  padding-left: max(1rem, env(safe-area-inset-left));
  padding-right: max(1rem, env(safe-area-inset-right));
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: sticky;
  top: 0;
  z-index: 100;
  box-shadow: var(--shadow-sm);
}
.topbar__brand {
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: .01em;
}
.topbar__actions {
  display: flex;
  gap: .5rem;
  align-items: center;
}
.topbar__user {
  font-size: var(--fs-sm);
  opacity: .88;
}

/* ── Demo-mode banner ─────────────────────────────
   Temporary: remove once real partner onboarding begins and the pilot
   test data is reset (see CLAUDE.md open follow-up items). Not a
   security control — just a visual cue for external testers/HQ
   colleagues that the data shown is test data, not live reporting. */
.demo-banner {
  background: var(--clr-accent);
  color: #1f2937;
  text-align: center;
  font-size: var(--fs-xs);
  font-weight: 600;
  padding: .35rem .75rem;
  letter-spacing: .01em;
}
/* .login-page is itself a centered flex column (no topbar to sit under),
   so without this the banner would shrink to its text width instead of
   spanning full width like it does on every other page. */
.login-page .demo-banner {
  align-self: stretch;
  margin-bottom: 1rem;
}

/* ── Buttons ─────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .4rem;
  padding: .55rem 1.1rem;
  border-radius: var(--radius);
  font-size: var(--fs-lg);
  font-weight: 600;
  cursor: pointer;
  border: none;
  transition: background .15s, opacity .15s;
  text-decoration: none;
}
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn-primary {
  background: var(--clr-primary);
  color: #fff;
}
.btn-primary:hover:not(:disabled) { background: var(--clr-primary-d); }

.btn-ghost {
  background: transparent;
  color: #fff;
  border: 1px solid rgba(255,255,255,.45);
  padding: .4rem .8rem;
  font-size: var(--fs-sm);
  font-weight: 500;
}
.btn-ghost:hover:not(:disabled) { background: rgba(255,255,255,.12); }

/* Secondary button for LIGHT backgrounds (2026-09-02).
   .btn-ghost is white text on transparent with a white border — correct on
   the dark topbar, and invisible (contrast 1.00, measured) anywhere on a
   card. That has now bitten three times: the dashboard link to budget
   comparison, and then the funding card's Modifier/Supprimer, which shipped
   fully built and literally unreadable. Patching instances did not close the
   class, so this is the reusable on-light variant to reach for instead.
   Do NOT "fix" .btn-ghost itself — it is right where it is used. */
.btn-quiet {
  background: #fff;
  color: var(--clr-primary);
  border: 1px solid var(--clr-border);
}
.btn-quiet:hover:not(:disabled) { background: #f1f5f9; }
/* Destructive row action — same weight, unmistakably different intent. */
.btn-quiet.danger { color: var(--clr-danger); }

.btn-full { width: 100%; }

/* Fixed-width action button — used for row actions (e.g. dashboard) where
   labels vary in length (Modifier/Edit vs Voir/View) but buttons must align. */
.btn-action {
  width: 5.5rem;
  padding: .25rem .5rem;
  font-size: var(--fs-sm);
}

/* Uniform-width nav button — admin console top nav, where labels vary a lot
   in length across FR/EN ("Vue d'ensemble" vs "Gérer les organisations").
   min-width (not width) so it never wraps/clips if a translation runs long.
   Padding/font-size are set explicitly (not just min-width) because this
   class is applied on top of both .btn-primary and .btn-ghost, which have
   different base padding/font-size — without overriding them here, the
   .btn-primary instance renders visibly larger than the .btn-ghost ones
   even though both satisfy the same min-width floor. */
.btn-nav {
  min-width: 11rem;
  white-space: nowrap;
  padding: .4rem .8rem;
  font-size: var(--fs-sm);
  font-weight: 500;
}

/* ── Form elements ───────────────────────────────── */
.form-group {
  display: flex;
  flex-direction: column;
  gap: .35rem;
  margin-bottom: 1rem;
}
label {
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--clr-text);
}
/* Every text-entry control shares one look. number/date/tel/search were
   MISSING from this list (punch list #9/#10): they fell back to the browser's
   default control font and padding, which is why numeric/currency fields
   (14 of them — budget, GESI counts, population categories) rendered cramped
   and in the system font next to their text-input siblings.
   `font: inherit`-style family on select matters too: Safari/Chrome render
   native selects in the system UI font unless the family is set explicitly. */
input[type="email"],
input[type="password"],
input[type="text"],
input[type="number"],
input[type="date"],
input[type="tel"],
input[type="search"],
select,
textarea {
  padding: .6rem .8rem;
  border: 1px solid var(--clr-border);
  border-radius: var(--radius);
  font-size: var(--fs-base);
  font-family: var(--font);
  color: var(--clr-text);
  background: #fff;
  transition: border-color .15s, box-shadow .15s;
}

/* Numeric/currency fields: comfortable width so thousands separators and
   USD amounts aren't clipped, and right-aligned tabular figures so columns
   of money line up. min-width (not width) keeps them fluid inside the
   responsive grids they sit in. */
input[type="number"] {
  min-width: 9rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* Inside the narrow two-column grids on phone widths, let them shrink
   rather than overflow the card. */
@media (max-width: 500px) {
  input[type="number"] { min-width: 0; width: 100%; }
}
/* Required-field validation feedback — shared by web and PWA forms.
   Was previously only defined page-locally in submission-new.html, so the
   PWA form's error messages rendered with no styling at all (invisible-
   looking plain text) and no field ever got the red-border highlight. */
/* Definition tooltip marker (2026-08-24). One shared convention for every field
   whose recording convention is not self-evident: a ⓘ after the label text, with
   the definition on the label's `title`. Used by the beneficiary sex fields and by
   the population-category block, which previously carried the marker on the web
   form and NO visible marker at all on the PWA. */
.field-info {
  /* A real <button> since 2026-08-27 (tap-to-reveal): `title` alone is
     hover-only and unreachable on touch, which made every definition
     unopenable on the PWA — the one platform used in the field. Styled to
     look like the inline marker it replaced. */
  cursor: pointer;
  color: var(--clr-text-muted);
  font-weight: 400;
  background: none;
  border: 0;
  padding: 0 .15rem;
  font: inherit;
  line-height: 1;
  vertical-align: baseline;
  /* Tap target enlarged with padding rather than a fixed min-width: the
     marker sits INLINE inside a label, so a hard 44px box would visibly
     distort both forms' layouts. Negative margins keep the glyph optically
     in place while the hit area grows around it. Larger than the bare glyph,
     honestly short of the 44px guideline — the inline position is the
     constraint. */
  padding: .4rem .55rem;
  margin: -.4rem -.2rem;
}
.field-info:focus-visible { outline: 2px solid var(--clr-primary); outline-offset: 2px; }

/* Inline definition panel revealed by the marker. Sibling of the label, never
   a child — i18n.applyToDOM() rewrites textContent on bare [data-i18n]
   elements and would destroy a nested panel on the next language toggle. */
.field-def {
  margin: .25rem 0 .4rem;
  padding: .5rem .6rem;
  border-left: 3px solid var(--clr-primary);
  background: var(--clr-bg-alt, #f6f7f9);
  color: var(--clr-text);
  font-size: var(--fs-xs);
  font-weight: 400;
  line-height: 1.45;
  border-radius: 0 4px 4px 0;
}

.field-error { border-color: var(--clr-danger) !important; }
.field-error-msg { color: var(--clr-danger); font-size: var(--fs-sm); margin-top: .2rem; }

/* Checkbox rows (2026-08-21). Measured cause of "the PSEA block renders
   smaller than its sibling sections": it was NOT the type scale — the PSEA
   label computes to --fs-md (14.4px) exactly like every other label, and the
   21 Aug sweep did not miss it. The row was 17px tall against 64.8px for every
   sibling form-group, because a bare checkbox has no border, no padding and the
   UA's own 13.3px control font, while each sibling is a 42px bordered, padded
   16px control. This gives the row the same control weight, following the
   existing .priority-item / .output-label pattern. */
.checkbox-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .6rem .75rem;
  border: 1px solid var(--clr-border);
  border-radius: var(--radius);
  background: var(--clr-surface);
  cursor: pointer;
  font-size: var(--fs-md);
}
.checkbox-row input[type=checkbox] {
  width: 1.15rem;
  height: 1.15rem;
  accent-color: var(--clr-primary);
  flex-shrink: 0;
}
/* Validation marks the checkbox itself (showFieldError targets 'g-psea'), but a
   border on a checkbox is invisible — turn the whole row instead. Progressive:
   without :has() support the row simply stays neutral, exactly as it did before. */
.checkbox-row:has(input.field-error) { border-color: var(--clr-danger); }

input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--clr-primary);
  box-shadow: 0 0 0 3px rgba(29,111,164,.18);
}

/* ── Alert / message boxes ───────────────────────── */
.alert {
  border-radius: var(--radius);
  padding: .75rem 1rem;
  font-size: var(--fs-md);
  display: none;
}
.alert.visible { display: block; }
.alert-error   { background: #fee2e2; color: #991b1b; border: 1px solid #fca5a5; }
.alert-info    { background: #dbeafe; color: #1e40af; border: 1px solid #93c5fd; }
.alert-success { background: #dcfce7; color: #166534; border: 1px solid #86efac; }
/* Amber, matching --clr-accent's existing "caution, not error" convention
   (see .demo-banner) — used for the duplicate-project soft warning, which
   must never block the user, only flag something worth a second look. */
.alert-warning { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }

/* ── Login page layout ───────────────────────────── */
.login-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}
.login-box {
  width: 100%;
  max-width: 400px;
}
.login-header {
  text-align: center;
  margin-bottom: 1.75rem;
}
.login-header h1 {
  color: var(--clr-primary);
  margin-bottom: .35rem;
}
.login-header p {
  font-size: var(--fs-md);
  color: var(--clr-text-muted);
}
.login-note {
  text-align: center;
  margin-top: 1rem;
  font-size: var(--fs-sm);
  color: var(--clr-text-muted);
}

/* ── Page main area ──────────────────────────────── */
main {
  padding: 1.5rem 1rem;
}

/* ── Dashboard table ─────────────────────────────── */
.table-wrap { overflow-x: auto; }
table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-md);
}
th {
  background: #f1f5f9;
  text-align: left;
  padding: .6rem .75rem;
  font-weight: 600;
  border-bottom: 2px solid var(--clr-border);
}
td {
  padding: .6rem .75rem;
  border-bottom: 1px solid var(--clr-border);
  vertical-align: middle;
}
tr:last-child td { border-bottom: none; }
tr:hover td { background: #f8fafc; }

/* ── Status badges ───────────────────────────────── */
.badge {
  display: inline-block;
  padding: .2rem .55rem;
  border-radius: 999px;
  font-size: var(--fs-xs);
  font-weight: 600;
}
.badge-draft     { background: #fef9c3; color: #713f12; }
.badge-submitted { background: #dcfce7; color: #166534; }

/* ── Utility ─────────────────────────────────────── */
.mt-1 { margin-top: .5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.mb-1 { margin-bottom: .5rem; }
.mb-2 { margin-bottom: 1rem; }
.flex  { display: flex; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-1 { gap: .5rem; }
.gap-2 { gap: 1rem; }
.text-muted { color: var(--clr-text-muted); font-size: var(--fs-md); }
.text-sm    { font-size: var(--fs-md); }

/* ── Responsive ──────────────────────────────────── */
@media (max-width: 480px) {
  h1 { font-size: 1.25rem; }
  .card { padding: 1rem; }
}

/* Gender marker questionnaire (migration 042). The evidence hint is INLINE
   helper text under each question — visible without any interaction, since a
   title tooltip never fires on touch. The legend is floated so a two-line
   question sits inside the box rather than on its border. */
.gm-heading { font-size: var(--fs-md); font-weight: 600; color: var(--clr-text); }
.gm-question {
  border: 1px solid var(--clr-border);
  border-radius: var(--radius);
  padding: .6rem .75rem .65rem;
  margin: 0 0 .6rem;
  min-width: 0;
}
.gm-q-text {
  float: left; width: 100%;
  padding: 0; margin: 0 0 .25rem;
  font-size: var(--fs-md); font-weight: 600; line-height: 1.35;
  color: var(--clr-text);
}
.gm-q-hint {
  clear: both;
  margin: 0 0 .5rem;
  font-size: var(--fs-sm); line-height: 1.4;
  color: var(--clr-text-muted);
}
.gm-q-options { display: flex; flex-wrap: wrap; gap: .6rem; }
.gm-q-opt {
  display: inline-flex; align-items: center; gap: .45rem;
  min-height: 40px; padding: .35rem .9rem;
  border: 1px solid var(--clr-border); border-radius: var(--radius);
  font-weight: 500; cursor: pointer;
}
.gm-q-opt input { width: 1.1rem; height: 1.1rem; margin: 0; accent-color: var(--clr-primary); }
.gm-q-opt:has(input:checked) { border-color: var(--clr-primary); background: var(--clr-bg-alt); }
#g-marker-questions.field-error .gm-question:not(:has(input:checked)) { border-color: var(--clr-danger); }
.gm-result {
  margin-top: .2rem; padding: .55rem .75rem;
  border-radius: var(--radius); background: var(--clr-bg-alt);
  font-size: var(--fs-md);
}
.gm-result:empty { display: none; }
.gm-result.gm-result-ok { border-left: 4px solid var(--clr-success); font-weight: 600; }
.gm-result.gm-result-incomplete { border-left: 4px solid var(--clr-accent); }
.gm-answer-block { margin-top: .75rem; }
.gm-answer-list { margin: .3rem 0 0 1.2rem; padding: 0; font-size: var(--fs-sm); line-height: 1.5; }
