/* brand.css — Syncc brand tokens (PR A foundation)
 *
 * Source of truth pulled from Claude Design's tokens-v3.jsx +
 * CampuSync Landing.html. This file ONLY registers tokens — it
 * does not restyle any existing surface. Subsequent PRs (admin
 * dashboard, my-day, people, wizard reskin, etc.) consume these
 * variables to land the new brand without churning every CSS file
 * at once.
 *
 * Naming convention: --brand-* for new tokens so they coexist
 * cleanly with the legacy --color-primary / --color-nav theme
 * variables in base.html. Once every consumer is migrated the
 * legacy block in base.html can be removed.
 */

:root {
  /* ── Brand teals — single accent system ─────────────────────────── *
   * `--brand-teal` is the working accent that most surfaces consume
   * (icons, buttons, focus rings). Iterated through:
   *   v1 = #44D6D6 (vivid) — too bright, washes out on white
   *   v2 = #0E5E48 (deep teal-charcoal) — too dark; buttons read
   *        muddy. Maintainer pushback: "buttons were good, dark
   *        green did not like it".
   * Retuned teal → SLATE (#5B7AB8, "steel blue") — the maintainer's
   * chosen brand default. `--brand-teal` is the legacy working-accent
   * token most surfaces consume; retuning it here (together with the
   * #1137 data-palette + theme-primary slate) flips the whole app to
   * slate. The token NAME is kept `--brand-teal` so every consumer keeps
   * working without a rename. (#00B0B1 was the prior teal; the `.dark`
   * override below brightens to a slate-bright.) */
  --brand-teal:        #5B7AB8;
  --brand-teal-bright: #93AEE6;
  --brand-teal-dark:   #486193;
  --brand-teal-soft:   rgba(91, 122, 184, 0.10);
  --brand-teal-line:   rgba(91, 122, 184, 0.28);
  --brand-teal-glow:   rgba(91, 122, 184, 0.45);

  /* ── Deep navy ground (dark mode + branded surfaces) ────────────── */
  --brand-navy:        #0A1628;
  --brand-navy-2:      #0E1D33;
  --brand-navy-3:      #142745;
  --brand-navy-black:  #060E1C;  /* footer-deep */
  --brand-page-deep:   #060B17;  /* app page bg (dark) — matches the My Day shell so every surface shares one background */
  /* Hairlines — bumped May 16 (was 0.08 / 0.04) so card / row
   * borders are *visible* but still soft.  The maintainer
   * reported "borderlines barely visible in both light and dark";
   * the old values met the Apple-HIG "hairline" word but failed
   * the read-at-a-glance test on real-device passes. */
  --brand-line-dk:     rgba(255, 255, 255, 0.14);
  --brand-line-dk-soft:rgba(255, 255, 255, 0.08);

  /* ── Light surfaces — warm-cool paper, not cold slate ───────────── */
  --brand-paper:       #FAFBFB;
  --brand-paper-2:     #F2F5F4;
  --brand-card:        #ffffff;
  /* Hairlines (light surfaces).  Bumped May 16 — same reasoning
   * as the dark-mode pair above; the prior 0.10 / 0.06 was
   * essentially invisible on white card-on-white-page contexts. */
  --brand-line-lt:     rgba(10, 22, 40, 0.16);
  --brand-line-lt-soft:rgba(10, 22, 40, 0.10);

  /* ── Ink scale (light mode text) ────────────────────────────────── */
  --brand-ink:         #0A1628;  /* primary */
  --brand-ink-2:       #2C3D52;  /* secondary */
  --brand-ink-3:       #5C6B7E;  /* meta */
  --brand-ink-4:       #8B98A8;  /* tertiary */

  /* ── Status (used sparingly — teal is primary) ──────────────────── */
  --brand-emerald:     #10B981;
  --brand-amber:       #B45309;
  --brand-orange:      #F97316;  /* break/passing-period accent */
  --brand-rose:        #E11D48;

  /* ── Typography ─────────────────────────────────────────────────── */
  --brand-font-display: 'Instrument Serif', 'Times New Roman', serif;
  --brand-font-body:    'DM Sans', -apple-system, BlinkMacSystemFont,
                        'Segoe UI', sans-serif;

  /* ── Geometry ───────────────────────────────────────────────────── */
  --brand-radius-btn:  10px;
  --brand-radius-card: 18px;
  --brand-radius-pill: 999px;

  /* ── Shadows ────────────────────────────────────────────────────── */
  --brand-shadow-sm:   0 1px 2px rgba(10, 22, 40, 0.04),
                       0 8px 24px -12px rgba(10, 22, 40, 0.10);
  --brand-shadow-cta:  0 1px 0 rgba(255, 255, 255, 0.30) inset,
                       0 8px 24px -8px rgba(0, 176, 177, 0.55);
  --brand-shadow-dk:   0 1px 0 rgba(255, 255, 255, 0.04) inset,
                       0 18px 40px -22px rgba(0, 0, 0, 0.60);

  /* ── Motion ─────────────────────────────────────────────────────── */
  --brand-ease:        cubic-bezier(0.32, 0.72, 0, 1);
  --brand-dur-fast:    180ms;
  --brand-dur:         250ms;
}

/* Dark-mode overrides — `.dark` class is set by the bootstrap script
 * in base.html before paint, so these are read on first render. */
.dark {
  --brand-paper:       var(--brand-navy);
  --brand-paper-2:     var(--brand-navy-2);
  --brand-card:        var(--brand-navy-2);
  --brand-line-lt:     var(--brand-line-dk);
  --brand-line-lt-soft:var(--brand-line-dk-soft);
  --brand-ink:         #ffffff;
  --brand-ink-2:       rgba(255, 255, 255, 0.78);
  --brand-ink-3:       rgba(255, 255, 255, 0.55);
  --brand-ink-4:       rgba(255, 255, 255, 0.38);
  --brand-shadow-sm:   var(--brand-shadow-dk);
  /* Brighten the slate accent in dark mode — a lighter steel-blue
   * (#93AEE6) reads cleanly on the navy ground (the light-mode #5B7AB8
   * would sink into a dark page). Was vivid teal #44D6D6. */
  --brand-teal:        #93AEE6;
  --brand-teal-soft:   rgba(147, 174, 230, 0.10);
  --brand-teal-line:   rgba(147, 174, 230, 0.28);
  --brand-teal-glow:   rgba(147, 174, 230, 0.45);
}

/* On phones/tablets, don't paint the document scrollbar — a native-app feel.
 * Scrolling is unchanged; mobile scrollbars are overlay, so there's no layout
 * shift. (In-app surfaces already hide their own inner scrollbars; this covers
 * the public pages + the page document.) */
@media (max-width: 1023px) {
  html { scrollbar-width: none; -ms-overflow-style: none; }
  html::-webkit-scrollbar { width: 0; height: 0; display: none; }
}

/* Dark-mode page background — navy base + a soft top-right teal
 * aurora, mirroring the My Day hero recipe at body scale.
 * Maintainer (May 14): "i love this navy green blend… could we
 * apply this with colors the same way in dark mode, when light
 * mode on keep the squares in light color." Light mode stays
 * untouched (cards + sections render on the slate-50 paper). The
 * gradient is fixed-attached so it doesn't move on scroll — feels
 * like a mood lit room rather than a parallax effect. */
html.dark body {
  background-color: var(--brand-page-deep);
  background-image:
    radial-gradient(ellipse 1100px 600px at 90% -5%,
                    rgba(68, 214, 214, 0.22) 0%,
                    rgba(0, 176, 177, 0.08) 40%,
                    transparent 70%),
    radial-gradient(ellipse 900px 500px at 10% 110%,
                    rgba(0, 176, 177, 0.08) 0%,
                    transparent 60%);
  background-attachment: fixed;
  background-repeat: no-repeat;
}

/* ── Brand primitives ─────────────────────────────────────────────── *
 * Tiny set of branded primitives subsequent PRs reuse. Keep this
 * file small — page-specific styles live in their own file (e.g.
 * admin-dashboard.css) and import these tokens. */

.brand-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font: 500 11px/1 var(--brand-font-body);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--brand-teal-dark);
}
.brand-eyebrow::before {
  content: '';
  width: 22px;
  height: 1px;
  background: var(--brand-teal);
}
.dark .brand-eyebrow {
  color: var(--brand-teal-bright);
}
.dark .brand-eyebrow::before {
  background: var(--brand-teal-bright);
}

.brand-display {
  font-family: var(--brand-font-display);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.012em;
  color: var(--brand-ink);
  margin: 0;
}
.brand-display em {
  font-style: italic;
  color: var(--brand-teal-dark);
}
.dark .brand-display em {
  color: var(--brand-teal-bright);
}

.brand-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font: 600 14.5px/1 var(--brand-font-body);
  letter-spacing: -0.005em;
  padding: 13px 22px;
  border-radius: var(--brand-radius-btn);
  border: 1px solid transparent;
  cursor: pointer;
  transition: all var(--brand-dur-fast) var(--brand-ease);
  white-space: nowrap;
  text-decoration: none;
}
.brand-btn-primary {
  background: var(--brand-teal);
  color: var(--brand-navy);
  box-shadow: var(--brand-shadow-cta);
}
.brand-btn-primary:hover {
  background: var(--brand-teal-bright);
  transform: translateY(-1px);
}
.brand-btn-secondary {
  background: transparent;
  color: var(--brand-ink-2);
  border-color: var(--brand-line-lt);
}
.brand-btn-secondary:hover {
  border-color: var(--brand-ink-3);
}
.dark .brand-btn-secondary {
  color: rgba(255, 255, 255, 0.85);
  border-color: rgba(255, 255, 255, 0.18);
}
.dark .brand-btn-secondary:hover {
  border-color: rgba(255, 255, 255, 0.45);
  background: rgba(255, 255, 255, 0.04);
}

.brand-card {
  background: var(--brand-card);
  border: 1px solid var(--brand-line-lt);
  border-radius: var(--brand-radius-card);
  padding: 24px;
  box-shadow: var(--brand-shadow-sm);
}

.brand-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  background: var(--brand-teal-soft);
  color: var(--brand-teal-dark);
  border: 1px solid var(--brand-teal-line);
  border-radius: var(--brand-radius-pill);
  font: 500 11px/1 var(--brand-font-body);
  letter-spacing: 0.04em;
}
.dark .brand-pill {
  color: var(--brand-teal-bright);
}

/* Pulsing live dot — used for "now" / presence indicators */
.brand-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand-teal-bright);
  box-shadow: 0 0 0 3px rgba(68, 214, 214, 0.18);
  animation: brand-pulse 2.4s ease-in-out infinite;
}
@keyframes brand-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.55; transform: scale(0.85); }
}

/* Aurora glow — the navy hero signature */
.brand-aurora {
  position: relative;
}
.brand-aurora::before {
  content: '';
  position: absolute;
  inset: -10% 20% auto 20%;
  height: 480px;
  background:
    radial-gradient(ellipse at 50% 0%, rgba(0, 176, 177, 0.32) 0%, transparent 55%),
    radial-gradient(ellipse at 80% 30%, rgba(0, 176, 177, 0.15) 0%, transparent 50%);
  filter: blur(20px);
  pointer-events: none;
  z-index: 0;
}
.brand-aurora > * {
  position: relative;
  z-index: 1;
}

/* ── Flip clock — live time in dashboard heroes ─────────────────────── *
 * Pairs with brand-clock.js. Hidden on phones (cluttered + battery-
 * unfriendly when the dashboard is rarely the foreground app on
 * mobile). The slide animation is suppressed under
 * prefers-reduced-motion so accessible users still get a readable
 * static value. */

.brand-clock {
  display: inline-block;
  font: 500 11px/1 var(--brand-font-body);
  letter-spacing: 0.16em;
  color: rgba(255, 255, 255, 0.85);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  /* Slide-in animation for the new value. .brand-clock-tick gets
     added by the JS each time the displayed value changes. */
  animation-fill-mode: both;
}
.brand-clock-tick {
  animation: brand-clock-slide 220ms cubic-bezier(0.32, 0.72, 0, 1);
}
@keyframes brand-clock-slide {
  from { transform: translateY(8px); opacity: 0.4; }
  to   { transform: translateY(0);   opacity: 1; }
}

/* Phone-only guard: hide the clock + the · separator that
   precedes it on viewports under the sm: Tailwind breakpoint
   (640 px). Tablets, laptops, and desktops all see the live
   time. The previous threshold (1023 px) was too aggressive —
   any browser at < 1024 px (laptop with devtools open, narrow
   tablet) lost the clock unnecessarily.
   No !important needed — equal specificity to the base
   `.brand-clock { display: inline-block }` declaration above,
   and source order wins. */
@media (max-width: 639px) {
  .brand-clock,
  .brand-clock-sep {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .brand-clock-tick { animation: none; }
}

/* 64px grid texture masked to fade — pairs with .brand-aurora */
.brand-grid-texture::after {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, rgba(255, 255, 255, 0.04) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 255, 255, 0.04) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, #000 0%, transparent 80%);
  mask-image: radial-gradient(ellipse 80% 60% at 50% 30%, #000 0%, transparent 80%);
  pointer-events: none;
}

/* ── Syncc loader ────────────────────────────────────────────────
   Brand-coded loading state. Same Tower mark as the static brand
   identifier — the 3 asymmetric dots animate with staggered phase to
   communicate "work happening". Every loading moment becomes a brand
   moment (maintainer May 15: "lets use bells always, we can switch"
   if 16px proves too small in practice).

   Usage: include partials/cs_loader.html or write the SVG inline with
   .cs-loader on the <svg>. Size via width/height attrs — single SVG
   scales from 16px (inline button spinner) to 200px (splash hero).

   Color tokens flip on dark surfaces via the existing html.dark
   cascade — bell silhouette inherits currentColor, accent dots use
   --brand-teal (light) / --brand-teal-bright (dark).
*/
.cs-loader {
  display: inline-block;
  vertical-align: middle;
  color: var(--brand-navy, #0A1628);
}
html.dark .cs-loader {
  color: var(--brand-paper, #FAFBFB);
}
.cs-loader .frame,
.cs-loader .bell {
  fill: currentColor;
}
.cs-loader .accent {
  fill: var(--brand-teal, #00B0B1);
  animation: cs-loader-pulse 1.05s ease-in-out infinite;
  transform-origin: center;
}
html.dark .cs-loader .accent {
  fill: var(--brand-teal-bright, #44D6D6);
}
.cs-loader .accent:nth-of-type(1) { animation-delay:   0ms; }
.cs-loader .accent:nth-of-type(2) { animation-delay: 180ms; }
.cs-loader .accent:nth-of-type(3) { animation-delay: 360ms; }

@keyframes cs-loader-pulse {
  0%, 100% { opacity: 0.25; }
  50%      { opacity: 1.00; }
}

/* Respect reduced-motion — drop the pulse, keep the dots fully
   opaque so the loader still communicates "working". */
@media (prefers-reduced-motion: reduce) {
  .cs-loader .accent {
    animation: none;
    opacity: 1;
  }
}

/* Inverse variant — for use on colored buttons / brand surfaces
   where the standard navy-bell + teal-dots won't contrast. Both
   the bell and the dots go white; the dots still pulse opacity so
   the loading cue lands. Use on teal-700 / navy / hero backgrounds. */
.cs-loader--inverse,
html.dark .cs-loader--inverse { color: #FFFFFF; }
.cs-loader--inverse .accent { fill: #FFFFFF; }

/* Auth-form submit busy state — login / signup buttons swap label
   for a {loader + "Signing in…"} on submit. The wiring is in
   static/js/auth-busy.js (sets data-loading="true" on the button);
   CSS does the visibility flip so there's no flash of empty
   button. Per CLAUDE.md "no hardcoded JS in templates". */
.auth-submit-busy {
  display: none;
  align-items: center;
  gap: 8px;
}
button[data-loading="true"] .auth-submit-idle { display: none; }
button[data-loading="true"] .auth-submit-busy { display: inline-flex; }
button[data-loading="true"] { cursor: progress; opacity: 0.88; }

/* ── .ml-* primitives · dark-mode parity (May 15 audit) ──────────────
   Maintainer flagged that the Events landing rendered with white
   card backgrounds on a navy dark-mode page — the ml-event-row /
   ml-tool / ml-empty primitives in custom.css hardcode #fff with
   no html.dark override. Plus the row hover used indigo accents
   (legacy, pre-brand-teal) that look out of place. Both fixed here. */

/* Hover state — switch from indigo to brand-teal on every surface,
   light and dark. The light-mode default in custom.css uses
   #c7d2fe (indigo-200) + rgba(99,102,241,...) box-shadow; both
   read out of place once the rest of the app is on teal. */
.ml-event-row:hover {
  border-color: var(--brand-teal-line, rgba(0, 176, 177, 0.28));
  box-shadow: 0 4px 14px -6px rgba(0, 176, 177, 0.18);
}

/* Dark-mode card surfaces — translucent navy fills + hairline
   borders, mirrors the rest of the brand-on-navy treatment. */
html.dark .ml-event-row,
html.dark .ml-tool {
  background-color: var(--brand-navy-2, #0E1D33);
  border-color: var(--brand-line-dk, rgba(255, 255, 255, 0.08));
}
html.dark .ml-tool:hover {
  border-color: rgba(255, 255, 255, 0.16);
  box-shadow: 0 6px 18px -6px rgba(0, 0, 0, 0.40);
}
html.dark .ml-event-row:hover {
  border-color: var(--brand-teal-line, rgba(68, 214, 214, 0.36));
  box-shadow: 0 4px 14px -6px var(--brand-teal-soft, rgba(68, 214, 214, 0.18));
}
html.dark .ml-empty {
  background-color: rgba(255, 255, 255, 0.03);
  border-color: var(--brand-line-dk, rgba(255, 255, 255, 0.08));
  color: rgba(255, 255, 255, 0.62);
}

/* Date tile — light-mode kept its old indigo gradient. Swap both
   modes to brand teal so the tile matches the rest of the palette. */
.ml-event-date {
  background: linear-gradient(140deg,
    var(--brand-teal-soft, rgba(0, 176, 177, 0.16)) 0%,
    var(--brand-teal-glow, rgba(0, 176, 177, 0.32)) 100%);
  color: var(--brand-teal-dark, #008687);
}
html.dark .ml-event-date {
  background: linear-gradient(140deg,
    var(--brand-teal-soft, rgba(68, 214, 214, 0.18)) 0%,
    var(--brand-teal-glow, rgba(0, 176, 177, 0.36)) 100%);
  color: var(--brand-teal-bright, #44D6D6);
}

/* Section labels + ink — make the "Events", "Tools" eyebrows and
   row-meta read on dark. Light-mode keeps existing slate-* values. */
html.dark .section-label,
html.dark .ml-kpi-label {
  color: rgba(255, 255, 255, 0.55);
}
html.dark .ml-event-row p {
  color: var(--brand-ink, #FAFBFB);
}
html.dark .ml-event-row .text-slate-500,
html.dark .ml-event-row .text-slate-900 {
  color: rgba(255, 255, 255, 0.72);
}
html.dark .ml-event-row .font-semibold.text-slate-900 {
  color: var(--brand-paper, #FAFBFB);
}
