/* ============ Design tokens ============ */
:root {
  --bg: #050807;
  --bg-1: #0b1110;
  --surface: #10201c;
  --surface-2: #17231f;
  --border: rgba(200, 208, 204, 0.16);
  --text: #f5f5f7;
  --text-dim: #9fb0aa;
  --text-faint: #7e8f89;
  --accent-1: #7fffd4;
  --accent-2: #8fe8d0;
  --danger: #ff5c72;
  --success: #7fe0a8;
  --accent-grad: linear-gradient(135deg, var(--accent-1), var(--accent-2));
  --glow-1: #0d2620;
  --glow-2: #081a17;
  --radius-sm: 10px;
  --radius-md: 16px;
  --radius-lg: 24px;
  --radius-pill: 999px;
  --container: 1160px;
  /* Nav uses a wider container than every other section (see
     .nav__inner.container below) — shared as a variable so the footer's own
     content can be aligned to match nav's real left edge without duplicating
     the magic number 1340 in two places. */
  --container-wide: 1340px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  /* --ease front-loads rất mạnh (gần hết quãng đường trong ~15-20% thời
     lượng) — hợp cho hover/reveal nhỏ, nhưng dùng cho max-height mở/đóng thẻ
     playbook (chênh lệch cả nghìn px) làm khối "bật/tắt" gần như tức thì rồi
     đứng yên hết phần còn lại của transition, cảm giác nhảy đột ngột. Easing
     riêng, đối xứng/dàn đều hơn suốt cả transition, dùng cho các chuyển động
     mở/đóng lớn (Risk Playbook step). */
  --ease-collapse: cubic-bezier(0.65, 0, 0.35, 1);

  /* Hero "digital galaxy" exception palette — curated mint/emerald tones for
     the hero's orbiting dots only; every other section keeps --accent-grad as
     its single accent, per design-system.md's "max 1 accent per screen". */
  --hero-dot-ice: #5fe0be;
  --hero-dot-violet: #a8ffe6;

  /* Sitewide glass/metal reskin tokens (additive, existing tokens unchanged) */
  --metal-grad: linear-gradient(135deg, #eef1ef 0%, #c8d0cc 45%, #7d8985 60%, #e4e8e6 100%);
  --glass-bg: rgba(20, 30, 30, 0.6);
  --glass-border: rgba(200, 208, 204, 0.12);
  --glass-blur: 18px;
  --depth-shadow: 0 8px 40px rgba(0, 0, 0, 0.5), 0 1px 0 rgba(127, 255, 212, 0.06) inset;

  /* Typography — Fraunces (luxury serif, real italic) for display headings,
     Space Grotesk (futuristic geometric sans) for eyebrow/label/stat accents. */
  --font-display: "Fraunces", Georgia, serif;
  --font-accent: "Space Grotesk", "Inter", sans-serif;

  /* Matches .nav__inner's explicit height (72px) + its 1px border-bottom —
     used by .roster-overlay to sit flush below the nav on its own page. */
  --nav-h: 73px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
/* Roster overlay is the only "page-filling" panel on the site (not a small
   centered modal) — while it's open, swiping/scrolling anywhere outside its
   own scrollable grid must not chain through to the homepage sitting behind
   it. Toggled in openRosterOverlay()/closeRosterOverlay() in main.js. */
body.roster-scroll-lock { overflow: hidden; overscroll-behavior: none; }

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }

/* Dark-background focus visibility (default browser ring is too faint here) */
a:focus-visible,
button:focus-visible,
.nav__link:focus-visible,
.kb-card:focus-visible,
.kb-panel__list button:focus-visible,
.accordion__trigger:focus-visible {
  outline: 2px solid var(--accent-1);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
ul { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, p { margin: 0; }

.container {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding: 0 24px;
}
.nav__inner.container { max-width: var(--container-wide); }

/* ============ Background FX ============ */
.bg-fx {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.bg-fx__glow {
  position: absolute;
  border-radius: 50%;
  filter: blur(120px);
  opacity: 0.55;
}
.bg-fx__glow--1 {
  width: 900px;
  height: 700px;
  top: -280px;
  left: 50%;
  transform: translateX(-50%);
  background: radial-gradient(circle at 50% 30%, var(--accent-1) 0%, var(--glow-1) 45%, transparent 70%);
  opacity: 0.35;
}
.bg-fx__glow--2 {
  width: 700px;
  height: 700px;
  bottom: -320px;
  right: -200px;
  background: radial-gradient(circle, var(--glow-2) 0%, transparent 70%);
  opacity: 0.5;
}
.bg-fx__grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, rgba(255,255,255,0.025) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255,255,255,0.025) 1px, transparent 1px);
  background-size: 64px 64px;
  mask-image: linear-gradient(to bottom, black, transparent 60%);
}

/* ============ Buttons ============ */
/* .btn's own display:inline-flex has the same specificity as the browser's
   default [hidden]{display:none} and wins by cascade order (author beats UA
   stylesheet) — without this, hidden=true on any .btn-classed element (e.g.
   #rosterEditModeBtn) silently renders anyway. Every other toggle-hidden
   class in this file has the same [hidden] override for the same reason. */
.btn[hidden] { display: none; }
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-weight: 600;
  font-size: 14px;
  padding: 12px 22px;
  border: none;
  white-space: nowrap;
  transition: transform 220ms var(--ease), box-shadow 220ms var(--ease), opacity 220ms var(--ease);
}
.btn:disabled { opacity: 0.45; cursor: not-allowed; pointer-events: none; }
.btn--pill { border-radius: var(--radius-pill); }
.btn--accent {
  background: var(--accent-grad);
  color: #0a0a0a;
  box-shadow: var(--depth-shadow);
}
.btn--accent:hover {
  box-shadow: var(--depth-shadow), 0 0 0 1px rgba(127,255,212,0.4), 0 8px 28px -6px rgba(143,232,208,0.55);
  transform: translateY(-1px);
}
.btn--outline {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
}
.btn--outline:hover {
  border-color: #2a2f3d;
  background: var(--surface-2);
}
.btn--lg { padding: 16px 32px; font-size: 15px; }

/* Generic "waiting on the server" state for any button — text hides, a
   small ring spinner takes its place, native disabled attribute blocks
   repeat clicks. Used on every form/action button that awaits api(). */
button.is-loading {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}
button.is-loading::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 15px;
  height: 15px;
  margin: -8px 0 0 -8px;
  border: 2px solid rgba(255,255,255,0.3);
  border-top-color: var(--accent-1);
  border-radius: 50%;
  animation: btnSpin 0.6s linear infinite;
}
@keyframes btnSpin { to { transform: rotate(360deg); } }

/* ============ Nav ============ */
.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  backdrop-filter: blur(var(--glass-blur));
  background: var(--glass-bg);
  border-bottom: 1px solid transparent;
  transition: border-color 300ms var(--ease), background 300ms var(--ease);
}
.nav.is-scrolled {
  border-bottom-color: var(--glass-border);
  background: rgba(5,8,7,0.82);
  box-shadow: var(--depth-shadow);
}
.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 72px;
  gap: 24px;
}

.brand { display: flex; align-items: center; gap: 10px; }
/* Wordmark-only logo (icon badge removed) — one continuous script-style
   mark, evoking the reference's glowing cursive wordmark, but built entirely
   from fonts already loaded on this page (Fraunces italic — decision:
   "Phương án A", no new font/asset, per mandatory-rules.md rule 7) and this
   site's own existing accent gradient/colors, not the reference's palette.
   filter:drop-shadow (not text-shadow) is required here specifically because
   the fill itself is background-clip:text + color:transparent — text-shadow
   would not glow correctly around a transparent-colored glyph, but
   drop-shadow works off the rendered alpha shape regardless of fill method. */
/* Same moving light-sweep technique as the login gate's OpsHub/tag
   (.auth-gate__mark/.auth-gate__tag, mint-text-shimmer keyframe) — reused
   here rather than duplicated, so a future speed/timing change to one stays
   in sync with the other unless deliberately diverged. Kept on the nav's own
   mint palette instead of switching to the gate's cooler chrome tones — the
   two were deliberately given different palettes when the gate was built
   (chrome = "outside the product", mint = the actual brand accent), and nav
   readability strongly favors mint against every section's own background
   this logo appears over, not a page-specific dark card. */
.brand__name {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: 21px;
  letter-spacing: -0.01em;
  background: linear-gradient(100deg,
    #2f5c4f 0%, #4a8874 20%, #7fffd4 42%,
    #ffffff 50%, #7fffd4 58%, #4a8874 80%, #2f5c4f 100%);
  background-size: 280% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 6px rgba(127,255,212,0.5)) drop-shadow(0 0 16px rgba(143,232,208,0.3));
  transition: filter 220ms var(--ease);
  animation: mint-text-shimmer 16s linear infinite;
}
.brand:hover .brand__name {
  filter: drop-shadow(0 0 9px rgba(127,255,212,0.65)) drop-shadow(0 0 22px rgba(143,232,208,0.45));
}
@media (prefers-reduced-motion: reduce) {
  .brand__name { animation: none; background-position: 0 0; }
}
.brand__sub {
  font-family: var(--font-accent);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.nav__links {
  display: flex;
  align-items: center;
  gap: 0;
  flex: 1;
  justify-content: flex-start;
  margin-left: 28px;
  white-space: nowrap;
  /* Hit-area padding, shared by every link/button below and by the
     ::before/::after underline offset formula — was a plain gap:16px with
     zero padding on each item (inline-default box, ~14-16px tall, shrink-
     wrapped to the glyphs), leaving a large dead zone above/below/around
     each link inside this 72px-tall bar. gap dropped to 0 and its 16px is
     redistributed as padding-x on each neighbor instead, so text-to-text
     spacing is unchanged (8px pad-right + 0 gap + 8px pad-left = 16px) —
     this only converts dead gutter space into real, clickable hit area. */
  --nav-link-pad-y: 16px;
  --nav-link-pad-x: 8px;
}
.nav__links > a,
.nav__link {
  position: relative;
  display: flex;
  align-items: center;
  padding: var(--nav-link-pad-y) var(--nav-link-pad-x);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--text-dim);
  white-space: nowrap;
  transition: color 180ms var(--ease), transform 180ms var(--ease);
}
.nav__links > a::before,
.nav__link::before {
  content: "";
  position: absolute;
  left: var(--nav-link-pad-x);
  /* bottom = pad-y - 8: keeps the underline pinned exactly 8px below the
     text (same as before this padding existed) regardless of pad-y's
     value — see the box-model reasoning in the nav hit-area plan. */
  bottom: calc(var(--nav-link-pad-y) - 8px);
  width: calc(100% - (var(--nav-link-pad-x) * 2));
  height: 1px;
  background: var(--metal-grad);
  opacity: 0;
  transition: opacity 220ms var(--ease);
}
/* Only the active tab gets this dim baseline (paired with its dot below) —
   every other tab shows nothing at all under it. */
.nav__links > a.is-active::before,
.nav__link.is-active::before {
  opacity: 0.22;
}
/* Small dot indicator (was a full-width sweeping underline bar) — sits
   centered under the text, on the same baseline as the ::before line
   above. Tied to .is-active (the currently navigated route, toggled by
   updateActiveNavLink() in main.js on every route change — click, back/
   forward, or initial load), NOT to :hover — it marks which tab you're
   currently on, not whichever one the mouse happens to be over. */
.nav__links > a::after,
.nav__link::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: calc(var(--nav-link-pad-y) - 11px);
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-grad);
  transform: translateX(-50%) scale(0);
  opacity: 0;
  transition: transform 220ms var(--ease), opacity 220ms var(--ease), box-shadow 220ms var(--ease);
}
/* Hover still brightens the text (a lighter, separate interaction) — it
   just no longer also shows the dot; only .is-active does that. */
.nav__links > a:hover,
.nav__links > .nav__link:hover,
.nav__item:hover .nav__link,
.nav__link[aria-expanded="true"],
.nav__links > a.is-active,
.nav__link.is-active {
  color: var(--text);
  transform: translateY(-2px);
  text-shadow: 0 0 10px rgba(127,255,212,0.5), 0 0 22px rgba(127,255,212,0.28);
}
.nav__links > a.is-active::after,
.nav__link.is-active::after {
  transform: translateX(-50%) scale(1);
  opacity: 1;
  box-shadow: 0 0 10px rgba(127,255,212,0.6);
}

.nav__item { position: relative; }
.nav__link {
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
}
.nav__chevron { transition: transform 220ms var(--ease); flex-shrink: 0; }
.nav__item:hover .nav__chevron,
.nav__link[aria-expanded="true"] .nav__chevron { transform: rotate(180deg); }

.nav__dropdown {
  position: absolute;
  top: calc(100% + 18px);
  left: -14px;
  min-width: 240px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  padding: 8px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-10px) scale(0.98);
  transition: opacity 240ms var(--ease), transform 240ms var(--ease), visibility 240ms;
  box-shadow: var(--depth-shadow), 0 20px 44px -12px rgba(0,0,0,0.65);
}
.nav__item:hover .nav__dropdown,
.nav__item.is-open .nav__dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0) scale(1);
}
.nav__dropdown a {
  display: block;
  padding: 11px 14px;
  border-radius: 9px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text-dim);
  transition: background 160ms var(--ease), color 160ms var(--ease), transform 160ms var(--ease);
}
.nav__dropdown a:hover {
  background: var(--surface-2);
  color: var(--text);
  transform: translateX(3px);
}

.nav__actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.nav__auth { position: relative; }
.nav__auth-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--text-dim);
  border-radius: var(--radius-pill);
  padding: 8px 16px;
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
  box-shadow: inset 0 0 0 1px rgba(200,208,204,0.08);
  transition: color 200ms var(--ease), border-color 200ms var(--ease), box-shadow 200ms var(--ease);
}
.nav__auth-btn:hover {
  color: var(--text);
  border-color: rgba(200,208,204,0.45);
  box-shadow: var(--depth-shadow);
}
.nav__auth-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  padding: 6px;
  min-width: 160px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 180ms var(--ease), transform 180ms var(--ease), visibility 180ms;
  box-shadow: var(--depth-shadow), 0 12px 32px -8px rgba(0,0,0,0.6);
}
.nav__auth.is-open .nav__auth-menu {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.nav__auth-menu li { list-style: none; }
.nav__auth-menu button {
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  font-family: inherit;
  padding: 9px 12px;
  border-radius: 8px;
  font-size: 13px;
  color: var(--text-dim);
  cursor: pointer;
  transition: background 150ms var(--ease), color 150ms var(--ease);
}
.nav__auth-menu button:hover { background: var(--surface-2); color: var(--text); }

.nav__mobile-login {
  margin-top: 14px;
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  padding: 12px 0;
  cursor: pointer;
}
.nav__mobile-auth-menu[hidden] { display: none; }
.nav__mobile-auth-menu {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}
.nav__mobile-auth-menu button {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text-dim);
  padding: 10px 0;
  cursor: pointer;
}

.nav__cta { display: inline-flex; }

.nav__toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 36px;
  height: 36px;
  background: none;
  border: none;
}
.nav__toggle span {
  display: block;
  height: 2px;
  width: 20px;
  background: var(--text);
  margin: 0 auto;
  transition: transform 250ms var(--ease), opacity 250ms var(--ease);
}
.nav__toggle.is-open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav__toggle.is-open span:nth-child(2) { opacity: 0; }
.nav__toggle.is-open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.nav__mobile {
  display: none;
  flex-direction: column;
  padding: 8px 24px 20px;
  border-bottom: 1px solid var(--glass-border);
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
}
.nav__mobile a,
.nav__mobile-link {
  padding: 12px 0;
  font-size: 15px;
  color: var(--text-dim);
  border-bottom: 1px solid var(--border);
}
.nav__mobile-link {
  display: block;
  width: 100%;
  background: none;
  border-left: none;
  border-right: none;
  border-top: none;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
.nav__mobile.is-open { display: flex; }

.nav__mobile-group { border-bottom: 1px solid var(--border); }
.nav__mobile-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: none;
  border: none;
  padding: 12px 0;
  font-size: 15px;
  font-family: inherit;
  color: var(--text-dim);
  cursor: pointer;
}
.nav__mobile-toggle svg { transition: transform 220ms var(--ease); }
.nav__mobile-toggle[aria-expanded="true"] { color: var(--text); }
.nav__mobile-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
.nav__mobile-sub {
  max-height: 0;
  overflow: hidden;
  transition: max-height 280ms var(--ease);
}
.nav__mobile-sub a {
  display: block;
  padding: 10px 0 10px 16px;
  font-size: 14px;
  border-bottom: none;
  color: var(--text-faint);
}
.nav__mobile-sub a:last-child { padding-bottom: 14px; }

/* ============ Hero ============ */
.hero {
  position: relative;
  z-index: 1;
  min-height: 92vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
  padding-top: 40px;
}

.hero__scene {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}
.hero__sky {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    #050807 0%,
    #0a1512 16%,
    #0f2a22 32%,
    #144a3a 50%,
    #1f7d63 66%,
    #4fd9b0 74%,
    #0d2620 90%,
    var(--bg) 100%
  );
}
.hero__horizon {
  position: absolute;
  left: 50%;
  bottom: 6%;
  width: 140%;
  height: 60%;
  transform: translateX(-50%);
  background:
    radial-gradient(ellipse 42% 55% at 62% 45%, rgba(160,255,225,0.55) 0%, rgba(110,230,190,0.4) 28%, rgba(70,200,165,0.22) 48%, transparent 70%),
    radial-gradient(ellipse 35% 40% at 22% 65%, rgba(40,70,65,0.3) 0%, transparent 60%);
  filter: blur(40px);
  mix-blend-mode: screen;
}
.hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(5,8,7,0.8) 0%, rgba(5,8,7,0.45) 38%, rgba(5,8,7,0.08) 62%, transparent 78%);
}
.hero__scene::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, transparent 0%, transparent 60%, var(--bg) 97%);
}

.hero3d-root {
  position: absolute;
  inset: 0;
}
.hero3d-root canvas {
  display: block;
  width: 100% !important;
  height: 100% !important;
}

.float-orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(1px);
  opacity: 0.7;
}
.float-orb--1 {
  width: 10px; height: 10px;
  top: 22%; right: 28%;
  background: var(--accent-1);
  box-shadow: 0 0 16px 4px rgba(127,255,212,0.5);
  animation: orbFloat 7s ease-in-out infinite;
}
.float-orb--2 {
  width: 6px; height: 6px;
  top: 38%; right: 12%;
  background: #fff;
  box-shadow: 0 0 10px 2px rgba(255,255,255,0.4);
  animation: orbFloat 9s ease-in-out infinite reverse;
}
@keyframes orbFloat { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-22px)} }

.hero__inner { position: relative; z-index: 1; padding-bottom: 56px; }
.hero__content { max-width: 620px; text-align: left; }

.hero__title {
  font-family: var(--font-display);
  font-size: clamp(36px, 6vw, 64px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.06;
  text-shadow: 0 4px 24px rgba(0,0,0,0.35);
}
.hero__title-accent {
  font-style: italic;
  background: var(--accent-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero__desc {
  margin: 22px 0 0;
  max-width: 460px;
  font-size: 17px;
  line-height: 1.6;
  color: var(--text-dim);
  text-shadow: 0 2px 16px rgba(5,8,7,0.9), 0 1px 4px rgba(5,8,7,0.9);
}

/* ============ Hero intro — "What Users Can Do" ============ */
.hero__intro { margin-bottom: 34px; }
/* Title: one consistent typographic form (no mixed italic/upright split)
   across the whole line, colored with the site's own accent gradient for a
   multi-tone "premium" look. filter:drop-shadow (not text-shadow) is
   required here — the fill is background-clip:text + color:transparent, and
   text-shadow doesn't render correctly around a transparent-colored glyph,
   same reasoning already documented on .brand__name above. Two drop-shadows
   stacked: a tight accent-colored glow (the "depth"/premium glow) plus a
   soft dark one (keeps the glyphs readable/lifted off the busy hero
   background, replacing the plain text-shadow the single-color version
   used). */
.hero__intro-title {
  font-family: var(--font-display);
  font-size: clamp(39px, 5.4vw, 54px);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.1;
  margin: 0 0 26px;
  background: var(--accent-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 22px rgba(127,255,212,0.35)) drop-shadow(0 6px 22px rgba(0,0,0,0.5));
}
.hero__intro-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.hero__intro-item {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 8px 10px;
  margin: -8px -10px;
  border-radius: 12px;
  transition: background 240ms var(--ease), transform 240ms var(--ease);
}
.hero__intro-item:hover {
  background: rgba(127,255,212,0.06);
  transform: translateX(4px);
}
.hero__intro-item-mark {
  flex-shrink: 0;
  width: 3px;
  height: 36px;
  margin-top: 2px;
  border-radius: 3px;
  background: var(--accent-grad);
  box-shadow: 0 0 10px rgba(127,255,212,0.35);
  transition: box-shadow 240ms var(--ease);
}
.hero__intro-item:hover .hero__intro-item-mark {
  box-shadow: 0 0 18px rgba(127,255,212,0.75);
}
.hero__intro-item-body { min-width: 0; }
.hero__intro-item-title {
  margin: 0 0 4px;
  font-family: var(--font-accent);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  text-shadow: 0 2px 12px rgba(5,8,7,0.9);
  transition: color 240ms var(--ease), text-shadow 240ms var(--ease);
}
.hero__intro-item:hover .hero__intro-item-title {
  color: var(--accent-1);
  text-shadow: 0 0 18px rgba(127,255,212,0.55);
}
.hero__intro-item-desc {
  margin: 0;
  max-width: 420px;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--text-dim);
  text-shadow: 0 2px 12px rgba(5,8,7,0.9);
}

@media (max-width: 640px) {
  .hero__intro { margin-bottom: 26px; }
  .hero__intro-item-mark { height: 32px; }
}

.qa-box {
  margin-top: 32px;
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  max-width: 480px;
  background: rgba(9,14,13,0.82);
  backdrop-filter: blur(8px);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 6px;
  transition: border-color 220ms var(--ease), box-shadow 220ms var(--ease);
}
.qa-box:focus-within {
  border-color: rgba(127,255,212,0.5);
  box-shadow: 0 0 0 4px rgba(127,255,212,0.08);
}
.qa-box__submit { flex-shrink: 0; }

.qa-box__select-wrap { flex: 1; min-width: 0; position: relative; }
.qa-box__select-wrap::after {
  content: "";
  position: absolute;
  right: 16px;
  top: 50%;
  width: 9px; height: 9px;
  border-right: 1.5px solid var(--text-dim);
  border-bottom: 1.5px solid var(--text-dim);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}
.qa-box__select {
  width: 100%;
  appearance: none;
  -webkit-appearance: none;
  background: none;
  border: none;
  border-radius: var(--radius-md);
  outline: none;
  color: var(--text);
  font-size: 14.5px;
  font-family: inherit;
  padding: 12px 36px 12px 16px;
  cursor: pointer;
}
.qa-box__select:focus-visible {
  outline: 2px solid var(--accent-1);
  outline-offset: 2px;
}
.qa-box__select option { background: var(--surface); color: var(--text); }

.qa-box__hint {
  margin-top: 12px;
  font-size: 12.5px;
  color: var(--text-dim);
  text-align: left;
  max-width: 480px;
  text-shadow: 0 2px 12px rgba(5,8,7,0.9), 0 1px 3px rgba(5,8,7,0.9);
}
.qa-box__trigger {
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
}
.qa-box__error {
  margin-top: 10px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--danger);
  text-align: left;
}

/* ============ Q&A picker modal (grouped question picker) ============ */
.qa-picker-panel { max-width: 640px; }
.qa-picker-groups {
  margin-top: 8px;
  max-height: 60vh;
  overflow-y: auto;
  padding-right: 6px;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.qa-picker-groups::-webkit-scrollbar { width: 6px; }
.qa-picker-groups::-webkit-scrollbar-track { background: transparent; }
.qa-picker-groups::-webkit-scrollbar-thumb { background: var(--border); border-radius: var(--radius-pill); }
.qa-picker-groups::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }

.qa-picker-group {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-bottom: 10px;
  transition: border-color 220ms var(--ease);
}
.qa-picker-group:last-child { margin-bottom: 0; }
.qa-picker-group.is-open { border-color: rgba(127,255,212,0.35); }
.qa-picker-group__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: none;
  border: none;
  color: var(--text);
  text-align: left;
  padding: 16px 18px;
  font-size: 14.5px;
  font-weight: 600;
}
.qa-picker-group__icon {
  flex-shrink: 0;
  width: 22px; height: 22px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--surface);
  font-size: 14px;
  color: var(--text-dim);
  transition: transform 250ms var(--ease), background 250ms var(--ease), color 250ms var(--ease);
}
.qa-picker-group.is-open .qa-picker-group__icon {
  transform: rotate(45deg);
  background: var(--accent-grad);
  color: #0a0a0a;
}
.qa-picker-group__panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height 320ms var(--ease);
}
.qa-picker-group__list {
  display: flex;
  flex-direction: column;
  padding: 0 10px 10px;
}
.qa-picker-question {
  width: 100%;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  text-align: left;
  font-family: inherit;
  font-size: 13.5px;
  line-height: 1.5;
  padding: 10px 12px;
  cursor: pointer;
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 220ms var(--ease), transform 220ms var(--ease), color 180ms var(--ease), background 180ms var(--ease);
}
.qa-picker-group.is-open .qa-picker-question {
  opacity: 1;
  transform: translateY(0);
  transition-delay: calc(var(--i, 0) * 30ms);
}
.qa-picker-question:hover { color: var(--text); background: var(--surface); }

/* floating cards inside hero scene */
.float-card {
  position: absolute;
  background: rgba(16,32,28,0.85);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 16px;
  box-shadow: 0 24px 60px -20px rgba(0,0,0,0.7);
}
.float-card--main {
  width: 240px;
  text-align: left;
}
.float-card--small {
  width: 180px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.float-card__row { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.float-card__avatar {
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--accent-grad);
  flex-shrink: 0;
}
.float-card__line {
  display: block;
  height: 8px;
  border-radius: 4px;
  background: var(--surface-2);
  width: 100%;
  margin: 8px 0;
}
.float-card__line--sm { width: 60px; margin: 0; }
.float-card__line--w70 { width: 70%; }
.float-card__line--w60 { width: 60%; margin: 0; }
.float-card__badge {
  display: inline-block;
  margin-top: 8px;
  font-size: 11px;
  font-weight: 600;
  color: #7fe0a8;
  background: rgba(127,224,168,0.1);
  padding: 4px 10px;
  border-radius: var(--radius-pill);
}
.float-card__check {
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--accent-grad);
  color: #0a0a0a;
  font-size: 13px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

/* ============ Trust (bottom bar of hero) ============ */
.trust__inner {
  position: relative;
  z-index: 1;
  padding: 40px 24px 0;
  border-top: 1px solid var(--border);
  background: rgba(5,8,7,0.5);
  backdrop-filter: blur(6px);
}
.trust__stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
  padding-bottom: 28px;
}
.stat { text-align: center; }
.stat__num {
  display: block;
  font-family: var(--font-accent);
  font-size: clamp(22px, 3vw, 30px);
  font-weight: 700;
  letter-spacing: -0.02em;
  background: var(--accent-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.stat__label {
  display: block;
  margin-top: 4px;
  font-size: 12px;
  color: var(--text-dim);
}
.stat__value { display: flex; align-items: baseline; justify-content: center; gap: 4px; }
.stat__unit { font-size: 14px; font-weight: 600; color: var(--text-dim); }

/* ============ Section shared ============ */
section { position: relative; z-index: 1; }
.section-head {
  text-align: center;
  max-width: 560px;
  margin: 0 auto 56px;
}
.eyebrow {
  display: block;
  font-family: var(--font-accent);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  background: var(--accent-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-stroke: 0.4px rgba(255,255,255,0.25);
  margin-bottom: 14px;
}
.section-head h2 {
  font-family: var(--font-display);
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
}
.section-head__desc {
  margin-top: 14px;
  font-size: 15px;
  line-height: 1.6;
  color: var(--text-dim);
}

/* ============ Testimonials (fanned-card carousel) ============ */
.testimonials { padding: 40px 0 120px; }

.testimonial-stack {
  display: grid;
  max-width: 640px;
  margin: 0 auto;
  min-height: 320px;
}

/* Mọi thẻ (ngắn hay dài) cùng chiếm 1 ô grid (grid-area: 1/1) thay vì
   absolute/inset:0 lấy chiều cao từ riêng thẻ đang active — với Grid, track
   tự động cao bằng NỘI DUNG DÀI NHẤT trong số toàn bộ thẻ (kể cả thẻ đang ở
   phía sau), rồi mọi thẻ (kể cả thẻ có nội dung ngắn) tự giãn full theo
   track đó (stretch mặc định) — nên 4 thẻ luôn cùng 1 kích thước cố định,
   không còn thẻ dài bị tràn chữ ra ngoài khi 1 thẻ ngắn hơn đang là thẻ
   chính. */
.testimonial-card {
  --stack-depth: 3;
  --stack-offset: 3;
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  text-align: center;
  padding: 44px 40px;
  font: inherit;
  color: inherit;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--depth-shadow);
  cursor: pointer;
  transform-origin: 50% 100%;
  transform:
    translate(calc(var(--stack-offset) * 26px), calc(var(--stack-depth) * -18px))
    rotate(calc(var(--stack-offset) * 5deg))
    scale(calc(1 - var(--stack-depth) * 0.045));
  opacity: calc(1 - var(--stack-depth) * 0.2);
  z-index: calc(4 - var(--stack-depth));
  transition: transform 480ms var(--ease), opacity 480ms var(--ease),
              border-color 220ms var(--ease), box-shadow 220ms var(--ease);
}
.testimonial-card[hidden] { display: none; }
/* Viền sáng CỐ ĐỊNH (không chỉ lúc hover) cho thẻ đang active, để luôn biết
   đang xem đúng thẻ nào — tái dùng màu mint hover-glow của thẻ phụ bên dưới
   (border 0.4 / ring 0.25) rồi tăng thêm 40% độ sáng (0.4→0.56, 0.25→0.35),
   vì đây là trạng thái thường trực cần nổi bật hơn 1 hiệu ứng chỉ hiện lúc
   hover; thêm 1 lớp glow toả mờ nhẹ (0 0 22px) để rõ là viền "sáng lên",
   không chỉ là 1 đường viền mỏng đổi màu. */
.testimonial-card[data-depth="0"] {
  cursor: default;
  border-color: rgba(127,255,212,0.56);
  box-shadow: var(--depth-shadow), 0 0 0 1px rgba(127,255,212,0.35), 0 0 22px rgba(127,255,212,0.22);
}
.testimonial-card:not([data-depth="0"]):hover,
.testimonial-card:not([data-depth="0"]):focus-visible {
  border-color: rgba(127,255,212,0.4);
  box-shadow: var(--depth-shadow), 0 0 0 1px rgba(127,255,212,0.25);
}
.testimonial-card__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 20px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text);
}
.testimonial-card__desc {
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-dim);
  max-width: 46ch;
}

.testimonial-dots { display: flex; align-items: center; justify-content: center; gap: 4px; margin-top: 36px; }
.testimonial-dot {
  position: relative;
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; padding: 0;
  cursor: pointer;
}
.testimonial-dot::after {
  content: "";
  width: 8px; height: 8px;
  border-radius: var(--radius-pill);
  background: var(--glass-border);
  transition: width 280ms var(--ease), background 280ms var(--ease), box-shadow 280ms var(--ease);
}
.testimonial-dot:hover::after { background: rgba(200,208,204,0.4); }
.testimonial-dot[aria-current="true"]::after {
  width: 26px;
  background: var(--accent-grad);
  box-shadow: 0 0 8px 1px rgba(127,255,212,0.4);
}

@media (prefers-reduced-motion: reduce) {
  .testimonial-card { transition: none; }
}

/* ============ Risk Playbook (vertical step timeline) ============ */
/* .kb (class chung của section này) không có padding riêng — dựa hoàn toàn
   vào margin của nội dung con. Ở route /playbook, #risk-playbook LUÔN LÀ
   section cuối cùng còn hiển thị ngay trước .footer (mọi section khác bị ẩn
   qua .route-section--hidden, xem applyRoute() trong main.js), trong khi
   .playbook-step:last-child lại cố tình không có padding-bottom (tránh tạo
   khoảng cuộn chết sau step cuối, xem .playbook-step bên dưới) — 2 điều này
   cộng lại khiến nội dung step cuối gần như chạm thẳng vào footer. Thêm
   khoảng thở riêng cho đúng section này. */
#risk-playbook { padding-bottom: 100px; }
.playbook-timeline {
  position: relative;
  max-width: 960px;
  margin: 0 auto;
  list-style: none;
  padding: 0;
}
/* 1 đường kẻ dọc liên tục, đặt tại đúng tâm ngang của node tròn (left = bán
   kính node), kéo từ tâm node ĐẦU tới tâm node CUỐI (top/bottom = bán kính
   node) — cách này không phụ thuộc chiều cao thật của từng bước (mô tả dài/
   ngắn khác nhau), luôn khớp đúng tâm node bất kể nội dung. */
.playbook-timeline::before {
  content: "";
  position: absolute;
  z-index: 0;
  left: 27px;
  top: 27px;
  bottom: 27px;
  width: 2px;
  background: linear-gradient(to bottom, transparent, var(--glass-border) 6%, var(--glass-border) 94%, transparent);
}
/* Đường sáng theo tiến độ cuộn — phủ lên trên đường kẻ mờ ở trên, chiều cao
   do JS set qua biến --pb-progress mỗi lần cuộn (rAF-throttled, xem
   updatePlaybookProgress() trong main.js), cùng kỹ thuật "JS set 1 biến CSS,
   CSS tự tính phần còn lại" đã dùng cho --stack-depth/--stack-offset ở
   Testimonials. z-index thấp hơn .playbook-step__node (1) để không đè lên
   icon node. */
.playbook-timeline::after {
  content: "";
  position: absolute;
  z-index: 0;
  left: 27px;
  top: 27px;
  width: 2px;
  height: var(--pb-progress, 0px);
  background: var(--accent-grad);
  box-shadow: 0 0 8px rgba(127, 255, 212, 0.6);
  transition: height 80ms linear;
  pointer-events: none;
}
.playbook-step {
  position: relative;
  display: flex;
  gap: 24px;
  padding-bottom: 640px; /* giảm từ 1320px xuống 320px (mất cân đối, đo thật content 485-1325px ở
    desktop), rồi tăng lại x2 lên 640px theo phản hồi thực tế — 320px hẹp tới mức đang xem nội dung
    step trước đã bị nhảy sang step sau */
  transition: opacity 260ms var(--ease), filter 260ms var(--ease);
}
.playbook-step:last-child { padding-bottom: 0; }
.playbook-step__node {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--depth-shadow);
  color: var(--accent-1);
}
.playbook-step__node svg { width: 24px; height: 24px; }
/* Cột trái (icon+tiêu đề+mô tả) và cột phải (thẻ minh hoạ) nằm chung 1 hàng
   con — tách riêng khỏi .playbook-step (hàng ngoài chứa node) để node luôn
   neo trên cùng đường kẻ dọc, không phụ thuộc chiều cao 2 cột bên trong. */
/* Padding trên toàn bộ .playbook-step__row (không chỉ padding-top như trước)
   — viền sáng của step active (.playbook-step.is-active .playbook-step__row
   bên dưới) bao quanh CẢ khối row, nếu row không có padding thì chữ/tiêu đề
   nằm sát ngay mép viền, nhìn như bị cắt. Áp dụng luôn cho cả step không
   active (không chỉ khi active) để tránh nhảy kích thước/layout mỗi lần
   chuyển active — viền chỉ hiện khi active nhưng khoảng đệm bên trong luôn
   cố định. */
/* Grid 2 cột (thay vì flex row cũ) để hàng thứ 2 — .playbook-step__details,
   Key Section — có thể trải full-width bên dưới, thay vì bị bó trong cùng
   cột 50% với video như trước. Tỉ lệ 46fr/54fr đo lại từ pixel thật của
   .playbook-step__body/.playbook-step__illustration TRƯỚC khi đổi sang grid
   (369px/435px ở viewport 1440px) để video không bị dịch vị trí/kích thước
   so với layout cũ. */
.playbook-step__row {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 46fr) minmax(0, 54fr);
  column-gap: 32px;
  row-gap: 22px;
  align-items: flex-start;
  padding: 18px 22px 22px;
}
.playbook-step__body { grid-column: 1; grid-row: 1; min-width: 0; }
.playbook-step__kicker {
  font-family: var(--font-accent);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-1);
  margin-bottom: 6px;
}
.playbook-step__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 19px;
  color: var(--text);
  margin-bottom: 6px;
  transition: font-size 350ms var(--ease);
}
.playbook-step__desc {
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-dim);
  overflow: hidden;
  /* Trần thật do JS đo (calibratePlaybookCardHeights, main.js), 320px chỉ là
     fallback — xem giải thích đầy đủ ở .playbook-step__illustration ngay
     dưới đây (cùng lý do, cùng cơ chế). */
  max-height: var(--pb-desc-max, 320px);
  opacity: 1;
  transition: max-height 620ms var(--ease-collapse), opacity 560ms var(--ease-collapse);
}
.playbook-step__desc strong { color: var(--accent-1); font-weight: 700; }

/* Thẻ kính CHỈ chứa video (từ khi tách Key Section ra .playbook-step__details
   full-width riêng, xem buildPlaybookIllustrationVideoCard() trong main.js)
   — background/border/radius/shadow/padding giữ NGUYÊN như trước, đúng yêu
   cầu "giữ vị trí + khung viền video" khi tách. */
.playbook-step__illustration {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--depth-shadow);
  padding: 22px 26px;
  overflow: hidden;
  /* Trước đây là 1 hằng số cố định (900px) đủ chỗ cho MỌI step bất kể step
     đó thật sự cần bao nhiêu — nhưng CSS max-height transition chỉ mượt khi
     giá trị đích gần với chiều cao thật: với trần lớn hơn nội dung thật
     nhiều lần, box đạt chiều cao thật rất sớm trong transition rồi "đứng
     yên" (max-height vẫn tăng nhưng không còn tác dụng gì), nhìn như bật/tắt
     gần như tức thì thay vì mượt suốt animation — đúng cảm giác "nhảy đột
     ngột" đã phản ánh. calibratePlaybookCardHeights() (main.js) đo đúng
     scrollHeight thật của từng thẻ (đo khi cộng bù đúng phần padding sẽ có
     lúc active) rồi set làm trần riêng qua biến này; 900px chỉ còn là
     fallback nếu JS chưa kịp chạy. */
  max-height: var(--pb-illustration-max, 900px);
  opacity: 1;
  transition: max-height 620ms var(--ease-collapse), opacity 560ms var(--ease-collapse),
    padding 620ms var(--ease-collapse), background 620ms var(--ease-collapse),
    border-color 620ms var(--ease-collapse), box-shadow 620ms var(--ease-collapse);
}
/* Video minh hoạ trong thẻ — khung viền kính đồng bộ design system, tỉ lệ
   khung giữ đúng theo video thật (aspect-ratio do JS set qua illustration.
   video.ratio) để không bao giờ bị letterbox/méo hình bất kể độ rộng cột. */
.playbook-step__illustration-video {
  position: relative;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--glass-border);
  box-shadow: 0 14px 32px rgba(0, 0, 0, 0.38);
  background: #04100d;
  /* Không còn margin-bottom — trước đây cần khoảng cách để tách khỏi label/
     terms nằm CHUNG thẻ; giờ thẻ .playbook-step__illustration chỉ có mình
     video, bỏ margin để padding trên/dưới thẻ đối xứng, viền ôm sát video. */
}
.playbook-step__illustration-video video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #04100d;
  cursor: pointer;
}
/* Nút phát lớn, giữa khung — ẩn khi video đang chạy (data-playing="true").
   Bấm lại (khi đang pause) cũng dùng chính nút này để phát tiếp. */
.playbook-step__illustration-video-play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(5, 8, 7, 0.3);
  border: none;
  padding: 0;
  cursor: pointer;
  transition: background 220ms var(--ease);
}
.playbook-step__illustration-video-play:hover { background: rgba(5, 8, 7, 0.46); }
.playbook-step__illustration-video-play__circle {
  width: 54px;
  height: 54px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--accent-grad);
  box-shadow: 0 8px 22px rgba(127, 255, 212, 0.35), 0 0 0 6px rgba(127, 255, 212, 0.12);
  transition: transform 220ms var(--ease);
}
.playbook-step__illustration-video-play:hover .playbook-step__illustration-video-play__circle { transform: scale(1.08); }
.playbook-step__illustration-video-play svg { width: 20px; height: 20px; fill: #0b1110; margin-left: 3px; }
.playbook-step__illustration-video[data-playing="true"] .playbook-step__illustration-video-play { display: none; }

/* Nút fullscreen riêng — góc trên-phải, luôn hiện (paused lẫn playing), nằm
   sau nút phát trong DOM nên tự vẽ đè lên trên, không bị nút phát (inset:0)
   chặn click dù nó to hơn hẳn. 2 icon (phóng to/thu nhỏ) xếp chồng, ẩn/hiện
   theo data-fullscreen — set bởi fullscreenchange listener trong JS. */
.playbook-step__illustration-video-fullscreen {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 2;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  background: rgba(5, 8, 7, 0.55);
  backdrop-filter: blur(4px);
  cursor: pointer;
  color: #f5f5f7;
  transition: background 200ms var(--ease), border-color 200ms var(--ease);
}
.playbook-step__illustration-video-fullscreen:hover {
  background: rgba(5, 8, 7, 0.75);
  border-color: rgba(127, 255, 212, 0.4);
}
.playbook-step__illustration-video-fullscreen svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.playbook-step__illustration-video-fullscreen svg.is-contract { display: none; }
.playbook-step__illustration-video[data-fullscreen="true"] .playbook-step__illustration-video-fullscreen svg.is-expand { display: none; }
.playbook-step__illustration-video[data-fullscreen="true"] .playbook-step__illustration-video-fullscreen svg.is-contract { display: block; }

/* Fullscreen thật — letterbox bằng object-fit:contain thay vì cover, để
   không bao giờ cắt mất nội dung khi khung màn hình thật khác tỉ lệ gốc
   video (1908:950); ở chế độ bình thường không cần đổi gì vì khung đã đặt
   đúng aspect-ratio khớp 100% video thật (xem inline style ratio ở JS). */
.playbook-step__illustration-video:fullscreen,
.playbook-step__illustration-video:-webkit-full-screen {
  width: 100vw;
  height: 100vh;
  max-width: none;
  border-radius: 0;
  border: none;
  box-shadow: none;
  background: #000;
  margin: 0;
}
.playbook-step__illustration-video:fullscreen video,
.playbook-step__illustration-video:-webkit-full-screen video {
  object-fit: contain;
}

/* Đèn spotlight theo con trỏ — glow tròn nhỏ, không chặn click (pointer-
   events:none) vì nằm đè lên video/nút bấm. Vị trí do JS set qua transform
   (translate), luôn ẩn khi chưa phát/đang pause (data-active). Nhịp "thở"
   nhẹ giúp dễ bắt mắt hơn — tắt khi prefers-reduced-motion. */
.playbook-step__illustration-cursor-spotlight {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  width: 34px;
  height: 34px;
  margin-left: -17px;
  margin-top: -17px;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(circle, rgba(127, 255, 212, 0.85) 0%, rgba(127, 255, 212, 0.35) 45%, rgba(127, 255, 212, 0) 72%);
  box-shadow: 0 0 18px 4px rgba(127, 255, 212, 0.45);
  transition: opacity 260ms var(--ease);
  will-change: transform;
}
/* animation chỉ chạy khi active — để nguyên trên base rule sẽ làm keyframe's
   own opacity (0.85-1) đè mất opacity:0 lúc ẩn, khiến spotlight không bao
   giờ tắt hẳn được dù pause. */
.playbook-step__illustration-cursor-spotlight[data-active="true"] {
  opacity: 1;
  animation: playbookCursorBreathe 1.6s ease-in-out infinite;
}
@keyframes playbookCursorBreathe {
  0%, 100% { scale: 1; opacity: 0.85; }
  50% { scale: 1.15; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .playbook-step__illustration-cursor-spotlight { animation: none; }
}

/* Thẻ kính riêng cho Key Section (label + note/rows/terms/groups) — full-
   width, nằm ngay dưới video (grid-row 2, span cả 2 cột). Tái dùng đúng
   design language "kính sang trọng" của .playbook-step__illustration (video
   card cũ) để 2 thẻ đồng bộ phong cách dù tách riêng. */
.playbook-step__details {
  grid-column: 1 / -1;
  grid-row: 2;
  min-width: 0;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--depth-shadow);
  padding: 22px 26px;
  overflow: hidden;
  /* Trần thật do JS đo (calibratePlaybookCardHeights, main.js) thay hằng số
     2600px cố định trước đây (đủ cho Step 2, 12 mục/2 nhóm, nhưng dư quá xa
     so với các step ngắn hơn — gây "bật/tắt gần như tức thì" khi transition,
     xem giải thích đầy đủ ở .playbook-step__illustration bên trên). 2600px
     chỉ còn là fallback nếu JS chưa kịp chạy. */
  max-height: var(--pb-details-max, 2600px);
  opacity: 1;
  transition: max-height 620ms var(--ease-collapse), opacity 560ms var(--ease-collapse),
    padding 620ms var(--ease-collapse), background 620ms var(--ease-collapse),
    border-color 620ms var(--ease-collapse), box-shadow 620ms var(--ease-collapse);
}
.playbook-step__illustration-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-1);
  margin-bottom: 10px;
}
.playbook-step__illustration-rows { display: flex; flex-direction: column; gap: 8px; }
.playbook-step__illustration-row {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 13px;
  color: var(--text-dim);
  padding: 6px 10px;
  background: rgba(127, 255, 212, 0.05);
  border: 1px solid var(--glass-border);
  border-radius: 8px;
}
.playbook-step__illustration-note { font-size: 14px; line-height: 1.7; color: var(--text-dim); }
.playbook-step__illustration-note strong { color: var(--accent-1); font-weight: 700; }
/* kind: terms — danh sách thuật ngữ/định nghĩa (prose thật, không phải dữ
   liệu ngắn kiểu code) — font thường, tách rõ term (đậm, accent) và
   definition (thường, mờ hơn) bằng <dl>/<dt>/<dd> đúng ngữ nghĩa. */
/* Grid nhiều cột (thay vì 1 cột dọc) — giờ .playbook-step__details full-
   width nên có đủ chỗ ngang; auto-fit tự co giãn số cột theo độ rộng thật
   (≈3 cột ở desktop rộng, ít hơn khi hẹp), giúp danh sách dài (Step 2: 12
   mục/2 nhóm, Step 3: 8 mục) không còn kéo dài xuống 1 cột duy nhất. */
.playbook-step__illustration-terms {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px 32px;
  margin: 0;
}
/* Mỗi ô grid = 1 cặp term+definition trọn vẹn (xem renderTermsList(),
   main.js) — đường kẻ mờ phía dưới giúp phân biệt rõ ranh giới giữa các mục
   khi xếp nhiều cột/nhiều hàng, tránh nhầm định nghĩa nào thuộc tiêu đề nào. */
.playbook-step__illustration-term-pair {
  padding-bottom: 14px;
  border-bottom: 1px solid var(--glass-border);
}
.playbook-step__illustration-term {
  font-size: 14px;
  font-weight: 700;
  color: var(--accent-1);
  margin: 0;
}
.playbook-step__illustration-term::after { content: ":"; }
.playbook-step__illustration-def {
  font-size: 13px;
  line-height: 1.6;
  color: var(--text-dim);
  margin: 4px 0 0;
}
/* Biến thể có nhóm (Step 2: tab Overview / tab Report) — mỗi nhóm 1 tiêu đề
   nhỏ + danh sách term/definition riêng (.playbook-step__illustration-terms
   ở trên, tái dùng nguyên vẹn bên trong mỗi nhóm). */
.playbook-step__illustration-groups { display: flex; flex-direction: column; gap: 22px; }
.playbook-step__illustration-group-heading {
  font-family: var(--font-accent);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-1);
  margin: 0 0 10px;
}

/* Step đang active (gần tâm màn hình nhất lúc cuộn — xem
   updatePlaybookProgress() trong main.js) được làm rõ/ưu tiên hiển thị; các
   step còn lại lùi lại. Độ mờ/nhoè của TOÀN step (opacity/filter) do JS set
   inline mỗi lần cuộn — MƯỢT và LIÊN TỤC theo khoảng cách thật tới tâm màn
   hình (không còn 2 trạng thái phẳng 1/0.4), transition ở .playbook-step
   ngay trên tự làm mượt mỗi lần JS cập nhật. Mô tả/thẻ "Example" của step
   không active co lại bằng max-height (không phải display:none) để có hiệu
   ứng đóng/mở mượt thay vì biến mất tức thì. */
.playbook-step:not(.is-active) .playbook-step__desc {
  max-height: 0;
  opacity: 0;
}
.playbook-step:not(.is-active) .playbook-step__illustration,
.playbook-step:not(.is-active) .playbook-step__details {
  max-height: 0;
  opacity: 0;
  padding-top: 0;
  padding-bottom: 0;
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
.playbook-step:not(.is-active) .playbook-step__title { font-size: 16px; }
.playbook-step.is-active .playbook-step__title { font-size: 22px; }
/* Viền + glow của step active tăng hẳn độ sáng/độ rõ so với trước (ring mờ
   0.16 → 0.6 + thêm 1 lớp glow toả ra ngoài, cùng tinh thần "đường sáng"
   --accent-grad đã dùng cho .playbook-timeline::after) — dễ nhận biết ngay
   đang xem step nào, không chỉ dựa vào chữ to/mờ. */
.playbook-step.is-active .playbook-step__node {
  box-shadow: 0 0 0 3px rgba(127, 255, 212, 0.6), 0 0 22px rgba(127, 255, 212, 0.4), var(--depth-shadow);
  transform: scale(1.18);
  transition: box-shadow 450ms var(--ease), transform 450ms var(--ease);
}
.playbook-step.is-active .playbook-step__row {
  box-shadow: 0 0 0 1.5px rgba(127, 255, 212, 0.65), 0 0 40px rgba(127, 255, 212, 0.22), 0 16px 44px rgba(0, 0, 0, 0.35);
  border-radius: var(--radius-lg);
  transition: box-shadow 450ms var(--ease);
}
@media (prefers-reduced-motion: reduce) {
  .playbook-step,
  .playbook-step__node,
  .playbook-step__row,
  .playbook-step__title,
  .playbook-step__desc,
  .playbook-step__illustration,
  .playbook-step__details {
    transition: none;
  }
}

/* ---------- "Quick Quiz" — mục thứ 8 cuối Risk Playbook timeline, xem
   buildPlaybookQuizStep() (main.js) ---------- */
/* Không có video minh hoạ cho mục này (illustration=null) — phần mở đầu
   (kicker/title/desc) chiếm full-width thay vì để trống nửa cột phải. Vô
   hại ở mobile vì .playbook-step__row đã reset về 1 cột ở đó rồi (xem khối
   @media (max-width: 640px) bên dưới). */
.playbook-step--quiz .playbook-step__body { grid-column: 1 / -1; }

.playbook-quiz { display: flex; flex-direction: column; gap: 20px; }
/* min-height tinh chỉnh tay đủ cho câu hỏi dài nhất trong 2 câu — cùng kỹ
   thuật "hand-tune 1 hằng số đủ rộng" đã dùng cho .playbook-step
   padding-bottom ở trên, để lần calibratePlaybookCardHeights() DUY NHẤT lúc
   tải trang (main.js) đo đúng trần cho CẢ 2 câu, chuyển câu (prev/next)
   không bao giờ vượt trần đã đo — xem comment đầy đủ trong
   buildPlaybookQuizStep(). Giá trị thật đo bằng Playwright, không đoán. */
/* 470px, không phải 1 con số đoán — đo thật bằng Playwright (Q2 dài hơn Q1,
   wrap tới 451px đúng tại 641px, điểm hẹp nhất còn dùng CSS này trước khi
   khối mobile @media(max-width:640px) tiếp quản). .playbook-timeline có
   max-width:960px (dòng ~1219) nên nội dung KHÔNG bao giờ rộng hơn ~960px dù
   viewport lớn hơn nữa — 641px THẬT SỰ là điểm hẹp nhất, không phải đoán —
   nghĩa là 1 giá trị duy nhất này đã đủ an toàn cho toàn bộ dải >640px, dù ở
   1440px nội dung thật chỉ cần ~331px (phần dư còn lại là khoảng thở, chấp
   nhận được, cùng tinh thần "hằng số tinh chỉnh rộng rãi" của
   .playbook-step padding-bottom phía trên). */
.playbook-quiz__question-wrap { min-height: 470px; }
.playbook-quiz__question {
  display: flex;
  gap: 8px;
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--text);
  margin: 0 0 18px;
}
.playbook-quiz__question-num { color: var(--accent-1); flex-shrink: 0; }
.playbook-quiz__options { display: flex; flex-direction: column; gap: 10px; border: 0; padding: 0; margin: 0; }
.playbook-quiz__option {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 13px 16px;
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  background: rgba(127, 255, 212, 0.03);
  cursor: pointer;
  transition: border-color 200ms var(--ease), background 200ms var(--ease);
}
.playbook-quiz__option:hover { border-color: rgba(127, 255, 212, 0.35); }
/* Cùng kỹ thuật .qa-box:focus-within đã có (dòng ~823) — vòng sáng focus
   hiện trên cả hàng dù input thật bị ẩn qua .sr-only bên trong. */
.playbook-quiz__option:focus-within {
  border-color: rgba(127, 255, 212, 0.5);
  box-shadow: 0 0 0 4px rgba(127, 255, 212, 0.08);
}
/* input:checked không có "parent selector" thuần CSS nào khác ngoài :has()
   để tô cả hàng — hỗ trợ đầy đủ trên mọi trình duyệt evergreen. */
.playbook-quiz__option:has(input:checked) {
  border-color: var(--accent-1);
  background: rgba(127, 255, 212, 0.09);
}
.playbook-quiz__option.is-correct { border-color: var(--success); background: rgba(127, 224, 168, 0.12); }
.playbook-quiz__option.is-incorrect { border-color: var(--danger); background: rgba(255, 92, 114, 0.1); }
.playbook-quiz__option-letter {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-accent);
  font-size: 12px;
  font-weight: 700;
  color: var(--accent-1);
  background: rgba(127, 255, 212, 0.1);
  border: 1px solid var(--glass-border);
}
.playbook-quiz__option.is-correct .playbook-quiz__option-letter { color: var(--success); border-color: var(--success); }
.playbook-quiz__option.is-incorrect .playbook-quiz__option-letter { color: var(--danger); border-color: var(--danger); }
.playbook-quiz__option-text { font-size: 14px; line-height: 1.5; color: var(--text-dim); }
.playbook-quiz__option:has(input:checked) .playbook-quiz__option-text,
.playbook-quiz__option.is-correct .playbook-quiz__option-text,
.playbook-quiz__option.is-incorrect .playbook-quiz__option-text { color: var(--text); }

/* Luôn nằm trong DOM, visibility:hidden mặc định (không display:none) — xem
   giải thích ở buildPlaybookQuizStep(): giữ chiều cao card ổn định giữa
   trạng thái đã/chưa submit, không cần recalibrate theo tương tác. */
/* line-height + min-height khai báo tường minh: 1 thẻ <p> RỖNG (chưa có
   textContent) không tạo line box nào cả, chỉ cao 0px, dù visibility:hidden —
   khác hẳn lúc có chữ thật sau khi submit (tạo line box thật, cao hẳn lên) —
   đo thật bằng Playwright phát hiện đúng chỗ hụt này (card cao thêm 16-32px
   sau khi submit dù đã set visibility:hidden mặc định). min-height cho đủ 2
   dòng (chữ "✗ Not quite — the correct answer is ..." có thể xuống 2 dòng ở
   màn hẹp) để giữ chiều cao card không đổi dù rỗng/1 dòng/2 dòng. */
.playbook-quiz__feedback { font-size: 13.5px; font-weight: 600; line-height: 1.45; min-height: calc(1.45em * 2); margin: -6px 0 0; visibility: hidden; }
.playbook-quiz__feedback.is-correct { color: var(--success); }
.playbook-quiz__feedback.is-incorrect { color: var(--danger); }

.playbook-quiz__controls { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.playbook-quiz__reset-btn {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  border: 1px solid var(--glass-border);
  background: transparent;
  color: var(--text-dim);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color 200ms var(--ease), color 200ms var(--ease);
}
.playbook-quiz__reset-btn:hover { border-color: var(--accent-1); color: var(--accent-1); }
.playbook-quiz__reset-btn svg { width: 17px; height: 17px; }
.playbook-quiz__pager { display: flex; align-items: center; gap: 10px; font-family: var(--font-accent); font-size: 13px; color: var(--text-dim); }
.playbook-quiz__pager-btn {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1px solid var(--glass-border);
  background: transparent;
  color: var(--text);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 200ms var(--ease), opacity 200ms var(--ease);
}
.playbook-quiz__pager-btn:hover:not(:disabled) { border-color: var(--accent-1); }
.playbook-quiz__pager-btn:disabled { opacity: 0.35; cursor: default; }
.playbook-quiz__submit-btn:disabled { opacity: 0.45; cursor: default; transform: none !important; box-shadow: none !important; }

@media (prefers-reduced-motion: reduce) {
  .playbook-quiz__option, .playbook-quiz__reset-btn, .playbook-quiz__pager-btn { transition: none; }
}

/* ============ Risk Control Services (6-group accordion) ============ */
.services { padding: 40px 0 120px; }
.service-accordion { }
.service-group {
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  margin-bottom: 14px;
  scroll-margin-top: 100px;
  overflow: hidden;
  backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--depth-shadow);
  transition: border-color 250ms var(--ease);
}
.service-group:last-child { margin-bottom: 0; }
.service-group:hover { border-color: #2a2f3d; }
.service-group__trigger {
  width: 100%;
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 24px 28px;
  background: none;
  border: none;
  font-family: inherit;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.service-group__icon {
  width: 44px; height: 44px;
  flex-shrink: 0;
  border-radius: 12px;
  background: var(--metal-grad);
  border: 1px solid var(--glass-border);
  box-shadow: var(--depth-shadow);
  color: #23262f;
  display: flex; align-items: center; justify-content: center;
  font-size: 19px;
  transition: box-shadow 220ms var(--ease), transform 220ms var(--ease);
}
.service-group__trigger:hover .service-group__icon,
.service-group__trigger:focus-visible .service-group__icon {
  box-shadow: var(--depth-shadow), 0 0 0 3px rgba(127,255,212,0.18);
  transform: translateY(-1px);
}
.service-group__head { flex: 1; }
.service-group__head h3 { font-size: 18px; font-weight: 700; letter-spacing: -0.01em; margin-bottom: 6px; }
.service-group__head p { font-size: 13.5px; color: var(--text-dim); line-height: 1.55; }
.service-group__chevron {
  flex-shrink: 0;
  margin-top: 12px;
  color: var(--text-dim);
  transition: transform 250ms var(--ease);
}
.service-group__trigger[aria-expanded="true"] .service-group__chevron { transform: rotate(180deg); }
.service-group__panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height 300ms var(--ease);
}
.service-group__list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  padding: 0 28px 24px;
}
.service-group__list li {
  padding: 14px 16px;
  background: var(--surface-2);
  border-radius: 10px;
}
.service-group__list li strong { display: block; font-size: 13px; font-weight: 600; margin-bottom: 4px; }
.service-group__list li span { display: block; font-size: 12.5px; color: var(--text-dim); line-height: 1.5; }

/* ============ Risk Knowledge Base ============ */
.kb-breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 20px;
  font-size: 13px;
  color: var(--text-faint);
}
.kb-breadcrumb a { color: var(--text-faint); transition: color 200ms var(--ease); }
.kb-breadcrumb a:hover { color: var(--text); }
.kb-breadcrumb span:last-child { color: var(--text-dim); }
.kb-item-grid {
  display: grid;
  /* 320px (not the original 260px) is deliberate: inside the 1160px
     .container, 260px let auto-fill pack 4 columns at desktop width,
     leaving too little text width for the 2-line description to show more
     than a few words before clamping. 320px forces 3 columns at desktop
     (4*320+3*20=1340 > 1112 available, so it drops to 3) while still fitting
     2 columns at tablet — same as before, no regression there. */
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 20px;
}
.kb-card {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 20px;
  text-align: left;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  padding: 32px;
  font-family: inherit;
  cursor: pointer;
  backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--depth-shadow);
  scroll-margin-top: 100px;
  transition: border-color 250ms var(--ease), transform 250ms var(--ease);
}
.kb-card:hover { border-color: #2a2f3d; transform: translateY(-4px); }
.kb-card[aria-expanded="true"] { border-color: rgba(127,255,212,0.5); }
.kb-card__icon {
  width: 64px; height: 64px;
  flex-shrink: 0;
  border-radius: 14px;
  background: var(--metal-grad);
  border: 1px solid var(--glass-border);
  box-shadow: var(--depth-shadow);
  color: #23262f;
  display: flex; align-items: center; justify-content: center;
  font-size: 28px;
  transition: box-shadow 250ms var(--ease);
}
.kb-card__icon svg { width: 28px; height: 28px; }
.kb-card:hover .kb-card__icon { box-shadow: var(--depth-shadow), 0 0 0 3px rgba(127,255,212,0.18); }
.kb-card__content { flex: 1; min-width: 0; }
.kb-card h3 { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; margin-bottom: 8px; color: var(--text); }
.kb-card p {
  font-size: 13px;
  color: var(--text-dim);
  line-height: 1.6;
  margin-bottom: 20px;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.kb-card__cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent-1);
}
.kb-card__arrow { transition: transform 220ms var(--ease); }
.kb-card[aria-expanded="true"] .kb-card__arrow { transform: translateX(4px) rotate(90deg); }

/* ============ Risk Knowledge Base — full detail page (/sop/<id>, /rules/<id>) ============ */
.kb-detail__container { max-width: 760px; }
.kb-detail__hero {
  height: 160px;
  border-radius: var(--radius-lg);
  background: radial-gradient(circle at 50% 40%, rgba(127,255,212,0.14), transparent 70%), var(--glass-bg);
  border: 1px solid var(--glass-border);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 28px;
}
.kb-detail__hero-icon {
  width: 88px; height: 88px;
  border-radius: 20px;
  background: var(--metal-grad);
  border: 1px solid var(--glass-border);
  box-shadow: var(--depth-shadow);
  color: #23262f;
  display: flex; align-items: center; justify-content: center;
}
.kb-detail__hero-icon svg { width: 44px; height: 44px; }
.kb-detail__tag {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-1);
  margin-bottom: 10px;
}
.kb-detail__title {
  font-family: var(--font-display);
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
  margin-bottom: 16px;
}
.kb-detail__meta { display: flex; align-items: center; gap: 16px; margin-bottom: 20px; }
.kb-detail__meta-item { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-faint); }
.kb-detail__meta-item svg { width: 15px; height: 15px; }
.kb-detail__desc {
  font-size: 16px;
  color: var(--text-dim);
  line-height: 1.65;
  margin-bottom: 40px;
  padding-bottom: 32px;
  border-bottom: 1px solid var(--border);
}
.kb-detail__body { display: flex; flex-direction: column; gap: 32px; padding-bottom: 96px; }
/* Reuse .kb-modal__section as-is (same function builds both the compact
   modal and this full page) — only scale up the label into an article-style
   H2 here, rather than the modal's small uppercase tag. scroll-margin-top
   keeps a section's heading from landing behind the fixed nav bar
   (--nav-h: 73px) when the right-side nav-tick's scrollIntoView jumps here. */
.kb-detail__body .kb-modal__section { scroll-margin-top: 100px; }
.kb-detail__body .kb-modal__section-label {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-transform: none;
  color: var(--text);
  margin-bottom: 14px;
}
.kb-detail__body .kb-modal__section li { font-size: 14.5px; }
.kb-detail__loading, .kb-detail__gate { font-size: 14px; color: var(--text-faint); padding: 8px 0; }

/* Right-side section navigator — desktop-only progressive enhancement,
   mirrors the "active tick widens" pattern confirmed live on sydexa.com's
   own article pages before building this (own markup/colors, not their
   code). Sits below .nav (z-index 50) and any modal (100). */
.kb-detail__nav {
  position: fixed;
  right: 32px;
  top: 50%;
  transform: translateY(-50%);
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
}
.kb-detail__nav[hidden] { display: none; }
@media (max-width: 1300px) {
  .kb-detail__nav { display: none !important; }
}
.kb-detail__nav-tick {
  background: none;
  border: none;
  padding: 6px 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
}
.kb-detail__nav-tick-bar {
  display: block;
  width: 22px;
  height: 3px;
  border-radius: 2px;
  background: var(--text-faint);
  opacity: 0.5;
  transition: width 300ms var(--ease), opacity 300ms var(--ease), background 300ms var(--ease);
}
.kb-detail__nav-tick:hover .kb-detail__nav-tick-bar { opacity: 0.8; }
.kb-detail__nav-tick.is-active .kb-detail__nav-tick-bar {
  width: 34px;
  opacity: 1;
  background: var(--accent-grad);
}

.kb-panel {
  margin-top: 20px;
  overflow: hidden;
  max-height: 0;
  transition: max-height 320ms var(--ease);
}
/* max-height is set inline by JS (scrollHeight) when opened, so any list length fits */
.kb-panel__list {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  padding-top: 4px;
}
.kb-panel__list li {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
}
.kb-panel__list button {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: none;
  border: none;
  font-family: inherit;
  color: var(--text);
  padding: 14px 16px;
  font-size: 13.5px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  transition: color 180ms var(--ease);
}
.kb-panel__list button:hover { color: var(--accent-1); }
.kb-panel__list button::after { content: "→"; color: var(--text-faint); flex-shrink: 0; }

.kb-modal[hidden] { display: none; }
.kb-modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.kb-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(5,8,7,0.75);
  backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity 220ms var(--ease);
}
.kb-modal__panel {
  position: relative;
  width: 100%;
  max-width: 560px;
  max-height: 85vh;
  overflow-y: auto;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-lg);
  padding: 32px;
  backdrop-filter: blur(var(--glass-blur));
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 220ms var(--ease), transform 220ms var(--ease);
  box-shadow: 0 30px 70px -20px rgba(0,0,0,0.7);
}
.kb-modal.is-open .kb-modal__backdrop { opacity: 1; }
.kb-modal.is-open .kb-modal__panel { opacity: 1; transform: translateY(0); }
.kb-modal__close {
  position: absolute;
  top: 18px; right: 18px;
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--surface-2);
  border: none;
  color: var(--text-dim);
  font-size: 14px;
  cursor: pointer;
  transition: color 180ms var(--ease), background 180ms var(--ease);
}
.kb-modal__close:hover { color: var(--text); background: var(--border); }
.kb-modal__close:focus-visible { border-radius: 50%; }
.kb-modal__tag {
  display: block;
  padding-right: 44px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--accent-1);
  margin-bottom: 12px;
}
.kb-modal__title { padding-right: 44px; font-size: 20px; font-weight: 700; letter-spacing: -0.01em; margin-bottom: 12px; }
.qa-modal-source { margin: -4px 0 16px; }
.qa-modal-source__btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  color: var(--text-dim);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  padding: 8px 14px;
  cursor: pointer;
  transition: color 180ms var(--ease), border-color 180ms var(--ease);
}
.qa-modal-source__btn:hover { color: var(--text); border-color: var(--accent-1); }
.kb-modal__body { font-size: 14px; color: var(--text-dim); line-height: 1.65; }
.kb-modal__sections { display: flex; flex-direction: column; gap: 18px; }
.kb-modal__section-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--accent-1);
  margin-bottom: 8px;
}
.kb-modal__section ul {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.kb-modal__section li {
  position: relative;
  padding-left: 16px;
  font-size: 13.5px;
  color: var(--text-dim);
  line-height: 1.55;
}
.kb-modal__section li::before {
  content: "";
  position: absolute;
  left: 0; top: 8px;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent-grad);
}
.kb-modal__section--outcome-group { margin-bottom: 4px; }
.kb-modal__section-sublabel { font-size: 12.5px; font-weight: 600; color: var(--text); margin-bottom: 6px; }
.kb-modal__section--highlight {
  background: rgba(127,255,212,0.08);
  border: 1px solid rgba(127,255,212,0.35);
  border-radius: var(--radius-md);
  padding: 14px 16px;
}
.kb-modal__section-highlight-tag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: var(--accent-1);
  margin-bottom: 8px;
}

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

/* ============ Auth form (login / change password) ============ */
.auth-form[hidden] { display: none; }
.auth-form { display: flex; flex-direction: column; gap: 14px; margin-top: 4px; }
.auth-form__field { display: flex; flex-direction: column; gap: 6px; }
.auth-form__field label { font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.auth-form__input {
  width: 100%;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text);
  font-family: inherit;
  font-size: 14.5px;
  padding: 11px 14px;
  transition: border-color 180ms var(--ease), box-shadow 180ms var(--ease);
}
.auth-form__input:focus-visible {
  outline: none;
  border-color: rgba(127,255,212,0.5);
  box-shadow: 0 0 0 4px rgba(127,255,212,0.08);
}
.auth-form__error { font-size: 12.5px; font-weight: 500; color: var(--danger); line-height: 1.5; }
.auth-form__success {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--success);
  line-height: 1.5;
  background: rgba(127,224,168,0.08);
  border: 1px solid rgba(127,224,168,0.25);
  border-radius: 10px;
  padding: 10px 12px;
}
.auth-form__submit { margin-top: 4px; width: 100%; }
.auth-forgot-link {
  background: none;
  border: none;
  font-family: inherit;
  font-size: 12.5px;
  color: var(--text-faint);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  align-self: center;
  transition: color 180ms var(--ease);
}
.auth-forgot-link:hover { color: var(--text-dim); }
.auth-forgot-note {
  font-size: 12.5px;
  color: var(--text-dim);
  line-height: 1.5;
  text-align: center;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 12px;
}
.auth-form__hint { font-size: 11.5px; color: var(--text-faint); line-height: 1.4; }

/* ============ Admin panel (account management, mcwbei/mcwleo/mcwalex) ============ */
.admin-panel { max-width: 620px; }
.admin-section { margin-top: 24px; }
.admin-section:first-of-type { margin-top: 20px; }
.admin-section__label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--accent-1);
  margin-bottom: 12px;
}
.admin-create-form { margin-top: 0; }
.admin-user-list { display: flex; flex-direction: column; gap: 8px; }
.admin-user-list__loading,
.admin-user-list__empty { font-size: 13px; color: var(--text-faint); padding: 8px 0; }
.admin-user-row {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 12px;
}
.admin-user-row__avatar {
  flex: none;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--accent-grad);
  color: #0b1110;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  justify-content: center;
}
.admin-user-row__info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.admin-user-row__name { font-size: 14px; font-weight: 600; color: var(--text); }
.admin-user-row__meta { font-size: 11.5px; color: var(--text-faint); }
.admin-user-row__reset-btn {
  flex: none;
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  padding: 7px 14px;
  cursor: pointer;
  transition: color 180ms var(--ease), border-color 180ms var(--ease);
}
.admin-user-row__reset-btn:hover { color: var(--text); border-color: var(--accent-1); }
.admin-reset-form {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.admin-reset-form[hidden] { display: none; }
.admin-reset-form__input {
  flex: 1 1 160px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  padding: 9px 12px;
}
.admin-reset-form__input:focus-visible {
  outline: none;
  border-color: rgba(127,255,212,0.5);
  box-shadow: 0 0 0 4px rgba(127,255,212,0.08);
}
.admin-reset-form__actions { flex: none; display: flex; gap: 8px; }
.admin-reset-form__confirm,
.admin-reset-form__cancel {
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  border-radius: 999px;
  padding: 9px 16px;
  cursor: pointer;
  border: none;
}
.admin-reset-form__confirm { background: var(--accent-grad); color: #0b1110; }
.admin-reset-form__cancel { background: none; border: 1px solid var(--border); color: var(--text-dim); }
.admin-reset-form__error {
  flex-basis: 100%;
  font-size: 12px;
  font-weight: 500;
  color: var(--danger);
}
.admin-user-row--just-reset { border-color: rgba(127,224,168,0.35); }

@media (max-width: 480px) {
  .admin-user-row { flex-wrap: wrap; }
  .admin-user-row__info { flex: 1 1 calc(100% - 44px); }
  .admin-user-row__reset-btn { flex: 1 1 100%; text-align: center; margin-top: 4px; }
}

/* ============ Feedback modal ============ */
.feedback-submit-form__textarea { resize: vertical; min-height: 96px; }
.feedback-list { display: flex; flex-direction: column; gap: 10px; max-height: 320px; overflow-y: auto; }
.feedback-list__empty { font-size: 13px; color: var(--text-faint); padding: 8px 0; }
.feedback-item {
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  padding: 14px 16px;
}
.feedback-item__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 6px 12px;
  font-size: 12px;
  color: var(--text-faint);
  margin-bottom: 6px;
}
.feedback-item__meta-left { display: flex; align-items: center; gap: 8px; min-width: 0; flex-wrap: wrap; }
.feedback-item__user { font-weight: 600; color: var(--text-dim); }
.feedback-item__category {
  flex: none;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-dim);
  font-size: 11px;
  font-weight: 600;
  padding: 2px 9px;
}
.feedback-item__message {
  font-size: 14px;
  color: var(--text);
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}
.feedback-item--reviewed { opacity: 0.55; }
.feedback-item__actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.feedback-item__action-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--text-dim);
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  padding: 7px 14px;
  cursor: pointer;
  transition: color 180ms var(--ease), border-color 180ms var(--ease);
}
.feedback-item__action-btn:hover { color: var(--text); border-color: var(--accent-1); }
.feedback-item__delete-confirm { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; }
.feedback-item__delete-confirm[hidden] { display: none; }
.feedback-item__delete-confirm-text { font-size: 12px; color: var(--text-faint); }

.feedback-filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px;
  margin-bottom: 12px;
  font-size: 12.5px;
  color: var(--text-dim);
}
.feedback-filter-bar__checkbox { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.feedback-filter-bar__select {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-dim);
  font-family: inherit;
  font-size: 12.5px;
  padding: 6px 10px;
}

/* ============ How it works (Process) ============ */
.how { padding: 32px 0 96px; }
.steps {
  max-width: 760px;
  margin: 0 auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.step {
  display: flex;
  align-items: flex-start;
  gap: 18px;
  padding: 22px 20px;
  border-top: 1px solid var(--border);
  transition: background 250ms var(--ease);
}
.step:first-child { border-top: none; }
.step:hover { background: var(--surface-2); }
.step__num {
  flex-shrink: 0;
  width: 72px;
  height: 72px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid rgba(127,255,212,0.3);
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.02em;
  background-image: var(--accent-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.step__content { flex: 1; min-width: 0; padding-top: 8px; }
.step__label { font-size: 20px; font-weight: 700; color: var(--accent-1); margin-bottom: 8px; }
.step p { color: var(--text-dim); font-size: 14px; line-height: 1.6; }

/* ============ FAQ / accordion ============ */
.faq { padding: 40px 0 120px; }
.accordion { max-width: 720px; margin: 0 auto; display: flex; flex-direction: column; gap: 12px; }
.accordion__item {
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--depth-shadow);
}
.accordion__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: none;
  border: none;
  color: var(--text);
  text-align: left;
  padding: 20px 24px;
  font-size: 15px;
  font-weight: 500;
}
.accordion__icon {
  flex-shrink: 0;
  width: 24px; height: 24px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--surface-2);
  font-size: 15px;
  color: var(--text-dim);
  transition: transform 250ms var(--ease), background 250ms var(--ease), color 250ms var(--ease);
}
.accordion__item.is-open .accordion__icon {
  transform: rotate(45deg);
  background: var(--accent-grad);
  color: #0a0a0a;
}
.accordion__panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height 320ms var(--ease);
}
.accordion__panel p {
  padding: 0 24px 20px;
  color: var(--text-dim);
  font-size: 14px;
  line-height: 1.65;
}

/* ============ Features ============ */
.features { padding: 40px 0 120px; }
.feature-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
}
.feature-card {
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  padding: 28px 24px;
  backdrop-filter: blur(var(--glass-blur));
  box-shadow: var(--depth-shadow);
  transition: border-color 250ms var(--ease), transform 250ms var(--ease);
}
.feature-card:hover { border-color: rgba(255,255,255,0.16); transform: translateY(-4px); }
.feature-card__icon {
  width: 44px; height: 44px;
  border-radius: 12px;
  background: var(--metal-grad);
  border: 1px solid var(--glass-border);
  color: #23262f;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px;
  margin-bottom: 18px;
}
.feature-card h3 { font-size: 16px; font-weight: 600; margin-bottom: 8px; }
.feature-card p { font-size: 13px; color: var(--text-dim); line-height: 1.6; }

/* ============ CTA ============ */
.cta { padding: 40px 0 140px; }
.cta__inner {
  text-align: center;
  position: relative;
  padding: 48px 24px;
}
.cta__inner::before {
  content: "";
  position: absolute;
  inset: -20px;
  background: radial-gradient(circle at 50% 30%, rgba(127,255,212,0.12), transparent 65%);
  pointer-events: none;
  z-index: -1;
}

.contact-bei {
  position: relative;
  display: flex;
  align-items: center;
  gap: 16px;
  max-width: 520px;
  margin: 0 auto;
  padding: 18px 22px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  text-align: left;
}
.contact-bei__avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--accent-grad);
  color: #0a0a0a;
  font-weight: 800;
  font-size: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.contact-bei__text { flex: 1; min-width: 0; }
.contact-bei__title { font-size: 14px; font-weight: 600; color: var(--text); }
.contact-bei__desc { margin-top: 2px; font-size: 13px; color: var(--text-dim); }
.contact-bei__btn { flex-shrink: 0; font-size: 13px; padding: 10px 18px; }

/* ============ Footer ============ */
/* A small fixed top padding is all that's needed now — just enough gap
   below the divider. (This used to be a large clamp() sized specifically to
   keep the watermark's own bottom-anchored position from clipping against
   this edge — removed once the watermark switched to top-anchoring below,
   which makes that whole calculation unnecessary: a top:X element can never
   render above its own starting point, so there's nothing left to clip.)
   `min-height` (via --footer-min-height, JS-measured in main.js) guards the
   OPPOSITE edge: .footer's own box height is otherwise driven purely by its
   flow content (.container's margin-top + tagline + credit row + padding),
   which can end up shorter than top:52px + the watermark's own rendered
   height — real, confirmed by screenshot: with `overflow:hidden` still
   needed (see .footer__watermark's own top-anchoring comment for why that
   property has to stay), a too-short box was silently cropping "MWC"'s own
   bottom off. Forcing the box at least as tall as the watermark's real
   bottom edge + a bottom gap fixes this at every breakpoint without
   guessing a fixed value the responsive clamp() font-size would outgrow. */
.footer { padding: 28px 0 32px; position: relative; z-index: 1; overflow: hidden; min-height: var(--footer-min-height, 460px); }
/* Decorative divider — replaces the old flat border-top. A thin horizontal
   accent-gradient line (faded at both ends) plus 3 small glowing dots along
   it, same emerald/mint accent tokens used everywhere else, echoing the
   reference site's own gradient-divider-with-sparkles look. Pure CSS via
   background-image layers on a ::before — no extra markup needed. Sits at
   .footer's own top edge (raised, see the padding-top comment above), so it
   now clears the watermark's tallest point with a comfortable margin instead
   of cutting through it. */
.footer::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 14px;
  z-index: 2;
  pointer-events: none;
  background-repeat: no-repeat;
  background-image:
    radial-gradient(circle, var(--accent-1) 0%, var(--accent-1) 45%, transparent 75%),
    radial-gradient(circle, var(--accent-2) 0%, var(--accent-2) 45%, transparent 75%),
    radial-gradient(circle, var(--accent-1) 0%, var(--accent-1) 45%, transparent 75%),
    linear-gradient(90deg, transparent 0%, rgba(127,255,212,0.55) 20%, rgba(143,232,208,0.85) 50%, rgba(127,255,212,0.55) 80%, transparent 100%);
  background-position: 18% center, 55% center, 86% center, center;
  background-size: 5px 5px, 4px 4px, 6px 6px, 100% 1px;
  filter: drop-shadow(0 0 5px rgba(127,255,212,0.5));
}
/* Large background wordmark with a mouse-follow spotlight reveal — confirmed
   against the real reference video frame-by-frame (extracted via a Swift/
   AVFoundation script, ffmpeg wasn't available): it's not a static glow, the
   bright patch tracks the cursor position, dim everywhere else. Two stacked
   text layers of identical size/position: -base is always-dim (the resting
   state), -glow is the same text in the accent gradient, clipped to a small
   circle via mask-image at the live cursor position (--mx/--my, set by
   main.js's footer mousemove handler) and faded in only while the pointer is
   actually over the footer (.footer__watermark.is-active), so no spotlight
   sits there before the user ever hovers. clamp() keeps size responsive with
   no separate media query. Sized +30% again on top of an already-applied
   +20% (was clamp(80,14vw,180) originally); the spotlight radius (168px →
   218px, also +30%) scales with the text so the reveal covers the same
   proportion of a letter as before.
   Anchored via `top` (not `bottom`) — deliberately, not cosmetic. A
   `bottom`-anchored position is measured from .footer's own bottom edge,
   which moves every time content further down (the tagline/credit rows)
   changes height — and since main.js also sizes THOSE based on the
   watermark's own measured position, a bottom-anchored watermark created a
   real circular layout dependency (clearing space below the brandmark made
   .footer taller, which pushed the bottom-anchored watermark down too,
   which moved the brandmark again, needing yet more clearance...), caught
   live via Playwright as a huge runaway offset. `top` has no such cycle —
   it's measured from the padding edge right below the divider, a fixed
   point nothing further down the page can ever move, which is also what
   "moved up, closer to the divider" (the user's own request) means in
   practice. */
.footer__watermark {
  position: absolute;
  left: 50%;
  top: 52px;
  transform: translateX(-50%);
  z-index: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: clamp(125px, 21.8vw, 281px);
  /* 1.3, not the tight 1 the rest of this file otherwise defaults to —
     confirmed via a live runtime CSS-override test (Playwright, hovering the
     exact spot the user screenshotted) that plain line-height:1 clips the
     spotlight glow VERTICALLY: `mask-image` is always confined to its own
     element's border-box, unlike plain overflow, and this italic serif
     glyph at large sizes genuinely paints outside a tight 1em box — the
     (unmasked) -base layer rendered the full "M" every time, only the
     masked -glow layer cut it off in a hard flat line top/bottom. The extra
     line-height gives the mask's own box enough headroom to contain the
     true glyph ink vertically. */
  line-height: 1.3;
  /* Same clipping bug, HORIZONTAL axis — hovering near the very start of
     "M" or end of "C" cut the glow off in a hard flat vertical line too,
     confirmed the same way (before/after screenshots via a live override).
     white-space:nowrap sizes this box to exactly the text's own rendered
     width, giving the mask zero horizontal room once the cursor nears
     either end. Padding widens the shared box (both this element AND
     .footer__watermark-glow below need the identical value — see that
     rule's own comment for why) without shifting the visually-centered text
     at all: left:50%/translateX(-50%) centers the whole (now wider) box,
     and since the padding is symmetric, the text within stays at the exact
     same visual center regardless of how much padding is added. */
  padding: 0 230px;
  white-space: nowrap;
  letter-spacing: -0.02em;
  pointer-events: none;
  user-select: none;
}
.footer__watermark-base,
.footer__watermark-glow { display: block; }
.footer__watermark-base { color: var(--text-dim); opacity: 0.07; }
.footer__watermark-glow {
  position: absolute;
  inset: 0;
  /* Must match .footer__watermark's own padding exactly. `inset:0` sizes
     this element's box to the PARENT's padding-box (i.e. this box is
     already the full padded width) — without its own matching padding,
     this layer's "MWC" text would render flush against that wider box's own
     edges (ignoring the parent's padding), landing ~230px to the left of
     where .footer__watermark-base's "MWC" renders (base is a normal-flow
     child, so IT already sits inset by the parent's padding) — a real,
     confirmed-via-screenshot double-image misalignment if this padding is
     ever changed on one side without the other. */
  padding: 0 230px;
  background: var(--accent-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 26px rgba(127,255,212,0.55));
  -webkit-mask-image: radial-gradient(circle 218px at var(--mx, 50%) var(--my, 50%), black 0%, transparent 75%);
  mask-image: radial-gradient(circle 218px at var(--mx, 50%) var(--my, 50%), black 0%, transparent 75%);
  opacity: 0;
  transition: opacity 300ms var(--ease);
}
.footer__watermark.is-active .footer__watermark-glow { opacity: 1; }
/* Small "OpsHub" logo, positioned right before/above the watermark's own "M"
   (was briefly centered over the whole "MWC" word — the user tried that,
   then explicitly asked to move it back: "giữ nguyên kích thước form, nhưng
   di chuyển về trước chữ M", i.e. same size/style, but flush with the M's
   own left edge again). Both the horizontal (--brandmark-left) and vertical
   (--brandmark-top) position are JS-measured (main.js's
   positionFooterBrandmark()), since the watermark's real rendered box
   (width AND height) depends on font metrics no CSS calc() can predict
   reliably across breakpoints — the fallback values are only a same-ballpark
   placeholder for the instant before that first measurement runs.
   `top`-anchored (not `bottom`), same non-circular reasoning as the
   watermark itself above — a `bottom` offset here would be measured from
   .footer's own (content-dependent) bottom edge again. Starts invisible and
   fades in once positioned, so it can't flash in the wrong spot for a frame
   on load. */
.footer__brandmark {
  position: absolute;
  left: var(--brandmark-left, 24px);
  top: var(--brandmark-top);
  z-index: 1;
  opacity: 0;
  transition: opacity 240ms var(--ease);
}
.footer__brandmark.is-positioned { opacity: 1; }
/* The logo itself is untouched — same .brand/.brand__name markup/style as
   the nav, per the user's explicit exception: only its position changes. */
.footer__brandmark-logo { display: inline-block; white-space: nowrap; }
/* Tagline ("OpsHub" heading + description) — now a plain left-aligned block
   inside .container (was pinned beside the watermark's M corner; the user
   asked for it to move to the page's left edge instead, per a 2nd annotated
   screenshot). Deliberately normal flow, not position:absolute — sitting in
   the same flow as .footer__inner below it means the two can never overlap
   regardless of how many lines the description wraps to at any breakpoint,
   the exact bug a previous absolutely-positioned version of this block hit
   at narrow widths. */
.footer__tagline {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-width: 420px;
  margin-bottom: 28px;
}
/* Shared base+glow structure for every "readable text with an MWC-style
   mouse-spotlight accent" element below (tagline title/desc, credit,
   contact) — confirmed with the user this should NOT be dim-by-default like
   MWC itself (that would make real sentences unreadable without hovering
   word by word): the base layer is always at full normal opacity, the glow
   layer only adds a small gradient sparkle exactly where the cursor is,
   using the same radial-gradient mask-image technique as MWC and the
   original "Website built by Bei" line. Each concrete element below only
   needs to set its own font-size/color and its own glow modifier's
   mask-image radius + --Xmx/--Xmy custom property names (all driven by the
   single shared applyFooterPointer() in main.js, no extra listeners). */
.footer__glow-base { display: block; }
.footer__glow-layer {
  position: absolute;
  inset: 0;
  display: block;
  background: var(--accent-grad);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  opacity: 0;
  transition: opacity 300ms var(--ease);
  pointer-events: none;
  user-select: none;
}
.footer__tagline-title.is-active .footer__glow-layer,
.footer__credit.is-active .footer__glow-layer { opacity: 1; }
.footer__tagline-title {
  position: relative;
  display: inline-block;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: clamp(22px, 2.6vw, 30px);
  line-height: 1.15;
  color: var(--text);
}
.footer__tagline-title-glow {
  filter: drop-shadow(0 0 10px rgba(127,255,212,0.5));
  -webkit-mask-image: radial-gradient(circle 46px at var(--tmx, 50%) var(--tmy, 50%), black 0%, transparent 75%);
  mask-image: radial-gradient(circle 46px at var(--tmx, 50%) var(--tmy, 50%), black 0%, transparent 75%);
}
/* --content-clear-top is JS-measured (main.js's positionFooterBrandmark())
   off the centered brandmark's real bottom edge — without it, the tagline
   below (plain left-aligned flow, wraps to near-full-width at narrow
   viewports) can land in the same vertical band as the centered "OpsHub"
   logo above and visually collide with it, a real overlap caught via
   screenshot at 375px. The fallback (260px) is only a same-ballpark value
   for the instant before that first measurement runs. */
/* Widened to match nav's own container (--container-wide, 1340px) instead of
   the default 1160px — nav's own left edge sits ~90px further left at wide
   viewports (its wider container hits its own max-width sooner), which made
   the footer's tagline/credit block visibly misaligned against it, confirmed
   by the user drawing a straight guide line down from nav in a screenshot.
   Below ~1388px both containers are fluid (100% + the same padding) and
   already line up on their own, so this only changes anything at wide
   desktop widths. */
.footer > .container { position: relative; z-index: 1; max-width: var(--container-wide); margin-top: var(--content-clear-top, 260px); }
.footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  padding-bottom: 24px;
  margin-bottom: 24px;
  border-bottom: 1px solid var(--border);
}
.footer__links a {
  font-size: 13px;
  color: var(--text-dim);
  transition: color 180ms var(--ease);
}
.footer__links a:hover { color: var(--text); }
.footer__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
}
/* Bottom row — replaces the old logo + "Website built by Bei" pairing (the
   logo moved up into .footer__brandmark beside/above the watermark). Uses the
   same shared .footer__glow-base/.footer__glow-layer structure as the
   tagline above — always-readable base, small mouse-spotlight glow on top,
   same technique "Website built by Bei" originally proved out. The sibling
   "Contact TG: Beiyex1" line that used to sit at the right of this row (via
   .footer__inner's own justify-content:space-between) was removed entirely
   per the user's request — .footer__inner now holds only this one line, and
   space-between with a single child simply sits it at flex-start (the left),
   no other change needed. Positioned/shifted left at runtime (main.js's
   positionFooterCredit()) so it can never render on top of the watermark's
   own horizontal span — see that function for why a fixed CSS offset can't
   do this reliably across breakpoints/font metrics. */
.footer__credit { position: relative; display: inline-block; color: var(--text-faint); font-size: 16px; font-weight: 700; }
.footer__credit-glow {
  filter: drop-shadow(0 0 6px rgba(127,255,212,0.5));
  -webkit-mask-image: radial-gradient(circle 34px at var(--crmx, 50%) var(--crmy, 50%), black 0%, transparent 75%);
  mask-image: radial-gradient(circle 34px at var(--crmx, 50%) var(--crmy, 50%), black 0%, transparent 75%);
}

/* ============ Scroll reveal ============ */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 700ms var(--ease), transform 700ms var(--ease);
  transition-delay: calc(var(--delay, 0) * 90ms);
}
.reveal.is-visible { opacity: 1; transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .reveal { transition: none; opacity: 1; transform: none; }
}

/* ============ Pseudo-route nav (no framework, no page reload) ============ */
.route-section--hidden { display: none !important; }
.no-smooth-scroll { scroll-behavior: auto !important; }

/* ============ Responsive ============ */
@media (max-width: 1180px) {
  .nav__links { display: none; }
  .nav__toggle { display: flex; }
  .nav__auth { display: none; } /* dedicated login entry point lives in the mobile drawer instead */
  /* Below the same cutover where the nav collapses to the mobile drawer, the
     hero text column occupies too much of the viewport for the 3D logo's
     responsive position/orbit-dot swing radius to stay clear of it (measured
     overlap at 768/900/960/1024px) — fall back to the flat gradient hero
     (matches hero3d/src/HeroScene.jsx's useCanMount3D breakpoint, which also
     skips mounting the canvas entirely here, not just CSS-hiding it). */
  .hero3d-root { display: none; }
}

@media (max-width: 960px) {
  .feature-grid { grid-template-columns: repeat(2, 1fr); }
  .trust__stats { grid-template-columns: repeat(2, 1fr); row-gap: 32px; }
  .hero { min-height: 82vh; }
  .service-group__list { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .nav__cta { display: none; }
  .hero { min-height: auto; padding: 120px 0 0; }
  .hero__scene .float-card, .hero__scene .float-orb { display: none; }
  .hero__horizon { height: 55%; bottom: 0; }
  .trust__stats { grid-template-columns: repeat(2, 1fr); }
  .qa-box { flex-direction: column; border-radius: var(--radius-md); padding: 10px; }
  .qa-box__select-wrap { width: 100%; }
  .qa-box__submit { width: 100%; }
  .feature-grid { grid-template-columns: 1fr; }
  .cta__inner { padding: 48px 20px; }
  .container { padding: 0 18px; }
  .contact-bei { flex-direction: column; text-align: center; }
  .contact-bei__text { text-align: center; }
  .brand__sub { display: none; }
  .service-group__trigger { padding: 20px; }
  .service-group__list { padding: 0 20px 20px; }
  .kb-card { padding: 24px 20px; }
  .kb-card__icon { width: 52px; height: 52px; font-size: 22px; }
  .kb-panel__list { grid-template-columns: 1fr; }
  .footer__links { gap: 16px; }
  .step { padding: 15px 13px; gap: 11px; }
  .step__num { width: 52px; height: 52px; font-size: 22px; }
  .step__label { font-size: 17px; }
  .testimonial-card[data-depth="1"], .testimonial-card[data-depth="2"], .testimonial-card[data-depth="3"] { display: none; }
  .testimonial-card { padding: 32px 24px; }
  .testimonial-card__title { font-size: 18px; }
  #risk-playbook { padding-bottom: 56px; }
  .playbook-timeline::before, .playbook-timeline::after { left: 21px; }
  .playbook-step { gap: 16px; padding-bottom: 400px; } /* giảm từ 780px xuống 200px rồi tăng lại x2
    lên 400px, cùng lý do/phản hồi ở rule desktop */
  .playbook-step__node { width: 44px; height: 44px; }
  .playbook-step__node svg { width: 20px; height: 20px; }
  .playbook-step__title { font-size: 17px; }
  /* Grid 1 cột — reset grid-column/row của cả 3 khối (body/video/details) về
     auto để trình duyệt tự xếp theo đúng thứ tự DOM (body → video →
     details), y hệt thứ tự hiển thị hiện tại trên mobile. */
  .playbook-step__row { grid-template-columns: 1fr; row-gap: 16px; column-gap: 0; }
  .playbook-step__body, .playbook-step__illustration, .playbook-step__details {
    grid-column: 1;
    grid-row: auto;
    width: 100%;
  }
  .playbook-step__illustration, .playbook-step__details { padding: 14px 16px; }
  .playbook-step__illustration-video { margin-left: -14px; margin-right: -14px; width: calc(100% + 28px); border-radius: 0; }
  /* Màn hẹp không đủ chỗ cho nhiều cột — ép về 1 cột, giữ đúng cách đọc
     tuần tự hiện tại thay vì auto-fit co lại quá chật. */
  .playbook-step__illustration-terms { grid-template-columns: 1fr; }
  .playbook-step__illustration-video-play__circle { width: 44px; height: 44px; }
  .playbook-step__illustration-video-play svg { width: 17px; height: 17px; }
  /* 1260px — đo thật bằng Playwright tại 320px (điện thoại hẹp nhất còn cần
     hỗ trợ, theo mandatory-rules.md rule 2 chỉ bắt buộc tới ~375px, nhưng đã
     đo sẵn tới 320px nên dùng luôn số an toàn hơn): Q2 wrap tới 1248px ở đó —
     chữ hẹp hơn wrap nhiều dòng hơn nên cần trần CAO hơn desktop, không phải
     thấp hơn. */
  .playbook-quiz__question-wrap { min-height: 1260px; }
  /* Tái dùng đúng kỹ thuật .qa-box/.qa-box__submit đã dùng cho ô nhập Q&A ở
     hero (dòng ~2929 khối này) — reset+pager đủ hẹp (~134px) để giữ chung 1
     hàng trong khoảng ~307px khả dụng ở 375px, chỉ nút Submit xuống hàng
     riêng full-width. */
  .playbook-quiz__controls { flex-wrap: wrap; row-gap: 14px; }
  .playbook-quiz__submit-btn { width: 100%; }
}

/* ============ Staff Shift Roster ============ */
.roster-overlay[hidden] { display: none; }
/* Roster opens as an in-page overlay filling the area below the nav (not a
   centered modal, not a separate page) — so this is styled as a page-level
   layout, not `.kb-modal`. It sits below .nav's z-index (50) on purpose:
   the nav must stay clickable, not dimmed/covered like a real overlay. */
.roster-overlay {
  position: fixed;
  top: var(--nav-h);
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  align-items: stretch;
  justify-content: center;
}
.roster-overlay__backdrop { display: none; }
.roster-overlay__panel {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--bg-1);
  padding: 32px clamp(20px, 4vw, 48px);
  opacity: 0;
  transition: opacity 220ms var(--ease);
}
.roster-overlay.is-open .roster-overlay__panel { opacity: 1; }

.roster-overlay__header {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-right: 44px;
  margin-bottom: 24px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--border);
  flex: none;
}
.roster-overlay__heading {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.roster-overlay__tag {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-1);
}
.roster-overlay__title {
  font-family: var(--font-display);
  font-size: 36px;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.1;
  color: var(--text);
}
/* Toolbar row: month-navigation controls on the left, action buttons on the
   right — kept as two visually distinct groups (see __nav-group/__toolbar-
   actions below) instead of one flat row of 7 mismatched controls. */
.roster-overlay__toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.roster-overlay__nav-group {
  display: flex;
  align-items: stretch;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 4px;
}
.roster-overlay__picker-field { display: flex; flex-direction: column; justify-content: center; gap: 4px; font-size: 11.5px; font-weight: 600; color: var(--text-dim); padding: 4px 12px; }
.roster-overlay__nav-group .roster-overlay__picker-field:not(:last-child) { border-right: 1px solid var(--border); }
.roster-overlay__select {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  padding: 8px 10px;
  cursor: pointer;
}
.roster-overlay__nav-group .roster-overlay__select { background: transparent; border: none; padding: 0; }
.roster-overlay__toolbar-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.roster-overlay__add-staff-form[hidden] { display: none; }
.roster-overlay__add-staff-form {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
  flex: none;
}
.roster-overlay__add-staff-input {
  flex: 1 1 220px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: inherit;
  font-size: 13.5px;
  padding: 9px 12px;
}
.roster-overlay__add-staff-input:focus-visible { outline: 2px solid var(--accent-1); outline-offset: 1px; }
.roster-overlay__add-staff-form .auth-form__error { flex: 1 1 100%; margin: 0; }

.roster-overlay__body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.roster-overlay__empty { padding: 40px 0; text-align: center; color: var(--text-dim); font-size: 14px; }
.roster-overlay__empty-clone[hidden] { display: none; }
.roster-overlay__empty-clone {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: center;
  gap: 12px;
  margin-top: 18px;
}
.roster-overlay__empty-clone select {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  padding: 8px 10px;
}

.roster-grid-scroll[hidden] { display: none; }
.roster-grid-scroll {
  flex: 1;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
.roster-grid {
  border-collapse: separate;
  border-spacing: 0;
  width: max-content;
  min-width: 100%;
  font-size: 12.5px;
}
.roster-grid th, .roster-grid td {
  padding: 6px 8px;
  text-align: center;
  white-space: nowrap;
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
}
.roster-grid thead th {
  position: sticky;
  z-index: 3;
  background: var(--surface-2);
  color: var(--text-dim);
  font-weight: 700;
}
/* Two stacked sticky header rows: a week-grouping band (colspan per 7-day
   block, matching the source roster sheet) on top, the existing day/weekday
   row directly under it. Each gets an explicit height so the second row's
   `top` offset lines up exactly below the first — same fixed-offset idea
   already used elsewhere in this grid, just for header rows instead of the
   (deliberately non-sticky) tfoot summary rows. */
.roster-grid thead tr:first-child th {
  top: 0;
  height: 26px;
  font-family: var(--font-accent);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding-top: 0;
  padding-bottom: 0;
}
.roster-grid thead tr:last-child th {
  top: 26px;
  font-size: 12.5px;
  padding-top: 10px;
  padding-bottom: 10px;
}
.roster-grid tbody td:first-child,
.roster-grid thead th:first-child {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface);
  text-align: left;
  font-weight: 700;
  color: var(--text);
  min-width: 130px;
  padding-left: 14px;
  border-right: 2px solid rgba(127,255,212,0.35);
  box-shadow: 4px 0 12px -6px rgba(0,0,0,0.5);
}
.roster-grid thead th:first-child { z-index: 4; }
/* :not(...) here matters: this selector's specificity (1 class + 1
   pseudo-class + 2 elements) otherwise beats .roster-cell--<code>'s (1
   class), so every coded cell on an even row would silently show this
   plain zebra tint instead of its real M/A/N/OFF/etc. color — invisible
   back when those colors were themselves faint gray overlays, but glaring
   now that they're bold, distinct hues. Exempt any cell that has a
   roster-cell--<code> class from the stripe; empty cells are unaffected. */
.roster-grid tbody tr:nth-child(even) td:not([class*="roster-cell--"]) { background: rgba(255,255,255,0.02); }
.roster-grid tbody tr:nth-child(even) td:first-child { background: var(--surface-2); }
/* Not sticky (unlike thead/the first/last-2 columns): with 4 rows now
   (On duty total + M/A/N breakdown) instead of the original 1, a sticky
   footer would permanently cover 4 rows' worth of staff data underneath it
   at every scroll position, not just at the bottom — worse than just
   scrolling to see it. Renders as a normal table footer at the true end
   of the table instead. */
.roster-grid tfoot td {
  background: var(--surface-2);
  font-weight: 700;
  color: var(--accent-1);
}
.roster-grid tfoot tr:last-child td { border-bottom: none; }
/* Tint the M/A/N breakdown rows with the same hue as their shift-code cells
   so they read as "belonging to" that shift at a glance. */
.roster-grid tfoot tr:nth-child(2) td { background: rgba(255,176,59,0.12); color: var(--text); }
.roster-grid tfoot tr:nth-child(3) td { background: rgba(56,161,255,0.12); color: var(--text); }
.roster-grid tfoot tr:nth-child(4) td { background: rgba(96,74,196,0.14); color: var(--text); }
.roster-grid tfoot tr:nth-child(2) td:first-child,
.roster-grid tfoot tr:nth-child(3) td:first-child,
.roster-grid tfoot tr:nth-child(4) td:first-child { font-weight: 600; }

/* Off days / Work days are always the last 2 columns (thead/tbody/tfoot all
   share the same column count), so nth-last-child(2)/last-child reliably
   target them regardless of month length. Fixed width is required for the
   `right` offset math below to line up. */
.roster-grid th:nth-last-child(2), .roster-grid th:last-child,
.roster-grid td:nth-last-child(2), .roster-grid td:last-child {
  position: sticky;
  width: 64px;
  min-width: 64px;
  font-weight: 700;
}
.roster-grid th:nth-last-child(2), .roster-grid td:nth-last-child(2) {
  border-left: 2px solid rgba(127,255,212,0.35);
  box-shadow: -4px 0 12px -6px rgba(0,0,0,0.5);
}
.roster-grid th:last-child, .roster-grid td:last-child { right: 0; }
.roster-grid th:nth-last-child(2), .roster-grid td:nth-last-child(2) { right: 64px; }
.roster-grid thead th:nth-last-child(2), .roster-grid thead th:last-child {
  z-index: 4;
  white-space: normal;
  line-height: 1.2;
}
.roster-grid tbody td:nth-last-child(2), .roster-grid tbody td:last-child {
  z-index: 2;
  background: var(--surface);
  text-align: center;
}
.roster-grid tbody tr:nth-child(even) td:nth-last-child(2),
.roster-grid tbody tr:nth-child(even) td:last-child { background: var(--surface-2); }
.roster-grid tfoot td:nth-last-child(2), .roster-grid tfoot td:last-child { z-index: 3; }

.roster-cell { min-width: 34px; cursor: pointer; transition: background 150ms var(--ease); }
.roster-cell:hover { background: rgba(127,255,212,0.08); outline: 1px dashed var(--border); outline-offset: -1px; }
.roster-cell--readonly { cursor: default; }
.roster-cell--readonly:hover { background: none; outline: none; }
/* Dawn/day/night palette for M/A/N (distinct hue AND lightness, not just
   varying opacity of the same gray like the old mint/silver/outline set).
   OFF stays a desaturated neutral so it still reads as "no shift". Rendered
   at a fixed translucent alpha (0.25, matching ROSTER_CELL_BG_ALPHA in
   main.js) against the app's dark glass background — matches the site's
   "Black Glassmorphism Luxury" aesthetic instead of the old fully-opaque,
   loudly-saturated blocks. text is light (#f5f5f7) for every code except
   N/OFF/RES, which already used light text before this change — a dark
   text color designed for a full-opacity fill fails contrast badly once
   the fill itself is translucent against a near-black backdrop (measured
   1.18-2.52:1 across alpha 0.20-0.40; light text on the same rendering
   measures 9.39-14.66:1, comfortably clearing AA-normal 4.5:1). All 15
   codes' underlying hex values here mirror DEFAULT_SHIFT_CODE_STYLES
   exactly and are overridden live by applyRosterCodeStyles() in main.js
   (which applies the same alpha via hexToRgba()) once an admin customizes
   them via the Manage Codes panel — this block is just the pre-JS/
   first-paint fallback. */
.roster-cell--m { background: rgba(255, 176, 59, 0.25); color: #f5f5f7; font-weight: 700; }
.roster-cell--a { background: rgba(56, 161, 255, 0.25); color: #f5f5f7; font-weight: 700; }
.roster-cell--n { background: rgba(96, 74, 196, 0.25); color: #f5f2ff; font-weight: 700; }
.roster-cell--off { background: rgba(110, 117, 111, 0.25); color: #c7cec9; font-weight: 600; }
.roster-cell--vl { background: rgba(226, 76, 100, 0.25); color: #f5f5f7; font-weight: 700; }
/* Work-group special markers (not a real M/A/N shift, but counted as work) */
.roster-cell--rdot { background: rgba(196, 154, 74, 0.25); color: #f5f5f7; font-weight: 700; }
.roster-cell--doj { background: rgba(120, 170, 220, 0.25); color: #f5f5f7; font-weight: 700; }
/* Off-group leave-type codes — same "not at work" family as OFF/VL, each
   given a distinguishable hue so a dense month grid stays scannable. */
.roster-cell--sl { background: rgba(255, 170, 60, 0.25); color: #f5f5f7; font-weight: 700; }
.roster-cell--l { background: rgba(255, 140, 160, 0.25); color: #f5f5f7; font-weight: 700; }
.roster-cell--ul { background: rgba(140, 108, 224, 0.25); color: #f5f5f7; font-weight: 700; }
.roster-cell--al { background: rgba(255, 120, 144, 0.25); color: #f5f5f7; font-weight: 700; }
.roster-cell--el { background: rgba(255, 110, 60, 0.25); color: #f5f5f7; font-weight: 700; }
.roster-cell--cl { background: rgba(255, 210, 90, 0.25); color: #f5f5f7; font-weight: 700; }
/* RES/UNR are flag-worthy anomalies (resigned, unreported) — kept visually
   the most saturated/alarming of the set. */
.roster-cell--res { background: rgba(220, 38, 50, 0.25); color: #fff5f5; font-weight: 700; }
.roster-cell--unr { background: rgba(255, 196, 0, 0.25); color: #f5f5f7; font-weight: 700; }
.roster-cell--selected { outline: 2px solid var(--accent-1); outline-offset: -2px; }
/* Section-divider row between the Morning/Afternoon/Night/Unassigned shift
   groups (see renderRosterGrid in main.js) — plain row, not sticky, higher
   specificity than the even/odd zebra striping so it always reads as a
   distinct label rather than just another data row. */
.roster-grid tbody tr.roster-grid__group-divider td {
  /* Overrides the sticky-left/right column rules, which would otherwise
     apply conflicting left:0/right:0 positioning to this row's single
     colspan'd cell (it's simultaneously :first-child and :last-child). */
  position: static;
  background: var(--surface-2);
  color: var(--text-dim);
  font-family: var(--font-accent);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-align: left;
  padding: 8px 14px;
  box-shadow: none;
  border-left: none;
  border-right: none;
}
.roster-grid tbody tr.roster-grid__group-divider--m td { color: #ffb03b; }
.roster-grid tbody tr.roster-grid__group-divider--a td { color: #5db2ff; }
.roster-grid tbody tr.roster-grid__group-divider--n td { color: #b8a6f0; }

.roster-grid__staff-name { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
.roster-grid__remove-btn {
  flex: none;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: none;
  border: 1px solid var(--border);
  color: var(--text-faint);
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
}
.roster-grid__remove-btn:hover { color: var(--danger); border-color: var(--danger); }
.roster-row-delete-confirm[hidden] { display: none; }
.roster-row-delete-confirm { display: flex; align-items: center; gap: 6px; white-space: normal; }
.roster-row-delete-confirm-text { font-size: 11px; color: var(--text-faint); }

.roster-cell-editor[hidden] { display: none; }
.roster-cell-editor {
  position: fixed;
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
  max-width: min(320px, 92vw);
  background: var(--surface);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  box-shadow: 0 20px 50px -15px rgba(0,0,0,0.7);
}
.roster-cell-editor__group { display: flex; flex-direction: column; gap: 4px; }
.roster-cell-editor__group-label {
  font-family: var(--font-accent);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-faint);
  padding: 0 2px;
}
.roster-cell-editor__group-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.roster-cell-editor__chip {
  border: none;
  border-radius: var(--radius-sm);
  min-width: 34px;
  padding: 8px 10px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  color: var(--text);
  background: var(--surface-2);
  transition: transform 150ms var(--ease);
}
.roster-cell-editor__chip:hover { transform: translateY(-1px); }
/* Chips now use the exact same opaque color as their cell (not a separate
   lighter variant) — with per-code color now admin-configurable, the chip
   doubles as a true live swatch of "what this code will look like". */
.roster-cell-editor__chip--m { background: #ffb03b; color: #241400; }
.roster-cell-editor__chip--a { background: #38a1ff; color: #04101f; }
.roster-cell-editor__chip--n { background: #604ac4; color: #f5f2ff; }
.roster-cell-editor__chip--rdot { background: #c49a4a; color: #2a1d06; }
.roster-cell-editor__chip--doj { background: #78aadc; color: #08131f; }
.roster-cell-editor__chip--off { background: #6e756f; color: #c7cec9; }
.roster-cell-editor__chip--vl { background: #e24c64; color: #2a0208; }
.roster-cell-editor__chip--sl { background: #ffaa3c; color: #241300; }
.roster-cell-editor__chip--l { background: #ff8ca0; color: #2b0810; }
.roster-cell-editor__chip--ul { background: #8c6ce0; color: #140a2e; }
.roster-cell-editor__chip--al { background: #ff7890; color: #2b0512; }
.roster-cell-editor__chip--el { background: #ff6e3c; color: #2a0c00; }
.roster-cell-editor__chip--cl { background: #ffd25a; color: #2b2000; }
.roster-cell-editor__chip--res { background: #dc2632; color: #fff5f5; }
.roster-cell-editor__chip--unr { background: #ffc400; color: #241a00; }
.roster-cell-editor__chip--clear { background: transparent; border: 1px solid var(--border); color: var(--text-faint); font-weight: 600; align-self: flex-start; }

/* ============ Manage shift codes panel (admin, Roster edit mode) ============ */
.roster-codes-list { display: flex; flex-direction: column; gap: 8px; }
.roster-codes-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 8px 12px;
}
.roster-codes-row__code { min-width: 40px; font-family: var(--font-accent); font-weight: 700; font-size: 13px; }
.roster-codes-row__label { flex: 1 1 auto; font-size: 13px; color: var(--text-dim); min-width: 120px; }
.roster-codes-row__toggle {
  display: flex;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.roster-codes-row__toggle-btn {
  border: none;
  background: transparent;
  color: var(--text-faint);
  font-size: 11.5px;
  font-weight: 700;
  padding: 6px 12px;
  cursor: pointer;
}
.roster-codes-row__toggle-btn.is-active { background: var(--accent-grad); color: #0b1110; }
/* Cell / Cell border / Text / Text border color pickers + a live preview
   swatch, so an admin sees the effect before hitting Save. */
.roster-codes-row__colors { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.roster-codes-row__color-field { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.roster-codes-row__color-label { font-size: 9px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.04em; }
.roster-codes-row__color-input {
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  background: none;
}
.roster-codes-row__preview {
  min-width: 40px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  font-family: var(--font-accent);
  font-size: 11px;
  font-weight: 700;
  padding: 0 8px;
}
.roster-codes-actions { display: flex; align-items: center; gap: 12px; margin-top: 16px; }

/* ============ Roster toolbar: Create button + Manual/Auto menu ============ */
.roster-overlay__create { position: relative; }
.roster-overlay__create-menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
  padding: 6px;
  min-width: 140px;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 180ms var(--ease), transform 180ms var(--ease), visibility 180ms;
  box-shadow: var(--depth-shadow), 0 12px 32px -8px rgba(0,0,0,0.6);
  z-index: 20;
}
.roster-overlay__create.is-open .roster-overlay__create-menu { opacity: 1; visibility: visible; transform: translateY(0); }
.roster-overlay__create-menu li { list-style: none; }
.roster-overlay__create-menu button {
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  font-family: inherit;
  padding: 9px 12px;
  border-radius: 8px;
  font-size: 13px;
  color: var(--text-dim);
  cursor: pointer;
  transition: background 150ms var(--ease), color 150ms var(--ease);
}
.roster-overlay__create-menu button:hover { background: var(--surface-2); color: var(--text); }

.roster-overlay__manual-popover {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  padding: 14px;
  min-width: 240px;
  z-index: 20;
  box-shadow: var(--depth-shadow), 0 12px 32px -8px rgba(0,0,0,0.6);
}
.roster-overlay__manual-popover[hidden] { display: none; }
.roster-overlay__manual-popover .roster-overlay__select { width: 100%; }

/* ============ Auto-create modal ============ */
.roster-auto-create__row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 14px; }
.roster-auto-create__row .roster-overlay__picker-field { flex: 1 1 160px; }
.roster-auto-create__remaining {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 0 0 14px;
  padding: 10px 16px;
  background: var(--surface-2);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-sm);
}
.roster-auto-create__remaining-num {
  font-family: var(--font-display);
  font-size: 28px;
  font-weight: 700;
  line-height: 1;
  color: var(--text);
}
.roster-auto-create__remaining-text { display: flex; flex-direction: column; gap: 2px; }
.roster-auto-create__remaining-text strong {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text);
}
.roster-auto-create__remaining-text span { font-size: 12.5px; color: var(--text-dim); }
.roster-auto-create__remaining--negative .roster-auto-create__remaining-num,
.roster-auto-create__remaining--negative .roster-auto-create__remaining-text strong { color: var(--danger); }
.roster-auto-create__remaining--complete .roster-auto-create__remaining-num,
.roster-auto-create__remaining--complete .roster-auto-create__remaining-text strong { color: var(--accent-1); }
.roster-auto-create__hint { font-size: 12.5px; color: var(--text-faint); margin: 0 0 10px; }
.roster-auto-create__staff-list { display: flex; flex-direction: column; gap: 8px; max-height: 320px; overflow-y: auto; margin-bottom: 8px; }
.roster-auto-create__staff-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 8px 12px;
}
.roster-auto-create__staff-name { flex: 1 1 140px; font-size: 13px; color: var(--text); }
/* Read-only now — staff identity/seniority/preferred-off are edited via
   Manage staff, not here, so these are plain text, not form controls. */
.roster-auto-create__staff-off-readonly { flex: 1 1 auto; font-size: 12px; color: var(--text-faint); }
.roster-auto-create__staff-seniority-readonly { min-width: 56px; text-align: right; font-size: 12px; color: var(--text-faint); }
.roster-auto-create__warnings {
  margin-top: 12px;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: rgba(255, 176, 59, 0.1);
  border: 1px solid rgba(255, 176, 59, 0.3);
  color: var(--text-dim);
  font-size: 12.5px;
}
.roster-auto-create__warnings[hidden] { display: none; }
.roster-auto-create__warnings ul { margin: 0; padding-left: 18px; }

/* ============ Manage staff modal (admin, Roster edit mode) — the
   site-wide staff master list, same row-based-panel shape as Manage shift
   codes above, but each row saves independently rather than one big form
   ============ */
.roster-staff-master-list { display: flex; flex-direction: column; gap: 8px; max-height: 420px; overflow-y: auto; margin-bottom: 8px; }
.roster-staff-master-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 8px 12px;
  position: relative;
}
.roster-staff-master-row__name {
  flex: 1 1 140px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  padding: 6px 8px;
}
.roster-staff-master-row__seniority,
.roster-staff-master-row__off-day {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  padding: 6px 8px;
}
.roster-staff-master-row__save { flex: none; }
.roster-staff-master-row__remove { flex: none; }
.roster-staff-master-row__error { flex: 1 1 100%; margin: 4px 0 0; }
.roster-staff-master-row__error[hidden] { display: none; }

/* ============ Delete month (admin, Roster edit mode) ============ */
.roster-overlay__delete-month { position: relative; }
.roster-overlay__delete-month-btn:hover { color: var(--danger); border-color: var(--danger); }
.roster-overlay__delete-month-confirm {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  gap: 8px;
  background: var(--glass-bg);
  backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-md);
  padding: 12px;
  min-width: 220px;
  max-width: 280px;
  z-index: 20;
  box-shadow: var(--depth-shadow), 0 12px 32px -8px rgba(0,0,0,0.6);
}
.roster-overlay__delete-month-confirm[hidden] { display: none; }

@media (max-width: 640px) {
  .roster-overlay__header { padding-right: 40px; }
  .roster-overlay__title { font-size: 24px; }
  .roster-overlay__toolbar { flex-direction: column; align-items: stretch; width: 100%; }
  .roster-overlay__nav-group { width: 100%; }
  .roster-overlay__toolbar-actions { width: 100%; }
  .roster-overlay__picker-field { flex: 1 1 auto; }
  .roster-overlay__manual-popover { left: 0; right: 0; min-width: 0; width: calc(100vw - 40px); max-width: 320px; }
  .roster-overlay__delete-month-confirm { left: 0; right: 0; min-width: 0; width: calc(100vw - 40px); max-width: 320px; }
  .roster-auto-create__row { flex-direction: column; }
  .roster-staff-master-row { flex-direction: column; align-items: stretch; }
  .roster-staff-master-row__name,
  .roster-staff-master-row__seniority,
  .roster-staff-master-row__off-day { width: 100%; }
}

/* ============ Auth gate (full-site login screen) ============ */
/* body.is-gated is baked directly into the raw HTML (see index.html) so the
   browser can never paint real content before this rule is parsed — makes
   "flash of real content before login" structurally impossible, no JS
   timing dependency. main.js only ever removes this class after a real
   session is confirmed by the server. */
/* .cta (#contact) is listed explicitly — it's a sibling of <main>, not a
   child (see index.html), so a selector matching only "main" would miss it
   and leave the locked contact card visible while gated. */
body.is-gated .nav,
body.is-gated main,
body.is-gated .cta,
body.is-gated .footer { display: none; }

.auth-gate[hidden] { display: none; }
.auth-gate {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
/* Ambient glow behind the card — the "floating in space" look common to
   premium SaaS login screens (Linear/Vercel/Stripe). Capped at 100vw so it
   can never exceed the viewport and cause horizontal scroll on narrow
   screens; heavily blurred so it reads as soft ambient light, not a visible
   shape. Kept at low opacity to stay inside design-system.md's "one accent
   moment per screen" rule rather than competing with the card itself. */
.auth-gate::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(640px, 100vw);
  height: min(640px, 100vw);
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle, var(--accent-1) 0%, var(--accent-2) 35%, transparent 72%);
  filter: blur(90px);
  opacity: 0.29;
  z-index: 0;
  pointer-events: none;
}
.auth-gate__card {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: 420px;
  text-align: center;
  padding: 52px 40px 44px;
  border-radius: var(--radius-lg);
  backdrop-filter: blur(var(--glass-blur));
  /* Two-layer background: the first (padding-box) gradient stands in for a
     flat --glass-bg fill, the second (border-box) is a soft diagonal
     gradient border — brighter at the top-left, fading toward the bottom —
     the "light-catching edge" technique dark glass cards use on
     Linear/Raycast-style apps. Two gradient layers are needed (not a plain
     background-color) because background-clip only separates background
     IMAGE layers by box, not background-color. */
  border: 1px solid transparent;
  background:
    linear-gradient(var(--glass-bg), var(--glass-bg)) padding-box,
    linear-gradient(160deg, rgba(255, 255, 255, 0.18), rgba(127, 255, 212, 0.12) 40%, rgba(255, 255, 255, 0.04) 70%) border-box;
  box-shadow: var(--depth-shadow), 0 30px 80px -30px rgba(127, 255, 212, 0.18);
  animation: auth-gate-in 560ms var(--ease) both;
}
/* Hairline shine along the top edge — a thin bright sliver simulating light
   catching the card's upper rim, the same glass-morphism cue Arc/Raycast use
   on dark cards. */
.auth-gate__card::after {
  content: "";
  position: absolute;
  top: 0;
  left: 14%;
  right: 14%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55), transparent);
  pointer-events: none;
}
@keyframes auth-gate-in {
  from { opacity: 0; transform: translateY(10px) scale(0.97); }
  to { opacity: 1; transform: none; }
}
/* Chrome/metal wordmark, now the card's only headline (title/subtitle text
   removed at the user's request — the mark itself IS the <h1>). Deepened
   into a genuine "dark space, one bright pass of light" gradient instead of
   the earlier uniformly-light metal fill: the resting stops sit low, close
   to the card's own surface tone, so the letters read as dim chrome most of
   the time; the white/mint peak is the one bright "comet" moment sweeping
   through — a bigger contrast swing reads as more "vũ trụ" (cosmic) than a
   flat shimmer. A soft mint drop-shadow keeps a faint halo around the glyphs
   even between passes, so the wordmark is never fully dark/illegible. Loop
   slowed twice now (5s → 10s → 16s) per direct feedback each time. */
.auth-gate__mark {
  display: block;
  margin: 0 0 26px;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: 75px;
  line-height: 1.1;
  letter-spacing: -0.015em;
  background: linear-gradient(100deg,
    #414f4b 0%, #5f6d69 14%, #8b9793 26%, #c8d0cc 36%,
    #eef1ef 44%, #ffffff 48%, #8fe8d0 50%, #ffffff 52%,
    #eef1ef 56%, #c8d0cc 64%, #8b9793 74%, #5f6d69 86%, #414f4b 100%);
  background-size: 280% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter: drop-shadow(0 0 29px rgba(143, 232, 208, 0.36));
  animation: mint-text-shimmer 16s linear infinite;
}
@keyframes mint-text-shimmer {
  from { background-position: 170% 0; }
  to { background-position: -170% 0; }
}
/* Same sweep, same 16s cycle, reused (not duplicated) from .auth-gate__mark
   above — both start together the moment the gate appears (no JS syncing
   needed), so the light reads as one pass moving down the whole card header
   rather than two independent shimmers drifting apart. Gradient stops swap
   the chrome tones for a dim-to-bright mint range so the tag keeps its own
   accent-color identity instead of copying the wordmark's metal palette. */
.auth-gate__tag {
  display: block;
  margin-bottom: 32px;
  font-family: var(--font-accent);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  background: linear-gradient(100deg,
    #2f5c4f 0%, #4a8874 20%, #7fffd4 42%,
    #ffffff 50%, #7fffd4 58%, #4a8874 80%, #2f5c4f 100%);
  background-size: 280% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: mint-text-shimmer 16s linear infinite;
}
/* Fixed min-height so the layout doesn't jump once Google's script finishes
   loading and renders its real button into this container. */
.auth-gate__gsi { display: flex; justify-content: center; min-height: 44px; }
.auth-gate__error { margin-top: 14px; }

@media (max-width: 480px) {
  .auth-gate__card { padding: 40px 24px 32px; }
  .auth-gate__mark { font-size: 52px; margin-bottom: 20px; }
  .auth-gate__tag { margin-bottom: 26px; }
}

@media (prefers-reduced-motion: reduce) {
  .auth-gate__card { animation: none; }
  .auth-gate__mark { animation: none; background-position: 0 0; }
  .auth-gate__tag { animation: none; background-position: 0 0; }
}
