/* =====================================================================
   app.css
   M-Pesa Dashboard interface
   ---------------------------------------------------------------------
   One stylesheet, no build step, no framework, no CDN. That last part is
   not laziness: the Content-Security-Policy in config/security.php only
   allows styles and scripts from this origin, and that policy is what
   makes a stored cross-site scripting payload useless. Adding a CDN link
   would mean weakening it.

   Structure
     1.  Design tokens
     2.  Reset and base
     3.  Layout shell (sidebar + main)
     4.  Sidebar
     5.  Top bar
     6.  Cards and surfaces
     7.  Statistic tiles
     8.  Chart
     9.  Tables
    10.  Forms
    11.  Buttons
    12.  Pills, badges, avatars
    13.  Alerts and empty states
    14.  Auth and installer screens
    15.  Utilities
    16.  Responsive
    17.  Print
    18.  Utilities that replace inline style attributes
    19.  Two-step verification
    20.  Radio choice rows
    21.  Billing: the line-item editor
    22.  The printed document
    23.  The print page
   ================================================================== */

/* ---------------------------------------------------------------------
   1. Design tokens

   The accent colour is written into the page by the layout, from the
   organisation's settings, so each deployment carries its own identity
   without a separate stylesheet.

   LIGHT AND DARK
   --------------
   Three blocks, in this order, and the order is load-bearing:

     :root                                    the light palette
     @media (prefers-color-scheme: dark)      applied unless the person
       :root:not([data-theme="light"])        asked for light
     :root[data-theme="dark"]                 applied when the person
                                              asked for dark

   The last two carry equal specificity, so the explicit choice wins by
   coming later in the file. That is what makes "Light" mean light on a
   phone set to dark, which the previous version of this file could not
   do: it had one @media block, no override, and a device set to dark
   forced the dashboard dark with no way out.

   The dark colours are named once, as --dark-*, and assigned twice, so
   there is exactly one place to change #1C1C1E rather than two that can
   drift apart.
   ------------------------------------------------------------------ */
:root {
    color-scheme: light;

    --accent:            #0A84FF;
    --accent-strong:     #0060DF;
    --accent-soft:       rgba(10, 132, 255, 0.10);
    --accent-softer:     rgba(10, 132, 255, 0.05);

    --green:             #34C759;
    --green-soft:        rgba(52, 199, 89, 0.12);
    --amber:             #FF9F0A;
    --amber-soft:        rgba(255, 159, 10, 0.14);
    --red:               #FF3B30;
    --red-soft:          rgba(255, 59, 48, 0.10);
    --purple:            #AF52DE;

    --bg:                #F5F5F7;
    --bg-elevated:       #FFFFFF;
    --bg-sunken:         #EFEFF2;
    --sidebar-bg:        rgba(255, 255, 255, 0.72);
    --sidebar-solid:     #FFFFFF;

    --text:              #1D1D1F;
    --text-secondary:    #6E6E73;
    --text-tertiary:     #9A9AA0;
    --text-on-accent:    #FFFFFF;

    --hairline:          rgba(0, 0, 0, 0.08);
    --hairline-strong:   rgba(0, 0, 0, 0.14);

    --shadow-sm:         0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 1px rgba(0, 0, 0, 0.03);
    --shadow-md:         0 4px 16px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.04);
    --shadow-lg:         0 12px 40px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.05);

    --radius-sm:         8px;
    --radius:            12px;
    --radius-lg:         18px;
    --radius-xl:         24px;

    --sidebar-width:     264px;
    --topbar-height:     64px;

    /* Apple's own stack first, then sensible fallbacks for Windows and
       Android, which is what most staff here will be using. */
    --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI Variable Text",
            "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI Variable Display",
            "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;

    --ease: cubic-bezier(0.32, 0.72, 0, 1);

    /* ---- The dark palette -----------------------------------------
       Declared here, applied by the two blocks below. Nothing reads
       these directly, so changing one changes both routes into dark
       mode at once. */
    --dark-bg:              #000000;
    --dark-bg-elevated:     #1C1C1E;
    --dark-bg-sunken:       #121214;
    --dark-sidebar-bg:      rgba(28, 28, 30, 0.72);
    --dark-sidebar-solid:   #1C1C1E;

    --dark-text:            #F5F5F7;
    --dark-text-secondary:  #98989D;
    --dark-text-tertiary:   #6E6E73;

    --dark-hairline:        rgba(255, 255, 255, 0.10);
    --dark-hairline-strong: rgba(255, 255, 255, 0.18);

    --dark-accent-soft:     rgba(10, 132, 255, 0.18);
    --dark-accent-softer:   rgba(10, 132, 255, 0.09);
    --dark-green-soft:      rgba(52, 199, 89, 0.18);
    --dark-amber-soft:      rgba(255, 159, 10, 0.20);
    --dark-red-soft:        rgba(255, 59, 48, 0.18);

    --dark-shadow-sm:       0 1px 2px rgba(0, 0, 0, 0.5);
    --dark-shadow-md:       0 4px 16px rgba(0, 0, 0, 0.5);
    --dark-shadow-lg:       0 12px 40px rgba(0, 0, 0, 0.6);
}

/* Dark because the device asks for it.
   The :not() is the whole point of this rule: somebody who has chosen
   the light theme keeps it even on a phone set to dark. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        color-scheme: dark;

        --bg:              var(--dark-bg);
        --bg-elevated:     var(--dark-bg-elevated);
        --bg-sunken:       var(--dark-bg-sunken);
        --sidebar-bg:      var(--dark-sidebar-bg);
        --sidebar-solid:   var(--dark-sidebar-solid);

        --text:            var(--dark-text);
        --text-secondary:  var(--dark-text-secondary);
        --text-tertiary:   var(--dark-text-tertiary);

        --hairline:        var(--dark-hairline);
        --hairline-strong: var(--dark-hairline-strong);

        --accent-soft:     var(--dark-accent-soft);
        --accent-softer:   var(--dark-accent-softer);
        --green-soft:      var(--dark-green-soft);
        --amber-soft:      var(--dark-amber-soft);
        --red-soft:        var(--dark-red-soft);

        --shadow-sm:       var(--dark-shadow-sm);
        --shadow-md:       var(--dark-shadow-md);
        --shadow-lg:       var(--dark-shadow-lg);
    }
}

/* Dark because the person asked for it.
   Same specificity as the rule above, deliberately placed after it, so
   an explicit choice beats the operating system in both directions. */
:root[data-theme="dark"] {
    color-scheme: dark;

    --bg:              var(--dark-bg);
    --bg-elevated:     var(--dark-bg-elevated);
    --bg-sunken:       var(--dark-bg-sunken);
    --sidebar-bg:      var(--dark-sidebar-bg);
    --sidebar-solid:   var(--dark-sidebar-solid);

    --text:            var(--dark-text);
    --text-secondary:  var(--dark-text-secondary);
    --text-tertiary:   var(--dark-text-tertiary);

    --hairline:        var(--dark-hairline);
    --hairline-strong: var(--dark-hairline-strong);

    --accent-soft:     var(--dark-accent-soft);
    --accent-softer:   var(--dark-accent-softer);
    --green-soft:      var(--dark-green-soft);
    --amber-soft:      var(--dark-amber-soft);
    --red-soft:        var(--dark-red-soft);

    --shadow-sm:       var(--dark-shadow-sm);
    --shadow-md:       var(--dark-shadow-md);
    --shadow-lg:       var(--dark-shadow-lg);
}

/* ---------------------------------------------------------------------
   1b. Density and corner style

   Two organisation-wide dials, applied as attributes on <html> by the
   layout. Both are deliberately small: they change spacing and radius,
   never colour, so nothing here can produce an unreadable combination.

   Compact takes roughly a fifth off the vertical padding, which is
   about four more table rows on a 13-inch laptop. It is not a separate
   design; it is the same one, tighter.
   ------------------------------------------------------------------ */
:root[data-density="compact"] {
    --topbar-height: 54px;
}
:root[data-density="compact"] .content        { padding: 16px 24px 40px; }
:root[data-density="compact"] .card__head     { padding: 11px 16px; }
:root[data-density="compact"] .card__body     { padding: 14px 16px; }
:root[data-density="compact"] .card__body--flush { padding: 0; }
:root[data-density="compact"] .card__foot     { padding: 9px 16px; }
:root[data-density="compact"] .nav__item      { padding: 6px 10px; }
:root[data-density="compact"] .topbar         { padding: 0 24px; }
:root[data-density="compact"] table.data thead th  { padding: 7px 12px; }
:root[data-density="compact"] table.data tbody td  { padding: 8px 12px; }

:root[data-radius="sharp"] {
    --radius-sm: 3px;
    --radius:    4px;
    --radius-lg: 6px;
    --radius-xl: 8px;
}
:root[data-radius="soft"] {
    --radius-sm: 12px;
    --radius:    18px;
    --radius-lg: 26px;
    --radius-xl: 34px;
}

/* ---------------------------------------------------------------------
   1c. The appearance screen's own controls
   ------------------------------------------------------------------ */
.swatches {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 10px;
}

.swatch {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border: 1px solid var(--hairline-strong);
    border-radius: var(--radius);
    cursor: pointer;
    transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}
.swatch:hover { background: var(--bg-sunken); }
.swatch input { margin: 0; flex-shrink: 0; }
.swatch__dots { display: flex; gap: 3px; flex-shrink: 0; }
.swatch__dot {
    width: 15px;
    height: 15px;
    border-radius: 50%;
    border: 1px solid var(--hairline-strong);
}
.swatch__name { font-size: 13px; font-weight: 500; }
.swatch input:checked ~ .swatch__name { font-weight: 650; }

/* The three-way Light / Dark / System control in the top bar. */
.theme-switch { display: flex; align-items: center; gap: 0; }
.theme-switch__set {
    display: flex;
    gap: 2px;
    padding: 2px;
    border: 1px solid var(--hairline);
    border-radius: 999px;
    background: var(--bg-sunken);
}
.theme-switch__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 26px;
    border: 0;
    background: transparent;
    border-radius: 999px;
    color: var(--text-tertiary);
    cursor: pointer;
    transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.theme-switch__btn:hover { color: var(--text); }
.theme-switch__btn[aria-pressed="true"] {
    background: var(--bg-elevated);
    color: var(--accent);
    box-shadow: var(--shadow-sm);
}
.theme-switch__btn svg { width: 15px; height: 15px; }

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

/* ---------------------------------------------------------------------
   2. Reset and base
   ------------------------------------------------------------------ */
*, *::before, *::after { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body {
    margin: 0;
    font-family: var(--font);
    font-size: 15px;
    line-height: 1.5;
    color: var(--text);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    letter-spacing: -0.011em;
}

h1, h2, h3, h4 {
    font-family: var(--font-display);
    margin: 0;
    font-weight: 600;
    letter-spacing: -0.021em;
    line-height: 1.2;
}

h1 { font-size: 28px; }
h2 { font-size: 20px; }
h3 { font-size: 17px; }
h4 { font-size: 15px; }

p { margin: 0 0 12px; }
p:last-child { margin-bottom: 0; }

a {
    color: var(--accent);
    text-decoration: none;
    transition: opacity 0.15s var(--ease);
}
a:hover { opacity: 0.75; }

/* A visible focus ring is an accessibility requirement, not decoration.
   :focus-visible means it appears for keyboard users without putting a
   ring around everything a mouse touches. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}

code, pre, .mono { font-family: var(--font-mono); font-size: 0.9em; }

pre {
    background: var(--bg-sunken);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    padding: 14px 16px;
    overflow-x: auto;
    margin: 0;
    font-size: 12.5px;
    line-height: 1.55;
}

hr {
    border: 0;
    border-top: 1px solid var(--hairline);
    margin: 20px 0;
}

/* Respect people who have asked their system to reduce motion. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* ---------------------------------------------------------------------
   3. Layout shell
   ------------------------------------------------------------------ */
.shell {
    display: flex;
    min-height: 100vh;
    min-height: 100dvh;
}

.main {
    flex: 1;
    min-width: 0;              /* Lets wide tables scroll instead of stretching the page. */
    display: flex;
    flex-direction: column;
    margin-left: var(--sidebar-width);
}

.content {
    flex: 1;
    padding: 24px 32px 56px;
    max-width: 1440px;
    width: 100%;
}

.page-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 22px;
}

.page-head__title { min-width: 0; }
.page-head__sub {
    margin: 6px 0 0;
    color: var(--text-secondary);
    font-size: 14px;
}
.page-head__actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: center;
}

/* ---------------------------------------------------------------------
   4. Sidebar

   Fixed, translucent, and scrollable on its own so a long menu never
   pushes the page. On phones it slides in over the content.
   ------------------------------------------------------------------ */
.sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--sidebar-width);
    background: var(--sidebar-bg);
    border-right: 1px solid var(--hairline);
    display: flex;
    flex-direction: column;
    z-index: 40;
    overflow-y: auto;
    overscroll-behavior: contain;
}

/* The frosted-glass effect, where the browser supports it. */
@supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) {
    .sidebar {
        -webkit-backdrop-filter: saturate(180%) blur(20px);
        backdrop-filter: saturate(180%) blur(20px);
    }
}
@supports not ((backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px))) {
    .sidebar { background: var(--sidebar-solid); }
}

/* The organisation can turn the frosting off. Worth having as a setting
   rather than a browser guess: backdrop-filter is the single most
   expensive thing this stylesheet asks for, and on the older machines in
   a parish office it shows as a sidebar that lags behind the scroll. */
:root[data-sidebar="solid"] .sidebar {
    background: var(--sidebar-solid);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

.brand {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 20px 20px 18px;
    text-decoration: none;
    color: inherit;
}
.brand:hover { opacity: 1; }

.brand__mark {
    width: 38px;
    height: 38px;
    border-radius: 10px;
    background: linear-gradient(145deg, var(--accent), var(--accent-strong));
    color: #fff;
    display: grid;
    place-items: center;
    font-weight: 600;
    font-size: 15px;
    letter-spacing: -0.02em;
    flex-shrink: 0;
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}
.brand__mark img { width: 100%; height: 100%; object-fit: cover; display: block; }

.brand__text { min-width: 0; }
.brand__name {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 15px;
    line-height: 1.25;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.brand__tag {
    font-size: 11.5px;
    color: var(--text-tertiary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.nav {
    flex: 1;
    padding: 4px 12px 16px;
}

.nav__group { margin-bottom: 18px; }

.nav__label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-tertiary);
    padding: 0 10px 6px;
}

.nav__item {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    font-size: 14px;
    font-weight: 450;
    margin-bottom: 1px;
    transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.nav__item:hover {
    background: var(--hairline);
    color: var(--text);
    opacity: 1;
}
.nav__item--active {
    background: var(--accent-soft);
    color: var(--accent);
    font-weight: 550;
}
.nav__item svg { width: 18px; height: 18px; flex-shrink: 0; }

.nav__count {
    margin-left: auto;
    background: var(--red);
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    display: grid;
    place-items: center;
}

.sidebar__footer {
    padding: 12px;
    border-top: 1px solid var(--hairline);
}

.user-chip {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px;
    border-radius: var(--radius-sm);
    color: inherit;
    transition: background 0.15s var(--ease);
}
.user-chip:hover { background: var(--hairline); opacity: 1; }
.user-chip__text { min-width: 0; flex: 1; }
.user-chip__name {
    font-size: 13.5px;
    font-weight: 550;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.user-chip__role {
    font-size: 11.5px;
    color: var(--text-tertiary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---------------------------------------------------------------------
   5. Top bar
   ------------------------------------------------------------------ */
.topbar {
    height: var(--topbar-height);
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 0 32px;
    border-bottom: 1px solid var(--hairline);
    background: var(--bg);
    position: sticky;
    top: 0;
    z-index: 30;
}

@supports (backdrop-filter: blur(20px)) {
    .topbar {
        -webkit-backdrop-filter: saturate(180%) blur(20px);
        backdrop-filter: saturate(180%) blur(20px);
    }
}

.topbar__title {
    font-family: var(--font-display);
    font-size: 16px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.topbar__spacer { flex: 1; }

.topbar__env {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    padding: 4px 9px;
    border-radius: 6px;
    background: var(--amber-soft);
    color: var(--amber);
}
.topbar__env--live { background: var(--green-soft); color: var(--green); }

/* ---------------------------------------------------------------------
   The side menu toggle.

   Visible at every width, not just on phones. Hiding the menu on a laptop
   is not a small-screen concession; it is how somebody reading a wide table
   of payments gets the 264px back.

   Two mechanisms, deliberately kept apart by width so they can never fight
   over the same element. Above 900px the stored value "collapsed" slides the
   panel out and the content widens into it. Below 900px the panel is off
   canvas to begin with and the stored value "open" slides it in over the
   page. One preference, read differently either side of the line.
   ------------------------------------------------------------------ */
.menu-toggle-form {
    display: flex;
    margin: 0;
}

@media (min-width: 901px) {
    :root[data-nav="collapsed"] .sidebar { transform: translateX(-100%); }
    :root[data-nav="collapsed"] .main    { margin-left: 0; }
}

.sidebar { transition: transform 0.28s var(--ease); }
.main    { transition: margin-left 0.28s var(--ease); }

@media (prefers-reduced-motion: reduce) {
    .sidebar, .main { transition: none; }
}

.menu-toggle {
    display: grid;
    width: 38px;
    height: 38px;
    border: 1px solid var(--hairline);
    background: var(--bg-elevated);
    border-radius: 10px;
    cursor: pointer;
    color: var(--text);
    place-items: center;
    padding: 0;
}

.scrim {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.35);
    z-index: 35;
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}
.scrim--open { display: block; }

/* ---------------------------------------------------------------------
   6. Cards and surfaces
   ------------------------------------------------------------------ */
.card {
    background: var(--bg-elevated);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

.card__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--hairline);
    flex-wrap: wrap;
}
.card__title { font-size: 15px; font-weight: 600; }
.card__sub { font-size: 13px; color: var(--text-secondary); margin-top: 2px; }
.card__body { padding: 20px; }
.card__body--flush { padding: 0; }
.card__foot {
    padding: 12px 20px;
    border-top: 1px solid var(--hairline);
    background: var(--bg-sunken);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
}

.grid { display: grid; gap: 18px; }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid--sidebar-right { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }

/* ---------------------------------------------------------------------
   7. Statistic tiles
   ------------------------------------------------------------------ */
.stat {
    background: var(--bg-elevated);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    padding: 18px 20px;
    box-shadow: var(--shadow-sm);
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}

.stat__label {
    font-size: 12.5px;
    font-weight: 550;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    gap: 7px;
}
.stat__label svg { width: 15px; height: 15px; opacity: 0.65; }

.stat__value {
    font-family: var(--font-display);
    font-size: 26px;
    font-weight: 600;
    letter-spacing: -0.025em;
    line-height: 1.15;
    /* Long shilling figures should shrink rather than break the tile. */
    overflow-wrap: anywhere;
}
.stat__value--sm { font-size: 21px; }

.stat__meta { font-size: 12.5px; color: var(--text-tertiary); }

.stat--accent { background: linear-gradient(155deg, var(--accent), var(--accent-strong)); border-color: transparent; }
.stat--accent .stat__label,
.stat--accent .stat__value,
.stat--accent .stat__meta { color: #fff; }
.stat--accent .stat__label { opacity: 0.85; }
.stat--accent .stat__meta { opacity: 0.8; }

/* ---------------------------------------------------------------------
   8. Chart

   Drawn as inline SVG by the server, so there is no charting library and
   nothing to load from a CDN. The JavaScript only handles the hover
   readout.
   ------------------------------------------------------------------ */
.chart { position: relative; width: 100%; }
.chart svg { display: block; width: 100%; height: auto; }
.chart__grid line { stroke: var(--hairline); stroke-width: 1; }
/* The gradient itself is defined inside the SVG, in partials/chart.php. */
.chart__area { fill: url(#chartFill); }
.chart__line { fill: none; stroke: var(--accent); stroke-width: 2.25; stroke-linejoin: round; stroke-linecap: round; }
.chart__dot { fill: var(--bg-elevated); stroke: var(--accent); stroke-width: 2; opacity: 0; transition: opacity 0.12s var(--ease); }
.chart__hit { fill: transparent; cursor: crosshair; }
.chart__hit:hover + .chart__dot,
.chart__hit:focus + .chart__dot { opacity: 1; }
.chart__axis { fill: var(--text-tertiary); font-size: 10.5px; font-family: var(--font); }

.chart__tip {
    position: absolute;
    pointer-events: none;
    background: var(--text);
    color: var(--bg-elevated);
    font-size: 12px;
    font-weight: 500;
    padding: 6px 10px;
    border-radius: 8px;
    white-space: nowrap;
    opacity: 0;
    transform: translate(-50%, -130%);
    transition: opacity 0.12s var(--ease);
    box-shadow: var(--shadow-md);
    z-index: 5;
}
.chart__tip--visible { opacity: 1; }

.segmented {
    display: inline-flex;
    background: var(--bg-sunken);
    border: 1px solid var(--hairline);
    border-radius: 9px;
    padding: 2px;
    gap: 2px;
}
.segmented a {
    padding: 5px 12px;
    border-radius: 7px;
    font-size: 12.5px;
    font-weight: 550;
    color: var(--text-secondary);
}
.segmented a:hover { opacity: 1; color: var(--text); }
.segmented a.is-active {
    background: var(--bg-elevated);
    color: var(--text);
    box-shadow: var(--shadow-sm);
}

/* ---------------------------------------------------------------------
   9. Tables

   Wrapped in .table-wrap, which scrolls horizontally on its own. This is
   what keeps a twelve-column payments table usable on a phone without
   the whole page sliding about.
   ------------------------------------------------------------------ */
.table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

table.data {
    width: 100%;
    border-collapse: collapse;
    font-size: 13.5px;
    min-width: 640px;
}

table.data thead th {
    text-align: left;
    font-size: 11.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.045em;
    color: var(--text-tertiary);
    padding: 10px 16px;
    border-bottom: 1px solid var(--hairline);
    background: var(--bg-sunken);
    white-space: nowrap;
    position: sticky;
    top: 0;
    z-index: 1;
}

table.data thead th a { color: inherit; display: inline-flex; align-items: center; gap: 4px; }

table.data tbody td {
    padding: 12px 16px;
    border-bottom: 1px solid var(--hairline);
    vertical-align: middle;
}

table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr { transition: background 0.12s var(--ease); }
table.data tbody tr:hover { background: var(--accent-softer); }

.td-strong { font-weight: 550; }
.td-mono { font-family: var(--font-mono); font-size: 12.5px; }
.td-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.td-muted { color: var(--text-secondary); }
.td-nowrap { white-space: nowrap; }

.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    font-size: 13px;
    color: var(--text-secondary);
}
.pager__links { display: flex; gap: 8px; }

/* ---------------------------------------------------------------------
   10. Forms
   ------------------------------------------------------------------ */
.field { margin-bottom: 16px; }
.field:last-child { margin-bottom: 0; }

.field__label {
    display: block;
    font-size: 13px;
    font-weight: 550;
    margin-bottom: 6px;
}

.field__hint {
    font-size: 12.5px;
    color: var(--text-secondary);
    margin-top: 6px;
    line-height: 1.45;
}

.field__error {
    font-size: 12.5px;
    color: var(--red);
    margin-top: 6px;
    font-weight: 500;
}

input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="date"], input[type="tel"], input[type="url"], input[type="search"],
select, textarea {
    width: 100%;
    font-family: inherit;
    font-size: 15px;
    color: var(--text);
    background: var(--bg-elevated);
    border: 1px solid var(--hairline-strong);
    border-radius: 10px;
    padding: 10px 13px;
    transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
    -webkit-appearance: none;
    appearance: none;
}

textarea { min-height: 90px; resize: vertical; line-height: 1.5; }

select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236E6E73' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 13px center;
    padding-right: 34px;
}

input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3.5px var(--accent-soft);
}

input.has-error, select.has-error, textarea.has-error {
    border-color: var(--red);
}
input.has-error:focus, select.has-error:focus, textarea.has-error:focus {
    box-shadow: 0 0 0 3.5px var(--red-soft);
}

input::placeholder, textarea::placeholder { color: var(--text-tertiary); }
input:disabled, select:disabled, textarea:disabled { opacity: 0.55; cursor: not-allowed; }

.field-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 16px;
}

.check {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 9px 11px;
    border-radius: 10px;
    cursor: pointer;
    transition: background 0.12s var(--ease);
}
.check:hover { background: var(--accent-softer); }

.check input[type="checkbox"], .check input[type="radio"] {
    width: 18px;
    height: 18px;
    margin: 1px 0 0;
    accent-color: var(--accent);
    flex-shrink: 0;
    cursor: pointer;
}

.check__text { min-width: 0; }
.check__title { font-size: 13.5px; font-weight: 500; }
.check__desc { font-size: 12px; color: var(--text-secondary); margin-top: 2px; line-height: 1.4; }

.filters {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: flex-end;
}
.filters .field { margin: 0; min-width: 150px; flex: 1 1 150px; }
.filters .field--wide { flex: 2 1 240px; }

/* The uploader on the profile screen. */
.uploader {
    display: flex;
    align-items: center;
    gap: 18px;
    flex-wrap: wrap;
}

.file-input {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 14px;
    border: 1px dashed var(--hairline-strong);
    border-radius: 10px;
    cursor: pointer;
    font-size: 13.5px;
    font-weight: 500;
    transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}
.file-input:hover { border-color: var(--accent); background: var(--accent-softer); }
.file-input input[type="file"] { display: none; }
.file-input__name { font-weight: 400; color: var(--text-secondary); }

/* ---------------------------------------------------------------------
   11. Buttons
   ------------------------------------------------------------------ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    font-family: inherit;
    font-size: 14px;
    font-weight: 550;
    line-height: 1;
    padding: 10px 16px;
    border-radius: 10px;
    border: 1px solid transparent;
    cursor: pointer;
    white-space: nowrap;
    transition: transform 0.1s var(--ease), background 0.15s var(--ease), opacity 0.15s var(--ease);
    text-decoration: none;
    -webkit-appearance: none;
    appearance: none;
}
.btn:hover { opacity: 0.9; }
.btn:active { transform: scale(0.975); }
.btn svg { width: 16px; height: 16px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }

.btn--primary { background: var(--accent); color: var(--text-on-accent); box-shadow: var(--shadow-sm); }
.btn--primary:hover { background: var(--accent-strong); opacity: 1; }

.btn--secondary {
    background: var(--bg-elevated);
    color: var(--text);
    border-color: var(--hairline-strong);
    box-shadow: var(--shadow-sm);
}
.btn--secondary:hover { background: var(--bg-sunken); opacity: 1; }

.btn--ghost { background: transparent; color: var(--text-secondary); }
.btn--ghost:hover { background: var(--hairline); color: var(--text); opacity: 1; }

.btn--danger { background: var(--red); color: #fff; }
.btn--danger-quiet { background: var(--red-soft); color: var(--red); }
.btn--danger-quiet:hover { background: var(--red); color: #fff; opacity: 1; }

.btn--sm { padding: 7px 12px; font-size: 13px; border-radius: 8px; }
.btn--lg { padding: 13px 22px; font-size: 15px; border-radius: 12px; }
.btn--block { width: 100%; }

.btn-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }

/* ---------------------------------------------------------------------
   12. Pills, badges, avatars
   ------------------------------------------------------------------ */
.pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    font-weight: 550;
    padding: 4px 9px;
    border-radius: 7px;
    white-space: nowrap;
    background: var(--hairline);
    color: var(--text-secondary);
}
.pill--ok    { background: var(--green-soft); color: var(--green); }
.pill--wait  { background: var(--accent-soft); color: var(--accent); }
.pill--warn  { background: var(--amber-soft); color: var(--amber); }
.pill--bad   { background: var(--red-soft);   color: var(--red); }
.pill--muted { background: var(--hairline);   color: var(--text-tertiary); }

.pill__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

.avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    object-fit: cover;
    background: var(--accent-soft);
    color: var(--accent);
    display: inline-grid;
    place-items: center;
    font-size: 12px;
    font-weight: 600;
    flex-shrink: 0;
    overflow: hidden;
    font-family: var(--font-display);
}
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.avatar--lg { width: 44px; height: 44px; font-size: 15px; }
.avatar--xl { width: 88px; height: 88px; font-size: 28px; border: 1px solid var(--hairline); }

.person { display: flex; align-items: center; gap: 10px; min-width: 0; }
.person__name { font-weight: 550; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.person__meta { font-size: 12px; color: var(--text-tertiary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---------------------------------------------------------------------
   13. Alerts and empty states
   ------------------------------------------------------------------ */
.alert {
    display: flex;
    gap: 12px;
    padding: 13px 16px;
    border-radius: var(--radius);
    font-size: 13.5px;
    line-height: 1.5;
    margin-bottom: 18px;
    border: 1px solid transparent;
}
.alert svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; }
.alert__body { min-width: 0; }
.alert__title { font-weight: 600; margin-bottom: 2px; }

.alert--success { background: var(--green-soft); color: var(--green); border-color: rgba(52,199,89,0.25); }
.alert--error   { background: var(--red-soft);   color: var(--red);   border-color: rgba(255,59,48,0.25); }
.alert--warning { background: var(--amber-soft); color: var(--amber); border-color: rgba(255,159,10,0.3); }
.alert--info    { background: var(--accent-soft); color: var(--accent); border-color: rgba(10,132,255,0.25); }

.empty {
    text-align: center;
    padding: 52px 24px;
    color: var(--text-secondary);
}
.empty svg { width: 40px; height: 40px; opacity: 0.35; margin-bottom: 12px; }
.empty__title { font-family: var(--font-display); font-size: 16px; font-weight: 600; color: var(--text); margin-bottom: 5px; }
.empty__text { font-size: 13.5px; max-width: 420px; margin: 0 auto 16px; line-height: 1.55; }

/* Key/value list used on detail screens. */
.kv { display: grid; gap: 0; }
.kv__row {
    display: grid;
    grid-template-columns: minmax(130px, 220px) 1fr;
    gap: 16px;
    padding: 11px 0;
    border-bottom: 1px solid var(--hairline);
    font-size: 13.5px;
}

/* Stacked variant, for a kv list inside a narrow side panel.
   The two-column form needs about 400px to work: below that the 130px key
   column plus the gap leaves the value so little room that "Ukambani Traders
   Limited" wraps one letter per line. Stacking is the honest answer, and it is
   what the mobile breakpoint already does to every kv list. */
.kv--stack .kv__row {
    grid-template-columns: minmax(0, 1fr);
    gap: 2px;
}
.kv--stack .kv__key { font-size: 12px; }
.kv__row:last-child { border-bottom: 0; }
.kv__key { color: var(--text-secondary); }
.kv__val { min-width: 0; overflow-wrap: anywhere; }

/* ---------------------------------------------------------------------
   14. Auth and installer screens
   ------------------------------------------------------------------ */
.auth {
    min-height: 100vh;
    min-height: 100dvh;
    display: grid;
    place-items: center;
    padding: 24px;
    background:
        radial-gradient(1000px 600px at 15% -10%, var(--accent-soft), transparent 60%),
        radial-gradient(800px 500px at 110% 110%, var(--accent-softer), transparent 55%),
        var(--bg);
}

.auth__card {
    width: 100%;
    max-width: 420px;
    background: var(--bg-elevated);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-lg);
    padding: 34px 32px 30px;
}
.auth__card--wide { max-width: 780px; }

.auth__brand { text-align: center; margin-bottom: 26px; }
.auth__mark {
    width: 56px;
    height: 56px;
    border-radius: 15px;
    background: linear-gradient(145deg, var(--accent), var(--accent-strong));
    color: #fff;
    display: inline-grid;
    place-items: center;
    font-family: var(--font-display);
    font-weight: 600;
    font-size: 21px;
    margin-bottom: 14px;
    box-shadow: var(--shadow-md);
    overflow: hidden;
}
.auth__mark img { width: 100%; height: 100%; object-fit: cover; }
.auth__title { font-size: 22px; }
.auth__sub { color: var(--text-secondary); font-size: 14px; margin-top: 5px; }
.auth__foot { margin-top: 22px; text-align: center; font-size: 13px; color: var(--text-secondary); }

.checklist { display: grid; gap: 2px; }
.checklist__item {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 9px 12px;
    border-radius: 9px;
    font-size: 13.5px;
}
.checklist__item--ok   { background: var(--green-soft); }
.checklist__item--fail { background: var(--red-soft); }
.checklist__mark { font-weight: 700; flex-shrink: 0; }
.checklist__item--ok .checklist__mark { color: var(--green); }
.checklist__item--fail .checklist__mark { color: var(--red); }
.checklist__detail { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }

.step-title {
    font-family: var(--font-display);
    font-size: 15px;
    font-weight: 600;
    margin: 26px 0 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--hairline);
}
.step-title:first-child { margin-top: 0; }

/* ---------------------------------------------------------------------
   15. Utilities
   ------------------------------------------------------------------ */
.stack   { display: grid; gap: 18px; }
.stack--sm { gap: 10px; }
.row     { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.row--between { justify-content: space-between; }
.muted   { color: var(--text-secondary); }
.tiny    { font-size: 12px; }
.small   { font-size: 13px; }
.strong  { font-weight: 600; }
.right   { text-align: right; }
.center  { text-align: center; }
.nowrap  { white-space: nowrap; }
.mt-0 { margin-top: 0; } .mt-1 { margin-top: 8px; } .mt-2 { margin-top: 16px; } .mt-3 { margin-top: 24px; }
.mb-0 { margin-bottom: 0; } .mb-1 { margin-bottom: 8px; } .mb-2 { margin-bottom: 16px; } .mb-3 { margin-bottom: 24px; }
.hide { display: none !important; }

.copy-field { display: flex; gap: 8px; align-items: center; }
.copy-field input {
    font-family: var(--font-mono);
    font-size: 12.5px;
    background: var(--bg-sunken);
}

/* Screen-reader-only text: present for assistive technology, invisible
   on screen. Used for icon-only buttons. */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---------------------------------------------------------------------
   16. Responsive

   Three breakpoints, matched to real devices rather than round numbers:
     1180px  laptops and small windows lose the two-column dashboard
      900px  tablets: the sidebar becomes a slide-over
      560px  phones: single column, larger touch targets
   ------------------------------------------------------------------ */
@media (max-width: 1180px) {
    .grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .grid--sidebar-right { grid-template-columns: minmax(0, 1fr); }
    .content { padding: 22px 24px 48px; }
    .topbar { padding: 0 24px; }
}

@media (max-width: 900px) {
    .sidebar {
        transform: translateX(-100%);
        transition: transform 0.28s var(--ease);
        box-shadow: var(--shadow-lg);
        background: var(--sidebar-solid);
    }
    .sidebar--open { transform: translateX(0); }
    .main { margin-left: 0; }

    /* The stored preference is a laptop one: it decides whether the menu is
       pinned open beside the page. On a phone the panel is an overlay and
       opening it is a passing action, so the script below normalises the
       stored value away at this width and uses .sidebar--open instead.

       These two rules are the no-JavaScript fallback, and the second one is
       the whole reason they are a pair. Sliding the panel over the page
       without moving the page puts the panel on top of the very button that
       closes it, and the menu can never be dismissed again. */
    :root[data-nav="open"] .sidebar { transform: translateX(0); }
    :root[data-nav="open"] .main    { margin-left: var(--sidebar-width); }
    .grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
    h1 { font-size: 23px; }
    .content { padding: 18px 16px 40px; }
    .topbar { padding: 0 16px; height: 58px; }
    .grid, .grid--2, .grid--3, .grid--4 { grid-template-columns: minmax(0, 1fr); }
    .card__head, .card__body { padding: 15px 16px; }
    .card__foot { padding: 12px 16px; }
    .auth__card { padding: 26px 20px 24px; border-radius: var(--radius-lg); }
    .stat__value { font-size: 23px; }
    .kv__row { grid-template-columns: minmax(0, 1fr); gap: 2px; }
    .kv__key { font-size: 12px; }

    /* Bigger tap targets on touch screens: 44px is Apple's own minimum. */
    .btn { padding: 12px 18px; min-height: 44px; }
    .btn--sm { padding: 9px 14px; min-height: 38px; }
    .nav__item { padding: 11px 10px; }
    .page-head__actions { width: 100%; }
    .page-head__actions .btn { flex: 1 1 auto; }
    .filters .field { flex: 1 1 100%; }
}

/* ---------------------------------------------------------------------
   17. Print

   Finance staff print the payments list for filing, so make that usable:
   drop the chrome, force light colours, keep the table.
   ------------------------------------------------------------------ */
@media print {
    .sidebar, .topbar, .page-head__actions, .filters, .pager, .btn, .scrim { display: none !important; }
    .main { margin-left: 0; }
    .content { padding: 0; max-width: none; }
    .card { border: 1px solid #ccc; box-shadow: none; break-inside: avoid; }
    body { background: #fff; color: #000; font-size: 11pt; }
    table.data { min-width: 0; font-size: 10pt; }
    table.data thead th { background: #eee; color: #000; }
    a { color: #000; text-decoration: none; }
}

/* ---------------------------------------------------------------------
   18. Utilities that replace inline style attributes

   These exist for one reason: the Content-Security-Policy blocks inline
   styles, and a style="" attribute cannot carry a nonce. Every layout
   value that used to sit in an attribute lives here instead, so the
   policy can stay strict.
   ------------------------------------------------------------------ */
.w-form        { max-width: 720px; }
.min0          { min-width: 0; }
.gap-xs        { gap: 2px; }
.gap-sm        { gap: 6px; }
.ml-1          { margin-left: 6px; }
.row--end      { justify-content: flex-end; }
.field--auto   { flex: 0 0 auto !important; min-width: 0 !important; }
.h-sm          { font-size: 18px; }
.h-md          { font-size: 19px; }
.upper         { text-transform: uppercase; }
.clickable     { cursor: pointer; }
.table--auto   { min-width: 0; }
.cell-clip     { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cell-wide     { max-width: 320px; }
.pre-clip      { max-width: 520px; }
.pill--lg      { font-size: 13px; padding: 6px 12px; }
.code-lg       { font-size: 15px; letter-spacing: 0.04em; }
.avatar--square { border-radius: 16px; }
.steps         { padding-left: 18px; margin: 0; line-height: 1.7; }

.error-code {
    font-family: var(--font-display);
    font-size: 44px;
    font-weight: 600;
    letter-spacing: -0.03em;
    color: var(--text-tertiary);
}

/* ---------------------------------------------------------------------
   19. Two-step verification
   ------------------------------------------------------------------ */
.totp-code {
    font-family: var(--font-mono);
    font-size: 26px;
    letter-spacing: 0.35em;
    text-align: center;
    padding: 14px 13px;
}

.qr {
    display: grid;
    place-items: center;
    padding: 16px;
    background: #FFFFFF;
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    width: max-content;
    margin: 0 auto;
}
.qr svg { display: block; width: 208px; height: 208px; }

.secret-key {
    font-family: var(--font-mono);
    font-size: 14px;
    letter-spacing: 0.08em;
    word-break: break-all;
    background: var(--bg-sunken);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    text-align: center;
}

.recovery-codes {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
    font-family: var(--font-mono);
    font-size: 14px;
    letter-spacing: 0.06em;
}
.recovery-codes span {
    background: var(--bg-sunken);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    padding: 9px 12px;
    text-align: center;
}
@media (max-width: 560px) {
    .recovery-codes { grid-template-columns: minmax(0, 1fr); }
    .qr svg { width: 176px; height: 176px; }
}

/* ------------------------------------------------------------------
 * 20. Radio choice rows
 * ------------------------------------------------------------------
 * Used by the two-step verification policy on the organisation settings
 * screen. A whole row is clickable, which is far easier on a phone than
 * a bare 16px radio button.
 * ---------------------------------------------------------------- */

.choice {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 16px;
    border: 1px solid var(--hairline-strong);
    border-radius: var(--radius);
    background: var(--bg-elevated);
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}

.choice:hover { border-color: var(--accent); }

.choice input[type="radio"],
.choice input[type="checkbox"] {
    margin-top: 2px;
    width: 18px;
    height: 18px;
    accent-color: var(--accent);
    flex: 0 0 auto;
}

.choice > span {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.choice strong { font-weight: 600; }
.choice .field__hint { margin: 0; }

.choice input[type="checkbox"] { border-radius: 5px; }

.choice:has(input:checked) {
    border-color: var(--accent);
    background: var(--accent-softer);
}

/* ---------------------------------------------------------------------
   21. Billing: the line-item editor

   A table of inputs rather than a stack of cards, because somebody
   pricing twelve lines wants to tab across a grid, not scroll through
   twelve fieldsets. The column widths are fixed so the figures stay in
   line as rows are added and removed.
   ------------------------------------------------------------------ */

.data--form td { padding: 6px 8px; vertical-align: top; }
.data--form th { padding: 10px 8px; }
.data--form input[type="text"],
.data--form input[type="number"] {
    padding: 8px 10px;
    font-size: 13.5px;
    border-radius: var(--radius-sm);
}
.data--form input[type="checkbox"] {
    width: 18px;
    height: 18px;
    accent-color: var(--accent);
}

.data--form .col-desc  { min-width: 260px; }
.data--form .col-qty   { width: 110px; }
.data--form .col-unit  { width: 92px; }
.data--form .col-price { width: 130px; }
.data--form .col-disc  { width: 112px; }
.data--form .col-tax   { width: 76px; text-align: center; }
.data--form .col-act   { width: 52px; }

.td-centre { text-align: center; vertical-align: middle !important; }

/* The row you are currently looking at, in the paper-trail panel. */
.row--current { background: var(--accent-softer); }

/* ---------------------------------------------------------------------
   22. The printed document

   One set of rules serves three places: the preview inside the detail
   screen, the dedicated print page, and the printer itself. The PDF is
   drawn separately by app/Core/Pdf.php and uses none of this. Both read
   the same stored figures, so the two can look different but cannot say
   different things.
   ------------------------------------------------------------------ */

.doc {
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--text);
}

.doc__head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 24px;
    padding-bottom: 16px;
    border-bottom: 2px solid var(--accent);
    margin-bottom: 22px;
}

.doc__org { display: flex; gap: 16px; align-items: flex-start; min-width: 0; }
.doc__logo { width: 72px; height: auto; max-height: 60px; object-fit: contain; }
.doc__orgname {
    font-family: var(--font-display);
    font-size: 17px;
    font-weight: 600;
    letter-spacing: -0.02em;
    margin-bottom: 4px;
}
.doc__orgline { font-size: 12px; color: var(--text-secondary); }

.doc__title { text-align: right; flex: 0 0 auto; }
.doc__kind {
    font-family: var(--font-display);
    font-size: 24px;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--accent);
    line-height: 1.1;
}
.doc__number { font-size: 12.5px; color: var(--text-secondary); margin-top: 4px; }

.doc__parties {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 24px;
    margin-bottom: 20px;
}

.doc__label {
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin-bottom: 6px;
}

.doc__client { font-size: 15px; font-weight: 600; margin-bottom: 4px; }

.doc__meta { display: grid; gap: 5px; align-content: start; min-width: 190px; }
.doc__meta--wide { max-width: 380px; margin-bottom: 22px; }
.doc__metarow {
    display: flex;
    justify-content: space-between;
    gap: 20px;
    font-size: 12.5px;
}
.doc__metarow > span { color: var(--text-tertiary); }
.doc__metarow > strong { font-weight: 600; }

.doc__subject { font-size: 14.5px; font-weight: 600; margin: 0 0 18px; }

.doc__items { width: 100%; border-collapse: collapse; margin-bottom: 4px; }
.doc__items thead th {
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-secondary);
    background: var(--bg-sunken);
    padding: 9px 10px;
    text-align: left;
}
.doc__items thead th.td-num { text-align: right; }
.doc__items tbody td {
    padding: 11px 10px;
    border-bottom: 1px solid var(--hairline);
    vertical-align: top;
}
.doc__col-n { width: 34px; color: var(--text-tertiary); font-size: 12px; }
.doc__note { font-size: 11.5px; color: var(--text-secondary); margin-top: 3px; }

.doc__totals {
    margin: 14px 0 0 auto;
    width: min(320px, 100%);
    display: grid;
    gap: 7px;
}
.doc__totalrow {
    display: flex;
    justify-content: space-between;
    gap: 20px;
    font-size: 13px;
    color: var(--text-secondary);
}
.doc__totalrow > span:last-child { color: var(--text); font-variant-numeric: tabular-nums; }
.doc__totalrow--grand {
    border-top: 1px solid var(--hairline-strong);
    padding-top: 9px;
    margin-top: 3px;
    font-size: 15.5px;
    font-weight: 600;
    color: var(--text);
}

.doc__deposit {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 20px;
    background: var(--bg-sunken);
    border-radius: var(--radius);
    padding: 14px 18px;
    margin-top: 20px;
}
.doc__depositlabel { font-weight: 600; font-size: 13.5px; }
.doc__depositfig {
    font-family: var(--font-display);
    font-size: 19px;
    font-weight: 600;
    color: var(--accent);
    white-space: nowrap;
}

.doc__amount {
    background: var(--bg-sunken);
    border-radius: var(--radius);
    padding: 18px 20px;
    margin-bottom: 12px;
}
.doc__amountfig {
    font-family: var(--font-display);
    font-size: 30px;
    font-weight: 700;
    letter-spacing: -0.03em;
    color: var(--accent);
    margin-top: 4px;
}
.doc__words { font-style: italic; color: var(--text-secondary); margin: 0 0 20px; font-size: 13px; }

.doc__block { margin-top: 20px; }
.doc__blocktext { margin: 0; font-size: 12.5px; color: var(--text-secondary); white-space: pre-line; }

.doc__footer {
    margin-top: 28px;
    padding-top: 12px;
    border-top: 1px solid var(--hairline);
    font-size: 11.5px;
    color: var(--text-tertiary);
    display: flex;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
}

@media (max-width: 640px) {
    .doc__head { flex-direction: column; }
    .doc__parties { grid-template-columns: minmax(0, 1fr); }
    .doc__title { text-align: left; }
    .doc__totals { width: 100%; }
    .doc__deposit { flex-direction: column; align-items: flex-start; }
}

/* ---------------------------------------------------------------------
   23. The print page

   /billing/{id}/print is the document and nothing else. The toolbar at
   the top is the only thing on it that is not part of the document, and
   it is the first thing @media print removes.
   ------------------------------------------------------------------ */

.printable { background: var(--bg); padding: 24px 16px 60px; }

.printable__bar {
    max-width: 820px;
    margin: 0 auto 18px;
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
}

.printable__sheet {
    max-width: 820px;
    margin: 0 auto;
    background: var(--bg-elevated);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    padding: 44px 46px 40px;
}

@media (max-width: 640px) {
    .printable { padding: 12px 8px 40px; }
    .printable__sheet { padding: 22px 18px 26px; }
}

/* ---------------------------------------------------------------------
   24. The broadcast schedule

   Seven columns of cards, one per day, collapsing to a single column on a
   phone. Deliberately not an hour grid with absolutely positioned blocks:
   that looks like a television listing and is unreadable on a handset,
   which is exactly where a presenter checks whether they are on tomorrow.
   A list per day answers "what is on Tuesday" at any width, prints
   correctly, and needs no JavaScript to lay out.
   ------------------------------------------------------------------ */

.week {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 10px;
    align-items: start;
}

.week__day {
    background: var(--bg-elevated);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    padding: 10px;
    min-height: 120px;
    display: flex;
    flex-direction: column;
    gap: 7px;
}

.week__day--today {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent-soft);
}

.week__head {
    display: flex;
    align-items: baseline;
    gap: 6px;
    flex-wrap: wrap;
    padding-bottom: 7px;
    border-bottom: 1px solid var(--hairline);
}

.week__name { font-weight: 600; font-size: 13px; }
.week__date { font-size: 12px; color: var(--text-tertiary); }
.week__none { margin: 0; font-size: 12.5px; color: var(--text-tertiary); }

.week__add {
    margin-top: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    padding: 6px;
    border-radius: var(--radius-sm);
    border: 1px dashed var(--hairline-strong);
    color: var(--text-tertiary);
    font-size: 12.5px;
    transition: color 0.15s var(--ease), border-color 0.15s var(--ease);
}
.week__add:hover { color: var(--accent); border-color: var(--accent); opacity: 1; }

/* One broadcast. */
.slot {
    display: flex;
    gap: 8px;
    padding: 8px;
    border-radius: var(--radius-sm);
    background: var(--bg-sunken);
    color: var(--text);
    transition: background 0.15s var(--ease);
}
.slot:hover { background: var(--accent-softer); opacity: 1; }

/* A cancelled slot stays on the grid. An empty space answers nobody's
   question about what happened to the programme that was there. */
.slot--off { opacity: 0.55; }
.slot--off .slot__title { text-decoration: line-through; }

.slot__flag { flex-shrink: 0; height: auto; align-self: stretch; }
.slot__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }

/* Time and status on one row, title on its own beneath it. A seven-column
   week gives each day around 150 pixels; putting the status pill beside the
   title left the title a column one character wide. */
.slot__top { display: flex; align-items: center; gap: 6px; justify-content: space-between; }

.slot__time { font-size: 11.5px; font-weight: 600; color: var(--text-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* break-word, not anywhere: only break a word that genuinely cannot fit,
   rather than every word at the column edge. */
.slot__title { font-size: 13px; font-weight: 550; overflow-wrap: break-word; line-height: 1.3; }
.slot__meta { font-size: 11.5px; color: var(--text-tertiary); overflow-wrap: break-word; line-height: 1.35; }
.slot__nobody { color: var(--amber); }
.slot__status { flex-shrink: 0; font-size: 10.5px; padding: 2px 7px; }

/* Below a laptop the seven columns stop being columns and become a diary,
   which is the shape a phone reads anyway. */
@media (max-width: 1100px) {
    .week { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 640px) {
    .week { grid-template-columns: minmax(0, 1fr); }
    .week__day { min-height: 0; }

    /* Three stacked tiles pushed the actual schedule a full screen down,
       on the device where somebody is checking whether they are on in the
       morning. Same three numbers, one row, above the fold. */
    .schedule-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
    .schedule-stats .stat { padding: 10px; border-radius: var(--radius); }
    .schedule-stats .stat__label { font-size: 10.5px; gap: 4px; }
    .schedule-stats .stat__label svg { display: none; }
    .schedule-stats .stat__value { font-size: 20px; }
}

/* A printed week goes on the studio wall, so it keeps its seven columns. */
@media print {
    .week { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px; }
    .week__add { display: none; }
    .slot { background: transparent; border: 1px solid #ddd; }
}

@media print {
    .printable { background: #fff; padding: 0; }
    .printable__bar { display: none !important; }
    .printable__sheet {
        max-width: none;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        padding: 0;
    }

    /* Print in ink the client can read, whatever theme the person at the
       screen happens to be using. */
    .doc, .doc__client, .doc__subject, .doc__totalrow > span:last-child { color: #000; }
    .doc__orgline, .doc__note, .doc__blocktext, .doc__totalrow { color: #333; }
    .doc__items thead th { background: #eee; color: #000; }
    .doc__deposit, .doc__amount { background: #f2f2f2; }
    .doc__block, .doc__deposit, .doc__totals { break-inside: avoid; }
    .doc__footer { color: #555; }
    .doc__items tbody tr { break-inside: avoid; }
}

/* ---------------------------------------------------------------------
   25. Two applications in one installation

   The chooser shown after signing in to somebody who holds both, and the
   switch in the top bar that moves between them without signing out.
   ------------------------------------------------------------------ */

.auth__heading { font-size: 18px; margin: 0 0 6px; }
.auth__lead { color: var(--text-secondary); font-size: 13.5px; margin: 0 0 18px; line-height: 1.5; }

/* One application, as a big target. A button rather than a link because
   choosing posts a form: it changes what this session is looking at, and a
   GET that changes state gets prefetched by browsers. */
.app-card {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    text-align: left;
    padding: 16px 18px;
    border: 1px solid var(--hairline-strong);
    border-radius: var(--radius-lg);
    background: var(--bg-elevated);
    color: var(--text);
    cursor: pointer;
    font: inherit;
    transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}
.app-card:hover { border-color: var(--accent); background: var(--accent-softer); }

.app-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    border-radius: var(--radius);
    background: var(--accent-soft);
    color: var(--accent);
}
.app-card__text { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.app-card__name { font-size: 15px; font-weight: 600; }
.app-card__tag { font-size: 12.5px; color: var(--text-secondary); line-height: 1.4; }
.app-card__go { color: var(--text-tertiary); flex-shrink: 0; }

/* The switch in the top bar. Only rendered for somebody who holds more than
   one, so it never appears as a control with nothing to do. */
.app-switch { display: flex; align-items: center; gap: 2px; padding: 2px; border: 1px solid var(--hairline); border-radius: 999px; background: var(--bg-sunken); }

.app-switch__btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border: 0;
    background: transparent;
    border-radius: 999px;
    color: var(--text-secondary);
    font: inherit;
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.15s var(--ease), color 0.15s var(--ease);
}
.app-switch__btn:hover { color: var(--text); }
.app-switch__btn[aria-current="true"] {
    background: var(--bg-elevated);
    color: var(--accent);
    font-weight: 650;
    box-shadow: var(--shadow-sm);
}
.app-switch__btn svg { width: 15px; height: 15px; flex-shrink: 0; }

/* On a phone the labels go and the icons carry it, so the switch still fits
   beside the page title. */
@media (max-width: 860px) {
    .app-switch__btn span { display: none; }
    .app-switch__btn { padding: 6px 9px; }
}

/* The override panel on a broadcast: amber, because it is asking somebody to
   confirm a clash rather than telling them one exists. */
.card__foot--warn { background: var(--amber-soft); border-top-color: var(--amber); display: block; }
.mb-1 { margin-bottom: 8px; }

/* ---------------------------------------------------------------------
   26. The permissions matrix

   Wide by nature: every ability down the side, every role across the top.
   The ability column is pinned so a tick three roles along still has a name
   attached to it, and the table scrolls sideways underneath rather than
   squeezing sixty rows of text into nothing.

   Deliberately not made responsive by stacking. A matrix that has been
   stacked into one column per role is no longer a matrix, and the entire
   value of this screen is being able to compare across a row.
   ------------------------------------------------------------------ */
.matrix-wrap {
    /* Scrolls in both directions itself, rather than letting the page do it.
       That is what makes the two pinned edges work: a sticky heading sticks
       to its scroll container, and if the container never scrolls, the
       heading slides away with the page and you are left reading sixty rows
       of ticks with no idea which role each column belongs to. */
    max-height: calc(100vh - 300px);
    min-height: 340px;
    overflow: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}

.matrix {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 13px;
}

.matrix th,
.matrix td {
    border-bottom: 1px solid var(--hairline);
    padding: 9px 10px;
    text-align: left;
    vertical-align: top;
}

.matrix thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--bg-elevated);
    font-size: 11px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-dim);
    white-space: nowrap;
}

.matrix__ability {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--bg-elevated);
    min-width: 240px;
    max-width: 340px;
    font-weight: 500;
}

.matrix thead .matrix__ability { z-index: 3; }

.matrix__name {
    display: block;
    color: var(--text);
}

.matrix__hint {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    font-weight: 400;
    line-height: 1.4;
    color: var(--text-dim);
}

.matrix__section th {
    background: var(--bg-sunken, var(--bg));
    font-size: 11px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-dim);
    padding-top: 14px;
}

 /* The heading spans the whole table, so sticking the cell itself does
    nothing: it is already at the left edge. The label inside it is what
    has to stay put. */
.matrix__section span {
    position: sticky;
    left: 10px;
    display: inline-block;
}

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

.matrix__cell input[type="checkbox"] {
    width: 17px;
    height: 17px;
    cursor: pointer;
}

.matrix__cell input[type="checkbox"]:disabled { cursor: not-allowed; opacity: 0.5; }

/* The column for your own role, marked so that a tick you are about to
   remove from yourself is not one you remove by accident. */
.matrix__role--mine,
.matrix__cell--mine { background: var(--accent-soft, rgba(10, 132, 255, 0.08)); }

.matrix__you {
    display: block;
    font-size: 10px;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--accent);
}

/* The panel's own way out, on the screens where it covers the page. */
.sidebar__close {
    display: none;
    position: absolute;
    top: 14px;
    right: 12px;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--hairline);
    background: var(--bg-elevated);
    border-radius: 9px;
    color: var(--text-dim);
    cursor: pointer;
    place-items: center;
    z-index: 1;
}

@media (max-width: 900px) {
    .sidebar { position: fixed; }
    .sidebar__close { display: grid; }
}

/* ---------------------------------------------------------------------
   27. Reference lists

   A column of list names beside the list being edited. On a phone it
   becomes a scrolling row of chips along the top, because ten stacked
   links before you reach the content is a page nobody scrolls past.
   ------------------------------------------------------------------ */
.grid--sidebar-left {
    display: grid;
    grid-template-columns: 220px minmax(0, 1fr);
    gap: 20px;
    align-items: start;
}

.list-nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: var(--bg-elevated);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    padding: 8px;
}

.list-nav__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 10px;
    border-radius: 9px;
    font-size: 13.5px;
    color: var(--text);
    text-decoration: none;
    white-space: nowrap;
}

.list-nav__item:hover { background: var(--bg-sunken); }

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

.list-nav__count {
    font-size: 11px;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}

.list-nav__item--active .list-nav__count { color: var(--accent); }

@media (max-width: 900px) {
    .grid--sidebar-left { grid-template-columns: minmax(0, 1fr); }
    .list-nav {
        flex-direction: row;
        overflow-x: auto;
        overscroll-behavior-x: contain;
    }
    .list-nav__count { display: none; }
}

/* Small form controls used inline inside a table row. */
.narrow { max-width: 110px; }
.muted  { color: var(--text-dim); }
svg.dot { flex-shrink: 0; }

/* ---------------------------------------------------------------------
   28. The calendar

   The timeline is one grid: an hour rail on the left and a column per day
   beside it, all the same height, so 09:00 on Monday is level with 09:00
   on Thursday. Shows are absolutely positioned inside their day, with the
   top and height coming from a nonce'd style block the page writes.

   That block is why there are no style attributes anywhere in these
   views: style-src in this application's Content-Security-Policy has no
   'unsafe-inline', so an attribute would be dropped and every show would
   pile up at midnight.
   ------------------------------------------------------------------ */
.cal-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.cal-bar__nav { display: flex; gap: 6px; }

.cal-bar__views {
    display: flex;
    gap: 2px;
    padding: 3px;
    background: var(--bg-sunken);
    border-radius: 10px;
}

.cal-view {
    padding: 6px 12px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-dim);
    text-decoration: none;
    white-space: nowrap;
}

.cal-view:hover { color: var(--text); }

.cal-view--on {
    background: var(--bg-elevated);
    color: var(--text);
    box-shadow: var(--shadow-sm);
}

.cal-filters { margin-bottom: 14px; }

.cal-filters__summary {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 16px;
    cursor: pointer;
    font-size: 13.5px;
    font-weight: 500;
    list-style: none;
}

.cal-filters__summary::-webkit-details-marker { display: none; }

/* ---- The timeline -------------------------------------------------- */
.cal-timeline {
    display: grid;
    grid-template-columns: 54px minmax(0, 1fr);
    gap: 8px;
    background: var(--bg-elevated);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    padding: 12px 12px 12px 4px;
    overflow-x: auto;
}

.cal-rail {
    display: flex;
    flex-direction: column;

    /* The rail starts level with the canvas, not with the day headings,
       so the hour marks line up with the lines they name. */
    padding-top: 46px;
}

.cal-rail__hour {
    flex: 1 0 44px;
    position: relative;
    font-size: 10.5px;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
    text-align: right;
    padding-right: 6px;
}

.cal-rail__hour span { position: relative; top: -6px; }

.cal-days {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(118px, 1fr);
    gap: 6px;
    min-width: 0;
}

.cal-timeline--day .cal-days { grid-auto-columns: minmax(0, 1fr); }

.cal-day { min-width: 0; }

.cal-day__head {
    height: 46px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 1px;
    padding-bottom: 6px;
}

.cal-day__head a { text-decoration: none; color: inherit; }
.cal-day__name { display: block; font-size: 12px; font-weight: 650; }
.cal-day__date { display: block; font-size: 11px; color: var(--text-dim); }

.cal-day--today .cal-day__name { color: var(--accent); }

.cal-canvas {
    position: relative;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--hairline);
    border-radius: 10px;
    overflow: hidden;
    background: var(--bg);
}

.cal-day--today .cal-canvas { border-color: var(--accent); }

.cal-canvas__hour {
    flex: 1 0 44px;
    border-bottom: 1px solid var(--hairline);
}

.cal-canvas__hour:last-child { border-bottom: none; }

.cal-empty {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-size: 11.5px;
    color: var(--text-dim);
    text-align: center;
    padding: 8px;
}

.cal-empty a { color: var(--text-dim); text-decoration: none; }
.cal-empty a:hover { color: var(--accent); }

/* ---- One show ------------------------------------------------------ */
.cal-block {
    position: absolute;
    display: flex;
    flex-direction: column;
    gap: 1px;
    overflow: hidden;
    padding: 4px 6px;
    border-radius: 7px;
    border-left: 3px solid var(--chan, var(--accent));
    background: var(--bg-elevated);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
    color: var(--text);
    font-size: 11.5px;
    line-height: 1.25;
    min-height: 18px;
}

.cal-block:hover {
    box-shadow: var(--shadow-md, var(--shadow-lg));
    z-index: 3;
}

/* Not yet agreed, so it should not look agreed. */
.cal-block--unsettled {
    background: var(--bg-sunken);
    border-left-style: dashed;
}

.cal-block--unsettled .cal-block__title { color: var(--text-dim); }

.cal-block--cancelled .cal-block__title { text-decoration: line-through; }

.cal-block__time {
    font-size: 10px;
    color: var(--text-dim);
    font-variant-numeric: tabular-nums;
}

.cal-block__title {
    font-weight: 600;
    overflow-wrap: break-word;
}

.cal-block__chan,
.cal-block__crew {
    font-size: 10px;
    color: var(--text-dim);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cal-block__flag {
    align-self: flex-start;
    margin-top: 2px;
    font-size: 9.5px;
    padding: 1px 5px;
}

/* Anything under about 40 minutes has room for one line and no more. */
.cal-block[class*="ep-"] { container-type: size; }

/* ---- The month ----------------------------------------------------- */
.cal-month { padding: 10px; }

.cal-month__head,
.cal-month__week {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
}

.cal-month__head {
    margin-bottom: 4px;
    font-size: 10.5px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-dim);
    text-align: center;
}

.cal-month__week { margin-bottom: 4px; }

.cal-cell {
    min-height: 104px;
    border: 1px solid var(--hairline);
    border-radius: 9px;
    padding: 5px;
    background: var(--bg);
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.cal-cell--outside { opacity: 0.45; }
.cal-cell--today { border-color: var(--accent); }

.cal-cell__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 12px;
    font-weight: 600;
}

.cal-cell__head a { color: inherit; text-decoration: none; }
.cal-cell--today .cal-cell__head a { color: var(--accent); }

.cal-cell__add {
    color: var(--text-dim);
    text-decoration: none;
    font-weight: 400;
    padding: 0 4px;
    border-radius: 5px;
}

.cal-cell__add:hover { background: var(--accent-soft); color: var(--accent); }

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

.cal-chip {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 2px 4px;
    border-radius: 6px;
    font-size: 10.5px;
    text-decoration: none;
    color: var(--text);
    background: var(--bg-sunken);
    min-width: 0;
}

.cal-chip:hover { background: var(--accent-soft); }
.cal-chip--unsettled { opacity: 0.65; }
.cal-chip__time { color: var(--text-dim); font-variant-numeric: tabular-nums; }
.cal-chip__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.cal-more {
    font-size: 10.5px;
    color: var(--text-dim);
    text-decoration: none;
    padding-left: 4px;
}

/* ---- The year ------------------------------------------------------ */
.cal-year {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 14px;
}

.cal-year__month {
    background: var(--bg-elevated);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    padding: 10px;
}

.cal-year__name {
    display: block;
    font-size: 12.5px;
    font-weight: 650;
    margin-bottom: 6px;
    color: inherit;
    text-decoration: none;
}

.cal-year__grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}

.cal-year__dow {
    font-size: 9px;
    color: var(--text-dim);
    text-align: center;
}

.cal-year__pad { aspect-ratio: 1; }

.cal-year__day {
    aspect-ratio: 1;
    display: grid;
    place-items: center;
    font-size: 9.5px;
    border-radius: 4px;
    text-decoration: none;
    color: var(--text-dim);
    background: var(--bg-sunken);
}

.cal-year__day--b1 { background: var(--accent-softer); color: var(--text); }
.cal-year__day--b2 { background: var(--accent-soft); color: var(--text); }
.cal-year__day--b3 { background: var(--accent); color: #FFFFFF; }
.cal-year__day--b4 { background: var(--accent-dark, var(--accent)); color: #FFFFFF; }
.cal-year__day--today { outline: 2px solid var(--accent); outline-offset: 1px; }

.agenda-day th {
    background: var(--bg-sunken);
    font-size: 11.5px;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.agenda-day th a { color: inherit; text-decoration: none; }
.nowrap { white-space: nowrap; }

@media (max-width: 900px) {
    .cal-year { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .cal-cell { min-height: 78px; }
}

@media (max-width: 640px) {
    .cal-bar { justify-content: stretch; }
    .cal-bar__views { overflow-x: auto; }
    .cal-timeline { grid-template-columns: 42px minmax(0, 1fr); }
    .cal-days { grid-auto-columns: minmax(104px, 1fr); }
    .cal-year { grid-template-columns: minmax(0, 1fr); }
    .cal-cell { min-height: 62px; padding: 3px; }
    .cal-chip__title { display: none; }
    .cal-month__head { font-size: 9px; }
}

/* A row of checkboxes that wraps, for tags and extra channels. */
.checks {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.checks .check {
    padding: 6px 10px;
    border: 1px solid var(--hairline);
    border-radius: 8px;
    font-size: 12.5px;
}

.checks .check:has(input:checked) {
    border-color: var(--accent);
    background: var(--accent-softer);
}

/* A face on the roster. Small enough that a table row stays a row. */
.staff-photo {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    object-fit: cover;
    display: grid;
    place-items: center;
    background: var(--bg-sunken);
    font-size: 11px;
    font-weight: 600;
    color: var(--text-dim);
    flex-shrink: 0;
}

.staff-photo--lg { width: 76px; height: 76px; font-size: 22px; }

.chan-logo {
    width: 26px;
    height: 26px;
    object-fit: contain;
    border-radius: 5px;
}

/* Server-drawn bar chart. Scales to whatever space it is given. */
.bars { width: 100%; height: auto; display: block; }
.bars__label { font-size: 12px; fill: var(--text); }
.bars__value { font-size: 11px; fill: var(--text-dim); font-variant-numeric: tabular-nums; }

/* A post-production note. */
.review { padding: 10px 0; border-bottom: 1px solid var(--hairline); }
.review:last-child { border-bottom: none; }
.review__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.review__part { margin-top: 6px; }
.review__label {
    display: block;
    font-size: 10.5px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-dim);
}

/* The three numbers above the calendar. Across, not stacked: three
   full-width tiles push the grid itself a whole screen down the page, which
   is the one thing somebody opened the calendar to see. */
.schedule-stats {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
    margin-bottom: 16px;
}

.stat--compact { padding: 12px 14px; }
.stat--compact .stat__value { font-size: 22px; }
