/* Sidebar
   =======
   One navigation component shared by every page (web/shared/js/sidebar.js builds the markup).
   The design is deliberately quiet: a light surface, one accent colour, and hierarchy
   carried by indent, weight and a guide line rather than by boxes inside boxes.

   Layout contract
   ---------------
   .layout-wrapper          flex row, full height
     .sidebar               fixed-width column: head / scrollable nav / identity
     .main-content          the page, scrolls on its own

   The top bar owns the window chrome (see css/topbar.css): it sets the sidebar's
   z-index, forces it to the viewport height, and moves its identity buttons into the
   drawer on a phone. This file owns what the menu itself looks like. */

.layout-wrapper {
  display: flex;
  min-height: 100vh;
}

.sidebar {
  /* Local design tokens: they describe the menu, so they belong to it rather than to
     the global palette in style.css. */
  --sidebar-w: 260px;
  --sidebar-rail-w: 72px;
  --sidebar-gutter: 12px;         /* left/right breathing room of the nav column */
  --sidebar-row-h: 44px;          /* every row is a touch target, so the height is the 44px a
                                     thumb needs rather than a density number. The menu is the
                                     same shape on a desktop and in the drawer. */
  --sidebar-icon-slot: 20px;      /* fixed, so every label starts at the same x */
  --sidebar-gap: 12px;            /* icon -> label */
  --sidebar-radius: 10px;
  --sidebar-accent: var(--color-agent-blue);
  --sidebar-accent-soft: rgba(0, 77, 96, 0.09);
  --sidebar-hover: rgba(23, 22, 24, 0.05);
  --sidebar-fg: #3f4245;          /* darker than --color-graphite: 0.9rem text needs
                                     7:1 on this surface to stay comfortable */
  --sidebar-fg-muted: #5c6063;

  width: var(--sidebar-w);
  flex: none;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  background-color: var(--color-light-gray);
  border-right: 1px solid var(--color-silver-mist);
  /* The rail animates, so width and padding have to agree on a duration — and it is the
     platform's own, so collapsing the rail and opening the phone drawer below settle together. */
  transition: width var(--motion-base) var(--motion-ease);
  position: sticky;
  top: 0;
  height: 100vh;
}

/* Head ---------------------------------------------------------------------
   Brand lock-up on the left, the collapse control on the right. The lock-up
   doubles as the way home, which is why it is a link and not just text. */

.sidebar-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 14px var(--sidebar-gutter);
  border-bottom: 1px solid var(--color-silver-mist);
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  /* The lock-up is a link home, so the whole thing is the target, not just the 32px logo. */
  min-height: var(--sidebar-row-h);
  text-decoration: none;
  border-radius: var(--radius-inputs);
}

.sidebar-brand-logo {
  width: 32px;
  height: 32px;
  flex: none;
  object-fit: contain;
}

.sidebar-brand-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.25;
}

.sidebar-brand-text strong {
  font-family: var(--font-eudoxus-sans);
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: -0.2px;
  color: var(--color-absolute-black);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar-brand-text small {
  font-family: var(--font-eudoxus-sans);
  font-size: 0.68rem;
  letter-spacing: 0.2px;
  /* The sidebar's own tint drops `--color-graphite` to 4.33:1, so the subtitle read as the
     platform's quietest line on every page. It keeps its size and takes the text role instead. */
  color: var(--text-subtle);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* An icon-only control: no box until you point at it, then a quiet fill. The square is the target
   size rather than the icon's, so the button stays comfortable in the rail. */
.sidebar-collapse-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  flex: none;
  border: none;
  border-radius: var(--radius-inputs);
  background: transparent;
  color: var(--sidebar-fg-muted);
  cursor: pointer;
  transition: background var(--motion-fast) var(--motion-ease), color var(--motion-fast) var(--motion-ease);
}

.sidebar-collapse-btn:hover {
  background: var(--sidebar-hover);
  color: var(--color-absolute-black);
}

.sidebar-collapse-btn svg {
  width: 18px;
  height: 18px;
}

/* The shared ring (see the Focus block in style.css), in the menu's own accent: the accent is what
   already means "you are here", so the ring around a row has to be that colour to read as part of
   the menu rather than as a warning. Only the colour differs — the thickness and the stand-off are
   the platform's. */
.sidebar-collapse-btn:focus-visible,
.sidebar-brand:focus-visible,
.sidebar-link:focus-visible,
.sidebar-sublink:focus-visible {
  outline: var(--focus-ring-width) solid var(--sidebar-accent);
  outline-offset: var(--focus-ring-offset);
}

/* Navigation --------------------------------------------------------------- */

.sidebar-nav {
  flex: 1;
  /* min-height: 0 is what lets a flex child actually scroll instead of growing. */
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sidebar-gutter);
  scrollbar-width: thin;
  scrollbar-color: rgba(23, 22, 24, 0.18) transparent;
}

.sidebar-nav::-webkit-scrollbar {
  width: 8px;
}

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

.sidebar-nav::-webkit-scrollbar-thumb {
  background-color: rgba(23, 22, 24, 0.16);
  border: 2px solid transparent;
  background-clip: content-box;
  border-radius: 99px;
}

.sidebar-nav::-webkit-scrollbar-thumb:hover {
  background-color: rgba(23, 22, 24, 0.3);
}

/* Rows --------------------------------------------------------------------- */

.sidebar-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--sidebar-icon-slot);
  height: var(--sidebar-icon-slot);
  flex: none;
}

.sidebar-icon svg {
  width: 18px;
  height: 18px;
}

.sidebar-link {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sidebar-gap);
  width: 100%;
  min-height: var(--sidebar-row-h);
  padding: 0 var(--sidebar-gutter);
  box-sizing: border-box;
  border: none;
  border-radius: var(--sidebar-radius);
  background: transparent;
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  line-height: 1.3;
  text-align: left;
  text-decoration: none;
  color: var(--sidebar-fg);
  cursor: pointer;
  transition: background var(--motion-fast) var(--motion-ease), color var(--motion-fast) var(--motion-ease);
}

.sidebar-link:hover {
  background: var(--sidebar-hover);
  color: var(--color-absolute-black);
}

/* The current page: a soft tint, the accent colour and a 3px bar at the edge, so it
   is findable by colour, by weight and by position at once. */
.sidebar-link.active {
  background: var(--sidebar-accent-soft);
  color: var(--sidebar-accent);
  font-weight: 600;
}

.sidebar-link.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  width: 3px;
  height: 18px;
  border-radius: 0 3px 3px 0;
  background: currentColor;
  transform: translateY(-50%);
}

.sidebar-label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A group header keeps its chevron on the far right; the icon and label stay
   together on the left inside their own flex row. */
.sidebar-dropdown-toggle {
  justify-content: space-between;
}

.sidebar-group-head {
  display: flex;
  align-items: center;
  gap: var(--sidebar-gap);
  min-width: 0;
}

.sidebar-link .chevron {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--color-graphite);
  transition: transform var(--motion-base) var(--motion-ease);
}

.sidebar-dropdown-toggle.open .chevron {
  transform: rotate(180deg);
}

/* A group whose page is open reads as part of the trail, even while collapsed. */
.sidebar-menu-item:has(.sidebar-sublink.active) > .sidebar-dropdown-toggle {
  color: var(--sidebar-accent);
  font-weight: 600;
}

/* The two plain links are day-to-day work; the groups behind the divider are tools
   and administration. Only the first group carries the rule. */
.sidebar-nav > .sidebar-menu-item:nth-of-type(1) {
  margin-top: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--color-slate-border);
}

/* Submenu ------------------------------------------------------------------
   Indented under its group and joined to it by a hairline that sits exactly on
   the icons' centre line above, so the eye can trace parent to child. */

.sidebar-submenu {
  display: grid;
  /* 0fr → 1fr is the whole animation, and the reason no height is written down anywhere: a
     grid track asked for `1fr` measures its own contents, so a group that gains or loses an
     entry (a translation, a role with one more permission) still opens to exactly its own
     height. A `max-height` would have to be guessed, and guessed wrong the first time it
     changed. */
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--motion-base) var(--motion-ease), margin var(--motion-base) var(--motion-ease);
  /* No room at all while closed, which is what `display: none` used to give for free; the
     rhythm below comes back with the entries. */
  margin: 0;
  /* 34px: the sublink text then lands at 56px, the exact x of the group's label,
     while the hairline below still hangs under the icons' centre line. */
  padding-left: 34px;
  position: relative;
}

.sidebar-submenu.open {
  grid-template-rows: 1fr;
  margin: 2px 0 6px;
}

/* The wrapper is what is clipped, and what the entries are laid out in now that the group
   itself is a grid of one cell. `min-height: 0` is the part that makes `0fr` possible: a grid
   item refuses to shrink below its contents unless it is told it may. */
.sidebar-submenu-inner {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-height: 0;
  overflow: hidden;
  /* A closed group's entries are still in the document — they have to be, or there would be
     no height to animate — so `visibility` is what keeps them out of the tab order and out
     of a screen reader's reach. It flips late on the way out and at once on the way in, so
     the clip and the disappearance happen together. */
  visibility: hidden;
  transition: visibility var(--motion-base) var(--motion-ease);
}

.sidebar-submenu.open > .sidebar-submenu-inner {
  visibility: visible;
}

.sidebar-submenu::before {
  content: '';
  position: absolute;
  left: 22px;
  top: 4px;
  bottom: 4px;
  width: 1px;
  background: var(--color-slate-border);
}

/* A sublink keeps the smaller type, because the indent already says which group it belongs to, but
   takes the same minimum height as the rows above it — a thumb cannot be asked to hit a 33px strip
   just because the entry sits one level down. */
.sidebar-sublink {
  display: flex;
  align-items: center;
  min-height: var(--sidebar-row-h);
  padding: 0 10px;
  border-radius: var(--radius-inputs);
  font-size: 0.875rem;
  line-height: 1.35;
  color: var(--sidebar-fg-muted);
  text-decoration: none;
  transition: background var(--motion-fast) var(--motion-ease), color var(--motion-fast) var(--motion-ease);
}

.sidebar-sublink:hover {
  background: var(--sidebar-hover);
  color: var(--color-absolute-black);
}

.sidebar-sublink.active {
  background: var(--sidebar-accent-soft);
  color: var(--sidebar-accent);
  font-weight: 600;
}

/* A source that has been failing for minutes (services/system/databaseHealth.js). The dot sits
   on the group, because the collapsed rail hides the entries inside it, and the count sits on the
   entry itself. Both are hidden until there is something to say, and neither is a link of its
   own: the entry underneath is still the way to the screen. */
.sidebar-alert-dot {
  width: 8px;
  height: 8px;
  flex: none;
  border-radius: 50%;
  background: var(--danger-solid);
  box-shadow: 0 0 0 2px var(--color-paper-white);
}

.sidebar-sublink.has-alert {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  color: var(--danger-fg);
  font-weight: 600;
}

.sidebar-alert-count {
  flex: none;
  min-width: 20px;
  padding: 1px 6px;
  border-radius: 100px;
  background: var(--danger-solid);
  color: var(--color-paper-white);
  font-size: 0.7rem;
  font-weight: 700;
  text-align: center;
}


/* Page side ---------------------------------------------------------------- */

.main-content {
  flex: 1;
  min-width: 0;
  margin: 18px;
  background-color: var(--color-paper-white);
  overflow-y: auto;
}

/* Drawer toggle: the page header shows it, and only a phone has room for it. It is the one control
   on a page that opens the menu, so it gets a full 44px square rather than a padded icon. */
.sidebar-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  margin-right: 12px;
  border: none;
  border-radius: var(--radius-inputs);
  background: transparent;
  color: var(--color-absolute-black);
  cursor: pointer;
  transition: background var(--motion-fast) var(--motion-ease);
}

.sidebar-toggle:hover {
  background: var(--sidebar-hover);
}

/* The scrim stays in the DOM and fades, so opening the drawer animates both
   halves together; visibility keeps it from catching clicks while closed. */
.sidebar-overlay {
  position: fixed;
  inset: 0;
  z-index: 999;
  background: rgba(23, 22, 24, 0.45);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--motion-base) var(--motion-ease), visibility var(--motion-base) var(--motion-ease);
}

.sidebar-overlay.open {
  opacity: 1;
  visibility: visible;
}

/* Collapsed rail -----------------------------------------------------------
   Desktop only: a phone does not have the width to spare for icons alone, so
   the drawer below stays full width. Every rule here is namespaced with
   .sidebar.collapsed to keep that boundary obvious. */

@media (min-width: 769px) {
  .sidebar.collapsed {
    width: 72px; /* mirrors --sidebar-rail-w */
  }

  .sidebar.collapsed .sidebar-head {
    flex-direction: column;
    gap: 8px;
    padding: 12px 8px;
  }

  /* The rail hides the brand's text, so the lock-up collapses to its 32px logo — which would leave a
     target narrower than a thumb. The link keeps a square of its own to be hit instead. */
  .sidebar.collapsed .sidebar-brand {
    justify-content: center;
    min-width: 44px;
  }

  .sidebar.collapsed .sidebar-brand-text,
  .sidebar.collapsed .sidebar-label,
  .sidebar.collapsed .chevron,
  .sidebar.collapsed .sidebar-submenu {
    display: none;
  }

  .sidebar.collapsed .sidebar-nav {
    padding: 10px 12px;
  }

  .sidebar.collapsed .sidebar-nav > .sidebar-menu-item:nth-of-type(1) {
    margin-top: 4px;
    padding-top: 10px;
  }

  .sidebar.collapsed .sidebar-link {
    position: relative;
    justify-content: center;
    gap: 0;
    padding: 0;
  }

  /* Rail only: a dot marks the group holding the page you are on, since its
     entries are out of reach at this width. */
  .sidebar.collapsed .sidebar-menu-item {
    position: relative;
  }

  .sidebar.collapsed .sidebar-menu-item:has(.sidebar-sublink.active)::before {
    content: '';
    position: absolute;
    top: 10px;
    right: 10px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--sidebar-accent);
  }

  /* One label is all the rail has room for, so it is read back out of the row
     itself rather than duplicated in the markup. */
  .sidebar.collapsed .sidebar-link[data-label]:hover::after,
  .sidebar.collapsed .sidebar-link[data-label]:focus-visible::after {
    content: attr(data-label);
    position: absolute;
    top: 50%;
    left: calc(100% + 10px);
    z-index: 1100;
    padding: 6px 10px;
    border-radius: 8px;
    background: var(--color-absolute-black);
    color: var(--color-paper-white);
    font-size: 0.8rem;
    font-weight: 500;
    line-height: 1.3;
    white-space: nowrap;
    box-shadow: 0 6px 16px -8px rgba(0, 0, 0, 0.5);
    transform: translateY(-50%);
    pointer-events: none;
  }

  /* The active bar has to stay inside the narrower row. */
  .sidebar.collapsed .sidebar-link.active::before {
    left: 1px;
    height: 16px;
  }
}

/* Motion is decoration here, never information. */
@media (prefers-reduced-motion: reduce) {
  .sidebar,
  .sidebar-link,
  .sidebar-sublink,
  .sidebar-submenu,
  .sidebar-submenu-inner,
  .sidebar-collapse-btn,
  .sidebar-link .chevron,
  .sidebar-overlay {
    transition: none;
  }
}

/* Phone drawer -------------------------------------------------------------
   Off-canvas until the page header's toggle opens it. Kept last in the file so
   the rail rules above cannot be re-opened by a later query. */

@media (max-width: 768px) {
  .sidebar {
    position: fixed;
    top: 0;
    z-index: 1000;
    width: min(288px, 86vw);
    transform: translateX(-100%);
    transition: transform var(--motion-base) var(--motion-ease);
  }

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

  .sidebar-toggle {
    display: inline-flex;
  }

  /* The rail is a desktop idea: in the drawer the menu always has its full width, so
     the control would look live and do nothing. The drawer closes itself instead. */
  .sidebar-collapse-btn {
    display: none;
  }
}
