/* App shell: reset, typography, topbar, sidebar, content, responsive. */

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

/* The hidden attribute must win over explicit displays (e.g. .btn { display: inline-flex }) */
[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
  padding: 0;
}

::selection {
  background: var(--accent);
  color: var(--accent-ink);
}

body {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading);
  color: var(--ink);
  background: var(--canvas);
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3,
p,
figure {
  margin: 0;
}

a {
  color: var(--accent);
  text-decoration: none;
}

button {
  font-family: inherit;
  font-size: inherit;
  cursor: pointer;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* ---- Shell grid ---- */

.app {
  height: 100vh;
  height: 100dvh; /* mobile: match the visible area so the browser chrome doesn't hide the bottom */
  overflow: hidden; /* topbar + sidebar stay put; only .content scrolls */
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  grid-template-rows: var(--topbar-h) 1fr;
  grid-template-areas:
    "brand topbar"
    "sidebar content";
}

/* ---- Topbar ---- */

.topbar {
  grid-area: topbar;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: 0 var(--space-5);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 20;
}

.topbar .actions {
  display: flex;
  align-items: center;
  margin-left: auto;
  gap: var(--space-2);
  margin-left: auto;
}

/* Office chip — flag-like context indicator */
.office-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  color: var(--ink-soft);
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}

.office-chip .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent);
}

/* The chip names the office; the code is its narrow-screen stand-in. */
.office-chip .office-code {
  display: none;
}

/* How many more offices this person works in beyond the primary one. */
.office-chip .office-more {
  margin-left: 2px;
  color: var(--ink-soft);
  font-size: var(--text-xs);
  font-weight: 500;
}

/* Icon-only affordances still expose a label via aria; text buttons keep text */
.icon-button {
  display: inline-grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--ink-soft);
  position: relative;
}

.icon-button:hover:not(:disabled) {
  background: var(--surface-sunken);
  color: var(--ink);
}

/* An end of the range greys out instead of looking pressable — the same read
   the date picker gives its own nav (.daterange-nav:disabled). The hover has to
   stop with it, or the button keeps reacting to a click it will not honour, and
   the pointer goes back to the arrow because `button` carries one globally. */
.icon-button:disabled {
  opacity: 0.3;
  cursor: default;
}

.icon-button .badge {
  position: absolute;
  top: 4px;
  right: 4px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-pill);
  background: var(--status-pending);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

.user-menu {
  position: relative;
}

.user-button {
  display: inline-grid;
  place-items: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--ink);
}

.user-button:hover .avatar {
  filter: brightness(1.12);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
}

.user-dropdown {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  min-width: 300px;
  padding: var(--space-2);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  z-index: 30;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease),
    visibility var(--dur-fast) var(--ease);
}

.user-dropdown.open {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

/* Bridges the gap to the button so the hover intent survives the mouse crossing it */
.user-dropdown::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: calc(-1 * var(--space-2));
  height: var(--space-2);
}

/* Desktop: pointer-capable devices reveal on hover/keyboard focus */
@media (hover: hover) and (pointer: fine) {
  .user-menu:hover .user-dropdown,
  .user-menu:focus-within .user-dropdown {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }
}

.user-info {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
}

.user-info .avatar {
  width: 42px;
  height: 42px;
  font-size: var(--text-sm);
}

.user-meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.user-name {
  font-weight: 600;
  color: var(--ink);
}

.user-email {
  font-size: var(--text-sm);
  color: var(--ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The positions this person holds across the files, plus their access switches
   — derived, never a stored label. */
.user-title {
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

.user-menu-sep {
  height: 1px;
  margin: var(--space-2) 0;
  background: var(--border);
}

.user-menu-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--ink);
  font-weight: 400;
}

.user-menu-item .icon {
  color: var(--ink-faint);
}

.user-menu-item:hover {
  background: var(--surface-sunken);
}

.user-logout {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--status-pending);
  font-weight: 500;
  text-align: left;
}

.user-logout:hover {
  background: var(--status-pending-soft);
}

/* ---- Notifications menu ---- */

.notif-menu {
  position: relative;
}

.notif-dropdown {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  width: 340px;
  max-width: calc(100vw - var(--space-4) * 2);
  padding: 0;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  z-index: 30;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease),
    visibility var(--dur-fast) var(--ease);
}

.notif-dropdown.open {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition-delay: 0s;
}

/* Bridges the gap under the button so hover survives the mouse crossing it.
   Lives on the menu (not the dropdown) because .notif-dropdown clips overflow. */
.notif-menu::after {
  content: "";
  position: absolute;
  top: 100%;
  right: 0;
  width: 38px;
  height: var(--space-2);
}

/* Desktop: pointer-capable devices reveal on hover/keyboard focus */
@media (hover: hover) and (pointer: fine) {

  .notif-menu:hover .notif-dropdown,
  .notif-menu:focus-within .notif-dropdown {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }
}

.notif-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}

.notif-title {
  font-weight: 600;
  color: var(--ink);
}

.notif-unread {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--accent);
}

.notif-mark {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent);
  font: inherit;
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
}

.notif-mark:hover {
  text-decoration: underline;
}

.notif-list {
  max-height: 360px;
  overflow-y: auto;
}

.notif-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  text-decoration: none;
  border-bottom: 1px solid var(--border-faint, var(--border));
}

.notif-item:last-child {
  border-bottom: 0;
}

.notif-item:hover {
  background: var(--surface-sunken);
}

.notif-item.unread {
  background: var(--accent-soft);
}

.notif-item.unread:hover {
  background: color-mix(in srgb, var(--accent-soft) 70%, var(--surface-sunken));
}

.notif-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
}

.notif-icon .icon {
  width: 18px;
  height: 18px;
}

.notif-icon[data-tone="info"] {
  background: var(--accent-soft);
  color: var(--accent);
}

.notif-icon[data-tone="warning"] {
  background: var(--status-progress-soft);
  color: var(--status-progress);
}

.notif-icon[data-tone="success"] {
  background: var(--status-done-soft);
  color: var(--status-done);
}

.notif-icon[data-tone="danger"] {
  background: var(--status-pending-soft);
  color: var(--status-pending);
}

.notif-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.notif-text {
  font-size: var(--text-sm);
  color: var(--ink);
  line-height: 1.35;
}

.notif-text strong {
  font-weight: 600;
}

.notif-time {
  font-size: var(--text-xs);
  color: var(--ink-faint);
}

.notif-foot {
  padding: var(--space-2);
  border-top: 1px solid var(--border);
}

.notif-all {
  display: block;
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-size: var(--text-sm);
  font-weight: 600;
  text-align: center;
  text-decoration: none;
}

.notif-all:hover {
  background: var(--surface-sunken);
}

.avatar {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--accent);
  color: #fff;
  font-size: var(--text-xs);
  font-weight: 700;
  transition: filter 0.15s ease, box-shadow 0.15s ease;
}

/* ---- Brand cell (above sidebar) ---- */

.brand {
  grid-area: brand;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0 var(--space-3) 0 0;
  box-shadow: inset -1px 0 0 hsl(0 0% 100% / 0.08);
  /* The wordmark is clipped by the narrowing cell instead of being switched off,
     which is what lets it fade with the sidebar. */
  overflow: hidden;
}

/* Los logos son marine oscuro; sobre el acento van en blanco */
.brand .mark,
.brand .wordmark {
  filter: brightness(0) invert(1);
}

.brand-logo {
  display: flex;
  align-items: center;
  gap: 0;
  text-decoration: none;
  min-width: 0;
}

/* Slot as wide as the collapsed rail so the isotype sits on the icon
   centerline (34px) in both states — no horizontal jump when collapsing. */
.brand .mark {
  height: 40px;
  width: var(--sidebar-w-collapsed);
  flex-shrink: 0;
  object-fit: contain;
  object-position: center;
}

.brand .name {
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: hidden;
  margin-left: calc(-1 * var(--space-3));
}

.brand .name .wordmark {
  height: 16px;
  width: auto;
  flex-shrink: 0;
}

.brand .name .tagline {
  font-size: var(--text-xs);
  color: var(--ink-faint);
  white-space: nowrap;
}

/* ---- Sidebar ---- */
/* Same background as canvas + border: one visual space, not two worlds. */

/* One canvas shared by the logo cell and the nav below it. They are separate
   grid areas, so `fixed` attachment is what makes both sample the same
   viewport-tall gradient — the effect runs through the logo instead of
   restarting under it. Sized to the rail so the glows land inside it. */
.brand,
.sidebar {
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23g)' opacity='0.4'/%3E%3C/svg%3E"),
    radial-gradient(
      95% 42% at 100% 0%,
      hsl(202 92% 54% / 0.5) 0%,
      transparent 70%
    ),
    radial-gradient(
      90% 45% at 0% 96%,
      hsl(187 90% 48% / 0.26) 0%,
      transparent 72%
    ),
    linear-gradient(
      168deg,
      hsl(205 76% 31%) 0%,
      hsl(208 74% 24%) 48%,
      hsl(212 68% 18%) 100%
    );
  background-size:
    140px 140px,
    var(--sidebar-w) 100vh,
    var(--sidebar-w) 100vh,
    var(--sidebar-w) 100vh;
  background-position: left top;
  background-repeat: repeat, no-repeat, no-repeat, no-repeat;
  background-attachment: fixed;
  background-blend-mode: overlay, screen, screen, normal;
}

.sidebar {
  grid-area: sidebar;
  padding: var(--space-4) var(--space-3);
  /* overflow-x hidden: during the open animation (translateX) the horizontal
     scrollbar must not peek out. */
  overflow: hidden auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* The default scrollbar paints a light grey track over the dark rail, so it is
   replaced by a thin translucent thumb with no track at all. */
.sidebar {
  scrollbar-width: thin;
  scrollbar-color: hsl(0 0% 100% / 0.22) transparent;
}

.sidebar::-webkit-scrollbar {
  width: 6px;
}

.sidebar::-webkit-scrollbar-track {
  background: transparent;
}

.sidebar::-webkit-scrollbar-thumb {
  background: hsl(0 0% 100% / 0.22);
  border-radius: 999px;
}

.sidebar:hover {
  scrollbar-color: hsl(0 0% 100% / 0.4) transparent;
}

.sidebar:hover::-webkit-scrollbar-thumb {
  background: hsl(0 0% 100% / 0.4);
}

.sidebar .field label {
  color: hsl(0 0% 100% / 0.7);
}

.sidebar .nav-collapse {
  color: hsl(0 0% 100% / 0.65);
}

.sidebar .nav-collapse:hover {
  background: hsl(0 0% 100% / 0.12);
  color: #fff;
}

/* Pinned open reads as a pressed toggle — unpinned, the sidebar only shows
   itself while the pointer is on it, so the button has to say which one it is. */
.sidebar .nav-collapse[aria-pressed="true"] {
  background: hsl(0 0% 100% / 0.14);
  color: #fff;
}

.nav-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Divider between groups: the break has no label, so a line stands in for it */
.nav-group + .nav-group {
  padding-top: var(--space-4);
  border-top: 1px solid hsl(0 0% 100% / 0.3);
}

.nav-group .group-label {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: hsl(0 0% 100% / 0.55);
}

.nav-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: hsl(0 0% 100% / 0.82);
  font-size: var(--text-base);
  line-height: 20px; /* matches the label box to the icon height (20px) so
                        expanded and collapsed measure the same */
  font-weight: 500;
  position: relative;
}

.nav-link:hover {
  background: hsl(0 0% 100% / 0.1);
  color: #fff;
}

/* nowrap: during the expand animation the sidebar is narrow; without this the
   label wraps to two lines until it finishes. This keeps it clipped to one line. */
.nav-link .label {
  white-space: nowrap;
}

/* Everything that only exists in the wide sidebar fades as the width animates,
   so opening and closing read as one movement instead of a width change with
   the text popping in and out on the first frame. */
.brand .name,
.nav-group .group-label,
.nav-link .label,
.nav-link .count,
.nav-parent .caret {
  transition: opacity var(--dur-nav) var(--ease);
}

.nav-link .icon {
  color: hsl(0 0% 100% / 0.65);
  flex-shrink: 0;
  transition: transform var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.nav-link:hover .icon {
  color: #fff;
  transform: scale(1.08);
}

.nav-link:active .icon {
  transform: scale(0.94);
}

/* Internal micro-gesture per icon: one part moves inside the glyph on hover */
.nav-link .icon [class] {
  transform-box: fill-box;
  transform-origin: center;
  transition: transform var(--dur) var(--ease);
}

.nav-link:hover .icon[data-icon="sun"] .rays {
  transform: rotate(45deg);
}

/* La solapa se abre girando desde su bisagra superior (12,7) y vuelve */
.nav-link .icon[data-icon="mail"] .flap {
  transform-box: view-box;
  transform-origin: 12px 7px;
}

.nav-link:hover .icon[data-icon="mail"] .flap {
  animation: mail-open 380ms var(--ease);
}

@keyframes mail-open {
  0% {
    transform: perspective(44px) rotateX(0deg);
  }
  50% {
    transform: perspective(44px) rotateX(62deg);
  }
  100% {
    transform: perspective(44px) rotateX(0deg);
  }
}

/* The anchor hangs from the ring (12,5): uses viewBox coords to pivot there */
.nav-link .icon[data-icon="anchor"] .fluke {
  transform-box: view-box;
  transform-origin: 12px 5px;
}

.nav-link:hover .icon[data-icon="anchor"] .fluke {
  animation: anchor-swing 420ms var(--ease);
}

@keyframes anchor-swing {
  0% {
    transform: rotate(0deg);
  }
  30% {
    transform: rotate(12deg);
  }
  62% {
    transform: rotate(-8deg);
  }
  100% {
    transform: rotate(0deg);
  }
}

/* The lines redraw left to right in a cascade; at rest they show complete */
.nav-link .icon[data-icon="list"] .l1,
.nav-link .icon[data-icon="list"] .l2,
.nav-link .icon[data-icon="list"] .l3 {
  stroke-dasharray: 13;
}

.nav-link:hover .icon[data-icon="list"] .l1 {
  animation: list-draw var(--dur) var(--ease) backwards;
}

.nav-link:hover .icon[data-icon="list"] .l2 {
  animation: list-draw var(--dur) var(--ease) 60ms backwards;
}

.nav-link:hover .icon[data-icon="list"] .l3 {
  animation: list-draw var(--dur) var(--ease) 120ms backwards;
}

@keyframes list-draw {
  from {
    stroke-dashoffset: 13;
  }
  to {
    stroke-dashoffset: 0;
  }
}

/* Like the operations icon: the document lines redraw in a cascade */
.nav-link .icon[data-icon="invoice"] .l1 {
  --len: 6;
}

.nav-link .icon[data-icon="invoice"] .l2 {
  --len: 4;
}

.nav-link .icon[data-icon="invoice"] .l1,
.nav-link .icon[data-icon="invoice"] .l2 {
  stroke-dasharray: var(--len);
}

.nav-link:hover .icon[data-icon="invoice"] .l1 {
  animation: stroke-draw var(--dur) var(--ease) backwards;
}

.nav-link:hover .icon[data-icon="invoice"] .l2 {
  animation: stroke-draw var(--dur) var(--ease) 70ms backwards;
}

/* Draws a stroke along its length (--len) from start to end */
@keyframes stroke-draw {
  from {
    stroke-dashoffset: var(--len);
  }
  to {
    stroke-dashoffset: 0;
  }
}

/* The check mark draws itself, reusing the invoice/operations stroke cascade */
.nav-link .icon[data-icon="shield"] .check {
  --len: 9;
  stroke-dasharray: var(--len);
}

.nav-link:hover .icon[data-icon="shield"] .check {
  animation: stroke-draw var(--dur) var(--ease) backwards;
}

/* The second person joins in: slides over from the first and fades up */
.nav-link:hover .icon[data-icon="users"] .p2 {
  animation: user-join 320ms var(--ease) backwards;
}

@keyframes user-join {
  from {
    transform: translateX(-4px);
    opacity: 0.2;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav-link .icon,
  .nav-link .icon [class] {
    transition: color var(--dur-fast) var(--ease);
  }

  .nav-link:hover .icon,
  .nav-link:active .icon,
  .nav-link:hover .icon [class] {
    transform: none;
    animation: none;
  }
}

.nav-link .count {
  margin-left: auto;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: hsl(0 0% 100% / 0.14);
  color: hsl(0 0% 100% / 0.9);
  font-size: var(--text-xs);
  font-weight: 700;
  line-height: 20px;
  text-align: center;
}

/* Active page — filled pill; identical collapsed or expanded */
.nav-link.active {
  background: hsl(0 0% 100% / 0.14);
  box-shadow: inset 0 1px 0 hsl(0 0% 100% / 0.12);
  color: #fff;
  font-weight: 600;
}

/* Indicator bleeding into the sidebar padding so it reads as a rail marker */
.nav-link.active::before {
  content: "";
  position: absolute;
  left: calc(-1 * var(--space-3));
  top: 0;
  bottom: 0;
  width: 3px;
  border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
  background: #fff;
}

.nav-link.active .icon {
  color: #fff;
}

.sidebar .spacer {
  margin-top: auto;
}

/* ---- Nav submenu (RPT-01: the reports hang off the Reports entry) ---- */

/* The parent entry is a button, not a link: it folds its submenu instead of
   navigating, so it needs the button chrome stripped back to the link's look. */
.nav-parent {
  width: 100%;
  border: none;
  background: none;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}

.nav-parent .caret {
  display: flex;
  margin-left: auto;
  color: hsl(0 0% 100% / 0.65);
  transition: transform var(--dur-fast) var(--ease);
}

.nav-parent[aria-expanded="true"] .caret {
  transform: rotate(180deg);
}

.nav-parent:hover .caret {
  color: #fff;
}

.nav-sub {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 2px 0 var(--space-2);
  padding-left: var(--space-6);
}

/* The display above outranks the UA rule for [hidden], so folding it needs this. */
.nav-sub[hidden] {
  display: none;
}

.nav-sublink {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  color: hsl(0 0% 100% / 0.72);
  font-size: var(--text-sm);
  line-height: 18px;
}

.nav-sublink:hover {
  background: hsl(0 0% 100% / 0.1);
  color: #fff;
}

.nav-sublink.active {
  background: hsl(0 0% 100% / 0.14);
  color: #fff;
  font-weight: 600;
}

/* ---- Content ---- */

.content {
  grid-area: content;
  /* no top padding: it would sit above the sticky thead as a gap. Moved to .inner so it scrolls away. */
  padding: 0 var(--space-5) var(--space-5);
  min-width: 0; /* let tables shrink instead of forcing page-wide scroll */
  min-height: 0; /* grid item must be able to shrink to become the scroll area */
  overflow: auto; /* the single vertical (and horizontal) scroll lives here */
  scrollbar-gutter: stable; /* reserve the gutter always so content doesn't shift when the scrollbar appears */
}

/* Fluid: dense ops data uses the full width instead of leaving side gutters.
   Forms/detail pages can wrap their own content in a narrower container. */
.content > .inner {
  width: 100%;
  padding-top: var(--space-5); /* scrolls away with content so the sticky thead pins flush to the top */
}

/* Detail pages: the sticky .detail-head sits flush at the top, so drop the top gap. */
.content > .inner:has(.detail-head) {
  padding-top: 0;
}

/* Page header */
.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-5);
}

.page-head .titles h1 {
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.page-head .titles p {
  margin-top: var(--space-1);
  color: var(--ink-faint);
  font-size: var(--text-sm);
}

.page-head .tools {
  display: flex;
  gap: var(--space-2);
  align-items: center;
}

/* ---- Desktop collapse (manual, persisted) ---- */
/* Toggle pinned to the sidebar bottom; hidden on tablet/mobile rails. */

.nav-collapse {
  display: none;
}

@media (min-width: 992px) {
  .nav-collapse {
    display: inline-grid;
    margin-top: auto;
  }

  /* Slide the rail open/closed. Gated by .nav-anim (added a frame after
     load) so the restored collapsed state doesn't animate on every page load. */
  body.nav-anim {
    transition: grid-template-columns var(--dur-nav) var(--ease);
  }

  /* .app IS the <body>, so target the element itself — not a descendant */
  body.nav-collapsed {
    grid-template-columns: var(--sidebar-w-collapsed) 1fr;
  }

  /* The text fades out and is clipped by the narrowing sidebar rather than
     disappearing on the first frame — the icons never move, because their left
     padding already puts them on the rail's centre line (12 + 12 + 10 = 34). */
  body.nav-collapsed .brand .name,
  body.nav-collapsed .nav-link .label,
  body.nav-collapsed .nav-link .count,
  body.nav-collapsed .nav-parent .caret {
    opacity: 0;
  }

  body.nav-collapsed .nav-group .group-label {
    opacity: 0;
  }

  /* The submenu is stacked height, not width, so it can't be clipped away: it
     folds shut, and the entry goes straight to its page instead. */
  body.nav-collapsed .nav-sub {
    display: none;
  }
}

/* ---- Unpinned rail opens under the pointer ---- */
/* The sidebar is always part of the layout: opening it widens its grid column
   and the content follows. It is never an overlay on desktop. Only where
   hovering is a real gesture — on touch the rail stays a rail. */
@media (min-width: 992px) and (hover: hover) {
  body.nav-collapsed.nav-peek {
    grid-template-columns: var(--sidebar-w) 1fr;
  }

  /* Held open, the rail looks exactly like the pinned sidebar. */
  body.nav-collapsed.nav-peek .brand .name,
  body.nav-collapsed.nav-peek .nav-link .label,
  body.nav-collapsed.nav-peek .nav-link .count,
  body.nav-collapsed.nav-peek .nav-parent .caret,
  body.nav-collapsed.nav-peek .nav-group .group-label {
    opacity: 1;
  }

  body.nav-collapsed.nav-peek .nav-sub {
    display: flex;
  }

  body.nav-collapsed.nav-peek .nav-sub[hidden] {
    display: none;
  }
}

/* Same gesture on the tablet rail, which is a rail by width, not by choice.
   Here the open rail floats over the content instead of widening its column:
   the topbar's own controls don't compress below ~604px, so at 768px pushing
   the content 180px right made the page 852px wide and scroll sideways. The
   rail leaves the grid for the whole range — its column keeps the 68px, so
   the closed state looks identical — and only its width animates. */
@media (min-width: 768px) and (max-width: 991px) and (hover: hover) {
  .sidebar {
    position: fixed;
    top: var(--topbar-h);
    left: 0;
    bottom: 0;
    width: var(--sidebar-w-collapsed);
    z-index: 40;
  }

  body.nav-anim .sidebar {
    transition: width var(--dur-nav) var(--ease);
  }

  body.nav-peek .sidebar {
    width: var(--sidebar-w);
    box-shadow: var(--shadow-lg);
  }

  /* The logo cell stays 68px wide, so its wordmark would only unfade into a
     clipped sliver — it keeps hidden while the rail below it opens. */
  body.nav-peek .nav-group .group-label,
  body.nav-peek .nav-link .label,
  body.nav-peek .nav-link .count,
  body.nav-peek .nav-parent .caret {
    opacity: 1;
  }

  body.nav-peek .nav-sub {
    display: flex;
  }

  body.nav-peek .nav-sub[hidden] {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  body.nav-anim,
  body.nav-anim .sidebar,
  .brand .name,
  .nav-group .group-label,
  .nav-link .label,
  .nav-link .count,
  .nav-parent .caret {
    transition: none;
  }
}

/* ---- Mobile drawer plumbing ---- */

.scrim {
  position: fixed;
  inset: 0;
  background: hsl(207 46% 13% / 0.4);
  z-index: 30;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur) var(--ease), visibility var(--dur);
}

.nav-toggle {
  display: none;
}

/* ---- Responsive ---- */

/* Tablet: collapse sidebar to icon rail */
@media (max-width: 991px) {
  .app {
    grid-template-columns: var(--sidebar-w-collapsed) 1fr;
  }

  .brand .name,
  .nav-group .group-label,
  .nav-link .label,
  .nav-link .count,
  .nav-parent .caret {
    opacity: 0;
  }

  .nav-sub {
    display: none;
  }
}

/* Mobile: sidebar becomes an off-canvas drawer */
@media (max-width: 767px) {
  .app {
    grid-template-columns: 1fr;
    grid-template-rows: var(--topbar-h) 1fr;
    grid-template-areas:
      "topbar"
      "content";
  }

  .brand {
    display: none;
  }

  /* Match the content's horizontal padding so the topbar aligns with the page. */
  .topbar {
    padding: 0 var(--space-4);
  }

  .nav-toggle {
    display: inline-grid;
  }

  /* "Steinweg South Africa & 1 other" doesn't fit a phone topbar next to the
     switcher — the chip falls back to the code it stands for, and the tooltip
     still spells out every office. */
  .office-chip .office-name {
    display: none;
  }

  .office-chip .office-code {
    display: inline;
  }

  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: var(--sidebar-w);
    z-index: 40;
    transform: translateX(-100%);
    transition: transform var(--dur) var(--ease);
    box-shadow: var(--shadow-lg);
  }

  /* The drawer shows the full sidebar, so the labels and the submenu come with it. */
  .brand .name,
  .nav-group .group-label,
  .nav-link .label,
  .nav-link .count,
  .nav-parent .caret {
    opacity: 1;
  }

  .nav-sub {
    display: flex;
  }

  .nav-sub[hidden] {
    display: none;
  }

  .sidebar .group-label {
    text-align: left;
  }

  body.drawer-open .sidebar {
    transform: translateX(0);
  }

  body.drawer-open .scrim {
    opacity: 1;
    visibility: visible;
  }

  .content {
    padding: 0 var(--space-4) var(--space-4);
  }

  .content > .inner {
    padding-top: var(--space-4);
  }

  /* The bell isn't flush to the screen edge, so a right:0 absolute dropdown hangs
     off the left and gets clipped by the app's overflow:hidden. Pin it to the
     viewport (fixed) under the topbar, right-aligned, so it always fits. */
  .notif-dropdown {
    position: fixed;
    top: calc(var(--topbar-h) + var(--space-2));
    right: var(--space-4);
    left: auto;
    width: min(340px, calc(100vw - var(--space-4) * 2));
    max-width: none;
  }
}

/* Role switcher (demo affordance) in the topbar */
.role-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
}

.role-switch-label {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--ink-faint);
  white-space: nowrap;
}

.role-switch .select {
  height: 34px;
  font-size: var(--text-sm);
  /* One line, always: the option names a person and their profile, and the box is
     wide enough for the longest of them. Should a longer name turn up, it clips
     with an ellipsis instead of growing a second line and pushing the topbar. */
  max-width: 280px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 991px) {
  .role-switch-label { display: none; }
}

@media (max-width: 767px) {
  .role-switch { display: none; }
}
