/* ============================================================================
   v3-primitives.css — the schedule-slot + status/people primitive (UI2)

   THE ONE IMPLEMENTATION. Design System v3, artifact D §06 (status & people)
   and §07 (schedule slots), verbatim from the operator bundle
   (sha256 40e1fcef…07a280, payload a24b9187-…).

   Consumers: tenant_admin.html, booking.html, employee.html — each links this
   file and uses the class names below. No surface re-declares them.

   Declared values only: every colour comes from design/tokens.json → v3, and
   the three AA corrections UI0 measured are applied here (they are exactly the
   slot/chip pairs this primitive paints):

     1. light muted text uses the declared neutral step #6B6479, not #79728C
        (5.64 on surface / 5.29 on canvas / 4.92 on surface-sunken)
     2. the Booked slot boundary uses accent (#4A2E8F light / #B79EE8 dark)
        instead of accent-border (#D6C9F0 / #4A3A75), which measured 1.56 / 1.79
        against the surfaces — below the 3.0 non-text requirement
     3. slot fills alone never separate states (fill vs surface is 1.15 light /
        1.07 dark), so every state carries a LABEL and, where the label cannot
        be shown, a DOT — colour is never the only cue (WCAG 1.4.1)

   Ordering rule (learned in UI1): :root and [data-ds-theme="dark"] are both
   specificity (0,1,0) on <html>, so SOURCE ORDER decides. The light block is
   first, dark second. Reordering them silently kills dark.
   ============================================================================ */

/* --------------------------------------------------------------- FACES --- */
/* UI7: the faces the type roles fall back to, declared HERE because this sheet
   is the one place every v3 surface gets its values from. The bundle's own
   display face (Big Shoulders Display) and UI face (Archivo) are still not
   vendored — the UI0 licence gate is open — so 'IBM Plex Sans' is what actually
   paints, and until this existed only tenant_admin.html declared the files: the
   booking page, the portal and the staff app were quietly painting a system
   font that the declared stacks never named. IBM Plex is OFL; the licence ships
   beside the files (backend/static/fonts/OFL-LICENSE.txt). Identical
   declarations in tenant_admin.html are harmless. */
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400;font-display:swap;src:url('/static/fonts/IBMPlexSans-Regular.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:600;font-display:swap;src:url('/static/fonts/IBMPlexSans-SemiBold.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url('/static/fonts/IBMPlexMono-Regular.woff2') format('woff2')}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url('/static/fonts/IBMPlexMono-SemiBold.woff2') format('woff2')}

/* ---------------------------------------------------------------- LIGHT --- */
:root{
  /* surfaces & text */
  --v3-canvas:#F8F7FB; --v3-surface:#FFFFFF; --v3-surface-sunken:#F1EEF7;
  --v3-text-primary:#161320; --v3-text-secondary:#544E63;
  --v3-text-muted:#6B6479; /* UI0 adjustment 1 — AA step, 5.64 on surface */
  --v3-border-subtle:#E7E2F0; --v3-border-default:#D6CEE6; --v3-border-strong:#BAAED3;
  /* accent (Persian indigo) — the declared DEFAULT, as a FALLBACK.
     A surface that must follow tenant branding declares the --v3-brand-accent
     hook in its own :root, pointing at its runtime --brand; this file never
     declares that name, so the consumer's hook always wins (a stylesheet cannot
     otherwise be told "only if unset" — declaring it here would re-override the
     consumer, which is exactly the cascade trap the render harness caught).
     The AA evidence in docs/product/ux/UI2_SLOT_PRIMITIVE.md is measured on the
     declared indigo family, so a surface that opts in owns its own measurement
     (cross-cutting rule: white-label is authoritative, indigo is only a default). */
  --v3-accent:var(--v3-brand-accent,#4A2E8F); --v3-accent-subtle:#F1EDFB; --v3-accent-border:#D6C9F0;
  --v3-on-accent:#FFFFFF; --v3-spark:#9A1A5D;
  /* status families (declared) */
  --v3-success:#0F7F52; --v3-success-bg:#ECFBF3;
  --v3-status-confirmed-fg:#0F7F52; --v3-status-confirmed-bg:#ECFBF3;
  --v3-status-pending-fg:#9A6300; --v3-status-pending-bg:#FDF3E1;
  --v3-status-cancelled-fg:#B3261E; --v3-status-cancelled-bg:#FBECEB;
  --v3-status-rescheduled-fg:#3B6FA6; --v3-status-rescheduled-bg:#EAF1FA;
  --v3-status-neutral-fg:var(--v3-text-secondary); --v3-status-neutral-bg:var(--v3-surface-sunken);
  /* app-only tones alias the declared families — no new hue is invented */
  --v3-status-info-fg:var(--v3-status-rescheduled-fg); --v3-status-info-bg:var(--v3-status-rescheduled-bg);
  --v3-status-warning-fg:var(--v3-status-pending-fg); --v3-status-warning-bg:var(--v3-status-pending-bg);
  --v3-status-danger-fg:var(--v3-status-cancelled-fg); --v3-status-danger-bg:var(--v3-status-cancelled-bg);
  /* artifact S declares a 'Next' status: the design's statusColor() paints it with the
     theme-aware accent pair (unlike the fixed plates above) */
  --v3-status-next-fg:var(--v3-accent); --v3-status-next-bg:var(--v3-accent-subtle);
  /* artifact-S band + elevation (declared in tokens.json -> v3.semantic) */
  --v3-accent-solid-band:#2C1A56; --v3-on-accent-band:#F4F0FC;
  --v3-shadow-1:0 1px 2px rgb(22 19 32 / .06), 0 0 0 1px rgb(22 19 32 / .04);
  /* slot states (design/tokens.json → v3.schedule_slot) */
  --v3-slot-open-bg:var(--v3-surface); --v3-slot-open-border:var(--v3-border-default); --v3-slot-open-label:var(--v3-text-muted);
  --v3-slot-booked-bg:var(--v3-accent-subtle); --v3-slot-booked-border:var(--v3-accent); --v3-slot-booked-label:var(--v3-accent);
  --v3-slot-blocked-bg:var(--v3-surface-sunken); --v3-slot-blocked-border:var(--v3-border-subtle); --v3-slot-blocked-label:var(--v3-text-muted);
  --v3-slot-selected-bg:var(--v3-accent); --v3-slot-selected-border:var(--v3-accent); --v3-slot-selected-label:var(--v3-on-accent);
  /* shape, type, motion */
  --v3-pill:999px; --v3-slot-radius:0; --v3-slot-h:52px; --v3-target-min:44px;
  --v3-font-display:'Big Shoulders Display',Archivo,'IBM Plex Sans',ui-sans-serif,system-ui,sans-serif;
  --v3-font-ui:Archivo,'IBM Plex Sans',ui-sans-serif,system-ui,sans-serif;
  --v3-font-data:'IBM Plex Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  --v3-dur-fast:140ms; --v3-dur-base:200ms; --v3-dur-slow:280ms;
  --v3-ease:cubic-bezier(.2,0,0,1);
}

/* ----------------------------------------------------------------- DARK --- */
/* After light on purpose — equal specificity means source order decides. */
[data-ds-theme="dark"]{
  --v3-canvas:#120F1B; --v3-surface:#1B1729; --v3-surface-sunken:#0D0A15;
  --v3-text-primary:#EEEAF6; --v3-text-secondary:#BBB3D1; --v3-text-muted:#8A82A0;
  --v3-border-subtle:#241F35; --v3-border-default:#2D2740; --v3-border-strong:#453B63;
  --v3-accent:var(--v3-brand-accent,#B79EE8); --v3-accent-subtle:#241A3D; --v3-accent-border:#4A3A75;
  --v3-on-accent:#1B1030; --v3-spark:#E8619F;
  --v3-success:#3EC084; --v3-success-bg:#152A20;
  /* artifact-S band + elevation, dark (tokens.json -> v3.semantic.dark) */
  --v3-accent-solid-band:#1B1030; --v3-on-accent-band:#EEE4FA;
  --v3-shadow-1:0 1px 2px rgb(0 0 0 / .35), 0 0 0 1px rgb(0 0 0 / .25);
  /* Confirmed is the ONE theme-aware status: the design's statusColor() maps it
     to the success family, which the token map declares per theme. */
  --v3-status-confirmed-fg:#3EC084; --v3-status-confirmed-bg:#152A20;
  /* Pending/Cancelled/Rescheduled keep the DECLARED plate pairs in both themes:
     the design's statusColor() returns those fixed hexes ("#9A6300", "#B3261E",
     "#3B6FA6") regardless of theme, so in dark the pills stay light on the dark
     canvas exactly as the bundle paints them. Do not "fix" this into invented
     dark hues — that would be a value the design never declared. */
}

/* --------------------------------------------------------- SCHEDULE SLOT --- */
/* Artifact D §07: 52px tall, square, 1px boundary, mono label, centred.
   Fill alone does not separate states (UI0 adjustment 3) — the label does. */
.v3-slot{
  box-sizing:border-box;
  min-height:var(--v3-slot-h);
  display:flex; align-items:center; justify-content:center; gap:6px;
  padding:6px 8px;
  border:1px solid var(--v3-border-default);
  border-radius:var(--v3-slot-radius);
  background:var(--v3-slot-open-bg);
  color:var(--v3-slot-open-label);
  font:700 12px/1.2 var(--v3-font-data);
  text-align:center;
  transition:background var(--v3-dur-fast) var(--v3-ease),
             border-color var(--v3-dur-fast) var(--v3-ease),
             color var(--v3-dur-fast) var(--v3-ease);
}
.v3-slot--open{background:var(--v3-slot-open-bg);border-color:var(--v3-slot-open-border);color:var(--v3-slot-open-label)}
.v3-slot--booked{background:var(--v3-slot-booked-bg);border-color:var(--v3-slot-booked-border);color:var(--v3-slot-booked-label)}
.v3-slot--blocked{background:var(--v3-slot-blocked-bg);border-color:var(--v3-slot-blocked-border);color:var(--v3-slot-blocked-label)}
.v3-slot--selected{background:var(--v3-slot-selected-bg);border-color:var(--v3-slot-selected-border);color:var(--v3-slot-selected-label)}

/* interactive slots: booking picker + admin lanes. Large targets, visible
   focus, no hover-only affordance (artifact D §03). */
.v3-slot--interactive{min-height:var(--v3-target-min);cursor:pointer}
.v3-slot--interactive:hover:not([disabled]):not(.v3-slot--selected){border-color:var(--v3-accent);color:var(--v3-accent)}
.v3-slot--interactive:focus-visible{outline:2px solid var(--v3-accent);outline-offset:2px}
.v3-slot[disabled],.v3-slot--disabled{opacity:.55;cursor:not-allowed}

/* The non-colour cue: a dot that survives when the label cannot be shown
   (narrow cells, dense month grids, the mobile day strip). */
.v3-slot__dot{
  flex:none; width:6px; height:6px; border-radius:var(--v3-pill);
  background:currentColor;
}
.v3-slot--dot-only{font-size:0}                 /* label hidden, dot carries shape */
.v3-slot--dot-only .v3-slot__dot{width:8px;height:8px}
.v3-slot--blocked .v3-slot__dot{background:transparent;border:2px solid currentColor}  /* blocked = hollow */
.v3-slot--open .v3-slot__dot{background:transparent;border:1px solid currentColor}      /* open = hairline */

/* --------------------------------------------------- STATUS & PEOPLE CHIPS --- */
/* Artifact D §06: pill, 6px dot in the fg colour, 13/700 label. */
.v3-chip{
  display:inline-flex; align-items:center; gap:6px;
  padding:6px 12px; border-radius:var(--v3-pill);
  background:var(--v3-status-neutral-bg); color:var(--v3-status-neutral-fg);
  border:1px solid transparent;
  font:700 13px/1.25 var(--v3-font-ui);
  white-space:nowrap;
}
.v3-chip__dot{flex:none;width:6px;height:6px;border-radius:var(--v3-pill);background:currentColor}
.v3-chip--confirmed{background:var(--v3-status-confirmed-bg);color:var(--v3-status-confirmed-fg)}
.v3-chip--pending{background:var(--v3-status-pending-bg);color:var(--v3-status-pending-fg)}
.v3-chip--cancelled{background:var(--v3-status-cancelled-bg);color:var(--v3-status-cancelled-fg)}
.v3-chip--rescheduled{background:var(--v3-status-rescheduled-bg);color:var(--v3-status-rescheduled-fg)}
.v3-chip--neutral{background:var(--v3-status-neutral-bg);color:var(--v3-status-neutral-fg)}
.v3-chip--info{background:var(--v3-status-info-bg);color:var(--v3-status-info-fg)}
.v3-chip--warning{background:var(--v3-status-warning-bg);color:var(--v3-status-warning-fg)}
.v3-chip--danger{background:var(--v3-status-danger-bg);color:var(--v3-status-danger-fg)}
/* app-only states mapped into declared families (design has no such plate):
   completed shares the Confirmed green (same happy-path lifecycle),
   no_show shares the Cancelled red (same not-happy-path). The word
   disambiguates; no new hue is invented. */
.v3-chip--completed{background:var(--v3-success-bg);color:var(--v3-success)}
.v3-chip--no_show{background:var(--v3-status-danger-bg);color:var(--v3-status-danger-fg)}
/* a blocked slot shows up in lists as a chip too */
.v3-chip--blocked{background:var(--v3-slot-blocked-bg);color:var(--v3-slot-blocked-label)}
.v3-chip--next{background:var(--v3-status-next-bg);color:var(--v3-status-next-fg)}

/* people: initials pill + name (artifact D §06) */
.v3-person{
  display:inline-flex; align-items:center; gap:10px;
  padding:8px 14px 8px 8px; border-radius:var(--v3-pill);
  background:var(--v3-surface); border:1px solid var(--v3-border-subtle);
  color:var(--v3-text-primary);
  font:600 13px/1.25 var(--v3-font-ui);
}
.v3-person__initials{
  flex:none;width:28px;height:28px;border-radius:var(--v3-pill);
  display:flex;align-items:center;justify-content:center;
  background:var(--v3-accent-subtle);color:var(--v3-accent);
  font:700 12px/1 var(--v3-font-ui);
}
.v3-person--info .v3-person__initials{background:var(--v3-status-info-bg);color:var(--v3-status-info-fg)}
.v3-person--warning .v3-person__initials{background:var(--v3-status-warning-bg);color:var(--v3-status-warning-fg)}
.v3-person--neutral .v3-person__initials{background:var(--v3-surface-sunken);color:var(--v3-text-secondary)}

/* interactive people pill (UI4 — the booking flow's `With` picker reuses THIS
   primitive rather than declaring a second one). Selection is a new state of the
   same component: it adds a boundary and a fill, and the accessible pressed
   state carries it too, so colour is never the only cue (artifact D §03/§06). */
.v3-person--interactive{cursor:pointer;min-height:var(--v3-target-min);padding:6px 14px 6px 6px;transition:border-color var(--v3-dur-fast) var(--v3-ease),background var(--v3-dur-fast) var(--v3-ease)}
.v3-person--interactive:hover:not([disabled]){border-color:var(--v3-border-strong)}
.v3-person--interactive:focus-visible{outline:2px solid var(--v3-accent);outline-offset:2px}
.v3-person--interactive[disabled]{opacity:.55;cursor:not-allowed}
/* Selection is carried by the BOUNDARY and the initials fill, not by tinting the
   label: a tenant whose brand is a dark violet (the platform default #4f46e5)
   measured 2.59 painting `accent` text on the dark accent-subtle plate. The label
   uses the neutral text role, which is readable on either plate and under any
   brand, and the accessible pressed state carries it too. */
.v3-person--selected,.v3-person--selected:hover{background:var(--v3-accent-subtle);border-color:var(--v3-accent);color:var(--v3-text-primary)}
.v3-person--selected .v3-person__initials{background:var(--v3-accent);color:var(--v3-on-accent)}

/* ------------------------------------------------------------ BEHAVIOUR --- */
@media (prefers-reduced-motion: reduce){
  .v3-slot,.v3-chip,.v3-person{transition:none}
}
@media (pointer:coarse){
  .v3-slot--interactive{min-height:var(--v3-target-min)}
  .v3-chip{min-height:28px}
}
/* forced-colors: keep the boundary and the label legible when the OS overrides
   our palette (colour is not the only cue, so the states survive). */
@media (forced-colors: active){
  .v3-slot{border-color:currentColor}
  .v3-slot--selected{outline:2px solid currentColor}
}
