@charset "UTF-8";
/* ============================================================================
   ALSALAM VENUES — DESIGN SYSTEM
   قصر السلام للمناسبات · السلام رويال — Bisha, Saudi Arabia

   Production stylesheet. Ships as-is. No build step, no preprocessor.
   Load once per page:  <link rel="stylesheet" href="assets/design-system.css">

   Document must declare:  <html lang="ar" dir="rtl">

   CONTENTS
     1.  Tokens — colour
     2.  Tokens — typography
     3.  Tokens — spacing, radii, borders, shadow
     4.  Tokens — motion
     5.  Tokens — layout
     6.  Reset and base
     7.  Typography classes
     8.  Numerals and bidirectional isolation
     9.  Layout primitives
     10. Surfaces — card, block, tile
     11. Buttons
     12. Form fields
     13. Choice controls — segmented, chips, checkbox
     14. Badges and status pills
     15. Progress track (tracking link)
     16. Key/value rows
     17. Calendar
     18. Tabs
     19. Bottom sheets
     20. Notices — info, warning, danger, success
     21. Skeletons and loading
     22. Empty states
     23. Signature pad
     24. Upload
     25. Image placeholders
     26. Admin shell — nav, dashboard
     27. Overflow and truncation utilities
     28. Motion — keyframes and transitions
     29. Reduced motion
     30. Desktop
     31. Print
============================================================================ */


/* ==========================================================================
   1. TOKENS — COLOUR
   Eighteen values. Nothing outside this list appears anywhere in the product.
   Every colour has one role. If a new need arises, add it here first.
   ========================================================================== */

:root {
  /* Ink and greens */
  --c-ink:            #16211D;  /* headings, primary text on light           */
  --c-green:          #1E3B32;  /* primary action, headers, borders          */
  --c-green-admin:    #15201C;  /* admin app background — one step darker    */

  /* Copper / gold — competing interest, deadlines, emphasis */
  --c-gold:           #A98545;  /* fills, bars, dots                          */
  --c-gold-light:     #E6C98D;  /* gold text ON DARK surfaces                 */
  --c-gold-chip:      #E6C98D;  /* a gold FILL carrying ink — stays gold in    */
                                /* both themes, since ink reads on it either   */
                                /* way. Split from --c-gold-light, which is a  */
                                /* TEXT colour and therefore darkens on light. */
  --c-gold-text:      #7D6231;  /* gold text on light surfaces                */
  --c-gold-text-dk:   #6D5528;  /* gold text on gold-tinted light surfaces    */

  /* Terracotta — urgency, decline, cancellation, failure */
  --c-clay:           #C8703A;  /* fills, urgent blocks                       */
  --c-clay-text:      #A4562A;  /* clay text on light surfaces                */
  --c-clay-deep:      #8F4A24;  /* decline / cancel screen headers            */
  --c-clay-light:     #F0B58A;  /* clay text ON DARK surfaces                 */

  /* Sage — confirmed, valid, complete */
  --c-sage:           #A3C4B4;

  /* Surfaces */
  --c-canvas:         #E8E2D6;  /* page background, public app                */
  --c-card:           #FBF7F0;  /* card background, public app                */
  --c-white:          #FFFFFF;  /* inner blocks, fields                       */
  --c-cream:          #F4EDE2;  /* text on dark, light buttons on dark        */
  --c-paper:          #FDFCF9;  /* signature pad only                         */

  /* Alpha derivations — never hand-write these, use the variable */
  --a-ink-04:  rgba(22,33,29,.04);
  --a-ink-06:  rgba(22,33,29,.06);
  --a-ink-07:  rgba(22,33,29,.07);
  --a-ink-08:  rgba(22,33,29,.08);
  --a-ink-12:  rgba(22,33,29,.12);
  --a-ink-18:  rgba(22,33,29,.18);
  --a-ink-35:  rgba(22,33,29,.35);
  --a-ink-45:  rgba(22,33,29,.45);
  --a-ink-55:  rgba(22,33,29,.55);
  --a-ink-60:  rgba(22,33,29,.60);
  --a-ink-72:  rgba(22,33,29,.72);
  --a-ink-78:  rgba(22,33,29,.78);

  --a-green-06: rgba(30,59,50,.06);
  --a-green-12: rgba(30,59,50,.12);
  --a-green-18: rgba(30,59,50,.18);
  --a-green-25: rgba(30,59,50,.25);

  --a-cream-05: rgba(244,237,226,.05);
  --a-cream-07: rgba(244,237,226,.07);
  --a-cream-10: rgba(244,237,226,.10);
  --a-cream-14: rgba(244,237,226,.14);
  --a-cream-18: rgba(244,237,226,.18);
  --a-cream-25: rgba(244,237,226,.25);
  --a-cream-30: rgba(244,237,226,.30);
  --a-cream-40: rgba(244,237,226,.40);
  --a-cream-50: rgba(244,237,226,.50);
  --a-cream-60: rgba(244,237,226,.60);
  --a-cream-72: rgba(244,237,226,.72);
  --a-cream-85: rgba(244,237,226,.85);

  --a-gold-12:  rgba(169,133,69,.12);
  --a-gold-16:  rgba(169,133,69,.16);
  --a-gold-22:  rgba(169,133,69,.22);
  --a-gold-35:  rgba(169,133,69,.35);
  --a-gold-45:  rgba(169,133,69,.45);
  --a-goldl-14: rgba(230,201,141,.14);
  --a-goldl-30: rgba(230,201,141,.30);

  --a-clay-14:  rgba(200,112,58,.14);
  --a-clay-16:  rgba(200,112,58,.16);
  --a-clay-20:  rgba(200,112,58,.20);
  --a-clay-35:  rgba(200,112,58,.35);
  --a-clay-45:  rgba(200,112,58,.45);

  --a-ink-25:  rgba(22,33,29,.25);

  /* Roles that invert in OPPOSITE directions between the two themes, and so
     cannot share one token. On a dark ground a surface is lighter than the
     page and a hairline is lighter too; on a light ground a surface is still
     lighter but a hairline must go darker. The fills keep the cream tokens;
     the lines move here. */
  --ad-line:   var(--a-cream-10);
  --ad-line-2: var(--a-cream-14);
  --ad-line-3: var(--a-cream-30);
  /* the well a field sits in: darker than its card on a dark ground, and
     lighter than it on a light one, so it always reads as "type here" */
  --ad-field:  rgba(11,20,17,.35);
  /* an empty track — a switch that is off, a month with no bookings. Like a
     field it is a recess, so it darkens on a light ground rather than lifting. */
  --ad-well:   var(--a-cream-10);

  /* --c-cream is text on a dark ground AND the fill of a light pill on that
     ground. The first themes, the second must not. */
  --c-cream-fill: #F4EDE2;

  /* Sage is a fill that carries ink text, and separately a text colour on
     dark. The fill works on either ground; the text does not. */
  --c-sage-text: var(--c-sage);

  /* The chart's two venue hues. A mark on a ground is the one thing that
     cannot be carried across themes by a token rename: the dark pair are pale
     so they read on a dark page, and pale marks vanish on a light one. Each
     pair was run through the palette validator against its own surface. */
  --viz-a: #E6C98D;
  --viz-b: #4E9C7F;

  --a-sage-14:  rgba(163,196,180,.14);
  --a-sage-25:  rgba(163,196,180,.25);
  --a-sage-30:  rgba(163,196,180,.30);
}


/* ==========================================================================
   2. TOKENS — TYPOGRAPHY
   Three families. Amiri for Arabic display, Noto Sans Arabic for interface,
   IBM Plex Mono for reference codes, Hijri dates, money and times.

   Minimums, enforced:
     public app body text     14px
     public app supporting    11px
     admin app supporting      9.5px   (admin only — never in the public app)
   ========================================================================== */

:root {
  --f-display: "Amiri", "Times New Roman", serif;
  --f-ui:      "Noto Sans Arabic", system-ui, -apple-system, sans-serif;
  --f-mono:    "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Display — Amiri */
  --t-display-lg: 700 30px/1.25 var(--f-display);
  --t-display:    700 22px/1.30 var(--f-display);
  --t-title:      700 19px/1.25 var(--f-display);
  --t-title-sm:   700 16px/1.30 var(--f-display);

  /* Interface — Noto Sans Arabic */
  --t-lead:       400 14px/1.80 var(--f-ui);   /* hero subheads, key prose   */
  --t-body:       400 13.5px/1.85 var(--f-ui); /* contract clauses, reasons  */
  --t-body-sm:    400 12.5px/1.80 var(--f-ui);
  --t-label:      600 13px/1.00 var(--f-ui);   /* field labels               */
  --t-value:      600 13px/1.40 var(--f-ui);   /* key/value right side       */
  --t-caption:    400 12.5px/1.40 var(--f-ui); /* key/value left side        */
  --t-hint:       400 11.5px/1.70 var(--f-ui); /* helper text under controls */
  --t-btn:        600 13.5px/1.00 var(--f-ui);
  --t-btn-sm:     600 12px/1.00 var(--f-ui);
  --t-badge:      600 11px/1.00 var(--f-ui);
  --t-section:    600 11px/1.00 var(--f-ui);   /* section eyebrow, tracked   */

  /* Admin only — below the public minimum, permitted on dark surfaces */
  --t-admin-hint: 400 11px/1.70 var(--f-ui);
  --t-admin-micro:600 10px/1.00 var(--f-mono);

  /* Mono */
  --t-ref:        600 12px/1.00 var(--f-mono);   /* QS-26-0142               */
  --t-ref-lg:     600 18px/1.00 var(--f-mono);   /* reference, hero size     */
  --t-num:        700 17px/1.00 var(--f-mono);   /* money, dashboard figures */
  --t-hijri:      400 10.5px/1.00 var(--f-mono); /* Hijri under Gregorian    */
  --t-meta:       400 9.5px/1.00 var(--f-mono);  /* list meta, admin only    */

  --track-ref:    .06em;   /* letter-spacing for reference codes */
  --track-eyebrow:.03em;
}


/* ==========================================================================
   3. TOKENS — SPACING, RADII, BORDERS, SHADOW
   Spacing is a 4px base. Only these steps are used.
   ========================================================================== */

:root {
  --s-1:  4px;
  --s-2:  6px;
  --s-3:  8px;
  --s-4:  9px;
  --s-5:  12px;
  --s-6:  14px;
  --s-7:  16px;
  --s-8:  18px;
  --s-9:  20px;
  --s-10: 22px;
  --s-11: 26px;
  --s-12: 32px;
  --s-13: 40px;

  /* Screen gutter — every full-width phone screen uses this left and right */
  --gutter: 20px;

  --r-sm:   8px;
  --r-md:   11px;
  --r-lg:   14px;
  --r-xl:   16px;
  --r-2xl:  18px;
  --r-card: 28px;   /* outer card / phone frame */
  --r-pill: 999px;

  --bd-hair:   1px solid var(--a-ink-07);
  --bd-light:  1px solid var(--a-ink-12);
  --bd-field:  1px solid var(--a-green-18);
  --bd-strong: 2px solid var(--c-green);
  --bd-dark:   1px solid var(--a-cream-14);

  --sh-card:  0 6px 24px rgba(22,33,29,.11);
  --sh-sheet: 0 -6px 24px rgba(22,33,29,.16);
  --sh-sm:    0 1px 3px rgba(22,33,29,.06);

  /* Focus ring — visible, never removed */
  --ring:      0 0 0 3px rgba(169,133,69,.45);
  --ring-dark: 0 0 0 3px rgba(230,201,141,.45);
}


/* ==========================================================================
   4. TOKENS — MOTION
   Restrained by intent: this is a formal business handling large sums.
   Nothing bounces, nothing overshoots, nothing loops except the skeleton.
   All values below are the real ones — read them, don't invent.
   ========================================================================== */

:root {
  /* Durations */
  --d-instant:  100ms;  /* tap feedback: scale/opacity on press             */
  --d-tab:      160ms;  /* tab content crossfade — no slide                 */
  --d-fade:     200ms;  /* state swap on the tracking link, expand/collapse */
  --d-sheet:    260ms;  /* bottom sheet rise and dismiss                    */
  --d-toast:    240ms;  /* toast in/out                                     */
  --d-draw:     400ms;  /* confirmation checkmark stroke draw               */
  --d-month:    220ms;  /* calendar month change                            */

  /* Dwell times — how long something stays before leaving on its own */
  --dwell-toast-ok:    3200ms;  /* success toast                            */
  --dwell-toast-err:   6000ms;  /* error toast — longer, it must be read    */
  --dwell-online:      2000ms;  /* "connection restored" bar                */
  --dwell-read-notif:  24h;     /* read notification stays faded one day    */

  /* Thresholds */
  --wait-silent:  400ms;   /* below this, show nothing at all               */
  --wait-slow:    10s;     /* above this, reassure that work continues      */
  --retry-auto:   15s;     /* automatic resend after a failed submit        */

  /* Easing */
  --ease-enter:  cubic-bezier(.22,.61,.36,1);    /* things arriving         */
  --ease-exit:   cubic-bezier(.55,.06,.68,.19);  /* things leaving          */
  --ease-move:   cubic-bezier(.4,0,.2,1);        /* things moving in place  */
  --ease-sheet:  cubic-bezier(.32,.72,0,1);      /* sheets — decisive       */

  /* Skeleton pulse */
  --d-pulse:     1600ms;
  --pulse-min:   .5;
  --pulse-max:   .9;
}


/* ==========================================================================
   5. TOKENS — LAYOUT
   ========================================================================== */

:root {
  --w-phone:        390px;   /* design width                                */
  --w-public-max:   560px;   /* public app never exceeds this, centred      */
  --w-admin-max:    1200px;  /* admin app on desktop                        */
  --w-sidebar:      220px;   /* admin desktop sidebar                       */
  --bp-desktop:     1024px;  /* admin switches to sidebar layout above this */

  --tap-min:        44px;    /* minimum touch target, never smaller         */
  --nav-h:          64px;    /* admin bottom nav height                     */

  --z-sticky:  10;
  --z-nav:     20;
  --z-sheet:   30;
  --z-toast:   40;
  --z-offline: 50;
}


/* ==========================================================================
   6. RESET AND BASE
   ========================================================================== */

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--c-canvas);
  color: var(--c-ink);
  font: var(--t-body-sm);
  text-wrap: pretty;
  -webkit-font-smoothing: antialiased;
}

/* Public app column. Admin overrides this — see section 26. */
.app {
  max-width: var(--w-public-max);
  margin-inline: auto;
  background: var(--c-card);
  min-height: 100vh;
}

img, svg { display: block; max-width: 100%; }

a { color: var(--c-green); text-decoration: none; }
a:hover { color: var(--c-gold); }

button {
  font: inherit; color: inherit; background: none;
  border: 0; padding: 0; cursor: pointer;
}

/* Focus is always visible. Do not remove this. */
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--r-sm); }
.on-dark :focus-visible { box-shadow: var(--ring-dark); }

/* Every interactive element meets the touch minimum */
button, a[role="button"], .btn, .tab, .chip, .seg-item, .day {
  min-height: var(--tap-min);
}


/* ==========================================================================
   7. TYPOGRAPHY CLASSES
   ========================================================================== */

.display-lg { font: var(--t-display-lg); color: var(--c-ink); margin: 0; }
.display    { font: var(--t-display);    color: var(--c-ink); margin: 0; }
.title      { font: var(--t-title);      color: var(--c-ink); margin: 0; }
.title-sm   { font: var(--t-title-sm);   color: var(--c-ink); margin: 0; }

.lead       { font: var(--t-lead);    color: var(--a-ink-72); margin: 0; }
.body       { font: var(--t-body);    color: var(--a-ink-78); margin: 0; }
.body-sm    { font: var(--t-body-sm); color: var(--a-ink-72); margin: 0; }
.hint       { font: var(--t-hint);    color: var(--a-ink-55); margin: 0; }

/* Section eyebrow — small tracked label above a group */
.eyebrow {
  font: var(--t-section);
  letter-spacing: var(--track-eyebrow);
  color: var(--a-ink-55);
  margin: 0 0 var(--s-5);
}

/* On dark surfaces */
.on-dark .display-lg,
.on-dark .display,
.on-dark .title,
.on-dark .title-sm { color: var(--c-cream); }
.on-dark .lead     { color: var(--a-cream-72); }
.on-dark .body,
.on-dark .body-sm  { color: var(--a-cream-85); }
.on-dark .hint     { font: var(--t-admin-hint); color: var(--a-cream-50); }
.on-dark .eyebrow  { color: var(--a-cream-50); }


/* ==========================================================================
   8. NUMERALS AND BIDIRECTIONAL ISOLATION

   Latin digits inside Arabic text reorder unless isolated. This is not
   cosmetic — an IBAN or a reference code renders wrong without it.
   Wrap EVERY Latin run in Arabic prose with .ltr.

   Money: Latin digits, thousands separator, tabular figures so columns
   align vertically. Never Arabic-Indic digits for money.
   ========================================================================== */

.ltr {
  direction: ltr;
  unicode-bidi: isolate;
  display: inline-block;
  text-align: left;
}

.mono { font-family: var(--f-mono); direction: ltr; unicode-bidi: isolate; }

.num {
  font-family: var(--f-mono);
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
}

.ref     { font: var(--t-ref);    letter-spacing: var(--track-ref); direction: ltr; unicode-bidi: isolate; }
.ref--lg { font: var(--t-ref-lg); letter-spacing: var(--track-ref); }

.money      { font: var(--t-num); font-variant-numeric: tabular-nums; direction: ltr; unicode-bidi: isolate; }
.money--due { color: var(--c-gold-text); }
.money--paid{ color: var(--c-green); }
.on-dark .money--due  { color: var(--c-gold-light); }
.on-dark .money--paid { color: var(--c-sage-text); }

/* Hijri, always secondary and always under its Gregorian date */
.hijri { font: var(--t-hijri); color: var(--a-ink-45); direction: ltr; unicode-bidi: isolate; }
.on-dark .hijri { color: var(--a-cream-40); }

/* Gregorian + Hijri pair */
.datepair { display: flex; flex-direction: column; gap: var(--s-1); }


/* ==========================================================================
   9. LAYOUT PRIMITIVES
   Sibling groups use flex/grid with gap — never margins between children.
   ========================================================================== */

.stack   { display: flex; flex-direction: column; gap: var(--s-5); }
.stack-2 { display: flex; flex-direction: column; gap: var(--s-2); }
.stack-3 { display: flex; flex-direction: column; gap: var(--s-3); }
.stack-4 { display: flex; flex-direction: column; gap: var(--s-4); }
.stack-7 { display: flex; flex-direction: column; gap: var(--s-7); }
.stack-9 { display: flex; flex-direction: column; gap: var(--s-9); }

.row       { display: flex; align-items: center; gap: var(--s-3); }
.row-start { display: flex; align-items: flex-start; gap: var(--s-3); }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }

.spread { flex: 1; min-width: 0; }   /* the shrinking half of a row */
.fixed  { flex: none; }              /* badges, icons — never compress */

/* Section — a titled group inside a screen, gutter applied */
.section { padding: var(--s-11) var(--gutter) 0; }
.section:last-child { padding-bottom: var(--s-11); }


/* ==========================================================================
   10. SURFACES
   card   — the outer container of a screen or a standalone panel
   block  — a white panel inside a screen (public app)
   tile   — a translucent panel inside a screen (admin app, dark)
   ========================================================================== */

.card {
  background: var(--c-card);
  border: var(--bd-light);
  border-radius: var(--r-card);
  box-shadow: var(--sh-card);
  overflow: hidden;
}

/* Admin variant — the whole screen surface is dark. Pair with .on-dark. */
.card--admin { background: var(--c-green-admin); color: var(--c-cream); }

.block {
  background: var(--c-white);
  border: var(--bd-light);
  border-radius: var(--r-2xl);
  padding: var(--s-7);
}
.block--flush   { padding: var(--s-1) var(--s-7); }  /* holds .kv rows only */
.block--emphasis{ border: var(--bd-strong); }        /* the executed contract */

.tile {
  background: var(--a-cream-07);
  border-radius: var(--r-xl);
  padding: var(--s-7);
  color: var(--c-cream);
}
.tile--flush { padding: var(--s-1) var(--s-7); }
.tile--quiet { background: var(--a-cream-05); }

/* Screen header band — the coloured strip at the top of a tracking state */
.band { padding: var(--s-9); color: var(--c-cream); }
.band--review   { background: var(--c-ink); }
.band--pending  { background: var(--c-green); }
.band--declined { background: var(--c-clay-deep); }
.band--ended    { background: rgba(22,33,29,.72); }
.band--confirmed{ background: var(--c-ink); position: relative; overflow: hidden; }

/* Single gold hairline across a confirmation header */
.band--confirmed::before {
  content: "";
  position: absolute; inset: 0 0 auto 0; height: 1px;
  background: linear-gradient(to left,
    rgba(230,201,141,0), var(--c-gold-light), rgba(230,201,141,0));
}


/* ==========================================================================
   11. BUTTONS
   Primary is filled green on light, filled cream on dark.
   One primary per view. Destructive is clay and always confirms.
   ========================================================================== */

.btn {
  display: flex; align-items: center; justify-content: center; gap: var(--s-3);
  padding: var(--s-6);
  border-radius: var(--r-lg);
  font: var(--t-btn);
  text-align: center;
  transition: transform var(--d-instant) var(--ease-move),
              opacity   var(--d-instant) var(--ease-move),
              background-color var(--d-instant) var(--ease-move);
}
.btn:active { transform: scale(.985); }

.btn--primary   { background: var(--c-green); color: var(--c-cream); }
.btn--secondary { border: 1px solid var(--a-green-25); color: var(--c-green); padding: 13px; }
.btn--danger    { background: var(--c-clay-deep); color: var(--c-white); }
.btn--block     { width: 100%; }
.btn--sm        { padding: var(--s-5) var(--s-6); font: var(--t-btn-sm); }

/* On dark surfaces the polarity inverts */
.on-dark .btn--primary   { background: var(--c-cream-fill); color: var(--c-ink); }
.on-dark .btn--secondary { border-color: var(--ad-line-3); color: var(--c-cream); }

/* Disabled — visibly inert, never hidden. Say why in a .hint underneath. */
.btn[disabled], .btn--disabled {
  background: var(--a-green-12);
  color: var(--a-ink-35);
  border-color: transparent;
  pointer-events: none;
}
.on-dark .btn[disabled], .on-dark .btn--disabled {
  background: var(--a-cream-14);
  color: var(--a-cream-40);
}

/* Busy — same width, same height, label swaps. Locked against double tap. */
.btn--busy { pointer-events: none; opacity: .75; }
.btn--busy .spinner { animation: spin 900ms linear infinite; }


/* ==========================================================================
   12. FORM FIELDS
   Errors appear on blur, never while typing, and say what to do.
   ========================================================================== */

.field { display: flex; flex-direction: column; gap: var(--s-3); }
.field__label { font: var(--t-label); color: var(--c-ink); }
.field__hint  { font: var(--t-hint);  color: var(--a-ink-55); }

.input {
  width: 100%;
  padding: var(--s-6) 15px;
  background: var(--c-white);
  border: var(--bd-field);
  border-radius: var(--r-lg);
  font: 400 13.5px/1.4 var(--f-ui);
  color: var(--c-ink);
  transition: border-color var(--d-instant) var(--ease-move),
              box-shadow  var(--d-instant) var(--ease-move);
}
.input::placeholder { color: var(--a-ink-35); }
.input:focus { border-color: var(--c-green); box-shadow: var(--ring); outline: none; }

.input--num  { font-family: var(--f-mono); direction: ltr; text-align: right; }
.input--area { min-height: 56px; line-height: 1.7; resize: vertical; }

.input--error { border: 1.5px solid var(--c-clay); }
.field__error {
  font: 500 11.5px/1.6 var(--f-ui);
  color: var(--c-clay-text);
}

/* Dark variant — admin app */
.on-dark .field__label { color: var(--c-cream); }
.on-dark .input {
  background: var(--ad-field);
  border: 1px solid var(--ad-line-2);
  color: var(--c-cream);
}
.on-dark .input::placeholder { color: var(--a-cream-40); }


/* ==========================================================================
   13. CHOICE CONTROLS
   ========================================================================== */

/* Segmented — 2 to 4 mutually exclusive options, equal width */
.seg {
  display: flex; gap: var(--s-2);
  background: var(--a-green-06);
  padding: var(--s-1);
  border-radius: var(--r-lg);
}
.seg-item {
  flex: 1; display: flex; align-items: center; justify-content: center;
  padding: 10px var(--s-1);
  border-radius: var(--r-md);
  font: 500 12px/1 var(--f-ui);
  color: var(--a-ink-55);
  transition: background-color var(--d-tab) var(--ease-move),
              color            var(--d-tab) var(--ease-move);
}
.seg-item[aria-selected="true"], .seg-item.is-on {
  background: var(--c-card);
  color: var(--c-ink);
  font-weight: 600;
  box-shadow: var(--sh-sm);
}
.on-dark .seg { background: var(--a-cream-07); }
.on-dark .seg-item { color: var(--a-cream-60); }
.on-dark .seg-item[aria-selected="true"],
.on-dark .seg-item.is-on { background: var(--c-cream-fill); color: var(--c-ink); box-shadow: none; }

/* Option cards — hall, section, period. Bigger targets than segmented. */
.options { display: flex; gap: var(--s-3); }
.option {
  flex: 1;
  padding: 13px;
  text-align: center;
  border: var(--bd-field);
  border-radius: var(--r-lg);
  font: 500 12.5px/1.3 var(--f-ui);
  color: var(--a-ink-60);
  transition: border-color var(--d-instant) var(--ease-move),
              background-color var(--d-instant) var(--ease-move);
}
.option.is-on {
  border: 1.5px solid var(--c-green);
  background: var(--a-green-06);
  color: var(--c-ink);
  font-weight: 600;
}
.on-dark .option { border-color: transparent; background: var(--a-cream-10); color: var(--a-cream-72); }
.on-dark .option.is-on { background: var(--a-cream-14); border: 1px solid var(--ad-line-3); color: var(--c-cream); }

/* Chips — multi-select, wraps */
.chips { display: flex; flex-wrap: wrap; gap: 7px; }
.chip {
  padding: 10px var(--s-7);
  border: var(--bd-field);
  border-radius: var(--r-pill);
  font: 500 12px/1 var(--f-ui);
  color: var(--a-ink-60);
}
.chip.is-on {
  border: 1.5px solid var(--c-green);
  background: var(--a-green-06);
  color: var(--c-ink);
  font-weight: 600;
}

/* Checkbox — terms agreement */
.check { display: flex; align-items: flex-start; gap: var(--s-5); padding: 15px;
         border-radius: var(--r-lg); background: var(--a-green-06); }
.check__box {
  flex: none; width: 21px; height: 21px;
  border: 1.5px solid var(--a-green-25);
  border-radius: var(--s-2);
  background: var(--c-white);
  display: flex; align-items: center; justify-content: center;
}
.check.is-on .check__box { background: var(--c-green); border-color: var(--c-green); color: var(--c-cream); }
.check__text { font: 400 12px/1.75 var(--f-ui); color: var(--a-ink-72); }


/* ==========================================================================
   14. BADGES AND STATUS PILLS
   One pill per state. Colour carries meaning — see the status map.
   ========================================================================== */

.badge {
  display: inline-flex; align-items: center;
  padding: var(--s-2) 10px;
  border-radius: var(--r-sm);
  font: var(--t-badge);
  white-space: nowrap;
}

.badge--review    { background: var(--a-cream-18); color: var(--c-cream); }
.badge--offer     { background: var(--a-goldl-14); color: var(--c-gold-light); }
.badge--awaiting  { background: var(--a-goldl-14); color: var(--c-gold-light); }
.badge--confirmed { background: var(--c-sage);      color: var(--c-ink); }
.badge--declined  { background: rgba(255,255,255,.2); color: var(--c-white); }
.badge--expired   { background: var(--a-cream-18); color: var(--c-cream); }
.badge--ended     { background: var(--a-cream-14); color: var(--c-cream); }

/* Admin list pills — on dark, solid fills */
.pill { display: inline-flex; padding: var(--s-2) var(--s-4);
        border-radius: 7px; font: 600 10.5px/1 var(--f-ui); white-space: nowrap; }
.pill--request  { background: var(--a-goldl-14); color: var(--c-gold-light); }
.pill--approve  { background: var(--c-gold-chip); color: var(--c-ink); }
.pill--sign     { background: var(--c-gold);       color: var(--c-ink); }
.pill--confirmed{ background: var(--c-sage);       color: var(--c-ink); }
.pill--ended    { background: var(--a-cream-14);   color: var(--a-cream-72); }

/* Unread dot — on a list row. Disappears on open and does not return. */
.newdot { flex: none; width: 8px; height: 8px; border-radius: 50%; }
.newdot--decision { background: var(--c-clay); }
.newdot--deadline { background: var(--c-gold); }
.newdot--info     { background: var(--c-sage); }
.newdot--read     { background: transparent; }

/* Count badge on a tab */
.count {
  position: absolute; top: 2px; inset-inline-start: 4px;
  min-width: 15px; height: 15px; padding: 0 3px;
  border-radius: var(--r-pill);
  font: 700 9px/15px var(--f-mono);
  text-align: center;
}
.count--decision { background: var(--c-clay);       color: var(--c-white); }
.count--deadline { background: var(--c-gold);       color: var(--c-ink); }
.count--info     { background: var(--c-sage);       color: var(--c-ink); }


/* ==========================================================================
   15. PROGRESS TRACK — the tracking link's spine
   Four steps: request, contract & signature, management approval, confirmed.

   THE STOP RULE: when a booking ends early, the track ENDS at the clay node.
   Do not render the remaining steps greyed out, and do not render the dashed
   connector after it. Showing steps that will never happen is a false promise.
   ========================================================================== */

.track { display: flex; flex-direction: column; gap: 2px; padding: var(--s-9) var(--gutter) 0; }

.track__step { display: flex; align-items: center; gap: 10px; }

.track__dot { flex: none; width: 9px; height: 9px; border-radius: 50%; }
.track__dot--done    { background: var(--c-green); }
.track__dot--active  { background: var(--c-gold); box-shadow: 0 0 0 4px var(--a-gold-22); }
.track__dot--future  { border: 1px solid var(--a-ink-35); }
.track__dot--stopped { background: var(--c-clay); box-shadow: 0 0 0 4px var(--a-clay-20); }
.track__dot--final   { background: var(--c-sage); box-shadow: 0 0 0 4px var(--a-sage-30); }
.track__dot--ended   { background: rgba(22,33,29,.55); }

.track__label--done    { font: 500 11.5px/1 var(--f-ui); color: var(--a-ink-45); }
.track__label--active  { font: 600 12.5px/1 var(--f-ui); color: var(--c-ink); }
.track__label--future  { font: 400 11.5px/1 var(--f-ui); color: var(--a-ink-35); }
.track__label--stopped { font: 600 12.5px/1 var(--f-ui); color: var(--c-clay-text); }

.track__link  { height: 14px; margin-inline-start: 4px; border-inline-end: 1px solid var(--a-green-12); }
.track__link--future { border-inline-end-style: dashed; }


/* ==========================================================================
   16. KEY/VALUE ROWS
   The details card on every tracking state, and every summary block.
   Same rows, same order, every time: hall, section, date, period.
   ========================================================================== */

.kv       { display: flex; justify-content: space-between; align-items: center;
            gap: var(--s-5); padding: var(--s-5) 0; border-bottom: var(--bd-hair); }
.kv:last-child { border-bottom: 0; }
.kv--top  { align-items: flex-start; }
.kv__k    { font: var(--t-caption); color: var(--a-ink-60); }
.kv__v    { font: var(--t-value);   color: var(--c-ink); text-align: left; }

.on-dark .kv    { border-bottom-color: var(--ad-line); }
.on-dark .kv__k { color: var(--a-cream-72); }
.on-dark .kv__v { color: var(--c-cream); }


/* ==========================================================================
   17. CALENDAR
   Gregorian leads, Hijri sits under it in mono. Never a toggle between them.
   ========================================================================== */

.cal { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--s-1); }

.cal__dow { text-align: center; font: 500 9.5px/1 var(--f-ui); color: var(--a-ink-45); }

.day {
  aspect-ratio: 1 / 1.15;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  border-radius: var(--r-md);
  background: var(--c-white);
  border: 1px solid var(--a-green-12);
  font: 600 14px/1 var(--f-ui);
  color: var(--c-ink);
  transition: transform var(--d-instant) var(--ease-move);
}
.day:active { transform: scale(.94); }
.day__hijri { font: 400 8.5px/1 var(--f-mono); color: var(--a-ink-45); }
.day__note  { font: 500 7.5px/1 var(--f-ui);   color: var(--c-gold-text); }

.day--available {}
.day--requested { background: var(--a-gold-16); border-color: var(--a-gold-45); }
.day--booked    { background: var(--c-ink); color: var(--c-cream); border-color: var(--c-ink); }
.day--booked .day__hijri { color: rgba(244,237,226,.6); }
.day--closed    { border-color: transparent; color: var(--a-ink-35);
                  background: repeating-linear-gradient(135deg,
                    rgba(22,33,29,.09) 0 4px, transparent 4px 8px); }
.day--soon,
.day--past      { background: var(--a-green-06); color: rgba(22,33,29,.28);
                  border-color: transparent; pointer-events: none; }
.day--selected  { outline: 2px solid var(--c-gold-light); outline-offset: -2px; }

/* Admin calendar sits on dark */
.on-dark .day        { background: var(--a-cream-07); border: 0; color: var(--c-cream); }
.on-dark .day__hijri { color: var(--a-cream-40); }
.on-dark .day--confirmed { background: var(--c-sage); color: var(--c-ink); }
.on-dark .day--requested { background: var(--a-gold-35); }
.on-dark .day--closed    { background: repeating-linear-gradient(135deg,
                             var(--a-cream-14) 0 4px, transparent 4px 8px); }

/* Month header, with its Hijri span underneath */
.cal-head { display: flex; align-items: center; justify-content: space-between; }
.cal-head__month { font: var(--t-title); color: var(--c-ink); text-align: center; }
.on-dark .cal-head__month { color: var(--c-cream); }

/* Legend */
.legend { display: flex; flex-wrap: wrap; gap: var(--s-5); }
.legend__item { display: flex; align-items: center; gap: 7px;
                font: 400 11.5px/1.4 var(--f-ui); color: var(--a-ink-60); }
.legend__swatch { flex: none; width: 15px; height: 15px; border-radius: 5px; }


/* ==========================================================================
   18. TABS
   Content crossfades. It does not slide — sliding implies a spatial
   relationship between statuses that does not exist.
   ========================================================================== */

.tabs { display: flex; gap: var(--s-2); background: var(--a-cream-07);
        padding: var(--s-1); border-radius: var(--r-lg); }
.tab  { flex: 1; position: relative; display: flex; align-items: center;
        justify-content: center; padding: 10px var(--s-1);
        border-radius: var(--r-md); font: 500 11.5px/1 var(--f-ui);
        color: var(--a-cream-60);
        transition: background-color var(--d-tab) var(--ease-move),
                    color var(--d-tab) var(--ease-move); }
.tab[aria-selected="true"] { background: var(--c-cream-fill); color: var(--c-ink); font-weight: 600; }

.tabpanel { animation: fade-in var(--d-tab) var(--ease-enter); }


/* ==========================================================================
   19. BOTTOM SHEETS
   Every admin decision happens in a sheet, never on a new page — the manager
   must not lose the record he is deciding about.
   Each sheet states its consequences immediately above its confirm button.
   ========================================================================== */

.sheet-scrim {
  position: fixed; inset: 0; z-index: var(--z-sheet);
  background: rgba(11,20,17,.55);
  animation: fade-in var(--d-sheet) var(--ease-enter);
}
.sheet {
  position: fixed; inset: auto 0 0 0; z-index: var(--z-sheet);
  max-height: 92vh; overflow-y: auto;
  background: var(--c-card);
  border-radius: var(--r-card) var(--r-card) 0 0;
  box-shadow: var(--sh-sheet);
  animation: sheet-up var(--d-sheet) var(--ease-sheet);
}
.sheet--dark { background: var(--c-green-admin); color: var(--c-cream); }
.sheet--tall { max-height: 85vh; }   /* long text: contract, undertaking */

.sheet__grab { padding: var(--s-6) var(--gutter) 0; display: flex; justify-content: center; }
.sheet__grab i { display: block; width: 38px; height: 4px;
                 border-radius: var(--r-pill); background: var(--a-cream-25); }
.sheet:not(.sheet--dark) .sheet__grab i { background: var(--a-ink-18); }

.sheet__head { padding: var(--s-7) var(--gutter) 0; }
.sheet__foot { padding: var(--s-7) var(--gutter) var(--s-11); display: flex; gap: var(--s-3); }


/* ==========================================================================
   20. NOTICES
   info    — neutral explanation
   warn    — gold: a deadline, competing interest, a caution
   danger  — clay: a failure, a decline, an irreversible consequence
   success — sage: something completed
   ========================================================================== */

.notice { padding: 15px; border-radius: var(--r-lg); font: var(--t-hint); }

.notice--info    { background: var(--a-green-06); color: var(--a-ink-72); }
.notice--warn    { background: var(--a-goldl-14); border: 1px solid var(--a-goldl-30); color: var(--a-ink-72); }
.notice--danger  { background: var(--a-clay-14);  border: 1px solid var(--a-clay-35);  color: var(--a-ink-72); }
.notice--success { background: var(--a-sage-25);  border: 1px solid var(--a-green-18); color: var(--a-ink-72); }

.on-dark .notice--info    { background: var(--a-cream-05); color: var(--a-cream-50); }
.on-dark .notice--warn    { background: var(--a-goldl-14); border-color: var(--a-goldl-30); color: var(--a-cream-72); }
.on-dark .notice--danger  { background: var(--a-clay-16);  border-color: var(--a-clay-35);  color: var(--a-cream-85); }

.notice__title { font: 600 12.5px/1.4 var(--f-ui); color: var(--c-ink); margin-bottom: var(--s-4); }
.on-dark .notice__title { color: var(--c-cream); }
.notice--danger .notice__title { color: var(--c-clay-text); }
.on-dark .notice--danger .notice__title { color: var(--c-clay-light); }

/* Consequence list — what will happen when this button is pressed */
.consequences { display: flex; flex-direction: column; gap: 7px; }
.consequence  { display: flex; gap: var(--s-3); font: 400 11.5px/1.6 var(--f-ui); }
.consequence::before { content: "·"; color: var(--c-clay-light); }

/* Toast — arrives from the bottom above the nav, leaves on its own */
.toast {
  position: fixed; z-index: var(--z-toast);
  inset-inline: var(--gutter); bottom: calc(var(--nav-h) + var(--s-5));
  padding: var(--s-6);
  border-radius: var(--r-lg);
  background: var(--c-ink); color: var(--c-cream);
  font: 500 12.5px/1.5 var(--f-ui);
  animation: toast-in var(--d-toast) var(--ease-enter);
}
.toast--error { background: var(--c-clay-deep); }

/* Offline bar — pinned under the header, covers nothing */
.offline-bar {
  position: sticky; top: 0; z-index: var(--z-offline);
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-5) var(--s-6);
  background: var(--c-ink); color: var(--c-cream);
  font: 500 11.5px/1.5 var(--f-ui);
}
.offline-bar::before { content: ""; flex: none; width: 7px; height: 7px;
                       border-radius: 50%; background: var(--c-gold-light); }


/* ==========================================================================
   21. SKELETONS AND LOADING
   Below --wait-silent nothing renders. Above it, a skeleton in the exact
   shape and position of the content it replaces, so nothing jumps on arrival.
   Spinners live inside buttons only.
   ========================================================================== */

.skel {
  background: var(--a-ink-08);
  border-radius: 7px;
  animation: pulse var(--d-pulse) var(--ease-move) infinite;
}
.on-dark .skel { background: var(--a-cream-07); }

.skel--line  { height: 11px; }
.skel--title { height: 20px; }
.skel--dot   { width: 9px; height: 9px; border-radius: 50%; }
.skel--block { height: 42px; border-radius: var(--r-lg); }

.spinner { width: 16px; height: 16px; }

/* Slow-connection reassurance, shown after --wait-slow */
.slow-note { font: var(--t-hint); color: var(--a-ink-45); }

/* Upload progress — real bytes, never an estimate */
.progress      { height: 5px; border-radius: var(--r-pill); background: var(--a-cream-14); }
.progress__bar { height: 5px; border-radius: var(--r-pill); background: var(--c-sage);
                 transition: width var(--d-fade) linear; }


/* ==========================================================================
   22. EMPTY STATES
   Three distinct kinds. Do not collapse them into one component.
     nothing-waiting — an achievement. Sage.
     no-results      — a search miss. Neutral, offers to clear filters.
     nothing-yet     — not started. Neutral, explains when it will fill.
   Optional public-site sections (FAQ, services, gallery) are REMOVED from
   the page when empty. The customer never sees a missing-content state.
   ========================================================================== */

.empty { padding: var(--s-9); border-radius: var(--r-xl); text-align: center; }
.empty__icon  { display: inline-block; }
.empty__title { font: 600 13.5px/1.4 var(--f-ui); color: var(--c-ink); margin-top: var(--s-5); }
.empty__text  { font: var(--t-hint); color: var(--a-ink-55); margin-top: var(--s-3); }

.empty--achievement { background: var(--a-sage-14); border: 1px solid var(--a-sage-30); }
.empty--achievement .empty__title { font: var(--t-title-sm); }
.empty--neutral     { background: var(--a-cream-06, var(--a-ink-04)); }

.on-dark .empty__title { color: var(--c-cream); }
.on-dark .empty__text  { color: var(--a-cream-60); }
.on-dark .empty--neutral { background: var(--a-cream-07); }


/* ==========================================================================
   23. SIGNATURE PAD
   Canvas. Stroke follows the finger with no smoothing lag and no lift
   animation. Clear is always available. The pad never scrolls the page —
   touch-action: none is required or the signature turns into a page scroll.
   ========================================================================== */

.sigpad {
  position: relative;
  height: 140px;
  border: 1px solid rgba(30,59,50,.22);
  border-radius: var(--r-lg);
  background: var(--c-paper);
  overflow: hidden;
  touch-action: none;
}
.sigpad__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.sigpad__rule   { position: absolute; inset: auto 18px 40px 18px;
                  border-bottom: 1px dashed rgba(30,59,50,.25); }
.sigpad__hint   { position: absolute; inset: auto 0 15px 0; text-align: center;
                  font: 400 11px/1 var(--f-ui); color: var(--a-ink-35); }
.sigpad.is-signed .sigpad__hint,
.sigpad.is-signed .sigpad__rule { display: none; }

/* Stroke: 2.6px, round cap, --c-ink */
:root { --sig-stroke: 2.6px; --sig-colour: #16211D; }


/* ==========================================================================
   24. UPLOAD
   ========================================================================== */

.upload {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-3);
  min-height: 96px; padding: var(--s-7);
  border: 1px dashed var(--a-green-25);
  border-radius: var(--r-lg);
  font: 400 11px/1.5 var(--f-ui);
  color: var(--a-ink-55);
  text-align: center;
}
.upload--error { border: 1.5px solid var(--c-clay); }

.upload-row { display: flex; gap: var(--s-5); align-items: center;
              padding: 13px; border-radius: var(--r-lg);
              background: var(--c-white); border: var(--bd-light); }
.upload-row__thumb { flex: none; width: 48px; height: 60px; border-radius: var(--s-4); }


/* ==========================================================================
   25. IMAGE PLACEHOLDERS
   Used wherever a real photograph has not been supplied. Replace the element
   with an <img src="assets/images/…"> — do not keep the placeholder in
   production. Never base64-embed photographs.
   ========================================================================== */

.imgph {
  display: flex; align-items: center; justify-content: center;
  text-align: center;
  font: 400 10px/1.5 var(--f-mono);
  color: rgba(30,59,50,.6);
  background-image: repeating-linear-gradient(135deg,
    rgba(30,59,50,.10) 0 6px, rgba(30,59,50,0) 6px 12px);
}
.on-dark .imgph {
  color: var(--a-cream-50);
  background-image: repeating-linear-gradient(135deg,
    rgba(244,237,226,.13) 0 6px, rgba(244,237,226,0) 6px 12px);
}


/* ==========================================================================
   26. ADMIN SHELL
   Five navigation items. Notifications are a bell on the dashboard, not a
   sixth item. Reports live inside the dashboard, not on their own screen.
   ========================================================================== */

.admin { background: var(--c-green-admin); color: var(--c-cream); min-height: 100vh; }

.admin-nav {
  position: sticky; bottom: 0; z-index: var(--z-nav);
  display: flex; justify-content: space-between;
  padding: 13px var(--s-8) var(--s-9);
  background: var(--c-green-admin);
  border-top: var(--bd-dark);
}
.admin-nav__item    { font: 600 10.5px/1 var(--f-ui); color: var(--a-cream-50); }
.admin-nav__item.is-on { color: var(--c-cream); }

/* Bell with unread count */
.bell { position: relative; width: 38px; height: 38px; border-radius: var(--r-lg);
        background: var(--a-cream-10); display: flex; align-items: center; justify-content: center; }
.bell__count { position: absolute; top: -3px; inset-inline-start: -3px;
               min-width: 17px; height: 17px; border-radius: var(--r-pill);
               background: var(--c-clay); color: var(--c-white);
               font: 700 9.5px/17px var(--f-mono); text-align: center; }

/* Dashboard figure tile */
.stat        { background: var(--a-cream-07); border-radius: var(--r-xl); padding: var(--s-6); }
.stat__label { font: 400 10.5px/1.35 var(--f-ui); color: var(--a-cream-60); }
.stat__value { font: var(--t-num); color: var(--c-cream); margin-top: 7px;
               font-variant-numeric: tabular-nums; direction: ltr; unicode-bidi: isolate; }
.stat--due   { background: var(--a-goldl-14); border: 1px solid var(--a-goldl-30); }
.stat--due .stat__label, .stat--due .stat__value { color: var(--c-gold-light); }

/* Horizontal meter — occupancy, conversion */
.meter       { height: 6px; border-radius: var(--r-pill); background: var(--ad-well); }
.meter__fill { height: 6px; border-radius: var(--r-pill); background: var(--c-gold); }
.meter__fill--good { background: var(--c-sage); }
.meter__fill--bad  { background: var(--c-clay); }

/* Twelve-month demand bars */
.bars      { display: flex; align-items: flex-end; gap: var(--s-1); height: 78px; }
.bars__bar { flex: 1; border-radius: var(--s-1) var(--s-1) 0 0; background: rgba(244,237,226,.28); }
.bars__bar--peak { background: var(--c-gold); }

/* Grouped requests — several bookings wanting one hall on one day */
.group        { border: 1.5px solid var(--a-gold-45); border-radius: var(--r-xl); overflow: hidden; }
.group__head  { background: var(--a-gold-16); padding: 11px var(--s-6);
                display: flex; justify-content: space-between; align-items: center;
                font: 600 11.5px/1 var(--f-ui); color: var(--c-gold-light); }
.group__row   { background: var(--a-cream-07); padding: 13px var(--s-6);
                display: flex; justify-content: space-between; align-items: center;
                border-bottom: 1px solid var(--ad-line); }
.group__row:last-child { border-bottom: 0; }


/* ==========================================================================
   27. OVERFLOW AND TRUNCATION
   Lists truncate to one line. Detail screens wrap in full — a customer's
   name on a contract is never clipped. Reasons are never truncated at all;
   they collapse behind a control instead.
   ========================================================================== */

.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wrap     { text-wrap: pretty; overflow-wrap: anywhere; }

/* Collapsible long text — four lines then a fade and a control */
.clamp {
  position: relative;
  max-height: calc(1.9em * 4);
  overflow: hidden;
  transition: max-height var(--d-fade) var(--ease-move);
}
.clamp::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 48px;
  background: linear-gradient(to top, var(--c-white), rgba(255,255,255,0));
}
.clamp.is-open { max-height: 200em; }
.clamp.is-open::after { display: none; }


/* ==========================================================================
   28. MOTION — KEYFRAMES
   ========================================================================== */

@keyframes fade-in   { from { opacity: 0; } to { opacity: 1; } }
@keyframes fade-up   { from { opacity: 0; transform: translateY(12px); }
                       to   { opacity: 1; transform: none; } }
@keyframes sheet-up  { from { transform: translateY(100%); }
                       to   { transform: none; } }
@keyframes toast-in  { from { opacity: 0; transform: translateY(12px); }
                       to   { opacity: 1; transform: none; } }
@keyframes pulse     { 0%,100% { opacity: var(--pulse-min); }
                       50%     { opacity: var(--pulse-max); } }
@keyframes spin      { to { transform: rotate(360deg); } }

/* Confirmation moment — the emotional peak, and deliberately restrained.
   0—300ms   card fades and rises 12px
   150—550ms the check draws as a single stroke (not a pop-in)
   200—600ms one gold hairline sweeps the header once and stops
   Nothing repeats. Nothing bounces. No confetti. */
@keyframes draw { from { stroke-dashoffset: 48; } to { stroke-dashoffset: 0; } }
@keyframes sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

.confirm-card  { animation: fade-up var(--d-fade) var(--ease-enter) both; }
.confirm-check { stroke-dasharray: 48; animation: draw var(--d-draw) var(--ease-enter) 150ms both; }
.confirm-sweep { animation: sweep 600ms var(--ease-move) 200ms both; }

/* State swap on the tracking link: the sections crossfade in place.
   Do not animate scroll position — the customer must not be moved. */
.state-swap { animation: fade-in var(--d-fade) var(--ease-enter); }

/* Month change */
.cal-swap { animation: fade-in var(--d-month) var(--ease-move); }


/* ==========================================================================
   29. REDUCED MOTION
   Everything decorative stops. Nothing functional is lost: sheets still
   appear, tabs still switch, the check still renders — instantly.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
  .skel { animation: none; opacity: .7; }
  .confirm-check { stroke-dashoffset: 0; }
  .confirm-sweep { display: none; }
  .btn:active { transform: none; }
  .day:active { transform: none; }
}


/* ==========================================================================
   30. DESKTOP
   The public app stays a single centred column at every width — 90% of its
   visitors are on a phone and widening it serves nobody.
   The admin app gains a sidebar and a two-column body. Same components,
   different arrangement. There is no third design.
   ========================================================================== */

@media (min-width: 1024px) {
  .admin { display: grid; grid-template-columns: var(--w-sidebar) 1fr; }

  .admin-nav {
    position: sticky; top: 0; align-self: start;
    flex-direction: column; justify-content: flex-start; gap: 7px;
    height: 100vh;
    padding: var(--s-9);
    border-top: 0;
    border-inline-start: var(--bd-dark);
  }
  .admin-nav__item {
    padding: 11px 13px; border-radius: var(--r-md);
    font: 500 11.5px/1 var(--f-ui);
  }
  .admin-nav__item.is-on { background: var(--a-cream-14); font-weight: 600; }

  .admin-body {
    max-width: var(--w-admin-max);
    padding: var(--s-11);
    display: grid; grid-template-columns: 1fr 1.35fr; gap: var(--s-6);
    align-items: start;
  }

  /* Sheets become centred dialogs on desktop */
  .sheet {
    inset: 50% auto auto 50%;
    transform: translate(50%, -50%);
    width: 480px; max-height: 82vh;
    border-radius: var(--r-card);
    animation: fade-up var(--d-sheet) var(--ease-sheet);
  }
  .sheet__grab { display: none; }
}


/* ==========================================================================
   31. PRINT
   Only two things are ever printed: the executed contract and the supplier
   permit. Both print on white, without navigation or actions.
   ========================================================================== */

@media print {
  body { background: #fff; }
  .admin-nav, .btn, .toast, .offline-bar, .sheet-scrim, .bell { display: none !important; }
  .card, .block { box-shadow: none; border-color: #999; break-inside: avoid; }
  .band { background: #fff !important; color: #000 !important; }
  .on-dark, .on-dark * { background: #fff !important; color: #000 !important; }
  a[href]::after { content: ""; }
}


/* ==========================================================================
   32. ADDENDUM — classes the mockups use that the package never declared
   --------------------------------------------------------------------------
   Not a design decision. The package's frames reference these names and its
   stylesheet does not define them, so they render unstyled. Sections 1—31
   above are the designer's file byte-for-byte; everything below is ours.

   Nineteen further undeclared names (ic, icn, icb, sw*, ty, tyn, sec, stt,
   sid, sub, grid, bdg, rowlbl, chipOn) appear only in mockup 01, the
   foundations review board. Those are review-canvas chrome and belong in
   mockups/mockup-page.css, not here.

   Values are derived from the surrounding frames, not invented: the band
   title matches --t-title on a dark surface, the sub matches the band's
   supporting line, and the copy row reuses the field and button metrics.
   See docs/DECISIONS.md §9.
   ========================================================================== */

/* The UA stylesheet's [hidden] rule is `display:none`, but ANY author rule
   that sets `display` outranks it — origin beats specificity here. The package
   sets display on .field, .row, .stack, .btn, .kv and more, so `hidden` on any
   of them does nothing: a hidden field still shows, and a zero-count badge
   renders as an empty dot.

   This repository already found and fixed this once, in v2 (commit dead3ce).
   The package reintroduces it. Restore it once, for the whole system. */
[hidden] { display: none !important; }

/* §17 styles .legend__item for a light surface only, so the admin calendar's
   legend rendered near-black on the dark app background — the one part of the
   calendar that explains the rest. */
.on-dark .legend__item { color: var(--a-cream-72); }

/* Tracking-link and permit header (mockups 04, 05). CHANGES.md §3 renames
   htitle -> band__title and hsub -> band__sub, so these were intended. */
.band__title {
  margin-top: var(--s-4);
  font: var(--t-title);
  color: var(--c-cream);
}
.band__sub {
  margin-top: var(--s-3);
  font: 400 12px/1.75 var(--f-ui);
  color: var(--a-cream-72);
}

/* Copy-link card — section 5 of the enforced structure, so it appears on
   every tracking state without exception (RULES.md §5). */
.copyrow {
  display: flex; align-items: center; gap: var(--s-3);
  margin-top: var(--s-4);
  padding: 10px var(--s-5);
  background: var(--c-white);
  border: var(--bd-light);
  border-radius: var(--r-md);
}
.copylink {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font: 500 11.5px/1.5 var(--f-mono);
  direction: ltr; unicode-bidi: isolate; text-align: left;
  color: var(--c-green);
}
.copybtn {
  flex: none;
  display: inline-flex; align-items: center; gap: var(--s-2);
  min-height: var(--tap-min);
  padding: 0 var(--s-5);
  border-radius: var(--r-sm);
  font: var(--t-btn-sm);
  color: var(--c-green);
  transition: background-color var(--d-instant) var(--ease-move);
}
.copybtn:active { background: var(--a-green-06); }
.on-dark .copybtn { color: var(--c-cream); }

/* Mockup 06 writes the sheet grab handle as a class rather than the <i>
   that section 19 styles. Accept both spellings. */
.sheet__grab-bar {
  display: block; width: 38px; height: 4px;
  border-radius: var(--r-pill);
  background: var(--a-cream-25);
}
.sheet:not(.sheet--dark) .sheet__grab-bar { background: var(--a-ink-18); }

/* Hall card on the public journey (mockup 02). */
.dv-card {
  background: var(--c-white);
  border: var(--bd-light);
  border-radius: var(--r-2xl);
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   Correction to §15. The package writes the progress-track connector as
   `border-inline-end` with `margin-inline-start`, which puts the line on the
   opposite edge from the dot it joins. In RTL the dot sits at the row's right
   and the border is drawn on the left — measured in the designer's own
   mockup 04, the connector lands 309px away from its dot.

   margin-inline-start is right; the border side is the typo. Moving it to
   inline-start puts the line 4px in from the row's start, which is the centre
   of the 9px dot, and it stays correct if the document is ever LTR.

   Reported for the package; overridden here so the track reads as one spine.
   -------------------------------------------------------------------------- */
.track__link {
  border-inline-end: 0;
  border-inline-start: 1px solid var(--a-green-12);
}
.track__link--future {
  border-inline-end-style: none;
  border-inline-start-style: dashed;
}

/* --------------------------------------------------------------------------
   §32.5 — two components the .on-dark pass missed.

   §22 gives .chip `color: var(--a-ink-60)` and `.chip.is-on` `color:
   var(--c-ink)`. Both are near-black, which is right on the cream public
   page and unreadable on the admin's ink background: measured on
   --c-ink-2 (#12160F), a-ink-60 comes out at 1.2:1.

   .field__hint is the same omission one line up in §14: the label is flipped,
   the hint under it is not. (--a-cream-60 rather than -50 for the reason
   given in §32.7: -50 measures 4.19:1 on the admin surface.)

   .on-dark already flips .field__label and .input (§14), so these are gaps in
   that same pass rather than new ideas.
   -------------------------------------------------------------------------- */
.on-dark .field__hint { color: var(--a-cream-60); }
.on-dark .chip {
  border: 1px solid var(--ad-line-2);
  color: var(--a-cream-72);
}
.on-dark .chip.is-on {
  border: 1.5px solid var(--c-gold);
  background: var(--a-gold-22);
  color: var(--c-cream);
}

/* --------------------------------------------------------------------------
   §32.6 — the admin calendar.

   §21 declares `.on-dark .day`, `.on-dark .day--requested` and
   `.on-dark .day--confirmed`, so the admin calendar was meant to work on dark.
   Four things it stops short of. Every ratio below is measured against the
   composited surface, not the token.

   1. `.cal__dow` keeps `--a-ink-45` — the weekday row measures 1.2:1 on the
      admin background, which is why it read as a blank strip above the grid.
      At --a-cream-60 it is 5.3:1.
   2. `.day--past` is overridden by `.on-dark .day` on both background and
      colour, so a day that has gone looks exactly like one that is free. The
      fill is what separates them on the public calendar; same here, with the
      number dimmed to --a-cream-60 (5.3:1) rather than to 2.4:1.
   3. The admin has four live states, not two. §22's pill ladder already
      orders them — request, approve, sign, confirmed — so a day takes the
      colour of the chip on that booking's own row rather than a second,
      unrelated colour language. --a-gold-35 is the package's own value for
      `day--requested`; the two middle fills are `pill--approve` and
      `pill--sign`.
   4. `.on-dark .hijri` and `.on-dark .day__hijri` are both --a-cream-40:
      3.2:1 at 10.5px and 3.0:1 at 8.5px. They are meant to read as secondary,
      and at --a-cream-60 they still do — 5.3:1 against full cream's 9.8:1 —
      but they can now be read.
   -------------------------------------------------------------------------- */
.on-dark .cal__dow    { color: var(--a-cream-60); }
.on-dark .hijri       { color: var(--a-cream-60); }
.on-dark .day__hijri  { color: var(--a-cream-60); }

.on-dark .day--past { background: transparent; color: var(--a-cream-60); }

.on-dark .day--requested .day__hijri { color: var(--a-cream-72); }
.on-dark .day--sent   { background: var(--c-gold-chip); color: var(--c-ink); }
.on-dark .day--signed { background: var(--c-gold);       color: var(--c-ink); }

/* The Hijri number sits on the fill, not on the page, in the three solid
   states. On --c-gold it needs full ink to clear 4.5:1. */
.on-dark .day--sent .day__hijri,
.on-dark .day--confirmed .day__hijri { color: rgba(22,33,29,.78); }
.on-dark .day--signed .day__hijri    { color: var(--c-ink); }

/* The legend swatch carries the day's own class so the key cannot drift from
   the grid — but it is not a control, so it must shed the day's geometry and
   the 44px touch minimum §6 gives every interactive element. Without
   min-height the key renders as four tall bars. */
.legend__swatch.day { aspect-ratio: auto; min-height: 0;
                      width: 15px; height: 15px;
                      border-radius: 5px; padding: 0; }

/* A future step's dot is drawn as an outline in --a-ink-35, invisible on dark */
.on-dark .track__dot--future { border-color: var(--ad-line-3); }

/* --------------------------------------------------------------------------
   §32.7 — secondary text sits just under AA, on both surfaces.

   §7 flips `.hint` and `.eyebrow` for dark surfaces at --a-cream-50. Composited
   over the admin background that measures 4.19:1 at 11px — close enough to
   pass a glance and not close enough to pass. The same value on `.ad-row-meta`
   (9.5px) and --a-cream-40 on the chart labels (8px, 3.22:1) are the same
   miss at smaller sizes.

   --a-cream-60 is 5.34:1 and still reads as secondary against full cream's
   9.84:1, which is what these classes are for.

   The light side has the same miss, in ten places: `.hint`, `.field__hint`,
   `.eyebrow` and `.upload` (--a-ink-55, 3.5–3.8:1), `.kv__k`, `.chip` and
   `.pg-why-text` (--a-ink-60, 4.0–4.4:1), `.hijri` and the two quiet
   `.track__label` steps (--a-ink-45, 2.8:1), and `.sigpad__hint` at
   --a-ink-35 (2.2:1) — an instruction you read before signing, not
   decoration.
   --a-ink-72 is the next step in the token set that clears 4.5:1 on every
   surface these appear on — 5.75:1 on --c-canvas, 5.45:1 on the tinted
   supplier strip, the darkest of them — and stays well short of --c-ink.

   The track's three label steps then differ by weight and size rather than by
   colour, which is where that hierarchy already lived: the dot beside each
   one carries the state, and the active step keeps full --c-ink.

   Three more sit on the coloured decline band and its notice, all just under:
   `.badge--declined` is the one badge of its family drawn as white on
   white-20 rather than as a recessed alpha chip (4.23:1, where --a-ink-35
   under cream gives 8.17:1); `.band__sub` at --a-cream-72 is 3.78:1 on
   --c-clay-deep; and `.notice--danger .notice__title` at --c-clay-text is
   4.29:1 where --c-clay-deep gives 5.33:1.

   `.count--decision` is the one badge of the three drawn in --c-white rather
   than --c-ink: 3.6:1 on clay where ink gives 4.6:1, and the other two counts
   already use ink.

   These were measured but not caught: the sweep composited each translucent
   ancestor as though it were opaque, so it reported a bright surface under
   dark text and passed everything on the admin's stacked alpha layers.
   -------------------------------------------------------------------------- */
.hint,
.hijri,
.kv__k,
.chip,
.eyebrow,
.field__hint,
.upload,
.sigpad__hint,
.track__label--done,
.track__label--future { color: var(--a-ink-72); }

.badge--declined { background: var(--a-ink-35); color: var(--c-cream); }
.band__sub       { color: var(--a-cream-85); }
.notice--danger .notice__title { color: var(--c-clay-deep); }
.on-dark .hint    { color: var(--a-cream-60); }
.on-dark .eyebrow { color: var(--a-cream-60); }
.count--decision  { color: var(--c-ink); }

/* --------------------------------------------------------------------------
   §32.8 — the dark sheet is the same colour as the page it opens over.

   §19 gives `.sheet--dark` `background: var(--c-green-admin)`, which is the
   exact value `.admin` paints the page with. The scrim between them lands on
   [16,25,22] against the page's [21,32,28] — measured, the sheet and the page
   behind it differ by 1.16:1, so the panel reads as the page changing rather
   than a panel arriving over it. The shadow §19 relies on does nothing on a
   dark ground.

   The mockups show these sheets on the white mockup page, so the collision
   was never visible to the designer — the same reason §14's .on-dark pass has
   the gaps in §32.5.

   Lifted by --a-cream-14 over the same ground (an opaque paint, not a
   translucent one — a modal must not show the page through it) it reads
   1.59:1 against the scrimmed page, and cream text on it measures 9.7:1. The
   top edge carries the rest at 2.0:1.
   -------------------------------------------------------------------------- */
.sheet--dark {
  background:
    linear-gradient(var(--a-cream-14), var(--a-cream-14)),
    var(--c-green-admin);
  border-top: 1px solid var(--ad-line-3);
}

/* --------------------------------------------------------------------------
   §32.9 — .bell is not the square it declares.

   §26 sets `.bell { width: 38px; height: 38px }`, and §6 gives every button
   `min-height: var(--tap-min)`. min-height wins over height, so the icon
   button renders 38 wide and 44 tall — a squashed rectangle, 6px out of line
   with the title beside it. It was masked while two of them sat side by side;
   with one button left in the header it reads as a mistake, which is how the
   owner found it.

   The touch minimum is right and stays: the button becomes a true 44 square
   rather than a 38 one stretched to reach it. Same shape of bug as the legend
   swatch in §32.6.
   -------------------------------------------------------------------------- */
.bell { width: var(--tap-min); height: var(--tap-min); min-height: var(--tap-min); }

/* --------------------------------------------------------------------------
   §32.10 — the financial panel's tones were written into an attribute.

   The booking detail marks المحصّل and المتبقي with `data-tone="success"` /
   `"warning"`. Nothing in this stylesheet has ever styled `[data-tone]`, so
   both figures rendered the same plain cream as the two beside them and the
   panel said nothing at a glance — which is the whole job of a figure tile.

   The vocabulary already existed: §29 gives `.stat--due` for money owed, and
   §11 gives `.money--paid` its sage on dark. The settled counterpart of
   `.stat--due` was simply never written, so here it is, built from the same
   two tokens. Fourth instance of state written into an invented attribute
   rather than the class the stylesheet declares.
   -------------------------------------------------------------------------- */
.stat--paid { background: var(--a-sage-14); border: 1px solid var(--a-sage-30); }
.stat--paid .stat__label, .stat--paid .stat__value { color: var(--c-sage-text); }

/* --------------------------------------------------------------------------
   §32.11 — the figure tile's two lines were one line.

   §29 declares `.stat__label` and `.stat__value` with `margin-top: 7px` on the
   value, and the markup writes both as `<span>`. A span is inline; margin-top
   does nothing to an inline box. So the label and the number rendered on the
   same line and overlapped by 18px — «الإجمالي128,000» — in every financial
   panel and on the dashboard, since the day the component was written.

   The fix is one word. The measured gap goes from -18px to +7px, which is what
   the rule always said.
   -------------------------------------------------------------------------- */
.stat__label, .stat__value { display: block; }

/* --------------------------------------------------------------------------
   §32.12 — the progress track, and the copy of it the admin drew.

   Two faults, one component.

   First the rhythm. A step is a dot beside a label with a timestamp under it
   when one exists. A booking whose offer went out in the same minute as the
   request has no separate offer_sent_at, so that step rendered 18px tall
   between two 45px ones and the four labels bunched together. `.track` had
   `gap: 2px`, which is a gap between rows of a list, not the spacing of a path.

   Second, the admin never used this component. It built its own rows with
   `.kv__v` for every label, so `.track__label--done/--active/--future` — here
   since §15 — were dead, all four steps rendered identically bright, and you
   could not see where a booking stood without decoding the dots. It uses the
   real one now, connectors included, so the manager and the customer are shown
   the same path.

   Those label classes were only ever given ink colours, for the public page.
   On the admin's dark ground they need cream, and so does the connector, whose
   `--a-green-12` hairline was invisible there — on the inline-start edge §32.3
   moved it to, not the inline-end one §15 names.
   -------------------------------------------------------------------------- */
.track { gap: 0; }
.track__step { align-items: flex-start; }
.track__dot { margin-top: 4px; }
.track__body { display: flex; flex-direction: column; gap: 3px; }

/* A done dot is --c-green (#1E3B32) — all but invisible on #15201C. Sage is
   the palette's completed-on-dark, the same one .money--paid uses. */
.on-dark .track__dot--done { background: var(--c-sage-text); }

.on-dark .track__label--done    { color: var(--a-cream-60); }
.on-dark .track__label--active  { color: var(--c-cream); }
/* Not a fainter cream for a future step: -50 measures 4.19:1 on this ground
   (§32.7). Its hollow dot and lighter weight carry the difference instead. */
.on-dark .track__label--future  { color: var(--a-cream-60); }
.on-dark .track__label--stopped { color: var(--c-clay-light); }
/* §32.3 already moved this connector from inline-end to inline-start; the
   colour has to follow it there, not to the edge the package named. */
.on-dark .track__link { border-inline-start-color: var(--ad-line-3); }

/* --------------------------------------------------------------------------
   §32.13 — the ended badge lightened the ground it needed to stand on.

   `.band--ended` is a 72% ink wash over the cream page, and cream text on it
   measures about 6:1. `.badge--ended` then lays 14% cream on top of that,
   which lifts the ground toward its own text and drops the pair to 4.28:1 —
   under AA on 11px, on the one screen a customer reads after their wedding.

   The same mistake §32.7 fixed for `.badge--declined`, and the same answer: a
   badge on an already-dark band deepens its ground rather than lightening it.
   -------------------------------------------------------------------------- */
.badge--ended { background: var(--a-ink-35); color: var(--c-cream); }

/* --------------------------------------------------------------------------
   §32.14 — the upload card read one word per line.

   `.upload` is a column flex, drawn for an icon above a label. The markup put
   loose text and three `<span class="ltr">` runs straight inside it, so each
   became its own flex item: «اضغط لإرفاق صورة الإيصال —» / JPG / أو / PNG /
   أو / PDF / «، بحد أقصى ١٠ ميجابايت», seven lines down a column, the comma
   orphaned at the start of the last one. On both upload cards, since they were
   written.

   The markup now wraps the label in one element. This makes it a proper block
   so it takes the card's width and wraps as a sentence.
   -------------------------------------------------------------------------- */
.upload__text { display: block; max-width: 28ch; }

/* --------------------------------------------------------------------------
   §32.15 — the info notice was unreadable on the admin's ground.

   §21's `.on-dark .notice--info` sets `color: var(--a-cream-50)`, and -50
   measures 4.19:1 on #15201C (§32.7 measured it once already for hints). Every
   `.notice--info` in the admin has therefore been under AA since the on-dark
   pass — found on the cancelled booking, whose reason for being cancelled was
   the least readable thing on its page.

   -72, the same as `.notice--warn` beside it.
   -------------------------------------------------------------------------- */
.on-dark .notice--info { color: var(--a-cream-72); }

/* ==========================================================================
   33. THE ADMIN'S LIGHT THEME

   The admin is dark because it is a back-office tool. Some offices are bright,
   so it can be switched. This is a palette, not a second stylesheet: every
   `.ad-*` rule and every `.on-dark` rule is written against tokens, so
   redefining the tokens moves the whole app without touching a single
   component. Nothing below changes a selector, a size or a layout.

   It is a DESIGNED light palette, not the dark one mirrored. A straight
   inversion was measured first and produced 44 contrast failures across six
   screens, for three reasons, each fixed here rather than papered over:

   1. A fill and a hairline invert in opposite directions. On a dark ground
      both are lighter than the page; on a light ground the fill is still
      lighter but the line must go darker. That is why `--ad-line-*` exists
      above — the borders were moved onto their own tokens first.

   2. Some tokens carry two roles. `--c-cream` is text on dark AND the fill of
      the selected pill; `--c-sage` is a fill that carries ink text AND a text
      colour on dark. Each was split before this block was written, so the
      halves can theme independently.

   3. The text ramp does not mirror, it compresses. Cream at 60% reads on a
      dark ground; ink at 60% measures 3.82:1 on this one and fails. Secondary
      text therefore lands on ink-72 here whatever it was there.

   Verified by sweeping every admin view and every booking state in both
   themes, through the real alpha stack.
   ========================================================================== */

[data-theme="light"] body,
[data-theme="light"] .admin { background: var(--c-canvas); }

/* Scoped to the root, not to `.admin`: a bottom sheet is appended to <body>,
   outside the app shell, so a `.admin`-scoped block left every decision the
   manager makes rendering as a dark slab over a light page. The attribute is
   only ever written by admin.html, so the public page is untouched. */
:root[data-theme="light"] {
  /* The ground, and the surfaces above it. White rather than ink: a card sits
     on top of the page in both themes, and on a light page that means lighter. */
  --c-green-admin: #E8E2D6;
  --a-cream-05: rgba(255,255,255,.40);
  --a-cream-07: rgba(255,255,255,.55);
  --a-cream-10: rgba(255,255,255,.70);
  --a-cream-14: rgba(255,255,255,.88);
  --a-cream-18: rgba(255,255,255,.96);

  /* Lines, which go the other way. */
  --ad-line:   var(--a-ink-12);
  --ad-line-2: var(--a-ink-25);
  --ad-line-3: var(--a-ink-45);
  --ad-field:  var(--c-white);
  --ad-well:   var(--a-ink-12);

  /* Text. Compressed upward, per (3) above. */
  --c-cream:    var(--c-ink);
  --a-cream-85: var(--a-ink-78);
  --a-cream-72: var(--a-ink-72);
  --a-cream-60: var(--a-ink-72);
  --a-cream-50: var(--a-ink-72);
  --a-cream-40: var(--a-ink-55);
  --a-cream-25: var(--a-ink-35);
  --a-cream-30: var(--a-ink-45);

  /* Accents. Every one of these already had an on-light twin in §1 — the
     palette was built for both grounds from the start, which is most of why
     this theme is a token block and not a rewrite. */
  /* --c-gold-text-dk exists in §1 for exactly this: gold text sitting on a
     gold-tinted light surface, which is what every pending chip is. The plain
     --c-gold-text measured 4.11:1 on that tint. */
  --c-gold-light: var(--c-gold-text-dk);
  /* Clay text on a 16% clay tint is the tightest pair in the app: --c-clay-text
     measures 3.55:1 on it and even --c-clay-deep only reaches 4.41:1. One notch
     deeper clears AA at 4.89:1 and is still unmistakably clay. */
  --c-clay-light: #86441F;
  /* and sage text on a sage-tinted card needs to go deeper still — #3F7A63
     measured 4.2:1 on it */
  --c-sage-text:  #2C6349;
  --a-goldl-14:   var(--a-gold-12);
  --a-goldl-30:   var(--a-gold-35);

  /* The selected pill inverts: a light fill on a dark bar becomes a dark fill
     on a light one, which is what the public page's primary button already
     does. */
  --c-cream-fill: var(--c-green);

  /* Validated against #F1EDE4, the tile this chart sits on: ΔE 19.3 to normal
     vision, 12.7 under protanopia, both marks clear of 3:1 on the surface. */
  --viz-a: #A07B38;
  --viz-b: #25634A;

  /* The focus ring is a role too: gold-on-dark there, gold-on-light here.
     Every `.on-dark :focus-visible` rule reads this one token, so redefining
     it moves every ring in the app. */
  --ring-dark: var(--ring);
}

[data-theme="light"] .btn--primary,
[data-theme="light"] .seg-item.is-on,
[data-theme="light"] .chipf[aria-pressed="true"],
[data-theme="light"] .tab[aria-selected="true"] { color: #F4EDE2; }
/* the count rides inside the chip, so it inverts with it */
[data-theme="light"] .chipf[aria-pressed="true"] .chipf__n { color: rgba(244,237,226,.72); }
/* the count rides inside the tab, so it inverts with it */
[data-theme="light"] .tab[aria-selected="true"] .tab__n { color: rgba(244,237,226,.72); }
