/*
 * Sawa Design Tokens
 * © 2026 Chadi Ikhoder — Original work built from scratch
 * Single source of truth for all colors, typography, spacing, and effects.
 * Import this before any page-specific stylesheet.
 * Licensed under the PolyForm Noncommercial License 1.0.0. See LICENSE.
 */

/* ─── Fonts ──────────────────────────────────────
 * Self-hosted instead of linked from fonts.googleapis.com.
 *
 * The Google stylesheet was a hard dependency on the internet: this project is
 * handed over as a folder and run on someone else's machine, and with no
 * connection every page dropped silently to Arial — the layout survived but
 * the whole typography changed. Shipping the files inside the folder makes the
 * design travel with the zip.
 *
 * Poppins is the `latin` subset as 6 static weights; Cairo is a variable font
 * (weight axis 200–1000) subset to `arabic`, used only by .sawa_arab for the
 * Arabic wordmark. No unicode-range is declared: the files are local, so there
 * is nothing to save by deferring a download, and per-glyph fallback already
 * handles any character these subsets lack.
 *
 * Paths are relative to this file, so they resolve wherever the folder is
 * unzipped — the same reason BASE_PATH is detected at runtime.
 */
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(../fonts/Poppins-Regular.woff2) format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(../fonts/Poppins-Medium.woff2) format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(../fonts/Poppins-SemiBold.woff2) format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(../fonts/Poppins-Bold.woff2) format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(../fonts/Poppins-ExtraBold.woff2) format('woff2');
}
@font-face {
  font-family: 'Poppins';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url(../fonts/Poppins-Black.woff2) format('woff2');
}
@font-face {
  font-family: 'Cairo';
  font-style: normal;
  font-weight: 200 1000;
  font-display: swap;
  src: url(../fonts/Cairo-Variable.woff2) format('woff2');
}

html {
  font-size: 62.5%;
  color-scheme: light;
}

:root {

  /* ─── Colors ──────────────────────────────────── */
  --color-primary:        #2563eb;
  --color-primary-dark:   #1d4ed8;
  --color-primary-light:  #eff6ff;
  --color-primary-glow:   rgba(37, 99, 235, 0.4);

  --color-success:        #16a34a;
  --color-success-dark:   #15803d;
  --color-success-light:  #dcfce7;
  --color-success-border: #86efac;

  --color-danger:         #ef4444;
  --color-danger-light:   #fef2f2;

  --color-warning:        #f59e0b;
  --color-warning-light:  #fffbeb;

  --color-purple:         #7c3aed;
  --color-purple-light:   #f5f3ff;

  --color-text:           #1f2937;
  --color-text-muted:     #4b5563;
  --color-text-light:     #6b7280;
  --color-text-navy:      #1A3D5D;

  --color-surface:        #ffffff;
  /* Text/icons sitting ON a saturated accent fill (primary button, badge,
     avatar). Stays white in BOTH themes — unlike --color-surface, which
     inverts. Do not substitute one for the other. */
  --color-on-accent:      #ffffff;
  --color-bg:             #f8fafc;
  --color-bg-deep:        #f1f5f9;
  --color-border:         #e5e7eb;
  /* Border for interactive controls (inputs, selects, checkboxes). Must stay
     >=3:1 against the field background per WCAG 1.4.11 — this is why it is
     darker than --color-border, which is decorative only (cards, dividers).
     Neutral grey, not slate: the previous #75899f (3.60:1) carried a blue cast
     that read as heavy on the login/signup fields. #868d96 is 3.35:1 — softer
     and untinted while still clearing 3:1. Do not lighten further by eye;
     slate-400 (#94a3b8) is only 2.56:1 and gray-300 is 1.47:1. */
  --color-border-control: #868d96;
  --color-dark:           #111827;
  --color-dark-soft:      #1e293b;

  /* ─── Auth brand surface ──────────────────────
     The gradient behind the login split-screen panel, the same gradient on
     small screens where that panel is hidden, and the signup brand band.
     Defined here rather than in login.css because signup.php loads only
     tokens.css + signup.css, so anything left in login.css is invisible to it.
     Login-only extras (--auth-tile-*, --auth-chip-*) stay in login.css. */
  --auth-panel:           linear-gradient(145deg, #dbeafe 0%, #bfdbfe 48%, #e0e7ff 100%);
  --auth-panel-fg:        #14294d;
  --auth-panel-soft:      rgba(20, 41, 77, 0.74);
  --auth-logo-filter:     none;

  /* ─── Typography ─────────────────────────────── */
  /* Poppins now ships with the project, so the fallbacks should never be
     reached — they are here for the case where a font file goes missing in
     transit. 'Segoe UI' and system-ui are far closer to Poppins than Arial. */
  --font:       'Poppins', 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --font-serif: Georgia, 'Times New Roman', serif;

  --text-xs:   1.2rem;   /* hints, captions    */
  --text-sm:   1.4rem;   /* secondary body     */
  --text-base: 1.6rem;   /* primary body       */
  --text-lead: 1.8rem;   /* lead paragraph     */
  --text-h4:   2.0rem;
  --text-h3:   2.4rem;
  --text-h2:   3.2rem;
  --text-h1:   4.8rem;   /* hero heading       */

  --weight-normal: 400;
  --weight-medium: 500;
  --weight-semi:   600;
  --weight-bold:   700;

  /* ─── Spacing (4 px grid) ────────────────────── */
  --sp-1:  0.4rem;
  --sp-2:  0.8rem;
  --sp-3:  1.2rem;
  --sp-4:  1.6rem;
  --sp-5:  2.0rem;
  --sp-6:  2.4rem;
  --sp-8:  3.2rem;
  --sp-10: 4.0rem;
  --sp-12: 4.8rem;
  --sp-16: 6.4rem;
  --sp-20: 8.0rem;
  --sp-24: 9.6rem;

  /* ─── Radii ──────────────────────────────────── */
  --r-sm:   6px;
  --r:      12px;
  --r-lg:   16px;
  --r-xl:   24px;
  --r-full: 9999px;

  /* ─── Shadows ────────────────────────────────── */
  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow:    0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);

  /* ─── Transitions ────────────────────────────── */
  --t:      0.2s ease;
  --t-slow: 0.4s ease;

  /* ─── Component tokens ───────────────────────── */
  --input-height:  4.8rem;
  --btn-radius:    var(--r-full);

  /* ─── Layout ─────────────────────────────────── */
  --nav-height:    6.4rem;
  --sidebar-width: 24rem;
  --container:     112rem;
}

html[data-theme="dark"] {
  color-scheme: dark;

  --color-primary:        #60a5fa;
  --color-primary-dark:   #3b82f6;
  --color-primary-light:  #172554;
  --color-primary-glow:   rgba(96, 165, 250, 0.35);

  --color-success:        #4ade80;
  --color-success-dark:   #22c55e;
  --color-success-light:  #052e16;
  --color-success-border: #166534;

  --color-danger:         #f87171;
  --color-danger-light:   #450a0a;

  --color-warning:        #fbbf24;
  --color-warning-light:  #422006;

  --color-purple:         #a78bfa;
  --color-purple-light:   #2e1065;

  --color-text:           #e5e7eb;
  --color-text-muted:     #cbd5e1;
  --color-text-light:     #94a3b8;
  --color-text-navy:      #e0f2fe;

  --color-surface:        #111827;
  /* Intentionally NOT inverted — accent fills stay saturated in dark mode,
     so their foreground must stay white. */
  --color-on-accent:      #ffffff;
  --color-bg:             #0f172a;
  --color-bg-deep:        #1e293b;
  --color-border:         #334155;
  /* See the light-mode note: #334155 is only ~1.8:1 on a dark field, so
     controls need their own lighter border to clear 3:1. */
  --color-border-control: #64748b;
  --color-dark:           #020617;
  --color-dark-soft:      #0f172a;

  /* Deep navy counterpart of the light auth gradient. */
  --auth-panel:           linear-gradient(145deg, #123760 0%, #1d4ed8 48%, #0f172a 100%);
  --auth-panel-fg:        #ffffff;
  --auth-panel-soft:      rgba(255, 255, 255, 0.78);
  --auth-logo-filter:     brightness(0) invert(1);
}

html, body {
  overflow-x: clip;
  max-width: 100%;
}

/* ── Hide scrollbar chrome everywhere ──────────────────────────────────────
   Requested globally: the page, the drawer and the campaign modal all still
   scroll by wheel, touch, keyboard and scrollIntoView — only the visible bar
   is removed. Worth knowing: a hidden scrollbar also removes the only visual
   cue that a region scrolls at all, so any container that relies on it needs
   its own affordance (a fade, a chevron, or content peeking past the edge). */
* {
  scrollbar-width: none;          /* Firefox */
  -ms-overflow-style: none;       /* legacy Edge */
}
*::-webkit-scrollbar {            /* Chrome, Safari */
  width: 0;
  height: 0;
}

html[data-theme="dark"] body {
  background-color: var(--color-bg);
  color: var(--color-text);
}

/* Controls, not cards — so this takes --color-border-control, not
   --color-border. This selector is (0,1,2) and outranks the page-level
   `.input-wrap input` (0,1,1), so whatever it sets is what the field
   actually paints; pointing it at the decorative token silently undid the
   3:1 fix and left every dark-mode field at 1.72:1. */
/* Text fields only. Button/submit inputs are <input> too, so without excluding
   them this global dark rule painted every such button #0f172a — the dark page
   background — leaving only the label visible (it out-specifies the page's own
   button colour). This fixes invisible dark-mode buttons app-wide, not just on
   signup. Checkboxes/radios are excluded too so they keep their native look. */
html[data-theme="dark"] input:not([type="submit"]):not([type="button"]):not([type="checkbox"]):not([type="radio"]),
html[data-theme="dark"] textarea,
html[data-theme="dark"] select {
  background-color: #0f172a;
  color: var(--color-text);
  border-color: var(--color-border-control);
}

html[data-theme="dark"] input::placeholder,
html[data-theme="dark"] textarea::placeholder {
  color: #64748b;
}

html[data-theme="dark"] .card,
html[data-theme="dark"] .dash-card,
html[data-theme="dark"] .login-card,
html[data-theme="dark"] .step_1,
html[data-theme="dark"] .basicinfo,
html[data-theme="dark"] .organisation_role,
html[data-theme="dark"] .role_cards,
html[data-theme="dark"] .action-card,
html[data-theme="dark"] .modal,
html[data-theme="dark"] .guest-donate-modal,
html[data-theme="dark"] #info_popover {
  background-color: var(--color-surface);
  color: var(--color-text);
  border-color: var(--color-border);
}

html[data-theme="dark"] .site-nav,
html[data-theme="dark"] .site-header,
html[data-theme="dark"] .nav-links-wrap {
  background: rgba(15, 23, 42, 0.92);
  border-color: rgba(148, 163, 184, 0.24);
}

html[data-theme="dark"] .nav-link,
html[data-theme="dark"] .site-nav-link,
html[data-theme="dark"] .site-brand-name,
html[data-theme="dark"] .nav-brand-name,
html[data-theme="dark"] h1,
html[data-theme="dark"] h2,
html[data-theme="dark"] h3,
html[data-theme="dark"] h4 {
  color: var(--color-text);
}

html[data-theme="dark"] p,
html[data-theme="dark"] small,
html[data-theme="dark"] label,
html[data-theme="dark"] .auth-sub,
html[data-theme="dark"] .auth-switch,
html[data-theme="dark"] .stat-tile-label,
html[data-theme="dark"] .camp-card-desc,
html[data-theme="dark"] .modal-sub {
  color: var(--color-text-muted);
}

/* ── Logo marks go white in dark mode ──────────────────────────────────────
   sawa_v2.svg is a two-tone blue/green mark drawn for light surfaces; on a
   dark background it reads muddy and low-contrast. brightness(0) flattens it
   to black and invert(1) turns that pure white, which is why the pair is used
   rather than a colour filter.

   Only the login panel and the signup band had this. Every container listed
   here was checked in dark mode first — a white mark on a light chip would
   erase itself, and .site-brand-mark and .admin-brand img both paint a chip
   behind the logo (they resolve to translucent blue over a dark header here,
   so they are safe). .logo_image keeps its drop-shadow by restating it: the
   filter property replaces, it does not accumulate. */
html[data-theme="dark"] .card-logo,
html[data-theme="dark"] .status-logo,
html[data-theme="dark"] .nav-logo,
html[data-theme="dark"] .sidebar-logo-circle img,
html[data-theme="dark"] .site-brand-mark img,
html[data-theme="dark"] .admin-brand img {
  filter: var(--auth-logo-filter);
}

html[data-theme="dark"] .logo_image {
  filter: var(--auth-logo-filter) drop-shadow(0 6px 16px rgba(0, 0, 0, 0.35));
}

.theme-toggle {
  width: 4rem;
  height: 4rem;
  border: 1px solid var(--color-border);
  border-radius: var(--r-full);
  background: var(--color-surface);
  color: var(--color-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex: 0 0 auto;
  box-shadow: var(--shadow-sm);
  transition: background var(--t), border-color var(--t), transform var(--t);
}

.theme-toggle:hover {
  background: var(--color-primary-light);
  transform: translateY(-1px);
}

.theme-toggle:focus-visible {
  outline: 3px solid var(--color-primary-glow);
  outline-offset: 2px;
}

.theme-toggle svg {
  width: 1.9rem;
  height: 1.9rem;
}

.theme-toggle .theme-sun { display: none; }
html[data-theme="dark"] .theme-toggle .theme-moon { display: none; }
html[data-theme="dark"] .theme-toggle .theme-sun { display: block; }

/* .theme-toggle--floating deleted — replaced by .access-control-pill in
   access.css. The shared [data-theme-toggle] sun/moon swap above still
   applies wherever the attribute is used (sidebar, settings row, etc). */

/* Skip-to-content link — visually hidden until keyboard-focused, then
   pops into view at top-left. Included in tokens.css so every page picks
   it up automatically without a per-page stylesheet edit. */
.skip-link {
  position: absolute;
  top: -100px;
  left: 8px;
  z-index: 9999;
  padding: 0.9rem 1.4rem;
  background: var(--color-primary);
  color: #ffffff;
  font-family: var(--font);
  font-size: 1.4rem;
  font-weight: 700;
  text-decoration: none;
  border-radius: 0.6rem;
  box-shadow: 0 6px 16px rgba(37, 99, 235, 0.3);
  transition: top 0.15s ease;
}
.skip-link:focus,
.skip-link:focus-visible {
  top: 8px;
  outline: 3px solid rgba(255, 255, 255, 0.6);
  outline-offset: 2px;
}

/* ── Hamburger → close toggle (shared) ─────────────────────────────────────
   Lives here rather than in a page stylesheet because both headers use it and
   both load tokens.css. js/burger.js swaps the button's SVG for these three
   bars and mirrors the panel's open state onto .is-open.

   The bars are absolutely positioned against a fixed 1.8rem box so rotating
   them cannot shift the button's size, and both states animate from the same
   origin — the middle of that box. */
.burger-toggle .burger-bars {
  position: relative;
  display: block;
  width: 1.8rem;
  height: 1.4rem;
}
.burger-toggle .burger-bars i {
  position: absolute;
  left: 0;
  width: 100%;
  height: 0.22rem;
  border-radius: 9999px;
  background: currentColor;
  transition: transform 0.24s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.16s linear;
}
.burger-toggle .burger-bars i:nth-child(1) { top: 0; }
.burger-toggle .burger-bars i:nth-child(2) { top: 50%; transform: translateY(-50%); }
.burger-toggle .burger-bars i:nth-child(3) { bottom: 0; }

/* Outer bars travel to the centre line first, then rotate onto it. The middle
   bar fades rather than moving, so nothing has to slide out of the box. */
.burger-toggle.is-open .burger-bars i:nth-child(1) {
  transform: translateY(0.59rem) rotate(45deg);
}
.burger-toggle.is-open .burger-bars i:nth-child(2) {
  opacity: 0;
  transform: translateY(-50%) scaleX(0.4);
}
.burger-toggle.is-open .burger-bars i:nth-child(3) {
  transform: translateY(-0.59rem) rotate(-45deg);
}

@media (prefers-reduced-motion: reduce) {
  .burger-toggle .burger-bars i { transition: none; }
}

/* ─── Live stat tiles ────────────────────────────
   js/live-stats.js writes new figures into [data-live-stat] in place. Without
   any signal the number simply differs from what the eye last registered, which
   reads as a rendering glitch rather than new information — so a changed tile
   flashes once in the accent colour and settles. The class is added only when
   the text actually changed, and never under reduced-motion (the script checks
   before adding it; the media query below is the belt to that braces). */
.stat-just-changed {
  animation: stat-change-flash 0.9s ease-out 1;
}

@keyframes stat-change-flash {
  0%   { color: var(--color-primary); transform: translateY(-0.2rem); }
  40%  { color: var(--color-primary); transform: translateY(0); }
  100% { color: inherit; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .stat-just-changed { animation: none; }
}
