/*
 * tokens.css — the visual identity of PicsZako.
 *
 * ---------------------------------------------------------------------------
 * INTENT
 *
 * Who is this human? A guest at a graduation in Nairobi or a wedding in Nakuru,
 * on a mid-range Android, on 4G that comes and goes. And a photographer between
 * jobs, checking whether the morning's shoot has sold.
 *
 * What must they do? Find themselves, and take their photo home. Get paid.
 *
 * What should it feel like? Like a current, well-made app — light, open,
 * confident — benchmarked against Kamero.ai, the category leader. A guest
 * decides whether to hand over M-Pesa money in the first two seconds of looking
 * at this page, and looking current is a large part of what earns that.
 *
 * ---------------------------------------------------------------------------
 * THE PALETTE, AND WHAT EACH COLOUR IS ALLOWED TO MEAN
 *
 * Four hues, four meanings, no overlap. Everything else is neutral.
 *
 *   blue   ACTION      — the thing to press. Buttons, links, active tabs.
 *   teal   RECOGNITION — face matching. "We found you", the live index,
 *                        anything the AI is doing. This is the product's actual
 *                        magic, so it gets a colour nothing else may use.
 *   green  MONEY       — M-Pesa. The pay button, a withdrawable balance, a
 *                        payment that landed. Never a generic "success".
 *   amber  ATTENTION   — needs looking at, not broken.
 *   red    LOSS        — deleting, failing, refusing.
 *
 * Blue and teal are close in temperature and far apart in job: one is what YOU
 * do, the other is what the SYSTEM does. Keeping them separate is why
 * "Finding faces — 23 of 30" never reads as something to press.
 *
 * ---------------------------------------------------------------------------
 * TWO ENGINEERING DECISIONS
 *
 * **Hex, not oklch.** The ramps were reasoned in oklch but ship as hex. oklch
 * needs Chrome 111+; a mid-range Android on an older WebView would get an
 * invalid custom property and an unstyled page. In this market that is not
 * hypothetical.
 *
 * **System fonts.** A display face would add character and would also be a
 * render-blocking download on the revenue path, against a first-paint budget
 * that took real work to earn (984ms → 604ms). Character comes from scale,
 * weight, tracking and tabular figures, which cost nothing.
 *
 * Contrast figures below are against white and were measured, not guessed.
 *
 * Load order: tokens.css → ux-baseline.css → page styles.
 */

:root {
  /* Native controls, scrollbars and the canvas behind the page follow the
     system appearance. Without this, checkboxes and scrollbars stay light and
     the dark palette below only half-lands. */
  color-scheme: light dark;

  /* ---- Neutrals -------------------------------------------------------- */
  /* Cool, with a trace of blue so they sit under the brand rather than
     fighting it. */
  --pz-ink:          #0F1115;  /* headings.            18.4:1               */
  --pz-ink-muted:    #4B5563;  /* body, secondary.      7.6:1               */
  --pz-ink-subtle:   #6B7280;  /* captions, metadata.   4.9:1               */
  --pz-ink-faint:    #9CA3AF;  /* disabled, decoration. 2.6:1 — never text  */

  --pz-surface:      #FFFFFF;  /* cards, sheets, the raised plane           */
  --pz-canvas:       #F5F7FA;  /* the page behind everything                */
  --pz-inset:        #EDF0F5;  /* wells, tracks, empty frames               */

  --pz-edge:         rgba(15, 17, 21, .10);
  --pz-edge-subtle:  rgba(15, 17, 21, .06);
  --pz-edge-strong:  rgba(15, 17, 21, .18);

  /* The inverted plane — toasts, the offline banner, the update bar. These
     deliberately oppose the page. Named as a pair because they must flip
     together: painting a background with --pz-ink and hard-coding #fff on it
     reads as near-white on near-white the moment --pz-ink is a dark-mode
     value. */
  --pz-inverse:      #0F1115;
  --pz-inverse-ink:  #FFFFFF;

  /* ---- Action ---------------------------------------------------------- */
  --pz-brand:        #1B6EF3;  /* white on it: 4.6:1                        */
  --pz-brand-strong: #1553CC;  /* hover / pressed                           */
  --pz-brand-ink:    #1550BE;  /* brand as TEXT on white: 5.6:1             */
  --pz-brand-soft:   #EAF1FE;
  --pz-brand-edge:   rgba(27, 110, 243, .34);

  /* ---- Recognition (the face matching) --------------------------------- */
  --pz-ai:           #0E9A8D;
  --pz-ai-ink:       #0B7C71;  /* as text on white: 4.6:1                   */
  --pz-ai-soft:      #E6F7F5;
  --pz-ai-edge:      rgba(14, 154, 141, .45);

  /* ---- Money ----------------------------------------------------------- */
  --pz-money:        #15803D;  /* white on it: 4.7:1                        */
  --pz-money-strong: #116632;
  --pz-money-soft:   #EAF7EE;

  /* ---- Attention ------------------------------------------------------- */
  --pz-alert:        #F59E0B;
  --pz-alert-ink:    #B45309;  /* as text on white: 4.8:1                   */
  --pz-alert-soft:   #FFF8EB;

  /* ---- Loss ------------------------------------------------------------ */
  --pz-loss:         #DC2626;  /* white on it: 4.5:1                        */
  --pz-loss-soft:    #FEF2F2;

  /* ---- Borrowed brand colours ------------------------------------------ */
  /* WhatsApp's own green. It is not one of the five meanings above and must
     never be used as one — it belongs to a channel, not to money or success.
     It is here rather than inline because an external brand colour is exactly
     the kind of value that gets copied to four places and then drifts. */
  --pz-whatsapp:        #128C7E;
  --pz-whatsapp-strong: #0F7367;

  /* ---- The hero -------------------------------------------------------- */
  /* The guest gallery opens on the event's own photographs behind a scrim, so
     these are the scrim rather than a decorative gradient. Deep and cool, so
     white type holds over any brightness of photo underneath. */
  /* .55 was not enough scrim. White at 88% over it, on the brightest cover
     photo a photographer might upload, measured 3.8:1 — under the 4.5:1 the
     HIG requires for text up to 17pt. At .68 the same text measures 5.8:1,
     and the cover photo is still clearly a photograph. */
  --pz-scrim-1:      rgba(8, 12, 22, .68);
  --pz-scrim-2:      rgba(8, 12, 22, .88);
  --pz-scrim-flat-1: #16203A;   /* when the event has no cover photo yet    */
  --pz-scrim-flat-2: #0B1020;

  /* ---- Compatibility aliases ------------------------------------------- */
  /* Surfaces were built against these names. `--pz-primary` in particular is
     overridden at runtime with the photographer's own brand colour, so it has
     to keep working. */
  --pz-primary:        var(--pz-brand);
  --pz-primary-strong: var(--pz-brand-strong);
  --pz-primary-soft:   var(--pz-brand-soft);
  --pz-accent:         var(--pz-ai);

  --pz-paper:      var(--pz-surface);
  --pz-mat:        var(--pz-canvas);
  --pz-mat-deep:   var(--pz-inset);
  --pz-surface-2:  var(--pz-canvas);
  --pz-surface-3:  var(--pz-inset);
  --pz-border:     var(--pz-edge);

  --pz-amber:      var(--pz-alert);
  --pz-amber-ink:  var(--pz-alert-ink);
  --pz-amber-soft: var(--pz-alert-soft);
  --pz-amber-edge: var(--pz-ai-edge);   /* the "found" edge is teal now     */

  --pz-success:    var(--pz-money);
  --pz-success-bg: var(--pz-money-soft);
  --pz-warning:    var(--pz-alert-ink);
  --pz-warning-bg: var(--pz-alert-soft);
  --pz-danger:     var(--pz-loss);
  --pz-danger-bg:  var(--pz-loss-soft);
  --pz-info:       var(--pz-brand-ink);
  --pz-info-bg:    var(--pz-brand-soft);

  /* ---- Type ------------------------------------------------------------ */
  --pz-font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
             "Helvetica Neue", Arial, sans-serif;
  --pz-font-num: ui-monospace, "SF Mono", "Cascadia Mono", "Segoe UI Mono",
                 "Roboto Mono", monospace;

  /* Sizes are rem, not px, so they follow the reader's own text-size setting.
     Fixed px ignores it entirely; Apple's HIG requires text to enlarge up to
     200% (Accessibility > Vision), and rem is how the web honours that. The
     steps are Apple's iOS text styles at the Large (default) Dynamic Type
     size, so the hierarchy is one somebody already proved legible:

       Subhead 15  Body 17  Title 3 20  Title 2 22  Title 1 28  Large Title 34

     14px stays the floor. Apple's iOS minimum is 11pt, but that is a native
     label read at arm's length, not a phone browser on 4G in daylight. */
  --pz-text-xs:   0.875rem;   /* 14px — the floor. Nothing smaller carries meaning */
  --pz-text-sm:   0.9375rem;  /* 15px — HIG Subhead                          */
  --pz-text-md:   1.0625rem;  /* 17px — HIG Body. Was 16px.                  */
  --pz-text-base: var(--pz-text-md);
  --pz-text-lg:   1.25rem;    /* 20px — HIG Title 3                          */
  --pz-text-xl:   1.375rem;   /* 22px — HIG Title 2                          */
  --pz-text-2xl:  clamp(1.75rem, 4vw, 2.125rem);   /* 28 - 34: Title 1 - Large Title */
  --pz-text-3xl:  clamp(2.125rem, 6vw, 3.25rem);   /* 34 - 52                */

  --pz-leading:       1.5;
  /* HIG leading, as a ratio of its own size: Large Title is 34/41 = 1.21,
     Body is 17/22 = 1.29. The old 1.12 was tighter than Apple ships and
     collided descenders on a two-line event name. */
  --pz-leading-tight: 1.21;
  --pz-leading-snug:  1.29;

  --pz-track-tight: -0.022em;
  --pz-track-normal: 0;
  --pz-track-wide:  0.05em;

  /* ---- Space (4px base) ------------------------------------------------ */
  --pz-1: 4px;   --pz-2: 8px;   --pz-3: 12px;  --pz-4: 16px;
  --pz-5: 20px;  --pz-6: 24px;  --pz-7: 28px;  --pz-8: 32px;
  --pz-10: 40px; --pz-12: 48px; --pz-16: 64px;

  /* ---- Shape ----------------------------------------------------------- */
  /* Rounded. It is the cheapest single signal that something was made this
     decade, and it is what the category looks like. */
  --pz-radius-sm:   8px;
  --pz-radius:     12px;
  --pz-radius-lg:  18px;
  --pz-radius-xl:  24px;
  --pz-radius-pill: 999px;

  /* ---- Depth ----------------------------------------------------------- */
  /* Soft shadows, one family, three steps. Cards lift a little, floating
     things lift a lot, nothing has a hard edge. */
  --pz-shadow-sm: 0 1px 2px rgba(15, 17, 21, .06),
                  0 1px 3px rgba(15, 17, 21, .04);
  --pz-shadow:    0 4px 12px rgba(15, 17, 21, .07),
                  0 2px 4px rgba(15, 17, 21, .04);
  --pz-shadow-lg: 0 16px 40px rgba(15, 17, 21, .14),
                  0 4px 10px rgba(15, 17, 21, .06);
  /* Buttons carry a tint of their own colour, which is what makes a CTA sit
     on the page rather than look printed onto it. */
  --pz-shadow-brand: 0 6px 18px rgba(27, 110, 243, .28);
  --pz-shadow-money: 0 6px 18px rgba(21, 128, 61, .26);

  /* ---- Motion ---------------------------------------------------------- */
  --pz-motion-fast: 120ms;
  --pz-motion:      180ms;
  --pz-motion-slow: 260ms;
  --pz-ease:        cubic-bezier(.4, 0, .2, 1);
  --pz-ease-out:    cubic-bezier(0, 0, .2, 1);
  --pz-ease-in:     cubic-bezier(.4, 0, 1, 1);
  --pz-ease-spring: cubic-bezier(.34, 1.4, .64, 1);

  /* ---- Layout ---------------------------------------------------------- */
  --pz-tap: 44px;
  --pz-measure: 66ch;
  --pz-container: 1140px;

  /* ---- Stacking -------------------------------------------------------- */
  --pz-z-sticky: 10;
  --pz-z-dropdown: 100;
  --pz-z-modal: 1000;
  --pz-z-toast: 1100;
  --pz-z-banner: 1200;
}

/* ===========================================================================
   Shared primitives, so surfaces stop re-inventing buttons.
   =========================================================================== */

.pz-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--pz-2);
  min-height: var(--pz-tap);
  padding: 0 var(--pz-5);
  border-radius: var(--pz-radius);
  border: 1px solid transparent;
  font: 600 var(--pz-text-md) / 1.2 var(--pz-font);
  cursor: pointer;
  text-decoration: none;
  transition: background var(--pz-motion-fast) var(--pz-ease),
              border-color var(--pz-motion-fast) var(--pz-ease),
              box-shadow var(--pz-motion) var(--pz-ease),
              transform var(--pz-motion-fast) var(--pz-ease-spring);
}
/* Press, not sink. A 1px nudge down reads as a dead key; a slight scale reads
   as a response. */
.pz-btn:active { transform: scale(.975); }

.pz-btn--primary {
  background: var(--pz-brand);
  color: #fff;
  box-shadow: var(--pz-shadow-brand);
}
.pz-btn--primary:hover { background: var(--pz-brand-strong); }

.pz-btn--secondary {
  background: var(--pz-surface);
  color: var(--pz-ink);
  border-color: var(--pz-edge-strong);
  box-shadow: var(--pz-shadow-sm);
}
.pz-btn--secondary:hover { background: var(--pz-canvas); }

.pz-btn--ghost {
  background: transparent;
  color: var(--pz-ink-muted);
  border-color: transparent;
  padding: 0 var(--pz-3);
}
.pz-btn--ghost:hover { color: var(--pz-ink); background: var(--pz-inset); }

/* Paying is the one action with its own colour: in Kenya, M-Pesa green IS the
   instruction — people recognise it before they read the label. */
.pz-btn--pay {
  background: var(--pz-money);
  color: #fff;
  box-shadow: var(--pz-shadow-money);
}
.pz-btn--pay:hover { background: var(--pz-money-strong); }

.pz-btn:focus-visible {
  outline: 2px solid var(--pz-brand);
  outline-offset: 2px;
}
.pz-btn:disabled { opacity: .5; box-shadow: none; cursor: not-allowed; }

.pz-card {
  background: var(--pz-surface);
  border: 1px solid var(--pz-edge-subtle);
  border-radius: var(--pz-radius-lg);
  box-shadow: var(--pz-shadow-sm);
  padding: var(--pz-6);
}

.pz-input {
  width: 100%;
  min-height: var(--pz-tap);
  padding: var(--pz-3) var(--pz-4);
  border: 1px solid var(--pz-edge-strong);
  border-radius: var(--pz-radius);
  font: 400 var(--pz-text-md) / var(--pz-leading) var(--pz-font);
  color: var(--pz-ink);
  background: var(--pz-surface);
  transition: border-color var(--pz-motion-fast) var(--pz-ease),
              box-shadow var(--pz-motion-fast) var(--pz-ease);
}
.pz-input::placeholder { color: var(--pz-ink-subtle); }
.pz-input:focus {
  border-color: var(--pz-brand);
  box-shadow: 0 0 0 3px var(--pz-brand-soft);
  outline: none;
}

.pz-field__prefix,
.pz-field__suffix { flex: none; white-space: nowrap; }

/* ---- The signature: recognition ------------------------------------------

   Face matching is the product; everything else is a shop. So the moment a
   photo is one you are in, it says so ON the photograph — a teal ring and a
   small tag, in the one colour nothing else in the interface may use.       */

.pz-found { box-shadow: 0 0 0 2px var(--pz-ai), var(--pz-shadow-sm); }

.pz-found__tag {
  position: absolute; top: var(--pz-2); left: var(--pz-2); z-index: 2;
  display: inline-flex; align-items: center; gap: 4px;
  height: 24px; padding: 0 8px;
  border-radius: var(--pz-radius-pill);
  background: var(--pz-ai); color: #fff;
  font-size: 12px; font-weight: 700;
  box-shadow: 0 2px 6px rgba(14, 154, 141, .4);
}

/* ---- Money ---------------------------------------------------------------
   Tabular figures in the sans face, so a cart total holds its column as it
   changes without reading like a log line. */
.pz-money {
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.005em;
}

/* Dense figures in the studio — ledger rows, analytics columns. */
.pz-data {
  font-family: var(--pz-font-num);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

.pz-muted   { color: var(--pz-ink-muted); }
.pz-subtle  { color: var(--pz-ink-subtle); font-size: var(--pz-text-xs); }
.pz-measure { max-width: var(--pz-measure); }

.pz-label {
  font-size: var(--pz-text-xs);
  font-weight: 600;
  letter-spacing: var(--pz-track-wide);
  text-transform: uppercase;
  color: var(--pz-ink-subtle);
}


/* ===========================================================================
   DARK APPEARANCE
   ---------------------------------------------------------------------------
   A guest opens this at an evening wedding, in a dark reception hall, on a
   phone that has been in dark mode since sunset. Until now they got a full
   white page. Apple's HIG treats both appearances as required, not optional:
   "Ensure the app looks good in both appearances."

   Driven by prefers-color-scheme only. The HIG is explicit that an app should
   not ship its own appearance switch — "Don't offer an app-specific
   appearance setting; it conflicts with the systemwide preference" — so there
   is deliberately no toggle and no persisted choice.

   This is not an inversion. Per the HIG the dark palette is dimmer
   backgrounds with brighter foregrounds, and iOS carries two dark background
   levels — base (dimmer, recedes) and elevated (brighter, advances) — so
   stacked dark surfaces still read as stacked. Here:

     --pz-canvas   base      the page
     --pz-surface  elevated  cards, sheets, the cart bar
     --pz-inset    wells and tracks, cut back into the base

   Contrast was measured against --pz-surface (#15181F), not guessed. The HIG
   floor is 4.5:1 and it asks custom colours to target 7:1, which the body and
   secondary text here clear.
   =========================================================================== */

@media (prefers-color-scheme: dark) {
  :root {
    /* ---- Neutrals ------------------------------------------------------ */
    --pz-ink:          #F2F4F8;  /* headings.            15.8:1             */
    --pz-ink-muted:    #B9C0CC;  /* body, secondary.      9.5:1             */
    --pz-ink-subtle:   #8C95A5;  /* captions, metadata.   5.8:1             */
    --pz-ink-faint:    #5A6070;  /* disabled, decoration. 2.3:1 — never text */

    --pz-surface:      #15181F;  /* elevated: cards, sheets, the raised plane */
    --pz-canvas:       #0B0D12;  /* base: the page behind everything        */
    --pz-inset:        #1E222B;  /* wells, tracks, empty frames             */

    /* On dark the inverted plane cannot invert to white — it lifts instead,
       sitting above --pz-surface so a toast still reads as floating. */
    --pz-inverse:      #2A303C;
    --pz-inverse-ink:  #F2F4F8;  /* 12.4:1                                  */

    /* Edges read as light on dark, not as darker dark. */
    --pz-edge:         rgba(255, 255, 255, .12);
    --pz-edge-subtle:  rgba(255, 255, 255, .07);
    --pz-edge-strong:  rgba(255, 255, 255, .22);

    /* ---- Action -------------------------------------------------------- */
    /* The button FILL is its own surface, so --pz-brand keeps carrying white
       at 4.6:1 and does not change. Only brand-as-text needs to brighten,
       because on #15181F the light-mode ink was 1.9:1 — invisible. */
    --pz-brand-ink:    #7FB0FF;  /* brand as TEXT on dark: 8.0:1            */
    --pz-brand-soft:   #12233F;  /* tinted dark, not a pale wash            */
    --pz-brand-edge:   rgba(127, 176, 255, .38);

    /* ---- Recognition (the face matching) -------------------------------
       --pz-ai is a FILL (the ring, the "You're in this" tag) and stays put:
       white on #0E9A8D is 3.5:1, and lifting it to #17B5A6 would have dropped
       that to 2.6:1 — under the 3:1 the HIG allows bold text. A saturated teal
       already reads as a ring against a #15181F tile. */
    --pz-ai-ink:       #5FD9CC;  /* teal as TEXT on dark: 10.3:1            */
    --pz-ai-soft:      #06312D;
    --pz-ai-edge:      rgba(95, 217, 204, .40);

    /* ---- Money ---------------------------------------------------------
       Same reasoning, and one more: M-Pesa green is recognised before the
       label is read. The fill does not move. */
    --pz-money-soft:   #0B2A17;

    /* ---- Attention ----------------------------------------------------- */
    --pz-alert-ink:    #FBBF24;  /* as text on dark: 10.5:1                 */
    --pz-alert-soft:   #2E2206;

    /* ---- Loss ---------------------------------------------------------- */
    --pz-loss-soft:    #33161A;

    /* ---- Depth ---------------------------------------------------------
       A drop shadow barely reads on a dark ground; on dark, elevation is
       carried by --pz-surface sitting brighter than --pz-canvas. The shadows
       stay only to seat floating things, and go deeper so they still do. */
    --pz-shadow-sm: 0 1px 2px rgba(0, 0, 0, .40);
    --pz-shadow:    0 4px 12px rgba(0, 0, 0, .46),
                    0 2px 4px rgba(0, 0, 0, .34);
    --pz-shadow-lg: 0 16px 40px rgba(0, 0, 0, .58),
                    0 4px 10px rgba(0, 0, 0, .40);
    --pz-shadow-brand: 0 6px 18px rgba(27, 110, 243, .40);
    --pz-shadow-money: 0 6px 18px rgba(21, 128, 61, .38);
  }

  /* Loss as text needs the same lift the other inks got. It has no dedicated
     ink token, so the one place it is used as type is corrected here. */
  .pz-field__error { color: #F87171; }        /* on dark: 6.4:1             */

  /* A white-background photo or logo glows on a dark page. The HIG asks for
     these to be slightly darkened in a Dark Mode context. Applied only to the
     photographer's uploaded logo, never to the photographs themselves — those
     are the product. */
  .g-hero__logo { filter: brightness(.92); }
}
