:root {
    /* Brand — identical in both themes */
    --pep-green: #5CAF2B;
    --pep-dark-green: #4a9622;
    --pep-light-green: #6BC239;

    /* Layout scale — one place to size the shell.
       The sidebar width feeds .main-content's offset (so the two can never
       disagree), and the gutters are fluid: they shrink on phones and grow on
       big screens without needing a media query per step. */
    --pep-sidebar-w: 260px;
    --pep-sidebar-collapsed-w: 84px;
    --pep-sidebar-peek-w: 288px;  /* the rail opened on hover, over the page */
    --pep-sidebar-offset: 0px;  /* becomes the sidebar width once it docks */
    --pep-gutter: clamp(12px, 2.2vw, 30px);
    --pep-gutter-y: clamp(12px, 1.6vw, 30px);
    /* Upper bound for a content column on ultrawide / 4K screens. Set high
       enough that a 2560px monitor is unaffected — wide data tables want the
       room — but low enough that a 3440px ultrawide does not stretch a form
       across a metre of glass. Full-bleed pages opt out with
       .content-area--wide. */
    --pep-content-max: 2200px;

    /* Semantic surface / text / border tokens (light values).
       The dark values live in the [data-bs-theme="dark"] block below;
       everything in the app references these, never raw hex, so the
       whole UI flips from one place. */
    --pep-bg: #f8f9fa;          /* app / body background */
    --pep-surface: #ffffff;     /* cards, top-bar, panels, dropdowns */
    --pep-surface-2: #f1f3f5;   /* subtle raised / hover rows */
    --pep-border: #dee2e6;      /* dividers, inputs */
    --pep-text: #212529;        /* primary text */
    --pep-text-muted: #6c757d;  /* secondary text */
    --pep-shadow: rgba(0, 0, 0, 0.08);
    /* Fixed dark fill for chips/toggles/tooltips that carry light text — the
       SAME value in both themes (never flips) so white text always reads. */
    --pep-fill-dark: #1f2937;

    /* Signature pads stay light in BOTH themes. The pad is exported as a
       transparent PNG with dark ink and composited onto white PDF pages, so
       the ink has to stay dark — which means the pad must never go dark or
       the stroke is invisible while signing. */
    --pep-signature-bg: #ffffff;
    --pep-signature-border: #adb5bd;

    /* Muted tints used by pills/badges/soft buttons. Each has a bg + fg. */
    --pep-info-bg: #e0f2fe;   --pep-info-fg: #0369a1;
    --pep-role-bg: #f3e8ff;   --pep-role-fg: #7c3aed;
    --pep-edit-bg: #e3f2fd;   --pep-edit-fg: #1976d2;   --pep-edit-bg-hover: #bbdefb;
    --pep-delete-bg: #ffebee; --pep-delete-fg: #c62828; --pep-delete-bg-hover: #ffcdd2;
    --pep-success-bg: #d4edda; --pep-success-fg: #155724;
    --pep-danger-bg: #fef2f2;  --pep-danger-fg: #dc2626;
    --pep-warning-bg: #fffbeb; --pep-warning-fg: #d97706;
    --pep-accent-soft: #f0f9eb; /* light-green selected/active row highlight */
    --pep-amount-positive: #16a34a;
    --pep-amount-negative: #dc2626;
}

[data-bs-theme="dark"] {
    --pep-green: #6dc23a;  /* nudged brighter for contrast on dark */
    --pep-bg: #121417;
    --pep-surface: #1c1f24;
    --pep-surface-2: #23272e;
    --pep-border: #2e343c;
    --pep-text: #e6e8eb;
    --pep-text-muted: #9aa4b1;
    --pep-shadow: rgba(0, 0, 0, 0.55);

    --pep-info-bg: #0d2b3a;   --pep-info-fg: #7ecbef;
    --pep-role-bg: #2c1e42;   --pep-role-fg: #c9a5f5;
    --pep-edit-bg: #14344d;   --pep-edit-fg: #7ec1f0;   --pep-edit-bg-hover: #1b466a;
    --pep-delete-bg: #3d1f22; --pep-delete-fg: #f19aa0; --pep-delete-bg-hover: #52272b;
    --pep-success-bg: #1c3a24; --pep-success-fg: #86d99b;
    --pep-danger-bg: #3d1f22;  --pep-danger-fg: #f19aa0;
    --pep-warning-bg: #3a2e12; --pep-warning-fg: #f0c674;
    --pep-accent-soft: #1e2b1c;
    --pep-amount-positive: #4ade80;
    --pep-amount-negative: #f87171;

    /* Still light — softened off-white so it doesn't glare against the dark
       chrome, while keeping the dark signature ink clearly readable. */
    --pep-signature-bg: #eceef1;
    --pep-signature-border: #7c848e;
}

/* Bootstrap's .bg-white / .bg-light / .text-dark are fixed colours that do
   NOT flip with data-bs-theme — remap them so utility-styled panels/text
   follow the theme. (.bg-body / .text-body already flip on their own.) */
[data-bs-theme="dark"] .bg-white { background-color: var(--pep-surface) !important; }
[data-bs-theme="dark"] .bg-light { background-color: var(--pep-surface-2) !important; }
[data-bs-theme="dark"] .text-dark { color: var(--pep-text) !important; }

/* Shared signature-pad frame — used by every hand-drawn signature canvas
   (Leistungsnachweis, offer acceptance, public document signing). */
.pep-signature-pad {
    background: var(--pep-signature-bg);
    border: 2px dashed var(--pep-signature-border);
    border-radius: 8px;
    position: relative;
}

.pep-signature-pad canvas {
    width: 100%;
    display: block;
    cursor: crosshair;
    touch-action: none;
}

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

body {
    background-color: var(--pep-bg);
    color: var(--pep-text);
    font-family: 'Barlow', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

/* Main content column. Mobile first: the sidebar is off-canvas, so no offset.
   Once it docks, <body> sets --pep-sidebar-offset and this margin follows —
   which is what keeps the full panel, the collapsed rail and the content edge
   in agreement instead of three hard-coded copies of 260px. */
.main-content {
    margin-left: var(--pep-sidebar-offset);
    min-height: 100vh;
    min-height: 100dvh;  /* mobile browser chrome must not add a phantom scroll */
    padding: 0;
    transition: margin-left var(--pep-motion-shell) var(--pep-ease);
}

/* The sidebar docks at Bootstrap's lg breakpoint. Below it — phones, portrait
   tablets, landscape phones — it stays off-canvas behind the hamburger so the
   whole width belongs to the content. */
@media (min-width: 992px) {
    body { --pep-sidebar-offset: var(--pep-sidebar-w); }
    body.sidebar-collapsed { --pep-sidebar-offset: var(--pep-sidebar-collapsed-w); }
}

/* Top Bar */
.top-bar {
    background: var(--pep-surface);
    padding: clamp(10px, 1.4vw, 18px) var(--pep-gutter);
    box-shadow: 0 2px 8px var(--pep-shadow);
    position: sticky;
    top: 0;
    z-index: 999;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: clamp(8px, 1vw, 12px);
    flex-wrap: wrap;
}

.top-bar h1 {
    font-size: clamp(17px, 1.05rem + 0.45vw, 26px);
    font-weight: 700;
    color: var(--pep-text);
    margin: 0;
    /* min-width:0 lets the title shrink; a floor of 200px used to force the
       header wider than a 360px phone and push the actions off-screen. */
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: break-word;
}

/* Several pages group the hamburger and the title in a wrapper div; without
   this the wrapper keeps its intrinsic width and the header cannot wrap. */
.top-bar > * {
    min-width: 0;
}

/* margin-left:auto keeps the actions (and the user chip) on the right edge
   when a wide clock widget wraps them onto their own line — space-between
   alone parks a lone item on the left. */
.top-bar-actions {
    display: flex;
    gap: 12px;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    margin-left: auto;
}

/* ── Top-bar actions ───────────────────────────────────────────────────────
   A record page's header often carries half a dozen actions. Painting them
   all solid green makes them read as half a dozen equally urgent decisions,
   so the tiers below give a page one solid action and keep the rest quiet:

     .top-bar-btn             secondary — the default
     .top-bar-btn-primary     the one action the page is for
     .top-bar-btn-danger      destructive; coloured text until hovered
     .top-bar-btn-attention   a pending nag, e.g. unconfirmed time slots
     .top-bar-btn-ghost       back links and other ways out

   .btn-danger-action is the older name several pages already use for their
   delete button and stays an alias of the danger tier. The same tiers answer
   to .pep-btn / .pep-btn-primary / .pep-btn-danger / .pep-btn-ghost for an
   action that lives in a filter bar or a panel header rather than the page
   header — same look, honest name. */
.top-bar-btn,
.pep-btn {
    background: var(--pep-surface);
    color: var(--pep-text);
    border: 1px solid var(--pep-border);
    padding: 8px 14px;
    border-radius: 10px;
    cursor: pointer;
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.25;
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 7px;
}

.top-bar-btn i,
.pep-btn i {
    font-size: 12.5px;
    color: var(--pep-text-muted);
}

.top-bar-btn:hover,
.pep-btn:hover {
    background: var(--pep-surface-2);
    border-color: var(--pep-green);
    color: var(--pep-text);
}

.top-bar-btn:hover i,
.pep-btn:hover i {
    color: var(--pep-green);
}

.top-bar-btn:focus-visible,
.pep-btn:focus-visible {
    outline: 2px solid var(--pep-green);
    outline-offset: 2px;
}

.top-bar-btn.dropdown-toggle::after {
    opacity: 0.45;
    margin-left: 2px;
}

.top-bar-btn-primary,
.pep-btn-primary {
    background: var(--pep-green);
    border-color: var(--pep-green);
    color: #fff;
}

.top-bar-btn-primary i,
.pep-btn-primary i {
    color: rgba(255, 255, 255, 0.85);
}

.top-bar-btn-primary:hover,
.pep-btn-primary:hover {
    background: var(--pep-dark-green);
    border-color: var(--pep-dark-green);
    color: #fff;
}

.top-bar-btn-primary:hover i,
.pep-btn-primary:hover i {
    color: #fff;
}

.top-bar-btn-danger,
.pep-btn-danger,
.btn-danger-action {
    color: var(--pep-danger-fg);
}

.top-bar-btn-danger i,
.btn-danger-action i {
    color: var(--pep-danger-fg);
}

.top-bar-btn-danger:hover,
.btn-danger-action:hover {
    background: var(--pep-danger-bg);
    border-color: var(--pep-danger-fg);
    color: var(--pep-danger-fg);
}

.top-bar-btn-danger:hover i,
.btn-danger-action:hover i {
    color: var(--pep-danger-fg);
}

.top-bar-btn-attention {
    background: var(--pep-danger-bg);
    border-color: var(--pep-danger-bg);
    color: var(--pep-danger-fg);
}

.top-bar-btn-attention i,
.top-bar-btn-attention:hover i {
    color: var(--pep-danger-fg);
}

.top-bar-btn-attention:hover {
    background: var(--pep-danger-bg);
    border-color: var(--pep-danger-fg);
    color: var(--pep-danger-fg);
}

/* A pair or run of related toggles — collapse/expand, list/calendar. Joined
   into one control so they read as a single choice rather than two actions. */
.pep-btn-group {
    display: inline-flex;
}

.pep-btn-group .pep-btn {
    border-radius: 0;
    margin-left: -1px;
}

.pep-btn-group .pep-btn:first-child {
    border-radius: 10px 0 0 10px;
    margin-left: 0;
}

.pep-btn-group .pep-btn:last-child {
    border-radius: 0 10px 10px 0;
}

.pep-btn-group .pep-btn:hover,
.pep-btn-group .pep-btn.active {
    position: relative;
    z-index: 1;
}

.pep-btn.active {
    background: var(--pep-accent-soft);
    border-color: var(--pep-green);
    color: var(--pep-green);
}

.pep-btn.active i {
    color: var(--pep-green);
}

/* Search and other inputs that sit in a row of .pep-btns, matched in height
   and radius so the row reads as one set of controls. */
.pep-field {
    display: inline-flex;
    align-items: stretch;
    background: var(--pep-surface);
    border: 1px solid var(--pep-border);
    border-radius: 10px;
    overflow: hidden;
    min-width: 0;
}

.pep-field:focus-within {
    border-color: var(--pep-green);
}

.pep-field > .pep-field-icon {
    display: flex;
    align-items: center;
    padding-left: 12px;
    color: var(--pep-text-muted);
    font-size: 12.5px;
}

.pep-field > input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    color: var(--pep-text);
    padding: 8px 12px;
    font-size: 13.5px;
}

.pep-field > input:focus {
    outline: none;
}

.top-bar-btn-ghost,
.pep-btn-ghost {
    background: transparent;
    border-color: transparent;
    color: var(--pep-text-muted);
}

.top-bar-btn-ghost:hover,
.pep-btn-ghost:hover {
    background: var(--pep-surface-2);
    border-color: transparent;
    color: var(--pep-text);
}

/* Theme toggle (sun / moon) and the skin toggle (blossom) — injected into
   .top-bar-actions by common.js */
.theme-toggle-btn,
.skin-toggle-btn {
    background: var(--pep-surface-2);
    color: var(--pep-text-muted);
    border: 1px solid var(--pep-border);
    width: 40px;
    height: 40px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 16px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.theme-toggle-btn:hover,
.skin-toggle-btn:hover {
    color: var(--pep-green);
    border-color: var(--pep-green);
}

.theme-toggle-btn--floating {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 1050;
    box-shadow: 0 4px 12px var(--pep-shadow);
}

/* The drifting layer behind the page for the decorated skins (petals for
   Sakura, pumpkins and bats for Halloween), built by pep-skin-fall.js. Fixed,
   behind every panel and never hit-testable, so it moves through the page's
   margins and gaps rather than across the data. Each skin's stylesheet
   colours the shapes and sets --pep-skin-fall-opacity. */
.pep-skin-fall {
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    overflow: hidden;
    opacity: var(--pep-skin-fall-opacity, .85);
}

.pep-skin-fall__item {
    position: absolute;
    top: -40px;
    left: var(--x, 50%);
    width: var(--size, 14px);
    height: var(--size, 14px);
    animation: pep-skin-fall var(--duration, 16s) linear var(--delay, 0s) infinite;
    will-change: transform;
}

.pep-skin-fall__item > svg {
    display: block;
    width: 100%;
    height: 100%;
    animation: pep-skin-sway var(--sway-duration, 4s) ease-in-out var(--delay, 0s) infinite alternate;
}

/* Things with wings cross the screen instead of falling, and flap. */
.pep-skin-fall__item--fly {
    top: var(--y, 20%);
    left: -40px;
    animation-name: pep-skin-fly;
}

.pep-skin-fall__item--fly > svg {
    animation-name: pep-skin-flap;
    transform-origin: 50% 40%;
}

/* Balloons rise and sink in place, the basket swinging a little under them. */
.pep-skin-fall__item--float {
    top: var(--y, 40%);
    animation-name: pep-skin-float;
    animation-timing-function: ease-in-out;
    animation-direction: alternate;
}

.pep-skin-fall__item--float > svg {
    animation-name: pep-skin-basket-swing;
    transform-origin: 50% 0;
}

@keyframes pep-skin-float {
    from { transform: translate3d(0, calc(var(--bob, 20vh) / 2), 0); }
    to   { transform: translate3d(var(--drift, 0px), calc(var(--bob, 20vh) / -2), 0); }
}

@keyframes pep-skin-basket-swing {
    from { transform: rotate(-4deg); }
    to   { transform: rotate(4deg); }
}

@keyframes pep-skin-fall {
    from { transform: translate3d(0, 0, 0) rotate(0deg); }
    to   { transform: translate3d(var(--drift, 80px), calc(100vh + 80px), 0) rotate(var(--spin, 360deg)); }
}

@keyframes pep-skin-sway {
    from { transform: translateX(-14px) rotate(-18deg); }
    to   { transform: translateX(14px) rotate(18deg); }
}

@keyframes pep-skin-fly {
    0%   { transform: translate3d(0, 0, 0); }
    25%  { transform: translate3d(28vw, calc(var(--rise, 0px) * .5 - 24px), 0); }
    50%  { transform: translate3d(55vw, var(--rise, 0px), 0); }
    75%  { transform: translate3d(82vw, calc(var(--rise, 0px) * .5 + 24px), 0); }
    100% { transform: translate3d(calc(100vw + 80px), 0, 0); }
}

@keyframes pep-skin-flap {
    from { transform: scaleY(1); }
    to   { transform: scaleY(.45); }
}

@media (prefers-reduced-motion: reduce) {
    .pep-skin-fall { display: none; }
}

@media print {
    .pep-skin-fall { display: none; }
}

/* Hamburger Menu Button */
.hamburger-btn {
    display: none;
    background: none;
    border: none;
    color: var(--pep-green);
    font-size: 24px;
    cursor: pointer;
    padding: 8px;
    border-radius: 8px;
}

.hamburger-btn:hover {
    background: var(--pep-surface-2);
    color: var(--pep-dark-green);
}

.user-avatar {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    border-radius: 50%;
    overflow: hidden;
    background: linear-gradient(135deg, var(--pep-green), var(--pep-dark-green));
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: 700;
}

/* Profile picture, when the user has uploaded one. */
.user-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: inherit;
    display: block;
}

/* Name (and optional role) beside the avatar in a page header. */
.user-info {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}

.user-name {
    font-weight: 600;
    font-size: 14px;
    color: var(--pep-text);
}

.user-role {
    font-size: 12px;
    color: var(--pep-text-muted);
}

.user-profile {
    cursor: pointer;
}

.user-profile:hover .user-avatar {
    transform: scale(1.1);
    box-shadow: 0 4px 12px rgba(92, 175, 43, 0.3);
}

a.user-info:hover .user-avatar,
a:has(.user-avatar):hover .user-avatar {
    transform: scale(1.1);
    box-shadow: 0 4px 12px rgba(92, 175, 43, 0.3);
}

/* Content Area */
.content-area {
    padding: var(--pep-gutter-y) var(--pep-gutter);
    max-width: var(--pep-content-max);
    margin-inline: auto;
}

/* Full-bleed pages — maps, gantt, the planner: they use every pixel of a wide
   monitor rather than centring a column in it. */
.content-area--wide {
    max-width: none;
}

/* Filter Bar (shared across list pages) */
.filter-bar {
    display: flex;
    gap: 12px;
    margin-bottom: 20px;
    flex-wrap: wrap;
    align-items: center;
}

/* Controls share the row and wrap as it narrows. min-width:0 defeats an
   input's intrinsic minimum size, which is what makes them push a filter row
   wider than the screen. */
.filter-bar .form-control,
.filter-bar .form-select {
    flex: 1 1 180px;
    min-width: 0;
    max-width: 220px;
}

/* Table Card */
.table-card {
    background: var(--pep-surface);
    border-radius: 12px;
    padding: 25px;
    box-shadow: 0 2px 8px var(--pep-shadow);
}

.table {
    margin-bottom: 0;
}

.table thead th {
    background-color: var(--pep-surface-2);
    border-bottom: 2px solid var(--pep-border);
    font-weight: 600;
    text-transform: uppercase;
    font-size: 13px;
    color: var(--pep-text-muted);
    padding: 15px;
}

.table tbody td {
    padding: 15px;
    vertical-align: middle;
}

/* Buttons */
.btn-action {
    border: none;
    padding: 8px 12px;
    border-radius: 6px;
    cursor: pointer;
    margin-right: 5px;
}

.btn-edit {
    background: var(--pep-edit-bg);
    color: var(--pep-edit-fg);
}

.btn-edit:hover {
    background: var(--pep-edit-bg-hover);
}

.btn-delete {
    background: var(--pep-delete-bg);
    color: var(--pep-delete-fg);
}

.btn-delete:hover {
    background: var(--pep-delete-bg-hover);
}

.btn-warning-action {
    background: var(--pep-warning-bg);
    color: var(--pep-warning-fg);
}

.btn-warning-action:hover {
    background: var(--pep-warning-bg);
    color: var(--pep-warning-fg);
    filter: brightness(0.96);
}

/* Badges */
.stat-badge {
    background: var(--pep-info-bg);
    color: var(--pep-info-fg);
    padding: 4px 12px;
    border-radius: 12px;
    font-size: 12px;
    font-weight: 600;
}

.role-badge {
    background: var(--pep-role-bg);
    color: var(--pep-role-fg);
    padding: 4px 10px;
    border-radius: 12px;
    font-size: 11px;
    font-weight: 600;
    margin-right: 4px;
    display: inline-block;
}

.info-badge {
    background: var(--pep-info-bg);
    color: var(--pep-info-fg);
    padding: 6px 14px;
    border-radius: 12px;
    font-size: 13px;
    font-weight: 600;
    display: inline-block;
    margin-right: 8px;
    margin-bottom: 8px;
}

/* Forms */
.form-label {
    font-weight: 600;
    color: var(--pep-text);
    margin-bottom: 8px;
}

.form-control, .form-select {
    border: 2px solid var(--pep-border);
    padding: 10px;
    border-radius: 8px;
}

.form-control:focus, .form-select:focus {
    border-color: var(--pep-green);
    box-shadow: 0 0 0 3px rgba(92, 175, 43, 0.1);
}

/* Modals */
.modal-content {
    border-radius: 12px;
    border: none;
}

.modal-header {
    background: linear-gradient(135deg, var(--pep-green), var(--pep-dark-green));
    color: white;
    border-radius: 12px 12px 0 0;
    padding: 20px;
}

.modal-title {
    font-weight: 700;
}

.btn-close {
    filter: brightness(0) invert(1);
}

/* Column Filter Dropdown */
.column-filter-dropdown {
    position: relative;
}

.column-filter-btn {
    background: var(--pep-surface);
    color: var(--pep-text);
    border: 2px solid var(--pep-border);
    padding: 10px 20px;
    border-radius: 8px;
    cursor: pointer;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 8px;
}

.column-filter-btn:hover {
    border-color: var(--pep-green);
    color: var(--pep-green);
}

.column-filter-menu {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    background: var(--pep-surface);
    border: 1px solid var(--pep-border);
    border-radius: 8px;
    box-shadow: 0 4px 12px var(--pep-shadow);
    padding: 12px;
    z-index: 1000;
    min-width: 200px;
    margin-top: 5px;
}

.column-filter-menu.show {
    display: block;
}

.column-filter-item {
    padding: 8px;
    cursor: pointer;
    display: flex;
    align-items: center;
    border-radius: 4px;
}

.column-filter-item:hover {
    background: var(--pep-surface-2);
}

.column-filter-item input[type="checkbox"] {
    margin-right: 8px;
    cursor: pointer;
}

.column-filter-item label {
    cursor: pointer;
    margin: 0;
    user-select: none;
}

/* Column Drag and Drop Styles */
th[data-column] {
    cursor: move;
    user-select: none;
    position: relative;
}

th[data-column]:hover::after {
    content: '⋮⋮';
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--pep-text-muted);
    font-size: 12px;
}

th.dragging {
    opacity: 0.5;
    background: var(--pep-edit-bg);
}

th.drag-over {
    border-left: 3px solid var(--pep-green);
}
/* ==========================================
   RESPONSIVE LAYER

   Ranges, not devices. Everything above is fluid; these queries carry only the
   changes a range genuinely needs. The edges are Bootstrap's own breakpoints so
   the utility grid and this file never disagree:
     <= 575.98px  phones
     <= 767.98px  phones + small tablets
     <= 991.98px  every width the sidebar is off-canvas for
     >= 1600px    large desktop / ultrawide / 4K
   ========================================== */

/* --- True at every size ------------------------------------------------- */

/* Media never widens its container. */
img,
video,
svg {
    max-width: 100%;
}

/* Long unbroken strings (URLs, IBANs, reference numbers) wrap instead of
   stretching the page. */
body {
    overflow-wrap: break-word;
}

/* A table may be wider than the screen — inside its own scroller, never by
   pushing the page sideways. */
.table-responsive {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Dropdowns and menus stay inside the viewport on a phone. */
.dropdown-menu {
    max-width: calc(100vw - 1rem);
}

/* Modals scroll their own body rather than running off the bottom of the
   screen. This matters as much on a short laptop (1366x768) and a landscape
   phone as it does on mobile. */
.modal-content {
    max-height: calc(100vh - 2rem);
    max-height: calc(100dvh - 2rem);
}

.modal-body {
    overflow-y: auto;
}

/* A few modals (the business card, for one) have no .modal-body at all, so the
   cap above would clip them with nothing to scroll. Those scroll themselves. */
.modal-content:not(:has(.modal-body)) {
    overflow-y: auto;
}

/* Hand-rolled overlay modals (leave requests, calendar events, HR approvals)
   are not Bootstrap .modal, so they need the same guarantees stated here
   rather than page by page: centred, never wider than the screen, and
   scrolling internally instead of running off the bottom of a short one. */
.modal-overlay,
.cal-modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1050;
    background: rgba(0, 0, 0, 0.4);
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.modal-overlay.active,
.cal-modal-overlay.active {
    display: flex;
}

.modal-box,
.cal-modal {
    max-height: calc(100vh - 2rem);
    max-height: calc(100dvh - 2rem);
    overflow-y: auto;
}

/* Page links wrap onto a second line instead of pushing the list card wider
   than the screen. */
.pagination {
    flex-wrap: wrap;
}

/* The column picker is a plain absolutely-positioned menu, so it needs its own
   promise that it fits: never past the right edge, never taller than the
   screen (a 480px-tall landscape phone showed a 603px menu with no scroll). */
.column-filter-menu {
    max-width: calc(100vw - 2rem);
    max-height: min(60vh, 420px);
    overflow-y: auto;
}

/* --- Off-canvas navigation range (below lg) ----------------------------- */
@media (max-width: 991.98px) {
    .hamburger-btn {
        display: block;
    }
}

/* On a touchscreen the one control that opens navigation has to be a real
   target, not a 24px glyph. */
@media (pointer: coarse) {
    .hamburger-btn {
        min-width: 44px;
        min-height: 44px;
    }
}

/* --- Phones and small tablets -------------------------------------------- */
@media (max-width: 767.98px) {
    .table-card {
        padding: 16px;
        border-radius: 8px;
    }

    /* Keep cells readable and let the wrapper scroll, rather than crushing
       columns until the text is unreadable. */
    .table {
        min-width: 500px;
    }

    .table thead th {
        padding: 10px;
        font-size: 11px;
    }

    .table tbody td {
        padding: 10px;
        font-size: 13px;
    }

    .top-bar-btn {
        padding: 8px 12px;
        font-size: 13px;
    }

    .btn-action {
        padding: 6px 8px;
        margin-right: 3px;
        font-size: 12px;
    }

    .modal-dialog {
        margin: 0.5rem;
        max-width: calc(100vw - 1rem);
    }

    .modal-dialog.modal-xl,
    .modal-dialog.modal-lg {
        max-width: calc(100vw - 1rem);
    }

    .modal-body {
        padding: 1rem;
    }

    .form-label {
        font-size: 13px;
        margin-bottom: 6px;
    }

    /* 16px is deliberate: iOS Safari zooms the whole page when a focused input
       is smaller than that, which breaks the layout the moment you type. */
    .form-control, .form-select {
        padding: 8px;
        font-size: 16px;
    }

    .stat-badge, .role-badge, .info-badge {
        font-size: 10px;
        padding: 3px 8px;
    }

    /* Anchored to a button that can sit anywhere in a wrapped filter row, this
       menu had no way to stay on screen — pinned to the left it ran off the
       right, pinned to the right it ran off the left. On a phone it becomes a
       sheet spanning the viewport instead, which always fits. */
    .column-filter-menu {
        position: fixed;
        left: 1rem;
        right: 1rem;
        top: auto;
        bottom: 1rem;
        margin: 0;
        width: auto;
        max-width: none;
        max-height: 50vh;
        overflow-y: auto;
    }
}

/* --- Small phones --------------------------------------------------------- */
@media (max-width: 575.98px) {
    /* The header keeps the hamburger and the title together on the first row
       and wraps only the actions underneath. (It used to become one centred
       column, which read as a broken page header on every screen.) */
    .top-bar-actions {
        width: 100%;
        justify-content: flex-start;
        gap: 6px;
    }

    .table-card {
        padding: 12px;
    }

    .table {
        min-width: 420px;
        font-size: 12px;
    }

    .table thead th {
        padding: 8px;
        font-size: 10px;
    }

    .table tbody td {
        padding: 8px;
        font-size: 12px;
    }

    .top-bar-btn {
        padding: 6px 10px;
        font-size: 12px;
        gap: 4px;
    }

    .btn-action {
        padding: 4px 6px;
        font-size: 10px;
    }

    .modal-footer {
        flex-direction: column;
    }

    .modal-footer .btn {
        width: 100%;
        margin-bottom: 8px;
    }

    /* One filter per row once two no longer fit. */
    .filter-bar .form-control,
    .filter-bar .form-select {
        flex-basis: 100%;
        max-width: none;
    }

    .nav-tabs {
        border-bottom: 2px solid var(--pep-border);
    }

    .nav-tabs .nav-link {
        padding: 8px 12px;
        font-size: 12px;
    }

    .tab-content {
        padding: 12px 0;
    }

    .row > [class*='col-'] {
        margin-bottom: 10px;
    }

    /* Timestamps are the first columns to go when the screen is this narrow. */
    .table thead th[data-column="created_at"],
    .table tbody td[data-column="created_at"],
    .table thead th[data-column="updated_at"],
    .table tbody td[data-column="updated_at"] {
        display: none;
    }
}

/* --- Short viewports: landscape phones, 1366x768-class laptops ----------- */
@media (max-height: 560px) {
    /* Vertical space is the scarce resource here, so the chrome gives some
       back: on a 480px-tall landscape phone the header and filter row used to
       take half the screen before a single row of data appeared. */
    .top-bar {
        padding-block: 8px;
    }

    .top-bar h1 {
        font-size: 17px;
    }

    .top-bar-btn {
        padding: 6px 12px;
    }

    .content-area {
        padding-block: 10px;
    }

    .filter-bar {
        margin-bottom: 12px;
    }

    .table thead th,
    .table tbody td {
        padding-block: 8px;
    }

    .sidebar-header {
        padding: 10px 8px;
    }

    .logo-container {
        height: 34px;
    }

    .menu-item {
        padding: 8px 14px;
    }

    .menu-section {
        padding: 0 12px 10px;
    }
}

/* --- Large desktop, ultrawide, 4K ---------------------------------------- */
@media (min-width: 1600px) {
    .content-area {
        padding: clamp(24px, 1.8vw, 40px) clamp(30px, 3vw, 56px);
    }
}

/* ── Amount / Price cell styling ── */
.amount-cell {
    font-weight: 700;
    font-size: 14px;
    text-align: right !important;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.amount-positive { color: var(--pep-amount-positive); }
.amount-negative { color: var(--pep-amount-negative); }
.amount-neutral  { color: var(--pep-text); }

.price-cell {
    font-weight: 600;
    color: var(--pep-text);
    text-align: right !important;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.cost-cell {
    font-weight: 500;
    color: var(--pep-text-muted);
    text-align: right !important;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
/* ==========================================
   MOTION LAYER
   ------------------------------------------
   Page transitions and micro-interactions, in one place.

   The numbers are deliberately small. These pages are measurably fast — a
   median navigation here is ~50ms of server time and ~110ms to DOMContentLoaded
   — so a transition longer than the page load would read as lag, not polish.
   Nothing in here delays a request: the navigation starts the moment the link
   is clicked and the browser plays the transition around the work, never
   in front of it.

   Everything animates `opacity` and `transform` (compositor properties) apart
   from the two shell widths, which are called out where they appear.
   ========================================== */

:root {
    /* Quick to leave, gentle to land. */
    --pep-ease: cubic-bezier(0.2, 0, 0.2, 1);

    --pep-motion-fast: 120ms;   /* menus, hovers, pressed states */
    --pep-motion-base: 160ms;   /* modals, panels, toasts */
    --pep-motion-shell: 200ms;  /* the sidebar opening / collapsing */
    --pep-motion-out: 90ms;     /* the outgoing page */
    --pep-motion-in: 150ms;     /* the incoming page */
}

/* ── Page transitions ──────────────────────────────────────────────────────
   The whole page-transition implementation: no router, no JavaScript, no
   dependency. The browser keeps the outgoing document painted while the next
   one loads, then cross-fades the two. Browsers without support ignore the
   at-rule and navigate exactly as they did before — the app does not depend
   on any of this working.

   MEASURED COST, and the reason this is one line you can delete: opting in
   defers the incoming document's first contentful paint by roughly 100-130ms
   (measured over three interleaved A/B passes, 8 navigations each; see
   .resp-audit/nav-perf.mjs). About two thirds of that is the browser's own
   snapshot-and-swap machinery and does not respond to the durations below —
   shortening them from 180ms to 120ms moved the total by less than the
   run-to-run variance.

   The screen is never blank for any of it: the old page stays fully painted
   until the new one is ready, where without the transition the swap is
   abrupt. But these pages reach first paint in ~100ms on their own, so this
   is real, and it is a judgement call rather than a free win. Deleting this
   at-rule reverts every page to the previous behaviour and costs nothing else
   in this file — the micro-interactions below are independent of it. */
@view-transition {
    navigation: auto;
}

/* The shell holds still while the content changes. Naming an element lifts it
   out of the root snapshot, so it is matched old-to-new and animated on its
   own terms instead of cross-fading along with the page body. Each name must
   be unique per document — these four elements are rendered once by the
   layout, never by a page. */
.sidebar { view-transition-name: pep-sidebar; }
.pep-chat-widget { view-transition-name: pep-chat-widget; }
.pep-call-widget { view-transition-name: pep-call-widget; }
.pep-nav-progress { view-transition-name: pep-nav-progress; }

/* Content: the outgoing page fades while the incoming one fades in and rises
   8px. The rise is a fixed distance, not a proportion of the viewport, so it
   is just as restrained on a 3440px monitor as on a 360px phone.

   The two overlap deliberately. An earlier version held the incoming page back
   by 30ms, which measured as 30ms of deferred first paint spent showing
   nothing but the old page on its way out — latency bought with no visible
   return. They now start together. */
::view-transition-old(root) {
    animation: pep-page-out var(--pep-motion-out) var(--pep-ease) both;
}

::view-transition-new(root) {
    animation: pep-page-in var(--pep-motion-in) var(--pep-ease) both;
}

/* The UA cross-fade uses mix-blend-mode: plus-lighter, which assumes the two
   snapshots sit exactly on top of each other. Ours are offset by 8px, and
   plus-lighter would brighten the seam between them. */
::view-transition-old(root),
::view-transition-new(root) {
    mix-blend-mode: normal;
}

@keyframes pep-page-out {
    to { opacity: 0; }
}

@keyframes pep-page-in {
    from { opacity: 0; transform: translateY(8px); }
}

/* The sidebar keeps its position and size; only its contents cross-fade, which
   is what carries the selected item from the old page to the new one. Short
   enough to read as the highlight moving rather than as the panel redrawing. */
::view-transition-group(pep-sidebar) {
    animation-duration: var(--pep-motion-fast);
    animation-timing-function: var(--pep-ease);
}

::view-transition-old(pep-sidebar) {
    animation: pep-fade-out var(--pep-motion-fast) var(--pep-ease) both;
}

::view-transition-new(pep-sidebar) {
    animation: pep-fade-in var(--pep-motion-fast) var(--pep-ease) both;
}

/* The floating chat and call widgets are identical on both pages, so any
   fade at all would read as a blink. They simply stay put. (When one exists
   on only one side of a navigation — the guest layout has neither — this
   leaves it appearing or disappearing instantly, which is correct: a call
   panel has no business fading across a login.) */
::view-transition-old(pep-chat-widget),
::view-transition-old(pep-call-widget) {
    animation: none;
    opacity: 0;
}

::view-transition-new(pep-chat-widget),
::view-transition-new(pep-call-widget) {
    animation: none;
    opacity: 1;
}

/* The progress bar belongs to the page that is leaving. */
::view-transition-old(pep-nav-progress) {
    animation: pep-fade-out var(--pep-motion-fast) var(--pep-ease) both;
}

::view-transition-new(pep-nav-progress) {
    animation: none;
    opacity: 1;
}

@keyframes pep-fade-out {
    to { opacity: 0; }
}

@keyframes pep-fade-in {
    from { opacity: 0; }
}

/* ── Navigation progress ───────────────────────────────────────────────────
   A hairline at the top of the window, shown by pep-nav.js only once a
   navigation has already taken longer than a fast page does (see the delay
   in that file). A navigation that completes quickly never reveals it, so
   the common case shows no loading indicator at all.

   The bar is driven by scaleX on a single element: no layout, no timers
   redrawing width, and it composites on its own layer. */
.pep-nav-progress {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 3px;
    z-index: 2000;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--pep-motion-fast) var(--pep-ease);
}

.pep-nav-progress::after {
    content: '';
    display: block;
    height: 100%;
    background: linear-gradient(90deg, var(--pep-green), var(--pep-light-green));
    box-shadow: 0 0 8px rgba(92, 175, 43, 0.5);
    transform: scaleX(var(--pep-nav-progress, 0));
    transform-origin: left center;
    transition: transform 220ms var(--pep-ease);
}

.pep-nav-progress.is-active {
    opacity: 1;
}

/* ── Micro-interactions ────────────────────────────────────────────────────
   These replace `transition: all`, which lets the browser animate layout
   properties (width, padding, margin) that happen to change alongside a
   colour, and which costs a style recalculation on every property. Naming the
   properties keeps each of these on the compositor. */
.top-bar-btn,
.pep-btn {
    transition: background-color var(--pep-motion-fast) var(--pep-ease),
                border-color var(--pep-motion-fast) var(--pep-ease),
                color var(--pep-motion-fast) var(--pep-ease),
                box-shadow var(--pep-motion-fast) var(--pep-ease),
                transform var(--pep-motion-fast) var(--pep-ease);
}

.top-bar-btn-primary:hover,
.pep-btn-primary:hover {
    box-shadow: 0 2px 10px rgba(92, 175, 43, 0.3);
}

.theme-toggle-btn,
.skin-toggle-btn {
    transition: background-color var(--pep-motion-fast) var(--pep-ease),
                color var(--pep-motion-fast) var(--pep-ease),
                border-color var(--pep-motion-fast) var(--pep-ease),
                transform var(--pep-motion-fast) var(--pep-ease);
}

.hamburger-btn {
    transition: background-color var(--pep-motion-fast) var(--pep-ease),
                color var(--pep-motion-fast) var(--pep-ease);
}

.user-avatar {
    transition: transform var(--pep-motion-base) var(--pep-ease),
                box-shadow var(--pep-motion-base) var(--pep-ease);
}

.user-profile {
    transition: transform var(--pep-motion-fast) var(--pep-ease);
}

.btn-action,
.column-filter-btn {
    transition: background-color var(--pep-motion-fast) var(--pep-ease),
                color var(--pep-motion-fast) var(--pep-ease),
                border-color var(--pep-motion-fast) var(--pep-ease),
                transform var(--pep-motion-fast) var(--pep-ease);
}

.column-filter-item {
    transition: background-color var(--pep-motion-fast) var(--pep-ease);
}

/* A button that is being pressed should say so — this is the difference
   between a page that responds and a page that merely eventually reacts. */
.top-bar-btn:active,
.pep-btn:active,
.btn-action:active,
.column-filter-btn:active,
.theme-toggle-btn:active,
.skin-toggle-btn:active,
.hamburger-btn:active {
    transform: translateY(1px);
}

/* An action that has been fired and is waiting on the server. Pages set this
   on the button they just disabled; it reads as "working", not "broken". */
.top-bar-btn:disabled,
.btn-action:disabled,
.btn:disabled {
    transition: opacity var(--pep-motion-fast) var(--pep-ease);
}

/* ── Modals ────────────────────────────────────────────────────────────────
   Bootstrap's default is a 300ms 50px drop. Replaced with a shorter settle
   that scales rather than travels, so a modal reads as arriving in place. */
.modal.fade .modal-dialog {
    opacity: 0;
    transform: scale(0.98);
    transition: transform var(--pep-motion-base) var(--pep-ease),
                opacity var(--pep-motion-base) var(--pep-ease);
}

.modal.show .modal-dialog {
    opacity: 1;
    transform: scale(1);
}

/* The hand-rolled overlays (leave requests, calendar events, HR approvals) are
   toggled display:none -> flex, so they get keyframes: an element starts its
   animations when it becomes displayed, which a transition cannot do. */
.modal-overlay.active,
.cal-modal-overlay.active {
    animation: pep-fade-in var(--pep-motion-fast) var(--pep-ease) both;
}

.modal-overlay.active .modal-box,
.cal-modal-overlay.active .cal-modal {
    animation: pep-pop-in var(--pep-motion-base) var(--pep-ease) both;
}

@keyframes pep-pop-in {
    from { opacity: 0; transform: scale(0.98); }
}

/* ── Dropdowns and menus ───────────────────────────────────────────────────
   Bootstrap's dropdowns are positioned by Popper, which writes `transform`
   inline; a keyframe touching transform outranks that inline style and would
   throw the menu into the corner. So these fade only. The app's own menus,
   which are positioned with plain CSS, get the small rise as well. */
.dropdown-menu.show {
    animation: pep-fade-in var(--pep-motion-fast) var(--pep-ease) both;
}

.column-filter-menu.show,
.profile-dropdown {
    animation: pep-menu-in var(--pep-motion-fast) var(--pep-ease) both;
}

@keyframes pep-menu-in {
    from { opacity: 0; transform: translateY(-4px); }
}

/* ── Loading feedback ──────────────────────────────────────────────────────
   The list pages drop a spinner over the table while a filter or a page change
   is in flight. Most of those come back in well under 200ms, and the spinner
   was appearing and vanishing as a flash. Holding it invisible for the first
   60% of a 260ms animation means a fast query never shows a spinner at all,
   and a slow one still gets its feedback. */
.spinner-overlay.active {
    animation: pep-delayed-in 260ms var(--pep-ease) both;
}

@keyframes pep-delayed-in {
    0%, 60% { opacity: 0; }
    100% { opacity: 1; }
}

/* ── Reduced motion ────────────────────────────────────────────────────────
   Someone who has asked for less motion gets less motion — never a slower
   app. Dropping the animations from the view-transition pseudo-elements makes
   the transition finish on the very next frame, so navigation ends up faster
   here than in the default case, not slower.

   Scoped on purpose rather than a blanket `* { animation: none }`: that would
   also stop the spinners (fa-spin), the Leaflet tile fades and the chart
   renders, which are information, not decoration. */
@media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*),
    ::view-transition-old(*),
    ::view-transition-new(*) {
        animation: none !important;
    }

    .modal.fade .modal-dialog,
    .modal.show .modal-dialog {
        transition: none;
        transform: none;
        opacity: 1;
    }

    .modal-overlay.active,
    .cal-modal-overlay.active,
    .modal-overlay.active .modal-box,
    .cal-modal-overlay.active .cal-modal,
    .dropdown-menu.show,
    .column-filter-menu.show,
    .profile-dropdown,
    .spinner-overlay.active {
        animation: none;
    }

    .top-bar-btn:active,
    .btn-action:active,
    .column-filter-btn:active,
    .theme-toggle-btn:active,
    .skin-toggle-btn:active,
    .hamburger-btn:active,
    .user-profile:hover .user-avatar,
    a.user-info:hover .user-avatar,
    a:has(.user-avatar):hover .user-avatar {
        transform: none;
    }

    .pep-nav-progress::after {
        transition: none;
    }

    /* The shell still moves — an off-canvas panel that teleports is harder to
       follow, not easier — but it gets there in half the time. */
    .main-content,
    .sidebar,
    .sidebar-collapse-btn {
        transition-duration: 100ms;
    }
}

/* ---------------------------------------------------------------------------
   Session change history

   The trail a session's edit indicator opens, shared by the Work Sessions,
   My Work Sessions and User Sessions pages.
   --------------------------------------------------------------------------- */
.history-list { list-style: none; margin: 0; padding: 0; }

.history-entry {
    padding: 10px 0;
    border-top: 1px solid var(--pep-border);
    font-size: 13px;
}

.history-entry:first-child { border-top: none; }
.history-when { font-size: 11px; color: var(--pep-text-muted); letter-spacing: .02em; }
.history-change { font-variant-numeric: tabular-nums; margin-top: 2px; }
.history-was { color: var(--pep-text-muted); text-decoration: line-through; }
.history-change .fa-arrow-right { color: var(--pep-text-muted); font-size: 10px; }
.history-who { font-size: 12px; margin-top: 2px; }
.history-role { color: var(--pep-text-muted); }
.history-reason { font-size: 12px; color: var(--pep-text-muted); margin-top: 3px; font-style: italic; }

.history-tag {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    background: var(--pep-surface-2);
    color: var(--pep-text-muted);
    vertical-align: middle;
}

/* The indicator itself: a row whose clock has been moved says so, quietly,
   and opens the trail that says by whom. */
.edited-flag {
    border: none;
    background: none;
    cursor: pointer;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 700;
    color: var(--pep-warning-fg);
    background: var(--pep-warning-bg);
    white-space: nowrap;
}

.edited-flag:hover { filter: brightness(1.08); }
.edited-flag i { font-size: 9.5px; margin-right: 3px; }

/* ---------------------------------------------------------------------------
   Row action buttons

   The small icon buttons that sit at the end of a session row — edit, force
   stop, withdraw. They lived in work-sessions/index.blade.php until the
   correction modals became a shared partial that draws them too, at which
   point a page that included the partial without that page's <style> got
   unstyled buttons.
   --------------------------------------------------------------------------- */
.admin-action-btn {
    border: none;
    background: none;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 6px;
    font-size: 12px;
    color: var(--pep-text-muted);
    transition: background var(--pep-motion-fast) var(--pep-ease), color var(--pep-motion-fast) var(--pep-ease);
}

.admin-action-btn:hover {
    background: var(--pep-surface-2);
    color: var(--pep-text);
}

.admin-action-btn.force-stop { color: #dc2626; }
.admin-action-btn.edit { color: #3b82f6; }

/* ---------------------------------------------------------------------------
   Clock status bar

   Whether you are on the clock, told as a dye dropped into the page header:
   the colour bleeds down from the top edge of .top-bar and drifts, the way ink
   spreads in still water. It is painted as background-image layers on the bar
   itself rather than an overlay, so nothing is laid over the header's buttons
   and the dropdowns inside it keep their own positioning.

   Green is on the clock, amber a personal break, red a client delay or being
   off the clock entirely. The two reds are separated by movement: a client
   delay is still running, so its dye keeps swirling, while off the clock is a
   flat still wash. Colour is never the whole message — #pepClockBar is a live
   region carrying the same state in words for screen readers.
--------------------------------------------------------------------------- */

/* The announcer. Nothing to see: the colour is the top bar itself. */
.pep-clock-bar {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* Each state is only a pair of colours; the dye itself is shared below. */
body[data-clock-state='working'] {
    --pep-dye: rgba(109, 194, 58, 0.62);
    --pep-dye-soft: rgba(109, 194, 58, 0.16);
}

body[data-clock-state='paused'] {
    --pep-dye: rgba(224, 163, 46, 0.62);
    --pep-dye-soft: rgba(224, 163, 46, 0.16);
}

body[data-clock-state='blocked'],
body[data-clock-state='off'] {
    --pep-dye: rgba(192, 57, 43, 0.62);
    --pep-dye-soft: rgba(192, 57, 43, 0.16);
}

/* Narrow plumes hanging from the top edge, each reaching a different depth,
   with clear water between them — an even band across the whole width would
   read as a coloured header rather than as something dropped into it. */
body[data-clock-state] .top-bar {
    background-image:
        radial-gradient(ellipse 54% 150% at 50% -22%, var(--pep-dye) 0%, var(--pep-dye-soft) 46%, transparent 76%),
        radial-gradient(ellipse 58% 130% at 50% -12%, var(--pep-dye) 0%, var(--pep-dye-soft) 40%, transparent 72%),
        radial-gradient(ellipse 50% 165% at 50% -30%, var(--pep-dye) 0%, var(--pep-dye-soft) 44%, transparent 78%),
        linear-gradient(180deg, var(--pep-dye-soft) 0%, transparent 70%);
    background-repeat: no-repeat;
    background-size: 50% 100%, 32% 66%, 62% 90%, 100% 100%;
    background-position: 2% 0, 45% 0, 84% 0, 0 0;
    animation: pep-dye-drift 26s ease-in-out infinite alternate;
}

/* Off the clock: the same red, but nothing moving in it. */
body[data-clock-state='off'] .top-bar {
    animation: none;
}

/* A client delay is time being lost while you wait — the dye is unsettled. */
body[data-clock-state='blocked'] .top-bar {
    animation-duration: 9s;
}

/* Each layer wanders at its own pace and swells, which is what reads as
   diffusion rather than a background sliding sideways. */
@keyframes pep-dye-drift {
    0% {
        background-size: 50% 100%, 32% 66%, 62% 90%, 100% 100%;
        background-position: 2% 0, 45% 0, 84% 0, 0 0;
    }

    50% {
        background-size: 64% 84%, 42% 100%, 48% 100%, 100% 100%;
        background-position: 14% 0, 57% 0, 94% 0, 0 0;
    }

    100% {
        background-size: 56% 100%, 38% 76%, 70% 72%, 100% 100%;
        background-position: -2% 0, 36% 0, 100% 0, 0 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    body[data-clock-state] .top-bar { animation: none; }
}

/* ============================================================
   Deep-link highlight
   ------------------------------------------------------------
   The row a notification pointed at. It pulses twice and then keeps a
   wash: someone arriving from a desktop alert may not be looking at the
   screen during the animation, and the wash is what tells them which
   row the alert meant when they do look.

   Painted on the cells, not the row: a <tr> is not a reliable box for
   background or box-shadow across browsers, and a highlight that only
   renders in some of them is worse than none.
   ============================================================ */
.pep-highlighted,
.pep-highlighted > td {
    /* Bootstrap paints table cells through an inset box-shadow the width of
       the cell (--bs-table-bg-state and friends), which sits on top of any
       background-color we set — so the tint has to be handed to Bootstrap's
       own variables as well as declared outright. */
    --bs-table-bg: rgba(92, 175, 43, .14);
    --bs-table-accent-bg: rgba(92, 175, 43, .14);
    --bs-table-bg-state: rgba(92, 175, 43, .14);
    background-color: rgba(92, 175, 43, .14) !important;
    animation: pep-highlight-pulse 1.6s ease-out 2;
}

.pep-highlighted > td:first-child,
div.pep-highlighted {
    border-left: 3px solid var(--pep-green);
}

@keyframes pep-highlight-pulse {
    0%, 100% { background-color: rgba(92, 175, 43, .14); }
    50%      { background-color: rgba(92, 175, 43, .38); }
}

@media (prefers-reduced-motion: reduce) {
    .pep-highlighted,
    .pep-highlighted > td { animation: none; }
}

/* ============================================================
   Searchable select (pep-search-select.js)
   ------------------------------------------------------------
   A <select> that asks the server as you type. The native element
   stays in the DOM as the value — see the script — so everything
   here dresses the input that sits in front of it.
   ============================================================ */
.pep-ss {
    position: relative;
}

.pep-ss-native {
    /* Present for form serialisation and the change event, never seen.
       Not display:none — a hidden-but-laid-out element keeps working with
       code that measures or focuses it. */
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.pep-ss-input {
    /* Room for the clear button and the caret. */
    padding-right: 30px;
}

.pep-ss-clear {
    position: absolute;
    top: 50%;
    right: 8px;
    transform: translateY(-50%);
    display: none;
    padding: 2px 4px;
    line-height: 1;
    font-size: 12px;
    color: var(--pep-text-muted);
    background: none;
    border: 0;
    cursor: pointer;
}

.pep-ss.has-value .pep-ss-clear {
    display: block;
}

.pep-ss-clear:hover {
    color: var(--pep-text);
}

.pep-ss-menu {
    display: none;
    position: absolute;
    z-index: 1060; /* above a Bootstrap modal's own content */
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    max-height: 280px;
    overflow-y: auto;
    background: var(--pep-surface);
    border: 1px solid var(--pep-border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}

.pep-ss.open .pep-ss-menu {
    display: block;
}

.pep-ss-option {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 7px 12px;
    font-size: 13px;
    color: var(--pep-text);
    cursor: pointer;
}

.pep-ss-option:hover,
.pep-ss-option.active {
    background: rgba(92, 175, 43, 0.12);
}

.pep-ss-blank {
    color: var(--pep-text-muted);
    font-style: italic;
    border-bottom: 1px solid var(--pep-border);
}

.pep-ss-sub {
    font-size: 11px;
    color: var(--pep-text-muted);
}

.pep-ss-note {
    padding: 10px 12px;
    font-size: 12px;
    color: var(--pep-text-muted);
    text-align: center;
}

.pep-ss-error {
    color: #dc2626;
}

/* "There are 1,982 more behind this search" — the fixed-size list this
   replaced gave no such hint, which is how three quarters of the customer
   base went missing without anybody being told. */
.pep-ss-more {
    border-top: 1px solid var(--pep-border);
}

/* ============================================================
   Rich text field (pep-rich-text.js)
   ------------------------------------------------------------
   A <textarea> with a small formatting toolbar. The textarea
   stays in the DOM as the value — see the script — so this
   dresses the editable box in front of it.
   ============================================================ */
.pep-rt {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.pep-rt-native {
    /* Present for serialisation, never seen. Not display:none — a
       hidden-but-laid-out element keeps working with code that
       measures or focuses it. */
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.pep-rt-toolbar {
    display: flex;
    align-items: center;
    gap: 2px;
    flex-wrap: wrap;
    padding: 4px 6px;
    border: 1px solid var(--pep-border);
    border-bottom: 0;
    border-radius: 6px 6px 0 0;
    background: var(--pep-surface-2);
}

.pep-rt-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    font-size: 12px;
    color: var(--pep-text-muted);
    background: none;
    border: 0;
    border-radius: 4px;
    cursor: pointer;
}

.pep-rt-btn:hover {
    color: var(--pep-text);
    background: var(--pep-border);
}

.pep-rt-swatches {
    position: relative;
    display: inline-flex;
}

.pep-rt-swatch-menu {
    display: none;
    position: absolute;
    z-index: 1061; /* above a modal, like the searchable select's menu */
    top: calc(100% + 4px);
    left: 0;
    gap: 4px;
    padding: 6px;
    background: var(--pep-surface);
    border: 1px solid var(--pep-border);
    border-radius: 6px;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
}

.pep-rt-swatches.open .pep-rt-swatch-menu {
    display: flex;
}

.pep-rt-swatch {
    width: 18px;
    height: 18px;
    padding: 0;
    border: 1px solid var(--pep-border);
    border-radius: 4px;
    cursor: pointer;
}

.pep-rt-editor {
    flex: 1 1 auto;
    min-height: 72px;
    max-height: 40vh;
    overflow-y: auto;
    border-radius: 0 0 6px 6px;
    /* Descriptions were plain text for years and are full of newlines. */
    white-space: pre-wrap;
    word-break: break-word;
}

.pep-rt-editor:focus {
    outline: none;
    border-color: var(--pep-green);
    box-shadow: 0 0 0 0.2rem rgba(92, 175, 43, 0.2);
}

.pep-rt-editor:empty::before {
    content: attr(data-placeholder);
    color: var(--pep-text-muted);
}

/* "None" — how a colour or a highlight comes back off. Drawn as an empty
   chip with a slash through it, because a swatch with no colour in it is
   otherwise indistinguishable from white. */
.pep-rt-swatch-none {
    position: relative;
    background: var(--pep-surface);
}

.pep-rt-swatch-none::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        to top right,
        transparent calc(50% - 1px),
        #d03b3b calc(50% - 1px),
        #d03b3b calc(50% + 1px),
        transparent calc(50% + 1px)
    );
}

/* Read-only rendering of the same content, wherever it is shown back. */
.pep-rt-content {
    white-space: pre-wrap;
    word-break: break-word;
}

/* Agent skills: the qualification badge (VEFK / EL / EUP, or whatever
   administrators add) and skill chips, shared by the skills page, the
   planning board and the planning dialog. Qualifications are an editable list,
   so the badge has one look rather than a colour per name. */
.pep-qual-badge {
    display: inline-block;
    padding: 1px 7px;
    border-radius: 6px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .02em;
    line-height: 1.6;
    vertical-align: middle;
    background: var(--pep-info-bg);
    color: var(--pep-info-fg);
}

.pep-skill-chip,
.pep-skill-missing {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.7;
    margin: 0 4px 4px 0;
    background: var(--pep-surface-2);
    color: var(--pep-text);
}
.pep-skill-missing {
    background: var(--pep-danger-bg);
    color: var(--pep-danger-fg);
}
.pep-skill-needs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px 0;
    font-size: 12px;
    color: var(--pep-text-muted);
}
.pep-skill-needs > span:first-child { margin-right: 6px; }

/* The unsigned-manual reminder (components/manual-reminder): a bar above every
   page's top bar that cannot be closed — only signing the manual removes it. */
.pep-manual-nag {
    display: flex;
    align-items: center;
    gap: 10px 14px;
    flex-wrap: wrap;
    padding: 10px var(--pep-gutter);
    background: #e0a800;
    color: #1d1d1d;
    font-weight: 700;
    font-size: 14px;
}
.pep-manual-nag > i { font-size: 18px; }
.pep-manual-nag > span { flex: 1 1 220px; }
.pep-manual-nag-btn {
    background: #1d1d1d;
    color: #fff;
    border-radius: 8px;
    padding: 6px 14px;
    text-decoration: none;
    white-space: nowrap;
}
.pep-manual-nag-btn:hover { color: #fff; background: #000; }
.pep-manual-nag.is-info { background: #2a78d6; color: #fff; }
.pep-manual-nag.is-danger { background: #c9302c; color: #fff; }
.pep-manual-nag + .pep-manual-nag { border-top: 1px solid rgba(0, 0, 0, .15); }
.pep-manual-nag-close {
    border: 0;
    background: transparent;
    color: inherit;
    font-size: 18px;
    line-height: 1;
    padding: 4px 6px;
    opacity: .8;
}
.pep-manual-nag-close:hover { opacity: 1; }
.pep-nag-popup-item { border-left: 4px solid #e0a800; padding: 2px 0 2px 12px; margin-bottom: 14px; white-space: normal; }
.pep-nag-popup-item.is-info { border-left-color: #2a78d6; }
.pep-nag-popup-item.is-danger { border-left-color: #c9302c; }
@media print { .pep-manual-nag { display: none !important; } }


/* ======================================================================
   Time picker
   ====================================================================== */

.pep-time {
    display: flex;
    align-items: center;
    gap: 2px;
}

.pep-time .pf-input-time {
    min-width: 58px;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.3px;
}

.pep-time-btn {
    border: 0;
    background: none;
    color: var(--pep-text-muted);
    padding: 2px 3px;
    font-size: 11px;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    border-radius: 4px;
    transition: opacity 0.12s ease, color 0.12s ease;
}

/* The clock only appears on the cell you are working in — twenty of them at
   once competed with the numbers they sit beside. */
.pep-time:hover .pep-time-btn,
.pep-time .pf-input-time:focus + .pep-time-btn,
.pep-time-btn:focus {
    opacity: 1;
}

.pep-time-btn:hover {
    color: var(--pep-green);
}

/*
 * The panel is fixed to the viewport and lives on <body>, not in the cell:
 * the sheet scrolls sideways inside a container with overflow, which would
 * clip anything positioned within it at the table's edge.
 */
.pep-time-panel {
    position: fixed;
    z-index: 1080;
    display: none;
    width: 168px;
    background: var(--pep-surface);
    border: 1px solid var(--pep-border);
    border-radius: 10px;
    box-shadow: 0 10px 30px var(--pep-shadow);
    overflow: hidden;
}

.pep-time-panel.is-open {
    display: block;
}

.pep-time-cols {
    display: grid;
    grid-template-columns: 1fr 1fr;
}

.pep-time-col + .pep-time-col {
    border-left: 1px solid var(--pep-border);
}

.pep-time-head {
    padding: 6px 0;
    text-align: center;
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--pep-text-muted);
    background: var(--pep-surface-2);
    border-bottom: 1px solid var(--pep-border);
}

.pep-time-list {
    max-height: 168px;
    overflow-y: auto;
    scrollbar-width: thin;
}

.pep-time-opt {
    display: block;
    width: 100%;
    border: 0;
    background: none;
    color: var(--pep-text);
    padding: 5px 0;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    text-align: center;
    cursor: pointer;
}

.pep-time-opt:hover {
    background: var(--pep-surface-2);
}

.pep-time-opt.is-selected {
    background: var(--pep-green);
    color: #fff;
    font-weight: 700;
}

.pep-time-actions {
    display: flex;
    border-top: 1px solid var(--pep-border);
}

.pep-time-action {
    flex: 1;
    border: 0;
    background: var(--pep-surface-2);
    color: var(--pep-text-muted);
    padding: 7px 0;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
}

.pep-time-action + .pep-time-action {
    border-left: 1px solid var(--pep-border);
}

.pep-time-action:hover {
    color: var(--pep-green);
}

/* A bottom-centred "done — undo?" bar: the inventory sheets and the planning
   board's Ctrl+Z share it. */
.pep-undo-bar,
.inv-undo-bar {
    position: fixed;
    left: 50%;
    bottom: 24px;
    transform: translateX(-50%);
    z-index: 1060;
    display: flex;
    align-items: center;
    gap: 12px;
    max-width: calc(100vw - 32px);
    padding: 10px 12px 10px 16px;
    border-radius: 12px;
    background: var(--pep-surface);
    border: 1px solid var(--pep-border);
    box-shadow: 0 6px 24px var(--pep-shadow);
    font-size: 13px;
}

.pep-undo-bar[hidden] { display: none; }

