/* ============================================
   Cable Manager - Global Styles
   Design System v1.0
   ============================================ */

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

:root {
  /* Colors - Green to Light Green to Cream White Theme */
  --primary: #2F8443;
  --primary-dark: #1F6B33;
  --primary-darker: #145224;
  --secondary: #63B52D;

  /* The palette is built around one green. The accent used to be a neon
     lime, which fought the deep green it sat next to - two unrelated
     greens on the same screen read as a mistake rather than a scheme.
     This one is the same hue family as the primary, lifted in brightness
     so it still reads as "good news" against it. */
  --gradient-green: linear-gradient(135deg, #0B822E 0%, #46A154 55%, #7DD35B 100%);
  --success: #46A154;
  --warning: #B77B12;
  --danger: #C13B47;
  --danger-light: rgba(193, 59, 71, 0.10);
  --success-light: rgba(70, 161, 84, 0.14);
  --warning-light: #FBF3E2;

  /* Badges used to carry a coloured halo, which drew the eye to the
     decoration rather than the status. A status marker should be legible,
     not luminous, so these are now the quiet lift every other raised
     surface gets. Kept under the old names so nothing has to be rewired. */
  --glow-success: 0 1px 2px rgba(11, 130, 46, 0.14);
  --glow-danger: 0 1px 2px rgba(120, 30, 40, 0.12);
  --surface: #ffffff;
  --surface-soft: #F3FBF2;
  --surface-strong: #E8F5E6;
  --glass-surface: rgba(255, 255, 255, 0.78);
  --bg-gradient: radial-gradient(circle at top left, rgba(125, 211, 91, 0.16), transparent 24%), radial-gradient(circle at 82% 10%, rgba(70, 161, 84, 0.10), transparent 26%);
  --shadow-xl: 0 24px 64px rgba(15, 70, 35, 0.10);

  --neutral-white: #F9FEFA;
  --neutral-light: #EEF8EC;
  --neutral-medium: #D6EBD2;
  --neutral-gray: #BFDDBB;
  --neutral-dark: #46564A;

  /* Body text was a warm mid-brown and secondary text lighter still. Both
     are darkened here: the smaller labels in this interface run at 10-13px,
     where the old grey fell close to the readable limit against a white
     card, and this system gets read on a projector as often as a monitor. */
  --text-primary: #1B2A1F;
  --text-secondary: #56685B;
  
  /* Spacing */
  --spacing-xs: 4px;
  --spacing-sm: 8px;
  --spacing-md: 12px;
  --spacing-lg: 16px;
  --spacing-xl: 24px;
  --spacing-2xl: 32px;
  --spacing-3xl: 48px;
  
  /* Border Radius */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
  --radius-full: 9999px;
  
  /* Shadows
     Tinted towards the green the interface is built from rather than pure
     black. Neutral black over a warm off-white reads as grey dirt under the
     card; a shadow carrying a little of the surrounding hue reads as light.
     Each step pairs a tight contact shadow with a wider, softer one, which
     is what gives a raised edge instead of a uniform smudge. */
  --shadow-light: 0 1px 2px rgba(15, 70, 35, 0.06);
  --shadow-md: 0 1px 2px rgba(15, 70, 35, 0.06), 0 4px 10px rgba(15, 70, 35, 0.07);
  --shadow-lg: 0 2px 4px rgba(15, 70, 35, 0.06), 0 12px 28px rgba(15, 70, 35, 0.10);
  
  /* Typography */
  --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-weight-regular: 400;
  --font-weight-medium: 500;
  --font-weight-semibold: 600;
  --font-weight-bold: 700;
  
  /* Transitions */
  --transition: all 0.2s ease-in-out;
}

/* Dark Theme Variables */
/* Dark theme
   This block used to cover the neutral ramp and the text, but not the
   surfaces, so switching the theme left the cards dark and the panels,
   table headings and card headings on top of them still white. Body text
   went pale as intended and then sat on those white panels, which is how
   the theme ended up with near-black text on dark cards in some places and
   pale text on white in others - unreadable in both directions.

   Everything the light theme paints a surface with is answered here, and
   the primary green is lifted: a deep forest green is legible on paper and
   on a white card, and almost black against a dark one. */
[data-theme="dark"] {
  --neutral-white: #171D19;
  --neutral-light: #0F1511;
  --neutral-medium: #2A3A2F;
  --neutral-gray: #3A4D40;
  --neutral-dark: #DDE8DF;
  --text-primary: #F0F5FA;
  --text-secondary: #A9B2BD;

  --surface: #171D19;
  --surface-soft: #1E2721;
  --surface-strong: #26322B;
  --glass-surface: rgba(23, 29, 25, 0.82);

  --primary: #7DD35B;
  --primary-dark: #9BE07E;
  --primary-darker: #BDEBA6;
  /* Used by the avatar gradient, and missing here until that gradient
     started drawing from the palette instead of naming its own colours. */
  --secondary: #46A154;
  --success: #7DD35B;
  --warning: #D7A13F;
  --danger: #E4707C;

  --bg-gradient: radial-gradient(circle at top left, rgba(125, 211, 91, 0.08), transparent 22%), radial-gradient(circle at 82% 10%, rgba(70, 161, 84, 0.06), transparent 24%);
  --gradient-green: linear-gradient(135deg, #1F6B33 0%, #46A154 100%);

  --shadow-light: 0 1px 4px rgba(0, 0, 0, 0.4);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 12px 24px rgba(0, 0, 0, 0.6);
  --shadow-xl: 0 24px 64px rgba(0, 0, 0, 0.6);
  --success-light: rgba(125, 211, 91, 0.16);
  --danger-light: rgba(228, 112, 124, 0.16);
  --warning-light: rgba(215, 161, 63, 0.16);
}

/* ============================================
   Base Styles
   ============================================ */

html,
body {
  width: 100%;
  height: 100%;
}

body {
  font-family: var(--font-family);
  font-size: 14px;
  font-weight: var(--font-weight-regular);
  line-height: 1.6;
  color: var(--text-primary);
  background: var(--bg-gradient), var(--neutral-light);
  min-height: 100vh;
  transition: background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body::before {
  content: '';
  position: fixed;
  inset: 0;
  background: radial-gradient(circle at top center, rgba(255, 255, 255, 0.35), transparent 25%);
  pointer-events: none;
  z-index: -1;
}

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

a:hover {
  color: var(--primary-dark);
  /* No underline. This rule reached every anchor in the interface, and
     because a:hover outranks a class on its own, it beat the
     text-decoration: none that .btn and .sidebar-nav-link set - so buttons
     and menu entries grew an underline when the pointer crossed them,
     which is not something a button does. The colour shift, and whatever
     the component does with its own background, already say it is live. */
}

/* ============================================
   Headings
   ============================================ */

h1 {
  font-size: 32px;
  font-weight: var(--font-weight-bold);
  line-height: 1.2;
  margin-bottom: var(--spacing-lg);
}

h2 {
  font-size: 24px;
  font-weight: var(--font-weight-bold);
  line-height: 1.3;
  margin-bottom: var(--spacing-lg);
}

h3 {
  font-size: 20px;
  font-weight: var(--font-weight-semibold);
  line-height: 1.4;
  margin-bottom: var(--spacing-md);
}

h4 {
  font-size: 16px;
  font-weight: var(--font-weight-semibold);
  line-height: 1.5;
  margin-bottom: var(--spacing-md);
}

p {
  margin-bottom: var(--spacing-md);
}

/* ============================================
   Buttons
   ============================================ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-sm);
  padding: 10px 20px;
  font-size: 14px;
  font-weight: var(--font-weight-semibold);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: var(--transition);
  text-decoration: none;
  white-space: nowrap;
}

/* Clicking a button used to leave the focus ring stuck on it until
   something else was clicked, so the last thing pressed always looked
   selected. Keyboard users still get the ring, which is the case that
   needs it - outline is painted outside the box, so neither state moves
   anything on the page. */
.btn:focus {
  outline: none;
}

.btn:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* Primary Button */
.btn-primary {
  background-color: var(--primary);
  color: var(--neutral-white);
  box-shadow: var(--shadow-light);
}

.btn-primary:hover {
  background-color: var(--primary-dark);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.btn-primary:active {
  background-color: var(--primary-darker);
  transform: translateY(0);
}

.btn-primary:disabled {
  background-color: var(--neutral-medium);
  color: var(--text-secondary);
  cursor: not-allowed;
  opacity: 0.7;
}

/* Secondary Button */
.btn-secondary {
  background-color: var(--neutral-medium);
  color: var(--text-primary);
  border: 1px solid var(--neutral-gray);
}

.btn-secondary:hover {
  background-color: var(--neutral-gray);
  border-color: #CED4DA;
}

/* Danger Button */
.btn-danger {
  background-color: var(--danger);
  color: var(--neutral-white);
}

.btn-danger:hover {
  background-color: #C82333;
}

/* Success Button */
.btn-success {
  background-color: var(--success);
  color: var(--neutral-white);
}

.btn-success:hover {
  background-color: #218838;
}

/* Size Variants */
.btn-sm {
  padding: 6px 12px;
  font-size: 12px;
}

.btn-lg {
  padding: 12px 24px;
  font-size: 16px;
}

.btn-block {
  display: flex;
  width: 100%;
}

.btn-icon {
  padding: 8px;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  font-size: 18px;
}

/* ============================================
   Cards
   ============================================ */

.card {
  background-color: var(--neutral-white);
  border-radius: var(--radius-lg);
  padding: 20px;
  border: 1px solid var(--neutral-medium);
  box-shadow: var(--shadow-light);
  transition: var(--transition);
}

.card:hover {
  box-shadow: var(--shadow-md);
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--spacing-lg);
  padding-bottom: var(--spacing-lg);
  border-bottom: 1px solid var(--neutral-medium);
}

.card-header h3 {
  margin: 0;
}

.card-body {
  margin-bottom: var(--spacing-lg);
}

.card-body:last-child {
  margin-bottom: 0;
}

.card-footer {
  display: flex;
  gap: var(--spacing-md);
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--neutral-medium);
}

/* Stat Card */
.stat-card {
  padding: 24px;
  position: relative;
  overflow: hidden;
}

.stat-card::before {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 80px;
  height: 80px;
  background-color: rgba(46, 139, 87, 0.06);
  border-radius: 0 0 0 80px;
}

.stat-value {
  font-size: 28px;
  font-weight: var(--font-weight-bold);
  color: var(--primary);
  margin-bottom: var(--spacing-sm);
  position: relative;
  z-index: 1;
}

.stat-label {
  font-size: 13px;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  position: relative;
  z-index: 1;
}

.stat-change {
  font-size: 12px;
  margin-top: var(--spacing-sm);
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
}

.stat-change.positive {
  color: var(--success);
}

.stat-change.negative {
  color: var(--danger);
}

/* Highlighted Card */
.card-highlight {
  background: linear-gradient(135deg, #F0F8FF 0%, #E3F2FD 100%);
  border-color: #B3E5FC;
}

/* ============================================
   Badges
   ============================================ */

.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-xs);
  padding: 4px 12px;
  font-size: 12px;
  font-weight: var(--font-weight-semibold);
  border-radius: var(--radius-full);
  white-space: nowrap;
}

.badge-success {
  background-color: var(--success-light);
  color: #155724;
  border: 1px solid #C3E6CB;
}

.badge-danger {
  background-color: var(--danger-light);
  color: #721C24;
  border: 1px solid #F5C6CB;
}

.badge-warning {
  background-color: var(--warning-light);
  color: #856404;
  border: 1px solid #FFEAA7;
}

.badge-inactive {
  background-color: #E2E3E5;
  color: #383D41;
  border: 1px solid #D6D8DB;
}

.badge-info {
  background-color: #D1ECF1;
  color: #0C5460;
  border: 1px solid #BEE5EB;
}

/* ============================================
   Input Fields
   ============================================ */

.form-group {
  margin-bottom: var(--spacing-lg);
}

label {
  display: block;
  margin-bottom: var(--spacing-sm);
  font-weight: var(--font-weight-medium);
  color: var(--text-primary);
}

input,
textarea,
select {
  width: 100%;
  padding: 10px 12px;
  font-family: var(--font-family);
  font-size: 14px;
  border: 1px solid var(--neutral-gray);
  border-radius: var(--radius-md);
  background-color: var(--neutral-white);
  color: var(--text-primary);
  transition: var(--transition);
}

/* The focus ring here was blue - left over from a theme this system no
   longer uses. It only ever showed on screens the enhancement stylesheet
   did not reach, which is exactly why it survived unnoticed. */
input:focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(46, 139, 87, 0.18);
}

input:disabled,
textarea:disabled,
select:disabled {
  background-color: var(--neutral-light);
  color: var(--text-secondary);
  cursor: not-allowed;
  opacity: 0.7;
}

input::placeholder {
  color: var(--text-secondary);
}

/* Filters sitting inside a card header, beside the heading.
 *
 * Distinct from .filters, which is the standalone bar in its own card above
 * a table. That one expects each control to be wrapped in a .filter-group
 * or .search-bar that shrink-wraps around it; its inputs rely on the
 * width: 100% above to fill those wrappers, so it is left alone.
 *
 * This one takes bare controls, because a header has no room for a div
 * around each of six of them. The rule above gives every input and select
 * width: 100%, which is right in a form where each field owns a line and
 * wrong here: in a flex row each control claims the full width and drops
 * onto a line of its own, turning a card header into a column three hundred
 * pixels tall with the heading stranded beside it in the white space.
 *
 * Only direct children are reset, so a control that does arrive wrapped
 * still behaves. The text box is the one that grows, holding a search term
 * of unpredictable length; dates and dropdowns stay the size of their
 * content, and the row still wraps when it genuinely runs out of room. */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--spacing-sm);
}

.filter-bar > input,
.filter-bar > select {
  width: auto;
  min-width: 0;
}

.filter-bar > input[type="text"],
.filter-bar > input[type="search"] {
  flex: 1 1 180px;
  min-width: 150px;
}

textarea {
  resize: vertical;
  min-height: 100px;
}

/* Form Help Text */
.form-text {
  display: block;
  font-size: 12px;
  color: var(--text-secondary);
  margin-top: var(--spacing-xs);
}

.form-text.error {
  color: var(--danger);
}

.form-text.success {
  color: var(--success);
}

/* ============================================
   Tables
   ============================================ */

.table-wrapper {
  overflow-x: auto;
  border-radius: var(--radius-lg);
  background-color: rgba(255, 255, 255, 0.96);
  border: 1px solid rgba(45, 95, 63, 0.08);
  box-shadow: var(--shadow-light);
}

table {
  width: 100%;
  border-collapse: collapse;
  border-spacing: 0;
  background: transparent;
}

thead {
  background-color: rgba(45, 95, 63, 0.08);
}

th {
  padding: 16px;
  text-align: left;
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
  border-bottom: 1px solid rgba(45, 95, 63, 0.12);
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

td {
  padding: 16px;
  border-bottom: 1px solid rgba(45, 95, 63, 0.08);
}

tbody tr {
  transition: var(--transition);
}

tbody tr:hover {
  background-color: rgba(46, 139, 87, 0.07);
}

tbody tr:last-child td {
  border-bottom: none;
}

/* Alternate row background */
tbody tr:nth-child(even) {
  background-color: #FAFBFC;
}

tbody tr:nth-child(even):hover {
  background-color: #F0F3F7;
}

/* Action buttons in tables */
.table-actions {
  display: flex;
  gap: var(--spacing-sm);
  align-items: center;
}

/* Responsive table */
@media (max-width: 768px) {
  table,
  thead,
  tbody,
  th,
  td,
  tr {
    display: block;
  }

  thead {
    display: none;
  }

  tr {
    margin-bottom: var(--spacing-lg);
    border: 1px solid var(--neutral-medium);
    border-radius: var(--radius-md);
    padding: var(--spacing-md);
  }

  td {
    display: flex;
    justify-content: space-between;
    padding: var(--spacing-md) 0;
    border: none;
  }

  td::before {
    content: attr(data-label);
    font-weight: var(--font-weight-semibold);
    min-width: 120px;
    color: var(--text-secondary);
  }
}

/* ============================================
   Navigation - Sidebar
   ============================================ */

.sidebar {
  width: 300px;
  background-color: rgba(255, 255, 255, 0.88) !important;
  backdrop-filter: blur(22px) saturate(180%) !important;
  -webkit-backdrop-filter: blur(22px) saturate(180%) !important;
  border-right: 1px solid rgba(45, 95, 63, 0.08);
  box-shadow: inset -1px 0 0 rgba(45, 95, 63, 0.06), 0 24px 60px rgba(24, 38, 51, 0.06);
  padding: 24px 0 16px;
  position: fixed;
  left: 0;
  top: 56px;
  bottom: 0;
  overflow-y: auto;
  z-index: 1000;
}

[data-theme="dark"] .sidebar {
  background-color: rgba(26, 29, 33, 0.86) !important;
}

.sidebar-logo {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  padding: 12px 20px;
  /* Was 2xl. The menu below it has thirteen entries and has to fit on a
     laptop; a third of the space above them was going to a gap. */
  margin-bottom: var(--spacing-md);
  font-weight: var(--font-weight-bold);
  color: var(--primary);
  font-size: 18px;
}

.sidebar-nav {
  list-style: none;
}

.sidebar-nav-item {
  margin: 0;
}

.sidebar-nav-link {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  /* Tightened from 14px/10px. Thirteen entries at the old size came to
     944px of menu, which overflowed any laptop screen - so every page
     carried a scrollbar down the side of the navigation regardless of
     what was on it. Still a comfortable target: 42px tall, well above
     the 24px the guidance asks for. */
  padding: 9px 18px;
  margin: 0 8px 4px;
  font-size: 15px;
  color: var(--text-primary);
  text-decoration: none;
  border-radius: 16px;
  transition: all 0.25s ease;
  position: relative;
  width: calc(100% - 16px);
  background-color: transparent;
}

.sidebar-nav-link:hover,
button.sidebar-nav-link:hover {
  background-color: rgba(46, 139, 87, 0.12) !important;
  color: var(--primary);
  box-shadow: inset 0 0 0 1px rgba(45, 95, 63, 0.08);
}

.sidebar-nav-link:focus,
.sidebar-nav-link:focus-visible,
a.sidebar-nav-link:focus,
a.sidebar-nav-link:focus-visible,
button.sidebar-nav-link:focus,
button.sidebar-nav-link:focus-visible {
  outline: none !important;
  box-shadow: none !important;
  outline-offset: 0 !important;
  border: none !important;
}

button.sidebar-nav-link::-moz-focus-inner {
  border: 0 !important;
  padding: 0 !important;
}

button.sidebar-nav-link {
  width: 100% !important;
  justify-content: flex-start !important;
  background: transparent !important;
  border: none !important;
  cursor: pointer !important;
}

.sidebar-nav-link.active {
  background: linear-gradient(90deg, rgba(45, 95, 63, 0.14) 0%, transparent 100%) !important;
  color: var(--primary);
  font-weight: 800;
  border-left: 5px solid var(--primary) !important;
  box-shadow: inset 0 0 0 1px rgba(45, 95, 63, 0.08);
}

.sidebar-nav-link.active::before {
  display: none;
}

.sidebar-icon {
  width: 22px;
  height: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 18px;
}

/* ============================================
   Staff dashboard
   ============================================ */

/* A stat card that is also the way in to the list it counts. Keeps the
   card's own colours rather than turning into a green underlined link, and
   still lifts on hover like the others, so it reads as something to press. */
.stat-link,
.stat-link:hover,
.stat-link:focus {
  display: block;
  color: inherit;
  text-decoration: none;
}

.stat-link:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* A grid column's minimum size defaults to the width of its content, so a
   table with an unbreakable line in it will widen its own column past its
   share and shove the card beside it off the edge. That is what cut the
   payments card in half at 1280px. Letting each card shrink to its share
   leaves the table to scroll inside its own box instead. Scoped to this page
   so no other screen's layout moves. */
.dash-grid > * {
  min-width: 0;
}

/* The second line under a row's main value: category and subject, account
   and ticket number. Secondary information, sized and coloured so. */
.dash-sub {
  margin-top: 2px;
  font-size: 12px;
  color: var(--text-secondary);
  max-width: 26ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* How long something has waited. Allowed to wrap - "Yesterday, 10:24 PM"
   is two short lines rather than the thing that forces the table wider than
   its card - with a floor so a bare date is never squeezed. */
.dash-when {
  min-width: 78px;
  font-size: 13px;
  color: var(--text-secondary);
}

/* An empty list is good news here - nothing waiting - so it is said plainly
   in the card rather than left as a blank table. */
.dash-empty {
  padding: var(--spacing-xl) var(--spacing-lg);
  color: var(--text-secondary);
  text-align: center;
  font-size: 14px;
}

.dash-note {
  margin: var(--spacing-md) var(--spacing-lg) var(--spacing-sm);
  font-size: 12px;
  color: var(--text-secondary);
}

/* A label on the left, a figure on the right, a hairline between rows. */
.dash-figure {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: var(--spacing-sm) 0;
  border-bottom: 1px solid var(--neutral-light);
  font-size: 14px;
}

.dash-figure:last-child {
  border-bottom: none;
}

.dash-figure strong {
  font-size: 18px;
  color: var(--primary);
}

/* The build number, under Logout. Present for whoever needs to ask which
   version is running, and quiet enough that nobody else notices it. */
.sidebar-version {
  list-style: none;
  padding: var(--spacing-sm) 18px var(--spacing-md);
  font-size: 11px;
  color: var(--text-secondary);
  text-align: center;
  letter-spacing: 0.3px;
}

/* Mobile Sidebar Toggle */
.sidebar-toggle {
  display: none;
}

@media (max-width: 768px) {
  .sidebar {
    width: 100%;
    height: 100%;
    position: fixed;
    left: -100%;
    top: 0;
    bottom: 0;
    transition: left 0.3s ease;
    border-right: none;
    z-index: 2000;
  }

  .sidebar.open {
    left: 0;
  }

  .sidebar-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .sidebar-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 1999;
    opacity: 0;
    transition: opacity 0.3s ease;
  }

  .sidebar-overlay.open {
    display: block;
    opacity: 1;
  }
}

/* ============================================
   Navigation - Top Bar
   ============================================ */

.navbar {
  height: 56px;
  background-color: var(--neutral-white);
  border-bottom: 1px solid var(--neutral-medium);
  padding: 0 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1001;
}

.navbar-left {
  display: flex;
  align-items: center;
  gap: var(--spacing-lg);
}

.navbar-brand {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  font-weight: var(--font-weight-bold);
  font-size: 18px;
  color: var(--primary);
}

.navbar-brand-icon {
  width: 32px;
  height: 32px;
  background: var(--gradient-green);
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--neutral-white);
  font-weight: var(--font-weight-bold);
}

.navbar-right {
  display: flex;
  align-items: center;
  gap: var(--spacing-lg);
}

.navbar-icon-btn {
  background: none;
  border: none;
  cursor: pointer;
  font-size: 20px;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  transition: var(--transition);
}

.navbar-icon-btn:hover {
  background-color: var(--neutral-light);
  color: var(--primary);
}

.navbar-profile {
  display: flex;
  align-items: center;
  gap: var(--spacing-md);
  cursor: pointer;
}

.navbar-profile-avatar {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-full);
  /* This was a gradient between two greens with the initials written over
     it, and the two greens were named here rather than taken from the
     palette. So they stayed light-theme greens while the initials turned
     dark with the theme, and in dark mode the initials disappeared into
     them. Measuring it turned up the same fault in light mode, less
     obviously: white initials over the pale end of that gradient came to
     2.17 against it, well under readable.
     A circle 36px across gains nothing from a gradient, and one flat colour
     is a background the initials can be guaranteed against - 7.2 to 1 in
     light, 7.7 to 1 in dark. */
  background: var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--neutral-white);
  font-weight: var(--font-weight-bold);
  font-size: 14px;
}

.navbar-profile-info {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.navbar-profile-name {
  font-size: 14px;
  font-weight: var(--font-weight-semibold);
  color: var(--text-primary);
}

.navbar-profile-role {
  font-size: 12px;
  color: var(--text-secondary);
}

@media (max-width: 768px) {
  .navbar {
    padding: 0 12px;
  }

  .navbar-profile-info {
    display: none;
  }
}

/* ============================================
   Main Layout
   ============================================ */

.app-container {
  display: flex;
  min-height: 100vh;
  margin-top: 56px;
  margin-left: 300px;
}

.sidebar {
  margin-top: 0;
}

.app-main {
  flex: 1;
  background-color: transparent;
  padding: 32px 32px 40px;
  overflow-y: auto;
  min-height: calc(100vh - 56px);
}

.app-main > .card:first-child {
  margin-top: 0;
}

@media (max-width: 768px) {
  .app-container {
    margin-left: 0;
  }

  .app-main {
    padding: 16px;
  }
}

/* ============================================
   System Icon Library (Unicode)
   ============================================ */

.icon-menu::before { content: "☰"; }
.icon-home::before { content: "🏠"; }
.icon-users::before { content: "👥"; }
.icon-bill::before { content: "📄"; }
.icon-chart::before { content: "📊"; }
/* Analytics and the financial reports both used the bar chart, and the
   subscriber list and the staff list both used the pair of people, so four
   entries in the sidebar were carrying two pictures between them. These
   give the second of each pair its own. */
.icon-ledger::before { content: "🧾"; }
.icon-staff::before  { content: "🧑‍💼"; }
.icon-settings::before { content: "⚙️"; }
.icon-logout::before { content: "🚪"; }
.icon-edit::before { content: "✎"; }
.icon-delete::before { content: "🗑️"; }
.icon-book::before { content: "📖"; }
.icon-bell::before { content: "🔔"; }
.icon-add::before { content: "➕"; }
.icon-download::before { content: "⬇️"; }
.icon-history::before { content: "📜"; }
.icon-profile::before { content: "👤"; }
.icon-arrow::before { content: "→"; }
.icon-arrow-up::before { content: "↑"; }
.icon-arrow-down::before { content: "↓"; }
.icon-close::before { content: "✕"; }
.icon-check::before { content: "✓"; }
.icon-search::before { content: "🔍"; }

/* ============================================
   Page Header
   ============================================ */

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--spacing-2xl);
  flex-wrap: wrap;
  gap: var(--spacing-lg);
}

.page-header-title {
  margin-bottom: 0;
}

.page-header-actions {
  display: flex;
  gap: var(--spacing-md);
  flex-wrap: wrap;
}

/* ============================================
   Grid Layout
   ============================================ */

.grid {
  display: grid;
  gap: var(--spacing-lg);
}

.grid-2 {
  grid-template-columns: repeat(2, 1fr);
}

.grid-3 {
  grid-template-columns: repeat(3, 1fr);
}

.grid-4 {
  grid-template-columns: repeat(4, 1fr);
}

@media (max-width: 1200px) {
  .grid-4 {
    grid-template-columns: repeat(2, 1fr);
  }

  .grid-3 {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (max-width: 768px) {
  .grid-2,
  .grid-3,
  .grid-4 {
    grid-template-columns: 1fr;
  }
}

/* ============================================
   Modals & Dialogs
   ============================================ */

.modal {
  display: none;
  position: fixed;
  z-index: 2000;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  animation: fadeIn 0.3s ease;
}

.modal.show {
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-content {
  background-color: var(--neutral-white);
  padding: 24px;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  max-width: 760px !important;
  width: 90%;
  max-height: 90vh;
  overflow-y: auto;
  animation: slideUp 0.3s ease;
}

@media (max-width: 768px) {
  .modal-content {
    max-width: 100%;
    width: 95%;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  }
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: var(--spacing-lg);
  padding-bottom: var(--spacing-lg);
  border-bottom: 1px solid var(--neutral-medium);
}

.modal-header h2 {
  margin: 0;
}

.modal-close {
  background: none;
  border: none;
  font-size: 24px;
  color: var(--text-secondary);
  cursor: pointer;
  padding: 0;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  transition: var(--transition);
}

.modal-close:hover {
  background-color: var(--neutral-light);
  color: var(--text-primary);
}

.modal-body {
  margin-bottom: var(--spacing-lg);
}

.modal-footer {
  display: flex;
  gap: var(--spacing-md);
  justify-content: flex-end;
  padding-top: var(--spacing-lg);
  border-top: 1px solid var(--neutral-medium);
}

/* ============================================
   Animations
   ============================================ */

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes slideUp {
  from {
    transform: translateY(20px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@keyframes shimmer {
  0% {
    background-position: -1000px 0;
  }
  100% {
    background-position: 1000px 0;
  }
}

/* ============================================
   Loading States
   ============================================ */

.skeleton {
  background: linear-gradient(
    90deg,
    var(--neutral-light) 25%,
    var(--neutral-medium) 50%,
    var(--neutral-light) 75%
  );
  background-size: 1000px 100%;
  animation: shimmer 2s infinite;
}

.skeleton-card {
  height: 120px;
  border-radius: var(--radius-lg);
  margin-bottom: var(--spacing-lg);
}

.skeleton-line {
  height: 12px;
  border-radius: var(--radius-sm);
  margin-bottom: var(--spacing-md);
}

.skeleton-avatar {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-full);
  display: inline-block;
}

/* ============================================
   Alert Messages
   ============================================ */

.alert {
  padding: 12px 16px;
  border-radius: var(--radius-md);
  border-left: 4px solid transparent;
  margin-bottom: var(--spacing-lg);
  display: flex;
  gap: var(--spacing-md);
  align-items: flex-start;
}

.alert-success {
  background-color: var(--success-light);
  border-color: var(--success);
  color: #155724;
}

.alert-danger {
  background-color: var(--danger-light);
  border-color: var(--danger);
  color: #721C24;
}

.alert-warning {
  background-color: var(--warning-light);
  border-color: var(--warning);
  color: #856404;
}

.alert-info {
  background-color: #D1ECF1;
  border-color: #0C5460;
  color: #0C5460;
}

/* The alert text colours above are dark inks chosen for light surfaces; on
   a dark surface they disappear into it. Each takes its status colour
   instead, which the dark palette already lifts to read on dark. */
[data-theme="dark"] .alert-success { color: var(--success); }
[data-theme="dark"] .alert-danger  { color: var(--danger); }
[data-theme="dark"] .alert-warning { color: var(--warning); }
[data-theme="dark"] .alert-info    { background-color: rgba(12, 84, 96, 0.28); border-color: #5FB7C8; color: #A9E2EC; }

.alert-icon {
  font-size: 18px;
  flex-shrink: 0;
}

.alert-close {
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  font-size: 16px;
  margin-left: auto;
  padding: 0;
}

/* ============================================
   Search Bar
   ============================================ */

.search-bar {
  display: flex;
  align-items: center;
  background-color: var(--neutral-white);
  border: 1px solid var(--neutral-gray);
  border-radius: var(--radius-md);
  padding: 0 12px;
  gap: var(--spacing-sm);
}

.search-bar input {
  border: none;
  flex: 1;
  padding: 10px 0;
  background: none;
}

.search-bar input:focus {
  outline: none;
  box-shadow: none;
}

.search-icon {
  color: var(--text-secondary);
  font-size: 18px;
}

/* ============================================
   Filter & Sort
   ============================================ */

.filters {
  display: flex;
  gap: var(--spacing-md);
  margin-bottom: var(--spacing-lg);
  flex-wrap: wrap;
}

.filter-group {
  display: flex;
  gap: var(--spacing-sm);
  align-items: center;
}

.filter-label {
  font-size: 13px;
  font-weight: var(--font-weight-semibold);
  color: var(--text-secondary);
  text-transform: uppercase;
}

/* ============================================
   Pagination
   ============================================ */

.pagination {
  display: flex;
  gap: var(--spacing-sm);
  align-items: center;
  justify-content: center;
  margin-top: var(--spacing-xl);
}

.pagination-btn {
  min-width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid var(--neutral-gray);
  background-color: var(--neutral-white);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: var(--transition);
}

.pagination-btn:hover {
  background-color: var(--neutral-light);
  border-color: var(--primary);
}

.pagination-btn.active {
  background-color: var(--primary);
  color: var(--neutral-white);
  border-color: var(--primary);
}

.pagination-btn:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

/* ============================================
   Tabs
   ============================================ */

.tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--neutral-medium);
  margin-bottom: var(--spacing-lg);
}

.tab-button {
  background: none;
  border: none;
  padding: 12px 20px;
  font-size: 14px;
  font-weight: var(--font-weight-medium);
  color: var(--text-secondary);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: var(--transition);
  position: relative;
  top: 1px;
}

.tab-button:hover {
  color: var(--text-primary);
}

.tab-button.active {
  color: var(--primary);
  border-bottom-color: var(--primary);
}

/* Anything that grows without limit - a delivery log, a subscriber's bills,
   a conversation - printed in full turns a page into something you scroll
   past rather than read. These two cap the height instead, so a page is
   the same length whether the list holds five rows or five hundred.
   Measured against the viewport as well, because a fixed height is most of
   a phone screen and would leave nothing else visible.

   Only for lists that really are unbounded. A list the business bounds on
   its own - a handful of service plans, fifteen kept backups - is better
   shown whole; capping it buys nothing and hides rows somebody wanted to
   see at a glance.

   A row here is about 65px, so the ceiling is set by how many rows are
   worth seeing before scrolling rather than by a round number: 620px is
   nine or so, where 460px was six and felt like being shown a keyhole. */
.table-scroll {
  /* clamp, not min(): a bare min(620px, 70vh) collapses to nothing wherever
     the viewport height reports as zero - a hidden tab, a print context, an
     embedded view - and takes the whole list with it. The floor means the
     worst case is a short box, never an empty one. */
  max-height: clamp(240px, 70vh, 620px);
  overflow-y: auto;
}

/* One element deeper than it needs to be: the sorting script puts .sortable
   on every header, and `table th.sortable` in ui-enhancements.css sets
   position: relative at the same specificity but from a later file, which
   quietly beat a shorter selector here and left the header scrolling away. */
.table-scroll table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  /* Opaque: the default thead tint is translucent, and rows would show
     through it as they scrolled underneath. */
  background-color: var(--neutral-light);
  /* A border on a sticky cell is dropped under border-collapse, so the
     dividing line is drawn inside the cell instead. */
  box-shadow: inset 0 -1px 0 rgba(45, 95, 63, 0.12);
}

/* The same treatment for a list of cards, rather than a table. */
.list-scroll {
  max-height: clamp(260px, 70vh, 520px);
  overflow-y: auto;
}

/* A list sharing a row with a chart takes a ceiling close to the chart's own
   drawn height rather than the taller general one, so the two cards finish
   level. Without it the longer list sets the height of the row and the chart
   beside it sits in a pool of white. */
.list-scroll--chart {
  max-height: clamp(180px, 24vh, 240px);
}

/* ============================================
   Charts (Placeholder)
   ============================================ */

.chart-container {
  background-color: var(--neutral-white);
  border-radius: var(--radius-lg);
  padding: 20px;
  border: 1px solid var(--neutral-medium);
  /* This was a flat 300px, from when the box was a placeholder with a line of
     text in it. The charts that now go inside draw at 150px for a donut and
     180px for bars, so every one of them sat in a box nearly twice its own
     height with the difference as white space above and below - and on the
     analytics and financial screens, which stack two of these in a row, that
     pushed the figures underneath off the bottom of a laptop screen.
     The box now takes its height from the chart. The minimum is only so that
     an empty one, or one still waiting on its data, does not collapse to
     nothing; it is a floor, not a size. */
  min-height: clamp(160px, 20vh, 200px);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
}

.chart-placeholder {
  text-align: center;
  font-size: 14px;
}

/* ============================================
   Utility Classes
   ============================================ */

.text-center {
  text-align: center;
}

.text-right {
  text-align: right;
}

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

.d-flex {
  display: flex;
}

.align-items-center {
  align-items: center;
}

.justify-content-between {
  justify-content: space-between;
}

.gap-2 {
  gap: var(--spacing-sm);
}

.gap-3 {
  gap: var(--spacing-md);
}

.gap-4 {
  gap: var(--spacing-lg);
}

.mt-2 {
  margin-top: var(--spacing-sm);
}

.mt-3 {
  margin-top: var(--spacing-md);
}

.mt-4 {
  margin-top: var(--spacing-lg);
}

.mb-2 {
  margin-bottom: var(--spacing-sm);
}

.mb-3 {
  margin-bottom: var(--spacing-md);
}

.mb-4 {
  margin-bottom: var(--spacing-lg);
}

.p-2 {
  padding: var(--spacing-sm);
}

.p-3 {
  padding: var(--spacing-md);
}

.p-4 {
  padding: var(--spacing-lg);
}

.opacity-50 {
  opacity: 0.5;
}

.opacity-75 {
  opacity: 0.75;
}

.overflow-hidden {
  overflow: hidden;
}

.rounded {
  border-radius: var(--radius-md);
}

/* ============================================
   Responsive
   ============================================ */

@media (max-width: 768px) {
  .page-header {
    flex-direction: column;
    align-items: flex-start;
  }

  .page-header-actions {
    width: 100%;
  }

  .btn-block {
    width: 100%;
  }

  h1 {
    font-size: 24px;
  }

  h2 {
    font-size: 20px;
  }
}

/* ============================================
   Dark theme - surfaces named directly
   ============================================ */

/* Three surfaces were written as white rather than as a token, so they
   stayed white when the theme went dark: the panel behind every table, the
   sidebar, and the soft highlight across the top of the page. The table one
   was the worst of them - pale text on a white panel, inside an otherwise
   dark page, which is where dark mode fell apart. */
[data-theme="dark"] .table-wrapper {
  background-color: rgba(26, 29, 33, 0.96);
  border-color: rgba(111, 191, 142, 0.10);
}

[data-theme="dark"] .sidebar {
  border-right-color: rgba(111, 191, 142, 0.10);
}

[data-theme="dark"] body::before {
  background: radial-gradient(circle at top center, rgba(255, 255, 255, 0.04), transparent 25%);
}

/* The navigation bar is on the same footing as the sidebar. */
[data-theme="dark"] .navbar {
  background-color: rgba(26, 29, 33, 0.88) !important;
  border-bottom-color: rgba(111, 191, 142, 0.10);
}

/* The badge fills are tinted with the status colour and rely on the surface
   beneath showing through. Stated here so they sit on the dark card rather
   than on whatever the light theme left behind. */
[data-theme="dark"] .modal-content,
[data-theme="dark"] .stat-card {
  background-color: var(--neutral-white);
}
