/* ──────────────────────────────────────────────────────────────────────
 * Global page-container alignment.
 *
 * The sub-tabs strip lives inside Metronic's `.kt-container-fixed`
 * which defaults to a centred max-width of 1280px + 24-30px padding.
 * Page content below (overview-page, settings-page, etc.) uses its own
 * padding system. Left edges didn't line up — sub-tabs appeared
 * flush-left while page content was inset.
 *
 * Fix: force every `.kt-container-fixed` to be full-width with a
 * uniform 28px/32px padding-inline (the same value SnovaHR uses for
 * its hero band, settings pages, and the rest of the surfaces).
 * The header keeps its own override that already follows this rule.
 * ────────────────────────────────────────────────────────────────────── */
.kt-container-fixed {
  max-width: 100% !important;
  width: 100% !important;
  margin-inline: 0 !important;
  padding-inline: 28px !important;
}
@media (min-width: 80rem) {
  .kt-container-fixed {
    padding-inline: 32px !important;
  }
}
/* Override the per-page paddings on the Overview shell so the hero +
 * grid + feed-row all align with the sub-tabs above. */
.overview-page,
.settings-page,
.profile-layout,
.home-stub-page {
  padding-inline: 28px !important;
}
@media (min-width: 80rem) {
  .overview-page,
  .settings-page,
  .profile-layout,
  .home-stub-page {
    padding-inline: 32px !important;
  }
}


/* ──────────────────────────────────────────────────────────────────────
 * Typography base token.
 *
 * NOTE — superseded by /static/css/premium-theme.css ("Quiet Authority"),
 * which is loaded AFTER this file in base.html and re-sets --font-sans to
 * Geist-first. We keep this declaration for safety so any rendering path
 * that bypasses premium-theme.css still gets the Inter-fallback chain
 * rather than a generic system font.
 * ────────────────────────────────────────────────────────────────────── */
:root {
  /* 2026-09-01: the UI font is the platform's own system font, copied
     VERBATIM from Time Tracker's --font-sans (its styles-*.css) so the two
     products render identically — Segoe UI on Windows, SF on macOS, Roboto
     on Android. Was 'Plus Jakarta Sans' (webfont), which made Snova.People
     read as a different product beside Time Tracker. */
  --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
               "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
html, body {
  font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
               "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-size: 14px;
  line-height: 1.5;
}


/* Snova.People — overlays on top of Metronic v9 Demo 1.
 *
 * The bulk of the styling comes from /static/metronic/css/core.bundle.css
 * + styles.css. This file holds:
 *   1. SnovaHR-specific brand mark in the sidebar (snova-brand.*)
 *   2. Profile dropdown panel (mirrors header.component.ts scoped styles).
 *   3. Legacy login / error / welcome styles preserved for /auth pages.
 *
 * Token names follow SnovaHR's source — colors taken verbatim from
 * snovahr-web/src/app/shared/components/layout/header/header.component.ts. */

/* ──────────────────────────────────────────────────────────────────────
 * 1. Sidebar brand mark (matches snovahr-web sidebar.component.ts:50-56).
 * ────────────────────────────────────────────────────────────────────── */
.snova-brand {
  color: #071437;
  text-decoration: none;
  height: 100%;
  /* Pixel-perfect vertical centering of the icon + wordmark.
   * line-height: 1 collapses the text line-box to its glyph height so
   * the flex centerline runs through the optical middle of both the
   * 28 px icon and the wordmark glyphs — without this, the wordmark
   * sits ~2 px low because of the inherited 1.5 line-height. */
  display: inline-flex;
  align-items: center;
  gap: 10px;
  line-height: 1;
}
.snova-brand:hover { text-decoration: none; }

.snova-brand__icon {
  /* 2026-09-01, "logo size increase some more": 30px -> 36px, to sit closer to
     Time Tracker's mark. Radius tracks the size so the chip keeps its shape. */
  width: 36px;
  height: 36px;
  border-radius: 11px;
  /* Fallback fill only. The final gradient + shadow are owned by
   * premium-theme.css §8 (.snova-brand__icon, !important) so the mark stays on
   * the Quiet-Authority palette; this keeps a sane fill if that layer is ever
   * removed. The size/radius above ARE the effective values (premium-theme
   * doesn't set them) — a slightly larger, softer chip than the old 28px/8px. */
  background: #2563EB;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 2px rgba(11, 33, 71, 0.08);
}
.snova-brand__icon-letter {
  color: #ffffff;
  /* Re-asserted with !important in design-system.css §7c — the blanket
     13.5px content rule there matches this span and would otherwise shrink
     the glyph inside the enlarged chip. Keep the two values in step. */
  font-size: 18px;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1;
}
.snova-brand__text {
  /* Also re-asserted in design-system.css §7c (see above) — without that the
     wordmark renders at the blanket 13.5px, which is what kept the logo
     looking small no matter what was set here. Keep the two in step. */
  font-size: 19.5px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: #0f172a;
  white-space: nowrap;
  /* .snova-brand is inline-flex + align-items:center, so line-height:1 lets
   * the wordmark's optical middle share the icon's centre-line. Nudge it down a
   * touch so the "Jesto People" wordmark sits on the icon's centre-line rather
   * than a hair high (overrides the +1px from snova-sidebar.css). */
  line-height: 1;
  position: relative;
  top: 1px;
  display: inline-block;
}
/* The wordmark is a <span>, but it also carries `default-logo`, whose
 * `height: 26px` (snova-sidebar.css) was authored for an <img> mark. On a
 * span that fixed box parks the 19.5px line at its top and drops the glyphs
 * below the chip's centre-line — which is what the old `top: 6px` nudge was
 * compensating for. Letting the box size to its own text hands the centring
 * back to the flex row, where it is exact at any font size. */
.kt-sidebar .kt-sidebar-header .snova-brand__text {
  height: auto;
}

/* Align the brand with the nav rail directly below it: the icon's optical
 * centre lines up with the nav-item icons, and the wordmark's left edge lines
 * up with the nav-item titles — so the logo and the navigation read as a
 * single, tidy column instead of the logo floating ~15px to the left.
 * (Measured on the resolved 280px sidebar: nav-icon centre ≈ 51px, nav-title
 * left ≈ 71px. With the 30px brand icon, a 36px header inset + a 5px brand
 * gap lands the icon centre on 51 and the wordmark left edge on 71 exactly.)
 * The collapsed-sidebar rules below use !important, so they still win here. */
/* Inset re-measured for the 36px chip (2026-09-01): 32px + half the chip puts
 * the icon centre on 50px against the nav icons' 50.5px. The wordmark now
 * starts ~3px right of the nav titles rather than dead on them — the bigger
 * mark cannot hold both alignments at once, and the icon centre-line is the
 * one the eye follows down the rail. */
.kt-sidebar .kt-sidebar-header { padding-left: 32px; }
.snova-brand { gap: 6px !important; }

/* ──────────────────────────────────────────────────────────────────────
 * Collapsed sidebar — resting rail + hover peek.
 *
 * While body carries the collapse class the sidebar has two states:
 *   RESTING  (`:not(:hover)`, or `.snova-hover-lock` straight after a click)
 *            → 64px icon-only rail, every label hidden.
 *   PEEK     (`:hover`)
 *            → the full 280px menu, floated OVER the page. `.kt-wrapper`
 *              padding and the header offset stay at the rail width, so no
 *              content reflows — only the toggle button moves the layout.
 *
 * `.snova-hover-lock` is added by _sidebar.html on toggle click and dropped
 * on mouseleave. It preserves the Phase 5o.1 fix ("the button doesn't close
 * immediately on click, I have to hover out"): collapsing with the cursor
 * still on the toggle snaps shut instead of staying open under the pointer.
 * ────────────────────────────────────────────────────────────────────── */

/* Point Metronic's layout token at our 64px rail.
 *
 * `.kt-wrapper`'s padding and `.kt-header`'s inset are set by Metronic from
 * var(--sidebar-width) with !important, from inside a cascade LAYER — and a
 * layered !important outranks an unlayered one, so no rule we write here can
 * beat them. The token is the only lever. Its own collapse plumbing
 * (`.demo1.kt-sidebar-collapse { --sidebar-width: var(--sidebar-width-collapse) }`)
 * never fires either, because the unlayered `body { --sidebar-width: 232px }`
 * below outranks a layered rule for NORMAL declarations. Result before this
 * rule: a 64px rail with the page still indented 232px — a 168px dead band.
 * Overriding the variable on the collapsed body fixes wrapper, header and
 * rail in one move. */
body.snova-sidebar-collapsed,
body.kt-sidebar-collapse {
  --sidebar-width: 64px;
  --sidebar-width-collapse: 64px;
}

body:is(.snova-sidebar-collapsed, .kt-sidebar-collapse) .kt-sidebar:is(:not(:hover), .snova-hover-lock) {
  width: 64px !important;
  min-width: 64px !important;
  max-width: 64px !important;
  overflow: visible !important;
}

/* PEEK — hovering the rail floats the full menu above the page (.kt-sidebar
 * is fixed + z-20, the header is z-10, so it draws over both). */
body:is(.snova-sidebar-collapsed, .kt-sidebar-collapse) .kt-sidebar:hover:not(.snova-hover-lock) {
  width: var(--sidebar-default-width, 280px) !important;
  min-width: var(--sidebar-default-width, 280px) !important;
  max-width: var(--sidebar-default-width, 280px) !important;
  overflow: visible !important;
  box-shadow: 0 18px 44px -24px rgba(15, 23, 42, 0.45);
}

/* RESTING: hide every text element. Icons stay. */
body:is(.snova-sidebar-collapsed, .kt-sidebar-collapse) .kt-sidebar:is(:not(:hover), .snova-hover-lock) :is(.kt-menu-title, .kt-menu-arrow, .kt-menu-heading, .kt-menu-accordion, .kt-menu-badge, .snova-brand__text) {
  display: none !important;
}

/* RESTING: centre the icon inside the link so it looks pillared. */
body:is(.snova-sidebar-collapsed, .kt-sidebar-collapse) .kt-sidebar:is(:not(:hover), .snova-hover-lock) .kt-menu-link {
  justify-content: center !important;
  padding-inline: 0 !important;
}

body:is(.snova-sidebar-collapsed, .kt-sidebar-collapse) .kt-sidebar:is(:not(:hover), .snova-hover-lock) .kt-menu-icon {
  margin: 0 !important;
  width: auto !important;
}

/* RESTING: keep the brand icon centred in the header. */
body:is(.snova-sidebar-collapsed, .kt-sidebar-collapse) .kt-sidebar:is(:not(:hover), .snova-hover-lock) .kt-sidebar-header {
  justify-content: center !important;
  padding-inline: 0 !important;
}

/* Shift the main content over by the rail width so the page doesn't leave an
 * empty band where the expanded sidebar used to live. Deliberately NOT
 * widened during PEEK — the peeked menu overlays the page instead of
 * pushing it. */
body.snova-sidebar-collapsed .kt-wrapper,
body.kt-sidebar-collapse .kt-wrapper {
  padding-inline-start: 64px;
}


/* Nav labels: give the line box room for descenders.
 *
 * `.kt-menu-title` is clipped (`overflow: hidden`, snova-sidebar.css) because
 * the collapse animation transitions its max-width. Its line-height was 1.2
 * top-level / 1.3 in the accordion, so at 13.5px the box is 16.2px while a
 * glyph with a descender needs ~17.6px — the tail of every g/y/p was cut off
 * by that clip (user report 2026-09-01: "letters like g they are showing only
 * half"). 1.5 clears the deepest descender at any size the blanket content
 * rule may impose, and the extra height lands as row breathing room.
 *
 * Selectors mirror the originals' specificity exactly; app.css loads after
 * snova-sidebar.css, so the tie resolves here. */
.kt-sidebar .kt-menu > .kt-menu-item > .kt-menu-link .kt-menu-title,
.kt-sidebar .kt-menu-accordion .kt-menu-item > .kt-menu-link .kt-menu-title {
  line-height: 1.5;
}


/* ──────────────────────────────────────────────────────────────────────
 * 2. Header / topbar — page-title and container-fixed override.
 *    Verbatim from header.component.ts:176-186.
 * ────────────────────────────────────────────────────────────────────── */
#headerContainer.kt-container-fixed {
  max-width: 100% !important;
  width: 100% !important;
  padding-inline: 28px !important;
}
@media (min-width: 80rem) {
  #headerContainer.kt-container-fixed {
    padding-inline: 32px !important;
  }
}

/* Tighten the global topbar height. The bar only carries the small action
 * cluster (search / notifications / avatar / dev persona switcher) — the page
 * title is intentionally not rendered here — so Metronic's default 70px leaves
 * a tall empty band above every page's content (most visible on the tabbed
 * screens like Onboarding). Both the fixed bar's height and the content
 * wrapper's top padding read from --header-height, so shrinking the variable
 * pulls them down together with no gap or overlap. 56px comfortably clears the
 * 36px action buttons. */
body.kt-header-fixed { --header-height: 56px; }

/* Narrow the sidebar (2026-08-11 by request). Metronic's vendored default is
 * 280px, which is wider than these short labels need and eats page width on
 * the dense admin tables. 232px still fits the longest item ("Data Migration")
 * without wrapping.
 *
 * Set on <body> rather than editing static/metronic/css/styles.css: that file
 * is vendored, and its declaration sits in `@layer components`, so an
 * unlayered rule here wins regardless of load order. The shell reads this
 * variable for the sidebar box AND the content wrapper's left offset, so both
 * move together — same approach as --header-height above. The collapsed width
 * is untouched, so the collapse toggle behaves exactly as before. */
body { --sidebar-width: 232px; --sidebar-default-width: 232px; }

/* Initials-avatar fill (2026-08-11 by request: "use the colour of Post to all").
 * Avatars were #1b84ff while primary buttons are #2563EB, so a "C" avatar sat
 * next to a Post button in two visibly different blues. One token now feeds
 * every initials avatar in the product — the shared _avatar.html macro plus the
 * handful of pages that paint their own (topbar profile chip, Team List,
 * Settings, Overview, Approvals inbox, Department tree, Files).
 * On :root, not body, so avatars inside portaled overlays resolve it too.
 * The fallback in each var() keeps them correct if this file fails to load. */
:root { --snova-avatar-bg: #2563EB; }

/* Avatar initials scale with their CIRCLE, not with body text (2026-08-17,
 * Profile: "adjust the alphabet").
 *
 * The letter is artwork sized to its circle, not content. The shared macro
 * (partials/_avatar.html) has always emitted `font-size: size*0.4` INLINE, and
 * the profile hero declares 27px for its 76px circle. But a stylesheet
 * `!important` beats a non-important inline style, so design-system.css §7
 * ("one content size, 13.5px") flattened every letter in the app: the 76px
 * hero circle held a 13.5px letter adrift in the middle of it, while the 28px
 * table circles went the other way and had theirs crowd the edge.
 *
 * The macro now also emits `--snova-av-fs` next to the inline size, so the
 * per-call-site value survives the override and is handed straight back here —
 * one rule for every avatar size in the app instead of a list. The fallback
 * only applies to a span carrying the class but not the variable. The hero
 * letter is authored in CSS rather than inline, so it is restored by name.
 *
 * SPECIFICITY, not load order, is what wins here: `main#content .x:not([..]):not([..])`
 * scores (1,3,1) and beats §7's (1,2,22) on class count — the same trick that
 * file already uses for status pills. So this can live beside the avatar token
 * it belongs to instead of in the middle of the size-exception list. */
main#content .snova-avatar__initials:not([class*="material-icons"]):not([class*="ki-"]) {
  font-size: var(--snova-av-fs, 13.5px) !important;
}
main#content .pf-avatar__initials:not([class*="material-icons"]):not([class*="ki-"]) {
  font-size: 27px !important;
}

/* Topbar page title — the current module name (My Space / Organization /
 * Calendar / …), server-resolved from the URL in web_home._page_title and
 * rendered on the LEFT of the global header. Previously that slot was an
 * empty spacer, leaving the bar as a bare strip of right-aligned icons; the
 * title gives it a clear anchor so it reads as a proper page header. */
.kt-topbar-title {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: #071437;
  line-height: 1.2;
  white-space: nowrap;
}
:root[data-theme="dark"] .kt-topbar-title,
.dark .kt-topbar-title { color: #f8fafc; }


/* ──────────────────────────────────────────────────────────────────────
 * 3. Profile dropdown — verbatim from header.component.ts:196-411.
 * ────────────────────────────────────────────────────────────────────── */
.profile-menu { position: relative; }

.profile-menu__trigger {
  appearance: none;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  border-radius: 9999px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
}
.profile-menu__trigger:focus-visible {
  outline: none;
  box-shadow: var(--qa-focus-ring);
}
.profile-menu__avatar {
  display: inline-flex;
  border-radius: 9999px;
  overflow: hidden;
}

.profile-menu__panel {
  position: absolute;
  top: calc(100% + 12px);
  right: 0;
  /* 280 -> 352px (2026-10-13): the role pill now sits beside your name and
     email. At 280px it cut even admin@snova.test short; at 320px a ~24-letter
     work address beside "Line Manager" still lacked 18-30px. 352px fits those;
     a longer address ends in "..." and shows whole on hover. Never wider than
     the screen, less a 12px margin each side. */
  width: min(352px, calc(100vw - 24px));
  background: #ffffff;
  border: 1px solid #e4e4e7;
  border-radius: 14px;
  box-shadow:
    0 10px 24px -8px rgba(15, 23, 42, 0.18),
    0 4px 8px -2px rgba(15, 23, 42, 0.06);
  padding: 6px;
  z-index: 60;
  animation: profileMenuIn 0.14s cubic-bezier(0.4, 0, 0.2, 1);
}
.profile-menu__panel.is-closing {
  animation: profileMenuOut 0.14s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes profileMenuIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes profileMenuOut {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-4px); }
}

.profile-menu__head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 12px 14px;
  border-bottom: 1px solid #f1f1f4;
}
.profile-menu__head-avatar { flex-shrink: 0; display: inline-flex; }
.profile-menu__head-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.profile-menu__head-name {
  font-size: 13px;
  font-weight: 600;
  color: #071437;
  letter-spacing: -0.01em;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.profile-menu__head-email {
  font-size: 12px;
  color: #4b5675;
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Your role, as a pill on the RIGHT of your name and email (2026-10-13, by
   request: "add role like this side"). The menu's own blue, in the role's own
   words (not upper-cased). It may shrink: a long custom role name ends in an
   ellipsis at 120px instead of squeezing your name out or widening the menu. */
.profile-menu__head-role {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 120px;
  padding: 4px 10px;
  border-radius: 9999px;
  background: rgba(27, 132, 255, 0.10);
  color: #1b84ff;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.profile-menu__head-badge {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #1b84ff;
  background: rgba(27, 132, 255, 0.10);
  padding: 3px 8px;
  border-radius: 9999px;
  flex-shrink: 0;
}

.profile-menu__list {
  list-style: none;
  margin: 6px 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.profile-menu__item,
.profile-menu__item:link,
.profile-menu__item:visited,
.profile-menu__item:active {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 9px 10px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  color: #252f4a !important;
  text-decoration: none !important;
  cursor: pointer;
  transition: background 0.16s cubic-bezier(0.4, 0, 0.2, 1), color 0.16s cubic-bezier(0.4, 0, 0.2, 1);
}
.profile-menu__item-icon {
  font-size: 18px;
  color: #4b5675;
  flex-shrink: 0;
  width: 18px;
  text-align: center;
  transition: color 0.16s cubic-bezier(0.4, 0, 0.2, 1);
}
.profile-menu__item-label { flex: 1; min-width: 0; color: inherit; }
.profile-menu__item:hover { background: #f8f9fb; color: #071437 !important; }
.profile-menu__item:hover .profile-menu__item-icon { color: #1b84ff; }
.profile-menu__item-pill {
  margin-left: auto;
  font-size: 11px;
  font-weight: 600;
  color: #ffffff;
  background: #f8285a;
  padding: 2px 7px;
  border-radius: 9999px;
  line-height: 1.4;
  min-width: 18px;
  text-align: center;
}

.profile-menu__foot {
  border-top: 1px solid #f1f1f4;
  padding: 8px 6px 6px;
  margin-top: 4px;
}
.profile-menu__logout {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 9px 12px;
  border-radius: 8px;
  border: 1px solid #e4e4e7;
  background: #ffffff;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: #252f4a !important;
  text-decoration: none !important;
  cursor: pointer;
  transition: background 0.16s cubic-bezier(0.4, 0, 0.2, 1),
              border-color 0.16s cubic-bezier(0.4, 0, 0.2, 1),
              color 0.16s cubic-bezier(0.4, 0, 0.2, 1);
}
.profile-menu__logout-icon {
  font-size: 16px;
  color: #4b5675;
  transition: color 0.16s cubic-bezier(0.4, 0, 0.2, 1);
}
.profile-menu__logout:hover {
  background: rgba(248, 40, 90, 0.06);
  border-color: rgba(248, 40, 90, 0.30);
  color: #f8285a !important;
}
.profile-menu__logout:hover .profile-menu__logout-icon { color: #f8285a; }


/* ──────────────────────────────────────────────────────────────────────
 * 4. Legacy /auth pages (login, error). Kept as-is because they predate
 *    the Metronic skin and SnovaHR's own login uses an equivalent card.
 * ────────────────────────────────────────────────────────────────────── */
.login-page, .error-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}
.login-card, .error-card {
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  padding: 2.5rem 3rem;
  text-align: center;
  max-width: 420px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
}
.login-card h1 { margin: 0 0 0.5rem 0; }
.login-card .subtitle { color: #6b7280; margin-bottom: 1.5rem; }
.dev-note { font-size: 0.85rem; color: #6b7280; margin-top: 1.5rem; }

/* ── Error / warning card (error.html — shared by 404 / 500 / 422) ── */
.error-card {
  padding: 2.25rem 2.5rem;
  max-width: 440px;
}
.error-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin: 0 auto 1.25rem;
  border-radius: 50%;
  color: #6b7280;
  background: #f3f4f6;
}
.error-card--warning .error-card__icon { color: #b45309; background: #fef3c7; }
.error-card__title {
  margin: 0 0 0.5rem 0;
  font-size: 1.2rem;
  font-weight: 600;
  color: #111827;
  line-height: 1.35;
}
.error-card__message {
  margin: 0 0 1.75rem 0;
  font-size: 0.92rem;
  color: #6b7280;
  line-height: 1.6;
}
.error-card__actions {
  display: flex;
  gap: 0.75rem;
  justify-content: center;
}
.error-card .btn { cursor: pointer; font-size: 0.9rem; }
/* Fallbacks: the error page loads only this sheet, which does not define the
   --qa-* colours (premium-theme.css does), so "Return home" had no fill and
   read as plain text. The blue is .btn-primary's. */
.error-card .btn--primary { background: var(--qa-primary, #1b84ff); color: var(--qa-primary-fg, #ffffff); text-decoration: none; }
.error-card .btn--primary:hover { text-decoration: none; opacity: 0.92; }
.error-card .btn--ghost {
  background: #ffffff;
  color: #374151;
  border-color: #d1d5db;
}
.error-card .btn--ghost:hover { background: #f9fafb; }


/* ──────────────────────────────────────────────────────────────────────
 * Sidebar — soften the active-state weight.
 *
 * Vendored snova-sidebar.css ships:
 *   .kt-menu-accordion .kt-menu-item.active > .kt-menu-link .kt-menu-title
 *     { color: #1b84ff; font-weight: 600; }
 * Plus the parent branch (.is-branch-active) inherits a similarly heavy
 * weight from Metronic defaults, which makes the active row read 700ish
 * against the 400 idle rows — too much contrast.
 *
 * Drop both back to the idle weight; the blue color alone is enough to
 * mark selection, matching the look from earlier in the session.
 * ────────────────────────────────────────────────────────────────────── */
.kt-sidebar .kt-menu-accordion .kt-menu-item.active > .kt-menu-link .kt-menu-title,
.kt-sidebar .kt-menu > .kt-menu-item.is-branch-active > .kt-menu-link .kt-menu-title,
.kt-sidebar .kt-menu > .kt-menu-item.active > .kt-menu-link .kt-menu-title {
  font-weight: 500 !important;
}

/* Cancel the 45° rotation on the expanded-state arrow container.
 * Vendored snova-sidebar.css rotates `.kt-menu-arrow` to 45deg when
 * the parent has `.show` — that was designed for a single morphing
 * chevron. Our template renders TWO icons (plus when collapsed, minus
 * when expanded); rotating the minus 45° tilts it into a diagonal
 * dash. Keep the container upright so each icon reads as itself. */
.kt-sidebar .kt-menu-item.show > .kt-menu-link .kt-menu-arrow {
  transform: none !important;
}


/* ──────────────────────────────────────────────────────────────────────
 * Global thin scrollbar — a slim, quiet bar so users always know where
 * they can scroll, without the heavy default scroll furniture stealing
 * attention. Applied app-wide so every overflow container (cards, drawers,
 * modals, drop-downs, tables, search results, the document itself) gets
 * the same clean look without us hunting them down by class name.
 *
 *   • Firefox          → scrollbar-width: thin + scrollbar-color
 *   • WebKit / Blink   → 8px track, rounded translucent thumb, no arrows
 *
 * Thumb is transparent until the container (or page) is hovered, then it
 * fades in — so static screens stay pristine and the bar appears right
 * when the user reaches for it. Track stays transparent throughout.
 * ────────────────────────────────────────────────────────────────────── */
html,
body,
* {
  scrollbar-width: thin !important;
  scrollbar-color: rgba(148, 158, 178, 0.45) transparent !important;
  -ms-overflow-style: auto !important;
}
*::-webkit-scrollbar {
  width: 6px !important;
  height: 6px !important;
  background: transparent !important;
}
*::-webkit-scrollbar-track,
*::-webkit-scrollbar-corner {
  background: transparent !important;
}
*::-webkit-scrollbar-thumb {
  background-color: rgba(148, 158, 178, 0.32) !important;
  border-radius: 999px !important;
  border: 1.5px solid transparent !important;   /* inset the thumb → thinner visual (~3px) */
  background-clip: content-box !important;
}
*:hover::-webkit-scrollbar-thumb {
  background-color: rgba(120, 130, 152, 0.55) !important;
}
*::-webkit-scrollbar-thumb:hover {
  background-color: rgba(95, 105, 128, 0.75) !important;
}

.btn {
  display: inline-block;
  padding: 0.6rem 1.5rem;
  border-radius: 6px;
  font-weight: 500;
  border: 1px solid transparent;
}
.btn-primary { background: #1b84ff; color: #ffffff; }
.btn-primary:hover { text-decoration: none; opacity: 0.92; }


/* ──────────────────────────────────────────────────────────────────────
 * 5. Global topbar separator.
 *
 * The Metronic header (.kt-header) only renders its bottom border once
 * the user scrolls (data-kt-sticky-class="border-b border-border"
 * swaps the class in on scroll). At the top of the page the header
 * floats borderless against the content, making the brand/profile bar
 * blend into the first row of page content.
 *
 * We want a permanent hairline between the global topbar and whatever
 * page renders below it, using the SAME color as the unified tab strip
 * baseline (tabs-unified.css → --tab-strip-line) so the two horizontal
 * rules read as one design language.
 * ────────────────────────────────────────────────────────────────────── */
.kt-header {
  border-bottom: 1px solid rgba(15, 23, 42, 0.10);
}

/* The sidebar's brand header (.kt-sidebar-header — "Snova.People" logo
 * row) sits at the same vertical band as .kt-header but is a separate
 * column to the left of it. Give it the SAME bottom hairline so the
 * two lines join into one continuous rule across the full viewport. */
.kt-sidebar-header {
  border-bottom: 1px solid rgba(15, 23, 42, 0.10);
}

/* …EXCEPT on screens that render the unified sub-tab strip (My Space,
 * Attendance → Team, Onboarding, and every other `_sub_tabs.html` page).
 * There, the tab strip already draws its own baseline JUST BELOW the tabs,
 * so the topbar hairline above them reads as a duplicate "extra" line.
 * Drop it on those pages only (Metronic-style: the rule sits below the tabs,
 * never above). Pages WITHOUT these tabs keep the separator untouched.
 * `:has()` scopes this per-page, the same way main#content:has(...) does above. */
/* 2026-08-10: extended from sub-tab pages to ALL pages — on screens without
   the tab strip (Leaves, Overtime, …) the leftover hairline read as a stray
   border under the topbar, inconsistent with the clean tab-strip pages. */
/* .kt-sidebar-header is NOT in this rule any more. The reasoning above is about
   the TOPBAR hairline sitting above the tab strip; the sidebar header is a
   different edge, and it now carries a deliberate divider between the brand row
   and the menu items (premium-theme.css). Killing its colour here left that
   border painted in transparent. */
.kt-header {
  border-bottom-color: transparent !important;
}

/* …and pull page content up snug against the topbar. main#content carries a
 * default pt-5 (20px) gap that, with the separator line now gone, reads as
 * dead space. The fixed-header clearance is provided separately, so trimming
 * this is safe.
 *
 * 2026-08-11: was scoped to `body:has(.snova-subtabs)`, so only tab-strip
 * pages got the tighter gap (5.25px) while Leaves / Overtime / Reports /
 * Operations / Notifications / Settings kept the full 20px — two different
 * top spacings across the app. Now applied to EVERY screen so they match. */
main#content {
  padding-top: calc(var(--spacing) * 1.5);
}

/* ── Back-to-Operations arrow ─────────────────────────────────────────────
   Shared affordance at the top-left of every Operations service screen
   (Holidays, Leave Types, Custom Fields, Soft Labels, Org Structure, Approval
   Flows, Files, …), matching .ot-header__back on Onboarding / Offboarding.
   Defined here in the global app.css because those screens each ship their own
   stylesheet — this way one class covers all of them. Links to /operations. */
.ops-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  flex: 0 0 auto;
  color: #4b5675;
  background: #f1f5f9;
  text-decoration: none;
  transition: background 120ms ease, color 120ms ease;
}
.ops-back:hover { background: #e2e8f0; color: #0f172a; }
.ops-back:focus-visible { outline: none; box-shadow: var(--qa-focus-ring); }
.ops-back .material-icons { font-size: 20px; line-height: 1; }

/* ── Breadcrumbs ─────────────────────────────────────────────────────────
   Rendered by partials/_breadcrumbs.html on the Operations screens. Sits above
   the page's own header, so it stays quiet: small, muted, no background — it
   orients you without competing with the screen title or the toolbar.
   The back arrow sits on the same line, to its left. */
.snova-crumbs {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-top: 2px;
  /* 10px → 4px (2026-08-14, "a lot of space"): the trail is one 12px line next
     to a 30px button, and on the screens that DON'T merge it into the next row
     (the Files tabs — see the lift rule below) it was a 42px band of mostly
     empty width. Paired with the lift drop from 40px to 34px so the screens
     that DO merge keep landing on one row. */
  padding-bottom: 4px;
}
/* Same affordance as .ops-back above (which these screens no longer draw
   themselves), a touch smaller so it sits with 12px text rather than a title. */
.snova-crumbs__back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  flex: 0 0 auto;
  border-radius: 8px;
  color: #4b5675;
  background: #f1f5f9;
  text-decoration: none;
  transition: background 120ms ease, color 120ms ease;
}
.snova-crumbs__back:hover { background: #e2e8f0; color: #0f172a; }
.snova-crumbs__back:focus-visible { outline: none; box-shadow: var(--qa-focus-ring); }
.snova-crumbs__back .material-icons { font-size: 18px; line-height: 1; }

/* ── One row: breadcrumb + arrow on the left, the screen's toolbar on the
   right ──────────────────────────────────────────────────────────────────
   The trail is rendered by base.html and the toolbar by each screen, so they
   are separate blocks and stacked as two rows with the trail's right half
   empty. Pulling the following block up merges them.

   Safe because every breadcrumbed screen's header is `justify-content:
   flex-end` — its controls hug the right, so the space the trail is lifted
   into was blank. The one flex-start header (approval_flows) lives at
   /admin/approval-flows, which has no trail at all.

   The trail keeps a stacking context so it always paints above the lifted
   row, and stops being a click target across the full width — otherwise its
   invisible right half would swallow clicks meant for the toolbar. */
main#content > .snova-crumbs {
  position: relative;
  z-index: 2;
  width: max-content;
  max-width: 100%;
  pointer-events: none;
}
main#content > .snova-crumbs > * { pointer-events: auto; }
/* ONLY screens whose first row is a right-aligned .ops-emp-page__head are
   lifted. The Files screens lead with their tab strip instead, and a blanket
   rule dragged those tabs on top of the trail — measured, not assumed.
   Same lesson again on 2026-08-14: Employee Information moved its action row
   BELOW the tabs, so its block now leads with the tab strip too and the lift
   dragged the tabs into the trail. The :not() guard skips any block whose
   FIRST row is the sub-tab strip; the master-data screens (Teams / BU / …)
   still lead with their toolbar and keep the merge. */
main#content > .snova-crumbs + *:has(.ops-emp-page__head):not(:has(> .snova-subtabs-bar:first-child)) {
  /* 40px → 34px: tracks the 6px taken off .snova-crumbs' padding-bottom above.
     Left unchanged it would over-pull and slide the toolbar into the trail.

     34px → 56px (2026-08-14, Operations → Shifts: the two halves of the merged
     row were visibly out of level). 34px aligned the two BOXES, but the
     toolbar's row is 21px taller than the trail — it wraps 10px of padding
     around a 36px control — so its contents sat ~22px BELOW the breadcrumb
     text beside them. The extra pull puts both halves on one centre line.

     It has to be done here, on the lift: the trail is in normal flow directly
     above this block, so padding the trail down (tried first) just carries
     this block down with it and the offset survives unchanged.

     55px and not 56: the rule below drops the head's 1px inline border-top,
     which lifts its contents by a further pixel. */
  margin-top: calc(-1 * var(--snova-crumbs-lift, 55px));
}
/* Move the toolbar's divider from ABOVE the row to BELOW it, on lifted screens
   only. Several of these templates carry an inline `border-top` on their
   `.ops-emp-page__head` (the pre-merge "division line above the toolbar"), and
   once the row is pulled up to sit level with the trail that line lands hard
   against the topbar's bottom edge and reads as a stray hairline. Below the
   row it does the job it was written for: separating the merged
   breadcrumb + toolbar row from the table card underneath.
   `!important` because the source declaration is an inline style. */
main#content > .snova-crumbs + *:has(.ops-emp-page__head):not(:has(> .snova-subtabs-bar:first-child)) .ops-emp-page__head {
  border-top: 0 !important;
  border-bottom: 1px solid var(--qa-hairline, #e4e4e7);
}
/* Divider under the breadcrumb.
   NOT on .snova-crumbs itself: the trail is width:max-content so the toolbar
   can share its row, so a border there stops at the end of the text instead of
   crossing the page. NOT on the sibling above either — that wraps the whole
   page, so the line landed under the table.
   The right element is the container holding the toolbar: it is full width and
   its bottom edge IS the bottom of the merged breadcrumb + toolbar row. */
main#content > .snova-crumbs + * > .kt-container-fixed:has(.ops-emp-page__head) {
  border-bottom: 1px solid var(--qa-hairline, #e4e4e7);
  padding-bottom: 10px;
}
/* Below the 2-column breakpoint the toolbars wrap and there is no longer spare
   width beside the trail, so let the two rows separate again. */
@media (max-width: 900px) {
  main#content > .snova-crumbs { width: auto; }
  main#content > .snova-crumbs + *:has(.ops-emp-page__head) { margin-top: 0; }
}
.snova-crumbs__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.snova-crumbs__item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  line-height: 1.4;
}
.snova-crumbs__link {
  color: #78829d;
  text-decoration: none;
  border-radius: 4px;
  transition: color 120ms ease;
}
.snova-crumbs__link:hover { color: #1b84ff; text-decoration: underline; }
.snova-crumbs__link:focus-visible { outline: 2px solid #1b84ff; outline-offset: 2px; }
.snova-crumbs__current { color: #071437; font-weight: 600; }
.snova-crumbs__sep { color: #c9cdd8; user-select: none; }

/* ──────────────────────────────────────────────────────────────────────
 * §8. SIDEBAR LETTERING (2026-09-02, matching the reference product)
 *
 * Idle rows were 600-weight in near-black (oklch(.155 .014 270)), which made
 * every item shout equally and left nothing for the selected row to say. The
 * reference sidebar runs medium-weight slate labels with muted icons, so the
 * blue active row is the only thing that stands out.
 *
 * Selectors mirror the ones they override (see §7c); app.css loads last, and
 * the `!important` matches the weight rule already asserted below for the
 * active row.
 * ────────────────────────────────────────────────────────────────────── */
/* `!important` because premium-theme.css paints every sidebar label
   `--qa-text-strong` with `!important` at a lower specificity; without it the
   colour here is ignored while the weight (which is !important) applies, which
   is the half-applied result this went through once already. */
.kt-sidebar .kt-menu > .kt-menu-item > .kt-menu-link .kt-menu-title,
.kt-sidebar .kt-menu-accordion .kt-menu-item > .kt-menu-link .kt-menu-title {
  /* Near-black words, grey furniture. The reference keeps its labels dark and
     legible; only the icons and the +/- sit back. Going grey here (#475467,
     then #344054) washed the whole menu out — corrected 2026-09-02. */
  color: #101828 !important;
  font-weight: 500 !important;
}
/* Icons sit back from the words rather than competing with them. */
.kt-sidebar .kt-menu > .kt-menu-item > .kt-menu-link .kt-menu-icon i,
.kt-sidebar .kt-menu-accordion .kt-menu-item > .kt-menu-link .kt-menu-icon i {
  color: #98a2b3;
}
/* Selection stays blue — icon included, so the row reads as one unit. */
.kt-sidebar .kt-menu-item.active > .kt-menu-link .kt-menu-icon i,
.kt-sidebar .kt-menu-item.is-branch-active > .kt-menu-link .kt-menu-icon i,
.kt-sidebar .kt-menu-item.show > .kt-menu-link .kt-menu-icon i {
  color: #2563eb;
}
.kt-sidebar .kt-menu-link:hover .kt-menu-title { color: #1d2433 !important; }
.kt-sidebar .kt-menu-link:hover .kt-menu-icon i { color: #667085; }
/* The ADMIN divider heading: quieter still, and clearly a label not an item. */
/* The +/- on an expandable row: grey and a touch larger, matching the
   reference product. It was near-black at 11px, which made it read as a
   control of equal weight to the item's own name. `!important` + the extra
   class for the same reason as the labels below. */
.kt-sidebar .kt-menu .kt-menu-link .kt-menu-arrow,
.kt-sidebar .kt-menu .kt-menu-link .kt-menu-arrow i {
  color: #475467 !important;
}
/* Bigger and heavier than the label text, the way the reference draws it — a
   hairline + at 11px reads as a stray mark rather than a control. The size
   needs `!important` because snova-sidebar.css pins 11px more specifically. */
.kt-sidebar .kt-menu .kt-menu-link .kt-menu-arrow i {
  font-size: 15px !important;
  font-weight: 700;
}
/* It brightens with the row, so hovering an item lights up the whole line. */
.kt-sidebar .kt-menu .kt-menu-link:hover .kt-menu-arrow,
.kt-sidebar .kt-menu .kt-menu-link:hover .kt-menu-arrow i,
.kt-sidebar .kt-menu-item.show > .kt-menu-link .kt-menu-arrow,
.kt-sidebar .kt-menu-item.show > .kt-menu-link .kt-menu-arrow i {
  color: #667085 !important;
}

/* Same `!important` story as the labels, plus one more class: premium-theme's
   heading rule is `.kt-sidebar .kt-menu-heading` with `!important` and loads
   AFTER this file, so an identical selector loses the tie on source order. */
.kt-sidebar .kt-menu .kt-menu-heading {
  color: #98a2b3 !important;
  font-weight: 600;
  letter-spacing: .04em;
}

/* Acknowledgement report icon on file lists (2026-10-14): green once everyone
   the file was sent to has acknowledged it (services/file_receipts.ack_progress). */
.ack-report-btn.is-done, .ack-report-btn.is-done .material-icons { color: #16a34a !important; }
.ack-report-btn.is-done:hover { background: rgba(22, 163, 74, .10) !important; }
