﻿/* ==========================================================================
   RADAR - Daveng Consulting Engineers
   Brand theme. Palette sampled from the logo artwork and daveng.co.za.

   Imported as the FIRST line of wwwroot/css/site.css.
   Never hard-code a colour, font or spacing value in a view. Use these
   variables and the dv- utility classes.

   Look and feel follows the Daveng public site: white and light, bright blue
   for action, navy reserved for the sidebar and for type, green only for
   positive outcomes. Soft diffuse shadows rather than hard borders.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Self-hosted Montserrat. No CDN - the portal must render offline in Week 5.
   -------------------------------------------------------------------------- */
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('../lib/fonts/montserrat-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('../lib/fonts/montserrat-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
    /* --- Primary brand, sampled from the logo --- */
    --dv-navy:          #1A1348;
    --dv-navy-deep:     #120C36;
    --dv-navy-soft:     #2A2260;
    --dv-blue:          #0071A8;
    --dv-blue-bright:   #0089CC;
    --dv-blue-light:    #2B7BB9;
    --dv-blue-deep:     #114675;
    --dv-green:         #7EC245;
    --dv-green-deep:    #5E9B2C;
    --dv-grey:          #808285;

    /* --- Chrome gradient ------------------------------------------------
       Drawn from the statistics band on daveng.co.za, which runs indigo to
       blue. Deepened from the band itself: a full height sidebar carries far
       more area than a small stat tile, so the same values read washed out.
       Used for the sidebar and the other large coloured surfaces so they
       read as one family. Both ends clear AA against white text by a wide
       margin - 12.1:1 at the top, 8.2:1 at the bottom. */
    --dv-chrome-from:   #222A6A;
    --dv-chrome-mid:    #1B3B77;
    --dv-chrome-to:     #134A72;

    /* --- Neutrals --- */
    --dv-text:          #2B2F3A;
    --dv-text-muted:    #6B7280;
    --dv-line:          #C9D2E2;
    --dv-line-soft:     #E6EAF3;
    --dv-surface:       #FFFFFF;
    --dv-canvas:        #F5F7FC;

    /* --- Tints, used for icon chips and soft fills --- */
    --dv-tint-blue:     #E6F2FA;
    --dv-tint-navy:     #ECEAF5;
    --dv-tint-green:    #EDF7E2;
    --dv-tint-amber:    #FEF4E2;
    --dv-tint-red:      #FDECEC;

    /* --- States --- */
    --dv-success:       #7EC245;
    --dv-warning:       #E8930C;
    --dv-danger:        #DC2626;
    --dv-info:          #0EA5E9;

    /* --- Map pins (Task 16) ---
       The live map reads these at runtime, by name, out of this file. A pin
       colour is therefore the SAME colour as that condition everywhere else in
       the portal and stays that way when the theme changes. Writing literal
       colours into dv-map.js would be a second copy of the palette waiting to
       drift, so the script is given a variable name and never a colour.

       The five conditions are the five the Asset Condition list holds, in its
       order: Very Good, Good, Fair, Poor, Very Poor. Deliberately NOT a green
       to red ramp alone - Very Good and Good, and Poor and Very Poor, differ in
       lightness as well, so the map is still readable to a colour blind reader
       and in black and white on a printed sheet.

       The printed static map cannot read these: Google's Static API takes a
       literal colour. Data/Maps.cs holds those two literals and says so. */
    --dv-pin-very-good: #2E7D1F;
    --dv-pin-good:      #7EC245;
    --dv-pin-fair:      #E8930C;
    --dv-pin-poor:      #DC2626;
    --dv-pin-very-poor: #7F1010;
    --dv-pin-unknown:   #808285;

    /* A pin that is not a condition: an asset with an open defect, the start
       and end of an inspection round, a photograph. */
    --dv-pin-default:   #1F4E79;
    --dv-pin-alert:     #C1272D;
    --dv-pin-route:     #1F4E79;

    /* --- Structure ---

       Leon, 17 September 2026: "The look and feel is very subtle. I can hardly
       see the lines of the textboxes." The portal runs on a tablet, so every
       box now has a definite edge, every card is a framed panel with a light
       shadow, and the controls are big enough to hit with a thumb. */
    --dv-radius:        12px;
    --dv-radius-sm:     10px;
    --dv-radius-pill:   999px;
    --dv-shadow:        0 1px 2px rgba(26, 19, 72, 0.04), 0 4px 16px rgba(26, 19, 72, 0.06);
    --dv-shadow-hover:  0 2px 4px rgba(26, 19, 72, 0.05), 0 12px 28px rgba(26, 19, 72, 0.12);
    --dv-shadow-lg:     0 16px 48px rgba(26, 19, 72, 0.18);
    --dv-shadow-bar:    0 1px 3px rgba(26, 19, 72, 0.07);

    --dv-sidebar-w:     236px;
    --dv-rail-w:        66px;
    --dv-appbar-h:      60px;

    /* --- Spacing scale --- */
    --dv-space-1:       4px;
    --dv-space-2:       8px;
    --dv-space-3:       12px;
    --dv-space-4:       16px;
    --dv-space-5:       24px;
    --dv-space-6:       32px;

    /* --- Field use --- */
    --dv-tap:           48px;

    /* The edge of anything a person types into or picks from, and the edge of
       a card. Darker than the old --dv-line, which was almost invisible on a
       tablet in daylight. */
    --dv-field-line:    #C6CFDF;
    --dv-field-hover:   #A5B2C9;
    --dv-card-line:     #D5DDEB;

    /* --- Type --- */
    --dv-font-head:     'Montserrat', 'Segoe UI', Helvetica, Arial, sans-serif;
    --dv-font-body:     'Segoe UI', Helvetica, Arial, sans-serif;
    --dv-font-pdf:      Calibri, sans-serif;
}

/* --------------------------------------------------------------------------
   Bootstrap 5 variable overrides
   -------------------------------------------------------------------------- */
:root {
    --bs-primary:       #0071A8;
    --bs-primary-rgb:   0, 113, 168;
    --bs-secondary:     #808285;
    --bs-success:       #7EC245;
    --bs-danger:        #DC2626;
    --bs-warning:       #E8930C;
    --bs-info:          #0EA5E9;
    --bs-body-color:    #2B2F3A;
    --bs-body-bg:       #F5F7FC;
    --bs-border-color:  #E7EAF2;
    --bs-link-color:    #0071A8;
    --bs-link-hover-color: #114675;
    --bs-border-radius: 10px;
    --bs-font-sans-serif: 'Segoe UI', Helvetica, Arial, sans-serif;
}

html {
    font-size: 14px;
}

@media (min-width: 768px) {
    html {
        font-size: 15px;
    }
}

body {
    font-family: var(--dv-font-body);
    color: var(--dv-text);
    background: var(--dv-canvas);
    margin-bottom: 0;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6,
.dv-kpi-value,
.navbar-brand {
    font-family: var(--dv-font-head);
    color: var(--dv-navy);
    font-weight: 700;
}

h4 {
    font-size: 1.3rem;
    letter-spacing: -0.3px;
    margin-bottom: 0;
}

a {
    color: var(--dv-blue);
    text-decoration: none;
}

a:hover {
    color: var(--dv-blue-deep);
    text-decoration: underline;
}

hr {
    border-color: var(--dv-line);
    opacity: 1;
}

/* ==========================================================================
   APPLICATION SHELL
   ========================================================================== */

.dv-shell {
    display: flex;
    min-height: 100vh;
    align-items: stretch;
}

/* --------------------------------------------------------------------------
   Sidebar
   -------------------------------------------------------------------------- */
.dv-sidebar {
    width: var(--dv-sidebar-w);
    flex: 0 0 var(--dv-sidebar-w);
    background: linear-gradient(165deg, var(--dv-chrome-from) 0%, var(--dv-chrome-mid) 52%, var(--dv-chrome-to) 100%);
    color: rgba(255, 255, 255, 0.82);
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 1040;
    /* Visible, not hidden: the Configure and user menus open upward out of the
       sidebar foot and must not be clipped by it. Scrolling is handled by
       .dv-nav alone. */
    overflow: visible;
    transition: width 0.2s ease, flex-basis 0.2s ease;
}

/* A soft edge, echoing the logo gradient. */
.dv-sidebar::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: 1px;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.28), rgba(126, 194, 69, 0.4) 60%, transparent);
}

/* --- Brand row --- */
.dv-brand-row {
    display: flex;
    align-items: center;
    gap: var(--dv-space-1);
    padding: var(--dv-space-3) var(--dv-space-3) var(--dv-space-3) 14px;
    flex-shrink: 0;
    position: relative;
}

.dv-brand-row::after {
    content: '';
    position: absolute;
    left: 14px;
    right: 14px;
    bottom: 0;
    height: 1px;
    background: linear-gradient(90deg, var(--dv-blue-bright), rgba(126, 194, 69, 0.6) 60%, transparent);
    opacity: 0.7;
}

.dv-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    min-width: 0;
    flex: 1;
}

.dv-brand:hover {
    text-decoration: none;
}

.dv-brand-mark {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    border-radius: var(--dv-radius-sm);
    background: var(--dv-surface);
    padding: 3px;
    object-fit: contain;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
}

.dv-brand-text {
    line-height: 1.15;
    min-width: 0;
    overflow: hidden;
}

.dv-brand-name {
    display: block;
    font-family: var(--dv-font-head);
    font-size: 1.05rem;
    font-weight: 800;
    letter-spacing: 3px;
    color: #FFFFFF;
    white-space: nowrap;
}

/* Sized to clear the collapse button without clipping - the full wordmark
   must always read "Daveng Consulting Engineers". */
.dv-brand-sub {
    display: block;
    font-size: 0.48rem;
    letter-spacing: 0.3px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.68);
    white-space: nowrap;
}

/* --- Collapse to rail --- */
.dv-rail-toggle {
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    border-radius: var(--dv-radius-sm);
    background: rgba(255, 255, 255, 0.14);
    color: rgba(255, 255, 255, 0.8);
    transition: background 0.15s, color 0.15s, transform 0.2s;
}

.dv-rail-toggle svg {
    width: 15px;
    height: 15px;
}

.dv-rail-toggle:hover {
    background: rgba(255, 255, 255, 0.16);
    color: #FFFFFF;
}

/* --- Navigation --- */
.dv-nav {
    flex: 1 1 auto;
    padding: var(--dv-space-3) 10px var(--dv-space-4);
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, 0.16) transparent;
}

.dv-nav::-webkit-scrollbar {
    width: 5px;
}

.dv-nav::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.16);
    border-radius: var(--dv-radius-pill);
}

.dv-nav-group-label {
    font-size: 0.58rem;
    font-weight: 800;
    letter-spacing: 1.1px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.58);
    padding: var(--dv-space-4) 10px var(--dv-space-1);
    white-space: nowrap;
    overflow: hidden;
}

.dv-nav-item {
    display: flex;
    align-items: center;
    gap: 11px;
    width: 100%;
    padding: 9px 10px;
    margin-bottom: 2px;
    border: none;
    border-radius: var(--dv-radius-sm);
    background: transparent;
    color: rgba(255, 255, 255, 0.88);
    font-size: 0.82rem;
    font-weight: 600;
    text-align: left;
    text-decoration: none;
    white-space: nowrap;
    position: relative;
    transition: background 0.15s ease, color 0.15s ease;
}

.dv-nav-item:hover {
    background: rgba(255, 255, 255, 0.14);
    color: #FFFFFF;
    text-decoration: none;
}

/* The sidebar is now blue, so the selected item inverts to white rather than
   trying to be a brighter blue against blue. */
.dv-nav-item.active {
    background: var(--dv-surface);
    color: var(--dv-navy);
    font-weight: 700;
    box-shadow: 0 2px 10px rgba(9, 24, 62, 0.24);
}

.dv-nav-item.active:hover {
    background: var(--dv-surface);
    color: var(--dv-navy);
}

.dv-nav-item.active .dv-nav-icon {
    opacity: 1;
    color: var(--dv-blue);
}

.dv-nav-item-sub {
    font-size: 0.78rem;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.76);
    padding-left: 18px;
}

.dv-nav-item-sub.active {
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-nav-item-sub .dv-nav-icon {
    width: 15px;
    height: 15px;
}

/* A module that sits underneath another, Documents under Reviews, used to be
   drawn one step in with a short rule beside it. Leon, 5 September 2026: it
   should look like every other item, so the class is no longer applied and the
   rules that drew it are gone rather than left here unused.

   The relationship is carried by the ORDER of the menu, which is where the rest
   of the grouping lives too. */

.dv-nav-icon {
    width: 17px;
    height: 17px;
    flex-shrink: 0;
    opacity: 0.82;
}

.dv-nav-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Release marker on a module that is not built yet. */
.dv-nav-caret {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    margin-left: auto;
    opacity: 0.55;
    transition: transform 0.18s ease;
}

[aria-expanded="true"] > .dv-nav-caret {
    transform: rotate(180deg);
}

/* --- Sidebar foot: Configure and the signed in user --- */
.dv-sidebar-foot {
    flex-shrink: 0;
    padding: var(--dv-space-2) 10px var(--dv-space-3);
}

.dv-nav-configure {
    margin-bottom: var(--dv-space-2);
}

.dv-sidebar-user {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 9px 8px;
    border: none;
    border-top: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 0;
    background: transparent;
    text-align: left;
    white-space: nowrap;
    transition: background 0.15s ease;
}

.dv-sidebar-user:hover {
    background: rgba(255, 255, 255, 0.12);
}

.dv-avatar {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--dv-blue-bright), var(--dv-green));
    color: #FFFFFF;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--dv-font-head);
    font-size: 0.7rem;
    font-weight: 800;
}

.dv-sidebar-user-text {
    flex: 1;
    min-width: 0;
    overflow: hidden;
}

.dv-sidebar-user-name {
    display: block;
    font-size: 0.78rem;
    font-weight: 700;
    color: #FFFFFF;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dv-sidebar-user-role {
    display: block;
    font-size: 0.65rem;
    color: rgba(255, 255, 255, 0.72);
}

/* Menus that open upward out of the sidebar foot. */
.dv-foot-menu {
    min-width: 220px;
    border: none;
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow-lg);
    padding: var(--dv-space-2);
    margin-bottom: var(--dv-space-2) !important;
}

.dv-foot-menu-head {
    font-size: 0.6rem;
    font-weight: 800;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    color: var(--dv-text-muted);
    padding: var(--dv-space-1) var(--dv-space-2) var(--dv-space-2);
}

.dv-foot-menu .dropdown-item {
    border-radius: var(--dv-radius-sm);
    font-size: 0.85rem;
    font-weight: 500;
    padding: 8px 10px;
}

.dv-foot-menu .dropdown-item:hover {
    background: var(--dv-tint-blue);
    color: var(--dv-blue-deep);
}

/* --------------------------------------------------------------------------
   Collapsed rail
   -------------------------------------------------------------------------- */
.dv-shell.dv-rail .dv-sidebar {
    width: var(--dv-rail-w);
    flex-basis: var(--dv-rail-w);
}

.dv-shell.dv-rail .dv-main {
    margin-left: var(--dv-rail-w);
}

.dv-shell.dv-rail .dv-brand-text,
.dv-shell.dv-rail .dv-nav-label,
.dv-shell.dv-rail .dv-nav-caret,
.dv-shell.dv-rail .dv-sidebar-user-text {
    display: none;
}

.dv-shell.dv-rail .dv-brand-row {
    flex-direction: column;
    gap: var(--dv-space-2);
    padding-left: var(--dv-space-2);
    padding-right: var(--dv-space-2);
}

.dv-shell.dv-rail .dv-brand {
    justify-content: center;
}

.dv-shell.dv-rail .dv-rail-toggle svg {
    transform: rotate(180deg);
}

.dv-shell.dv-rail .dv-nav,
.dv-shell.dv-rail .dv-sidebar-foot {
    padding-left: var(--dv-space-2);
    padding-right: var(--dv-space-2);
}

.dv-shell.dv-rail .dv-nav-item,
.dv-shell.dv-rail .dv-sidebar-user {
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
    gap: 0;
}

.dv-shell.dv-rail .dv-nav-group-label {
    height: 1px;
    padding: 0;
    margin: var(--dv-space-3) auto var(--dv-space-2);
    width: 22px;
    background: rgba(255, 255, 255, 0.14);
    color: transparent;
    overflow: hidden;
}

/* Name the icon on hover when the labels are gone. The nav scrolls, so a
   tooltip drawn inside it would be clipped - site.js positions this one
   against the viewport instead. */
.dv-rail-tip {
    position: fixed;
    background: var(--dv-navy);
    color: #FFFFFF;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 6px 11px;
    border-radius: var(--dv-radius-sm);
    box-shadow: var(--dv-shadow-lg);
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.12s ease;
    z-index: 1200;
}

.dv-rail-tip.show {
    opacity: 1;
}

/* Applied by the inline script before first paint so the rail never flashes. */
.dv-pre-collapsed .dv-sidebar {
    width: var(--dv-rail-w);
    flex-basis: var(--dv-rail-w);
}

.dv-pre-collapsed .dv-main {
    margin-left: var(--dv-rail-w);
}

/* --------------------------------------------------------------------------
   Main column and app bar
   -------------------------------------------------------------------------- */
.dv-main {
    flex: 1 1 auto;
    min-width: 0;
    margin-left: var(--dv-sidebar-w);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    transition: margin-left 0.2s ease;
}

/* Light app bar, echoing the white header band on the Daveng website. */
.dv-appbar {
    height: var(--dv-appbar-h);
    background: var(--dv-surface);
    border-bottom: 1px solid var(--dv-line);
    box-shadow: var(--dv-shadow-bar);
    display: flex;
    align-items: center;
    gap: var(--dv-space-3);
    padding: 0 var(--dv-space-5);
    position: sticky;
    top: 0;
    z-index: 1030;
}

.dv-appbar-title {
    font-family: var(--dv-font-head);
    font-size: 1rem;
    font-weight: 700;
    color: var(--dv-navy);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    letter-spacing: -0.2px;
}

/* The scope note that used to sit under the page heading. */
.dv-appbar-subtitle {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--dv-blue-deep);
    background: var(--dv-tint-blue);
    border-radius: var(--dv-radius-pill);
    padding: 4px 12px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 340px;
    flex-shrink: 1;
}

.dv-appbar-centre {
    flex: 1 1 auto;
    display: flex;
    justify-content: flex-end;
    min-width: 0;
}

/* With the heading in the app bar the content can start straight in. */
.dv-content > .dv-toolbar:first-child,
.dv-content > .row:first-child,
.dv-content > .dv-card:first-child,
.dv-content > form:first-child {
    margin-top: 0;
}

@media (max-width: 991.98px) {
    .dv-appbar-subtitle {
        display: none;
    }
}

.dv-hamburger {
    display: none;
    background: transparent;
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-sm);
    color: var(--dv-navy);
    width: 38px;
    height: 34px;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.dv-hamburger svg {
    width: 18px;
    height: 18px;
}

.dv-content {
    flex: 1 1 auto;
    padding: var(--dv-space-5) var(--dv-space-5) var(--dv-space-6);
    min-width: 0;
}

.dv-footer {
    padding: var(--dv-space-3) var(--dv-space-5);
    border-top: 1px solid var(--dv-line);
    background: var(--dv-surface);
    color: var(--dv-text-muted);
    font-size: 0.73rem;
}

/* --- Slide-over drawer below 992px --- */
.dv-scrim {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(18, 12, 54, 0.5);
    z-index: 1035;
}

@media (max-width: 991.98px) {
    .dv-sidebar,
    .dv-shell.dv-rail .dv-sidebar,
    .dv-pre-collapsed .dv-sidebar {
        width: var(--dv-sidebar-w);
        flex-basis: var(--dv-sidebar-w);
        transform: translateX(-100%);
        transition: transform 0.22s ease-in-out;
        box-shadow: var(--dv-shadow-lg);
    }

    .dv-shell.dv-drawer-open .dv-sidebar {
        transform: translateX(0);
    }

    .dv-shell.dv-drawer-open .dv-scrim {
        display: block;
    }

    /* The rail is a desktop idea. On a tablet the drawer is always full width. */
    .dv-shell.dv-rail .dv-brand-text,
    .dv-shell.dv-rail .dv-nav-label,
        .dv-shell.dv-rail .dv-nav-caret,
    .dv-shell.dv-rail .dv-sidebar-user-text {
        display: block;
    }

    .dv-shell.dv-rail .dv-nav-item,
    .dv-shell.dv-rail .dv-sidebar-user {
        justify-content: flex-start;
        gap: 11px;
        padding-left: 10px;
    }

    .dv-shell.dv-rail .dv-brand-row {
        flex-direction: row;
    }

    .dv-shell.dv-rail .dv-nav-group-label {
        height: auto;
        width: auto;
        background: none;
        color: rgba(255, 255, 255, 0.32);
        padding: var(--dv-space-4) 10px var(--dv-space-1);
        margin: 0;
    }

    .dv-rail-toggle {
        display: none;
    }

    .dv-main,
    .dv-shell.dv-rail .dv-main,
    .dv-pre-collapsed .dv-main {
        margin-left: 0;
    }

    .dv-hamburger {
        display: inline-flex;
    }

    .dv-appbar,
    .dv-content {
        padding-left: var(--dv-space-3);
        padding-right: var(--dv-space-3);
    }
}

/* ==========================================================================
   PROJECT CONTEXT SELECTOR
   ========================================================================== */

.dv-project-selector {
    position: relative;
    max-width: 460px;
    width: 100%;
}

.dv-project-toggle {
    display: flex;
    align-items: center;
    gap: var(--dv-space-2);
    width: 100%;
    padding: 6px 14px;
    background: var(--dv-tint-blue);
    border: 1px solid rgba(0, 113, 168, 0.18);
    border-radius: var(--dv-radius-pill);
    color: var(--dv-navy);
    font-size: 0.8rem;
    font-weight: 600;
    text-align: left;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.dv-project-toggle:hover:not(:disabled) {
    background: #DAEBF7;
    border-color: var(--dv-blue);
    color: var(--dv-navy);
}

.dv-project-toggle:disabled {
    background: var(--dv-line-soft);
    border-color: var(--dv-line);
    color: var(--dv-text-muted);
    cursor: not-allowed;
}

.dv-project-toggle .dv-nav-icon {
    color: var(--dv-blue);
    opacity: 1;
}

.dv-project-number {
    font-family: var(--dv-font-head);
    font-weight: 800;
    letter-spacing: 0.2px;
    white-space: nowrap;
}

.dv-project-name {
    color: var(--dv-text-muted);
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.dv-project-menu {
    min-width: 360px;
    max-height: 62vh;
    overflow-y: auto;
    padding: var(--dv-space-2);
    border: none;
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow-lg);
}

.dv-project-menu .dropdown-item {
    border-radius: var(--dv-radius-sm);
    padding: 9px 10px;
    font-size: 0.83rem;
    white-space: normal;
}

.dv-project-menu .dropdown-item.active,
.dv-project-menu .dropdown-item:active {
    background: var(--dv-tint-blue);
    color: var(--dv-navy);
}

.dv-project-menu .dropdown-item:hover {
    background: var(--dv-line-soft);
}

.dv-project-filter {
    margin-bottom: var(--dv-space-2);
}

/* ==========================================================================
   BUTTONS
   ========================================================================== */

.btn {
    border-radius: var(--dv-radius-sm);
    font-weight: 600;
    transition: all 0.18s ease;
    /* A thumb on a tablet, not a mouse pointer. */
    min-height: 42px;
}

.btn-sm {
    font-size: 0.8rem;
    padding: 7px 15px;
    min-height: 38px;
}

/* A link dressed as a button, in a table row or beside a heading, keeps its
   own size. */
.btn-link.table-action-link,
.btn-link.p-0 {
    min-height: 0;
}

/* Blue is action, per the brand guide. */
.btn-primary {
    background: linear-gradient(135deg, var(--dv-blue-bright), var(--dv-blue));
    border: none;
    color: #FFFFFF;
    box-shadow: 0 2px 8px rgba(0, 113, 168, 0.28);
}

.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
    background: linear-gradient(135deg, var(--dv-blue), var(--dv-blue-deep));
    color: #FFFFFF;
    box-shadow: 0 4px 14px rgba(0, 113, 168, 0.36);
    transform: translateY(-1px);
}

.btn-secondary {
    background: var(--dv-surface);
    border: 1px solid var(--dv-line);
    color: var(--dv-text);
}

.btn-secondary:hover,
.btn-secondary:focus,
.btn-secondary:active {
    background: var(--dv-tint-blue);
    border-color: rgba(0, 113, 168, 0.35);
    color: var(--dv-blue-deep);
}

.btn-danger {
    background: var(--dv-danger);
    border-color: var(--dv-danger);
}

.btn-danger:hover,
.btn-danger:focus {
    background: #B91C1C;
    border-color: #B91C1C;
}

.btn:focus-visible {
    box-shadow: 0 0 0 0.2rem rgba(0, 113, 168, 0.3);
    outline: none;
}

/* ==========================================================================
   FORMS
   ========================================================================== */

.form-control,
.form-select {
    border: 1px solid var(--dv-field-line);
    border-radius: var(--dv-radius-sm);
    font-size: 0.92rem;
    color: var(--dv-text);
    background-color: var(--dv-surface);
    min-height: var(--dv-tap);
    padding: 0.55rem 0.8rem;
}

/* A box inside a table row or a toolbar stays compact, and keeps the same
   edge so it still reads as a box. */
.form-control-sm,
.form-select-sm {
    border: 1px solid var(--dv-field-line);
    border-radius: var(--dv-radius-sm);
    min-height: 40px;
}

.form-control:hover,
.form-select:hover,
.form-control-sm:hover,
.form-select-sm:hover {
    border-color: var(--dv-field-hover);
}

.form-control:focus,
.form-select:focus {
    border-color: var(--dv-blue);
    box-shadow: 0 0 0 0.2rem rgba(0, 113, 168, 0.2);
}

.form-control::placeholder {
    color: #98A2B3;
}

/* The unit beside a box, such as the m on an instructed quantity, carries the
   same edge and height as the box itself. */
.input-group-text {
    border: 1px solid var(--dv-field-line);
    background: var(--dv-canvas);
    color: var(--dv-text);
    font-weight: 600;
}

.input-group > .form-control,
.input-group > .form-select {
    min-height: var(--dv-tap);
}

textarea.form-control {
    min-height: 140px;
}

.col-form-label,
.form-label {
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--dv-navy);
}

/* Leon, 17 September 2026: no up and down arrows on a quantity. They are a
   mouse control, they sit under a thumb on a tablet, and a quantity is typed
   rather than nudged one at a time. */
input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    appearance: none;
    margin: 0;
}

/* A tick box carries the same soft corner as the boxes around it. A radio
   stays round. */
.form-check-input {
    width: 1.3rem;
    height: 1.3rem;
    border: 1px solid #B4BFD3;
}

.form-check-input[type="checkbox"] {
    border-radius: 5px;
}

.form-check-label {
    font-size: 0.9rem;
}

.form-check-input:checked {
    background-color: var(--dv-blue);
    border-color: var(--dv-blue);
}

.form-text {
    color: var(--dv-text-muted);
    font-size: 0.78rem;
}

.text-danger {
    color: var(--dv-danger) !important;
    font-size: 0.8rem;
}

/* ==========================================================================
   TABLES
   ========================================================================== */

.table-wrapper {
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    overflow: hidden;
    overflow-x: auto;
}

.table {
    margin-bottom: 0;
    border-collapse: collapse;
}

/* Table headers use the same chrome family as the sidebar, so the whole
   portal reads as one colour scheme. */
.table > thead th,
.table thead th {
    background: linear-gradient(90deg, var(--dv-chrome-from), var(--dv-chrome-mid));
    color: #FFFFFF;
    font-weight: 700;
    text-transform: uppercase;
    font-size: 0.72rem;
    letter-spacing: 0.6px;
    padding: 11px 12px;
    border-bottom: none;
    border-top: none;
    vertical-align: middle;
    white-space: nowrap;
}

.table > tbody td,
.table tbody td {
    font-size: 0.85rem;
    padding: 10px 12px;
    border-top: 1px solid var(--dv-line);
    border-bottom: none;
    border-color: var(--dv-line);
    vertical-align: middle;
    color: var(--dv-text);
}

.table > tbody > tr:first-child > td {
    border-top: none;
}

.table-hover > tbody > tr:hover > * {
    background-color: var(--dv-tint-blue);
    color: var(--dv-navy);
}

.table-striped > tbody > tr:nth-of-type(odd) > *,
.table-striped > tbody > tr:nth-of-type(even) > * {
    background-color: var(--dv-surface);
}

.table-action-link {
    color: var(--dv-blue);
    font-weight: 600;
    font-size: 0.8rem;
    text-decoration: none;
    white-space: nowrap;
}

.table-action-link:hover {
    color: var(--dv-blue-deep);
    text-decoration: underline;
}

/* SNAG S4. A delete is a POST now, so the control beside Edit and View is a
   submit button rather than a link. It has to read as the same thing, so the
   browser's own button chrome comes off and .table-action-link does the rest.
   The tap target rule still applies: nothing here changes its size. */
.dv-link-button {
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    line-height: inherit;
    cursor: pointer;
    vertical-align: baseline;
}

.dv-link-button:focus-visible {
    outline: 2px solid var(--dv-blue);
    outline-offset: 2px;
}

/* SNAG S3. An opened sync batch sits under its own row, set back so it
   reads as belonging to the row above rather than as another batch. The
   device reference is long and meaningless to read, so it is small and wraps
   rather than pushing the table sideways. */
.dv-sync-detail > td {
    background-color: var(--dv-surface);
    padding: 0.5rem 0.75rem 0.75rem 1.5rem;
}

/* SNAG S11. A message can be a paragraph of SQL Server's own words, so it
   wraps rather than pushing the table sideways, and the stack trace keeps its
   line breaks and scrolls inside its own box. */
.dv-error-message {
    max-width: 40rem;
    word-break: break-word;
}

.dv-error-detail {
    font-size: 0.72rem;
    white-space: pre-wrap;
    word-break: break-word;
    max-height: 22rem;
    overflow: auto;
}

.dv-sync-ref {
    font-size: 0.72rem;
    word-break: break-all;
    color: var(--dv-muted, #6b7280);
}

.table-action-sep {
    color: var(--dv-line);
    margin: 0 3px;
}

/* --- Sortable --- */
.sortable th {
    cursor: pointer;
    position: relative;
    user-select: none;
    padding-right: 24px;
}

.sortable th:hover {
    background: linear-gradient(90deg, var(--dv-chrome-mid), var(--dv-chrome-to));
}

.sortable th::after {
    content: '\2195';
    position: absolute;
    right: 9px;
    opacity: 0.4;
    font-size: 0.7rem;
}

.sortable th.sort-asc::after {
    content: '\2191';
    opacity: 1;
}

.sortable th.sort-desc::after {
    content: '\2193';
    opacity: 1;
}

.sortable th.no-sort {
    cursor: default;
    padding-right: 12px;
}

.sortable th.no-sort::after {
    content: '';
}

.sortable th.no-sort:hover {
    background: linear-gradient(90deg, var(--dv-chrome-from), var(--dv-chrome-mid));
}

/* ==========================================================================
   CARDS, PAGE HEADER, KPI TILES
   ========================================================================== */

.dv-page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-3);
    margin-bottom: var(--dv-space-2);
}

.dv-page-head h4 {
    margin: 0;
}

.dv-page-sub {
    font-size: 0.8rem;
    color: var(--dv-text-muted);
}

.dv-card,
.card {
    background: var(--dv-surface);
    border: 1px solid var(--dv-card-line);
    border-radius: var(--dv-radius);
    box-shadow: 0 1px 3px rgba(16, 24, 40, 0.06);
}

.dv-card {
    padding: var(--dv-space-5);
    margin-bottom: var(--dv-space-4);
}

/* The card's own heading sits on a tinted strip across the top of the panel,
   whether it stands alone or shares its row with a button. Leon, 18 September
   2026: the round's Route Position panel is an ordinary card now, so it takes
   the strip with the rest of them. */
.dv-card > .dv-card-heading,
.dv-card > div:first-child:has(> .dv-card-heading) {
    background: linear-gradient(180deg, #F7F9FE, #F1F5FC);
    border-bottom: 1px solid #DFE5F1;
    margin: calc(var(--dv-space-5) * -1) calc(var(--dv-space-5) * -1) var(--dv-space-5);
    padding: var(--dv-space-4) var(--dv-space-5);
    border-radius: calc(var(--dv-radius) - 1px) calc(var(--dv-radius) - 1px) 0 0;
}

.dv-card > div:first-child:has(> .dv-card-heading) > .dv-card-heading {
    margin: 0;
    padding: 0;
    background: none;
    border-bottom: none;
}

.dv-card-heading {
    font-family: var(--dv-font-head);
    font-size: 0.72rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.9px;
    color: var(--dv-navy);
    margin-bottom: var(--dv-space-4);
    padding-bottom: var(--dv-space-3);
    border-bottom: 1px solid var(--dv-line);
    display: flex;
    align-items: center;
    gap: var(--dv-space-2);
}

.dv-card-heading::before {
    content: '';
    width: 3px;
    height: 14px;
    background: linear-gradient(180deg, var(--dv-blue-bright), var(--dv-green));
    border-radius: var(--dv-radius-pill);
    flex-shrink: 0;
}

/* --- KPI tiles --- */
.dv-kpi {
    display: block;
    background: var(--dv-surface);
    border: none;
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    /* Tightened: these sit above the content on every register and were
       costing about a third of the fold before anything useful appeared. */
    padding: var(--dv-space-3) var(--dv-space-4);
    height: 100%;
    text-decoration: none;
    position: relative;
    overflow: hidden;
    transition: box-shadow 0.2s ease, transform 0.2s ease;
}

/* A colour bar down the leading edge rather than a heavy underline. */
.dv-kpi::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    background: var(--dv-blue);
}

a.dv-kpi:hover {
    box-shadow: var(--dv-shadow-hover);
    transform: translateY(-3px);
    text-decoration: none;
}

.dv-kpi-navy::before  { background: linear-gradient(180deg, var(--dv-navy), var(--dv-navy-soft)); }
.dv-kpi-blue::before  { background: linear-gradient(180deg, var(--dv-blue-bright), var(--dv-blue)); }
.dv-kpi-green::before { background: linear-gradient(180deg, var(--dv-green), var(--dv-green-deep)); }
.dv-kpi-grey::before  { background: linear-gradient(180deg, #A2A5AC, var(--dv-grey)); }

.dv-kpi-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--dv-space-3);
}

/* Circular icon chip, echoing the round icons on the Daveng website. */
.dv-kpi-icon {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--dv-tint-blue);
    color: var(--dv-blue);
}

.dv-kpi-icon svg {
    width: 16px;
    height: 16px;
}

.dv-kpi-navy  .dv-kpi-icon { background: var(--dv-tint-navy);  color: var(--dv-navy); }
.dv-kpi-blue  .dv-kpi-icon { background: var(--dv-tint-blue);  color: var(--dv-blue); }
.dv-kpi-green .dv-kpi-icon { background: var(--dv-tint-green); color: var(--dv-green-deep); }
.dv-kpi-grey  .dv-kpi-icon { background: var(--dv-line-soft);  color: var(--dv-grey); }

.dv-kpi-value {
    font-family: var(--dv-font-head);
    font-size: 1.4rem;
    font-weight: 800;
    color: var(--dv-navy);
    line-height: 1.1;
    letter-spacing: -0.5px;
}

.dv-kpi-label {
    font-size: 0.62rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.7px;
    color: var(--dv-text-muted);
    margin-top: 2px;
}

.dv-kpi-hint {
    font-size: 0.72rem;
    color: var(--dv-text-muted);
    margin-top: var(--dv-space-2);
    padding-top: var(--dv-space-2);
    border-top: 1px solid var(--dv-line);
}

.dv-kpi-placeholder {
    background: transparent;
    border: 1px dashed var(--dv-line);
    box-shadow: none;
    opacity: 0.8;
}

.dv-kpi-placeholder::before {
    display: none;
}

.dv-kpi-placeholder .dv-kpi-value,
.dv-kpi-placeholder .dv-kpi-label {
    color: var(--dv-grey);
}

.dv-kpi-placeholder .dv-kpi-icon {
    background: var(--dv-line-soft);
    color: var(--dv-line);
}

/* ==========================================================================
   CHARTS  (pure CSS and inline SVG - no external library, works offline)
   ========================================================================== */

.dv-barchart {
    display: block;
}

.dv-bar-row {
    margin-bottom: var(--dv-space-3);
}

.dv-bar-row:last-child {
    margin-bottom: 0;
}

.dv-bar-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: 0.8rem;
    margin-bottom: 5px;
}

.dv-bar-name {
    color: var(--dv-text);
    font-weight: 600;
}

.dv-bar-value {
    font-family: var(--dv-font-head);
    font-weight: 800;
    color: var(--dv-navy);
}

.dv-bar-track {
    height: 8px;
    border-radius: var(--dv-radius-pill);
    background: var(--dv-line-soft);
    overflow: hidden;
}

.dv-bar-fill {
    height: 100%;
    border-radius: var(--dv-radius-pill);
    background: linear-gradient(90deg, var(--dv-blue-bright), var(--dv-blue));
    transition: width 0.5s ease;
}

.dv-bar-fill.good  { background: linear-gradient(90deg, var(--dv-green), var(--dv-green-deep)); }
.dv-bar-fill.fair  { background: linear-gradient(90deg, #F5B942, var(--dv-warning)); }
.dv-bar-fill.poor  { background: linear-gradient(90deg, #EF6B6B, var(--dv-danger)); }
.dv-bar-fill.info  { background: linear-gradient(90deg, var(--dv-blue-bright), var(--dv-blue)); }
.dv-bar-fill.neutral { background: linear-gradient(90deg, #B9BDC6, var(--dv-grey)); }

/* Progress ring */
.dv-ring-wrap {
    display: flex;
    align-items: center;
    gap: var(--dv-space-5);
}

.dv-ring {
    width: 116px;
    height: 116px;
    flex-shrink: 0;
    transform: rotate(-90deg);
}

.dv-ring-track {
    fill: none;
    stroke: var(--dv-line-soft);
    stroke-width: 11;
}

.dv-ring-fill {
    fill: none;
    stroke: url(#dvRingGradient);
    stroke-width: 11;
    stroke-linecap: round;
    transition: stroke-dasharray 0.6s ease;
}

.dv-ring-label {
    text-align: center;
}

.dv-ring-figure {
    font-family: var(--dv-font-head);
    font-size: 1.7rem;
    font-weight: 800;
    color: var(--dv-navy);
    line-height: 1;
}

.dv-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-3);
    margin-top: var(--dv-space-3);
    font-size: 0.78rem;
    color: var(--dv-text-muted);
}

.dv-legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
}

.dv-legend-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* C37. A key that carries the number and the share, not just the colour. The
   label stays muted and the value does not, because the value is the thing
   being looked up. */
.dv-legend-label {
    color: var(--dv-text-muted);
}

.dv-legend-value {
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-legend-share {
    color: var(--dv-text-muted);
}

/* ==========================================================================
   STATUS BADGES
   ========================================================================== */

.dv-badge {
    display: inline-block;
    font-family: var(--dv-font-head);
    font-size: 0.65rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 4px 10px;
    border-radius: var(--dv-radius-pill);
    white-space: nowrap;
    line-height: 1.5;
}

.dv-badge-good    { background: var(--dv-tint-green); color: var(--dv-green-deep); }
.dv-badge-fair    { background: var(--dv-tint-amber); color: #B4700A; }
.dv-badge-poor    { background: var(--dv-tint-red);   color: #B91C1C; }
.dv-badge-info    { background: var(--dv-tint-blue);  color: var(--dv-blue-deep); }
.dv-badge-neutral { background: var(--dv-line-soft);  color: var(--dv-text-muted); }

/* ==========================================================================
   LIST TOOLBAR
   ========================================================================== */

.dv-toolbar {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: var(--dv-space-2);
    margin-bottom: var(--dv-space-4);
}

.dv-toolbar-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-2);
}

.dv-search {
    width: 260px;
}

.dv-filter-select {
    width: 180px;
}

.dv-actions-col {
    width: 220px;
}

.dv-tick-col {
    width: 80px;
}

.dv-icon-btn {
    padding: 6px 8px;
    line-height: 1;
    display: inline-flex;
    align-items: center;
}

.dv-icon-btn img {
    width: 20px;
    height: 20px;
    display: block;
}

.dv-viewswitch {
    display: inline-flex;
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-sm);
    overflow: hidden;
    background: var(--dv-surface);
}

.dv-viewswitch a {
    padding: 7px 14px;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--dv-text-muted);
    text-decoration: none;
    border-right: 1px solid var(--dv-line);
}

.dv-viewswitch a:last-child {
    border-right: none;
}

.dv-viewswitch a.active {
    background: var(--dv-blue);
    color: #FFFFFF;
}

.dv-viewswitch a:hover:not(.active) {
    background: var(--dv-tint-blue);
    color: var(--dv-blue-deep);
    text-decoration: none;
}

/* ==========================================================================
   EMPTY STATE
   ========================================================================== */

.dv-empty {
    background: var(--dv-surface);
    border: 1px dashed var(--dv-line);
    border-radius: var(--dv-radius);
    padding: var(--dv-space-6) var(--dv-space-5);
    text-align: center;
}

.dv-empty-title {
    font-family: var(--dv-font-head);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--dv-navy);
    margin-bottom: var(--dv-space-2);
}

.dv-empty-text {
    font-size: 0.87rem;
    color: var(--dv-text-muted);
    margin-bottom: var(--dv-space-4);
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
}

/* ==========================================================================
   DETAIL PATTERN
   ========================================================================== */

.dv-detail-head {
    background: linear-gradient(125deg, var(--dv-chrome-from) 0%, var(--dv-chrome-mid) 55%, var(--dv-chrome-to) 100%);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-5);
    margin-bottom: var(--dv-space-4);
    color: rgba(255, 255, 255, 0.85);
    position: relative;
    overflow: hidden;
}

.dv-detail-head::after {
    content: '';
    position: absolute;
    right: -60px;
    top: -80px;
    width: 260px;
    height: 260px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(0, 137, 204, 0.28), transparent 70%);
    pointer-events: none;
}

.dv-detail-head-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--dv-space-3);
    margin-bottom: var(--dv-space-4);
    position: relative;
}

.dv-detail-number {
    font-family: var(--dv-font-head);
    font-size: 1.5rem;
    font-weight: 800;
    color: #FFFFFF;
    letter-spacing: -0.4px;
    line-height: 1.1;
}

.dv-detail-name {
    font-size: 0.9rem;
    color: rgba(255, 255, 255, 0.72);
    margin-top: 3px;
}

.dv-detail-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-5);
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.6);
    position: relative;
}

.dv-detail-meta strong {
    color: #FFFFFF;
    font-weight: 600;
}

.dv-detail-actions {
    margin-left: auto;
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
    align-items: center;
}

.dv-tabs {
    display: flex;
    gap: 4px;
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: 5px;
    margin-bottom: var(--dv-space-4);
    overflow-x: auto;
}

.dv-tab {
    padding: 9px 18px;
    font-family: var(--dv-font-head);
    font-size: 0.76rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--dv-text-muted);
    text-decoration: none;
    border-radius: var(--dv-radius-sm);
    white-space: nowrap;
    transition: all 0.15s ease;
}

.dv-tab:hover {
    color: var(--dv-navy);
    background: var(--dv-line-soft);
    text-decoration: none;
}

.dv-tab.active {
    color: #FFFFFF;
    background: linear-gradient(135deg, var(--dv-blue-bright), var(--dv-blue));
    box-shadow: 0 2px 8px rgba(0, 113, 168, 0.3);
}

.dv-tab.disabled {
    color: #C3C8D4;
    pointer-events: none;
}

/* A count carried on a tab, for example the number of assets on a project. */
.dv-tab-count {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: var(--dv-radius-pill);
    background: var(--dv-line-soft);
    color: var(--dv-text-muted);
    font-size: 0.68rem;
    font-weight: 800;
}

.dv-tab.active .dv-tab-count {
    background: rgba(255, 255, 255, 0.24);
    color: #FFFFFF;
}

/* ==========================================================================
   ECSA STAGE STRIP

   Named apart from .dv-stepper on purpose. The certificate approval chain uses
   that name for something quite different, a white card grid with its own
   counter, and it is declared later in this file. When both shared a name the
   approval rules won here: this strip took a white background while keeping
   its white text, so the one stage with no state class disappeared.
   ========================================================================== */

.dv-stage-strip {
    display: flex;
    border-radius: var(--dv-radius-sm);
    overflow: hidden;
    background: rgba(255, 255, 255, 0.08);
    position: relative;
}

.dv-stage {
    flex: 1 1 0;
    min-width: 0;
    text-align: center;
    padding: 8px 4px;
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.3px;
    color: rgba(255, 255, 255, 0.72);
    border-right: 2px solid rgba(12, 20, 52, 0.45);
    position: relative;
}

.dv-stage:last-child {
    border-right: none;
}

.dv-stage-n {
    display: block;
    font-family: var(--dv-font-head);
    font-size: 0.72rem;
    font-weight: 800;
}

.dv-stage.done {
    background: rgba(126, 194, 69, 0.3);
    color: rgba(255, 255, 255, 0.88);
}

.dv-stage.active {
    background: linear-gradient(135deg, var(--dv-blue-bright), var(--dv-blue));
    color: #FFFFFF;
}

/* ==========================================================================
   LABEL / VALUE ROWS
   ========================================================================== */

.dv-rows {
    display: block;
}

.dv-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--dv-space-3);
    padding: 9px 0;
    border-bottom: 1px solid var(--dv-line-soft);
    font-size: 0.85rem;
}

.dv-row:last-child {
    border-bottom: none;
}

.dv-row-label {
    color: var(--dv-text-muted);
    font-size: 0.8rem;
    /* Short labels hold their width; a long one - the recent activity list
       carries whole sentences - wraps rather than pushing the page sideways. */
    flex: 0 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.dv-row-value {
    font-weight: 600;
    color: var(--dv-text);
    text-align: right;
}

.dv-row-time {
    font-size: 0.73rem;
    color: var(--dv-text-muted);
    white-space: nowrap;
    flex-shrink: 0;
}

/* ==========================================================================
   PROJECT CARDS
   ========================================================================== */

.dv-proj-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--dv-surface);
    border: none;
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-5);
    text-decoration: none;
    color: var(--dv-text);
    position: relative;
    overflow: hidden;
    transition: box-shadow 0.2s ease, transform 0.2s ease;
}

.dv-proj-card::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    background: linear-gradient(180deg, var(--dv-blue-bright), var(--dv-green));
    opacity: 0;
    transition: opacity 0.2s ease;
}

.dv-proj-card:hover {
    box-shadow: var(--dv-shadow-hover);
    transform: translateY(-3px);
    text-decoration: none;
    color: var(--dv-text);
}

.dv-proj-card:hover::before {
    opacity: 1;
}

.dv-proj-number {
    font-family: var(--dv-font-head);
    font-size: 1.1rem;
    font-weight: 800;
    color: var(--dv-navy);
    letter-spacing: -0.3px;
}

.dv-proj-name {
    font-size: 0.86rem;
    color: var(--dv-text-muted);
    margin-top: 3px;
}

.dv-proj-stage {
    font-size: 0.72rem;
    color: var(--dv-text-muted);
    margin-top: var(--dv-space-2);
}

/* Reflows from three columns to fewer as the card narrows, so the same markup
   works at full width and two across. */
.dv-proj-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--dv-space-2);
    margin: var(--dv-space-4) 0;
}

.dv-proj-party {
    background: var(--dv-canvas);
    border-radius: var(--dv-radius-sm);
    border-top: 3px solid var(--dv-line);
    padding: 10px 12px;
    min-width: 0;
}

.dv-proj-party.client      { border-top-color: var(--dv-blue-bright); }
.dv-proj-party.consultant  { border-top-color: var(--dv-navy); }
.dv-proj-party.contractor  { border-top-color: var(--dv-green); }

.dv-proj-party-role {
    font-size: 0.58rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.8px;
    color: var(--dv-text-muted);
    margin-bottom: 3px;
}

.dv-proj-party-org {
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--dv-navy);
    overflow: hidden;
    text-overflow: ellipsis;
}

.dv-proj-party-contact {
    font-size: 0.73rem;
    color: var(--dv-text-muted);
    margin-top: 2px;
}

/* Pushed to the bottom so footers line up across a row of cards. */
.dv-proj-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-3) var(--dv-space-4);
    font-size: 0.76rem;
    color: var(--dv-text-muted);
    padding-top: var(--dv-space-3);
    border-top: 1px solid var(--dv-line);
    margin-top: auto;
}

.dv-proj-value {
    font-family: var(--dv-font-head);
    color: var(--dv-navy);
    font-weight: 800;
    margin-left: auto;
}

@media (max-width: 575.98px) {
    .dv-proj-value {
        margin-left: 0;
    }
}

/* ==========================================================================
   ASSET CARDS
   ========================================================================== */

.dv-asset-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--dv-surface);
    border: none;
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-5);
    text-decoration: none;
    color: var(--dv-text);
    position: relative;
    overflow: hidden;
    transition: box-shadow 0.2s ease, transform 0.2s ease;
}

/* A condition stripe down the leading edge - the card's headline fact. */
.dv-asset-card::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    background: var(--dv-line);
}

.dv-asset-card.good::before { background: linear-gradient(180deg, var(--dv-green), var(--dv-green-deep)); }
.dv-asset-card.fair::before { background: linear-gradient(180deg, #F5B942, var(--dv-warning)); }
.dv-asset-card.poor::before { background: linear-gradient(180deg, #EF6B6B, var(--dv-danger)); }
.dv-asset-card.info::before { background: linear-gradient(180deg, var(--dv-blue-bright), var(--dv-blue)); }

.dv-asset-card:hover {
    box-shadow: var(--dv-shadow-hover);
    transform: translateY(-3px);
    text-decoration: none;
    color: var(--dv-text);
}

.dv-asset-head {
    display: flex;
    align-items: flex-start;
    gap: var(--dv-space-3);
    margin-bottom: var(--dv-space-3);
}

.dv-asset-number {
    font-family: var(--dv-font-head);
    font-size: 1.1rem;
    font-weight: 800;
    color: var(--dv-navy);
    letter-spacing: -0.3px;
}

.dv-asset-category {
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--dv-blue);
    margin-top: 2px;
}

.dv-asset-desc {
    font-size: 0.88rem;
    color: var(--dv-text);
    margin-bottom: var(--dv-space-3);
}

/* Location facts as small labelled tiles. */
.dv-asset-facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: var(--dv-space-2);
    margin-bottom: var(--dv-space-3);
}

.dv-asset-fact {
    background: var(--dv-canvas);
    border-radius: var(--dv-radius-sm);
    padding: 8px 10px;
    min-width: 0;
}

.dv-asset-fact-label {
    font-size: 0.58rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.7px;
    color: var(--dv-text-muted);
    margin-bottom: 2px;
}

.dv-asset-fact-value {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--dv-navy);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dv-asset-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-2) var(--dv-space-4);
    font-size: 0.74rem;
    color: var(--dv-text-muted);
    padding-top: var(--dv-space-3);
    border-top: 1px solid var(--dv-line);
    margin-top: auto;
}

.dv-asset-barcode {
    font-family: Consolas, 'Courier New', monospace;
    letter-spacing: 1px;
    color: var(--dv-navy);
    font-weight: 700;
}

.dv-asset-project {
    margin-left: auto;
    font-weight: 700;
    color: var(--dv-navy);
}

@media (max-width: 575.98px) {
    .dv-asset-project {
        margin-left: 0;
    }
}

/* ==========================================================================
   TEAM COLUMNS AND CHECKLIST
   ========================================================================== */

.dv-team-col {
    background: var(--dv-surface);
    border: none;
    border-top: 3px solid var(--dv-line);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-4);
    height: 100%;
}

.dv-team-col.client      { border-top-color: var(--dv-blue-bright); }
.dv-team-col.consultant  { border-top-color: var(--dv-navy); }
.dv-team-col.contractor  { border-top-color: var(--dv-green); }

.dv-team-col-head {
    font-size: 0.6rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--dv-text-muted);
    margin-bottom: var(--dv-space-3);
}

.dv-team-member {
    padding: var(--dv-space-3) 0;
    border-bottom: 1px solid var(--dv-line-soft);
}

.dv-team-member:last-child {
    border-bottom: none;
}

.dv-team-name {
    font-size: 0.86rem;
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-team-role {
    font-size: 0.76rem;
    color: var(--dv-blue);
    font-weight: 600;
}

.dv-team-contact {
    font-size: 0.75rem;
    color: var(--dv-text-muted);
}

.dv-check {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    padding: 10px 0;
    border-bottom: 1px solid var(--dv-line-soft);
    font-size: 0.85rem;
}

.dv-check:last-of-type {
    border-bottom: none;
}

.dv-check-mark {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.7rem;
    font-weight: 800;
    color: #FFFFFF;
    background: var(--dv-line);
    margin-top: 1px;
}

.dv-check.done .dv-check-mark {
    background: linear-gradient(135deg, var(--dv-green), var(--dv-green-deep));
}

.dv-check.done .dv-check-text {
    color: var(--dv-text-muted);
}

.dv-check-text {
    color: var(--dv-text);
}

/* ==========================================================================
   GROUPED CARD CAPTURE
   The second capture pattern: long forms broken into cards with the label
   stacked above its control, rather than the horizontal row layout used for
   short forms. See Build/ScreenPatterns.md.
   ========================================================================== */

.dv-field {
    margin-bottom: var(--dv-space-4);
}

.dv-field > .form-label {
    margin-bottom: 5px;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-field .form-text {
    margin-top: 5px;
}

/* A value the portal owns and the user cannot type over. */
.dv-readonly {
    background: var(--dv-line-soft);
    font-family: var(--dv-font-head);
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-readonly:focus {
    background: var(--dv-line-soft);
    box-shadow: none;
}

/* Action bar under a grouped card form. */
.dv-form-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: var(--dv-space-3);
    margin-top: var(--dv-space-4);
    padding: var(--dv-space-4);
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
}

/* Condition segmented control. Built from radio inputs so it posts without
   script, and coloured from the same status palette used everywhere else. */
.dv-cond-group {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
}

.dv-cond-btn {
    flex: 1 1 0;
    min-width: 90px;
    text-align: center;
    padding: 10px var(--dv-space-3);
    border-radius: var(--dv-radius-sm);
    border: 1px solid transparent;
    font-family: var(--dv-font-head);
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.4px;
    cursor: pointer;
    transition: all 0.15s ease;
}

.dv-cond-btn:hover {
    filter: brightness(0.96);
}

/* Unselected: the soft status tint. Selected: the full status colour. */
.dv-cond-btn.dv-badge-good    { background: var(--dv-tint-green); color: var(--dv-green-deep); }
.dv-cond-btn.dv-badge-fair    { background: var(--dv-tint-amber); color: #B4700A; }
.dv-cond-btn.dv-badge-poor    { background: var(--dv-tint-red);   color: #B91C1C; }
.dv-cond-btn.dv-badge-info    { background: var(--dv-tint-blue);  color: var(--dv-blue-deep); }
.dv-cond-btn.dv-badge-neutral { background: var(--dv-line-soft);  color: var(--dv-text-muted); }

.btn-check:checked + .dv-cond-btn.dv-badge-good {
    background: var(--dv-green-deep);
    color: #FFFFFF;
    box-shadow: 0 2px 10px rgba(94, 155, 44, 0.35);
}

.btn-check:checked + .dv-cond-btn.dv-badge-fair {
    background: var(--dv-warning);
    color: #FFFFFF;
    box-shadow: 0 2px 10px rgba(232, 147, 12, 0.35);
}

.btn-check:checked + .dv-cond-btn.dv-badge-poor {
    background: var(--dv-danger);
    color: #FFFFFF;
    box-shadow: 0 2px 10px rgba(220, 38, 38, 0.35);
}

.btn-check:checked + .dv-cond-btn.dv-badge-info {
    background: var(--dv-blue);
    color: #FFFFFF;
}

.btn-check:checked + .dv-cond-btn.dv-badge-neutral {
    background: var(--dv-grey);
    color: #FFFFFF;
}

.btn-check:focus-visible + .dv-cond-btn {
    border-color: var(--dv-blue);
    box-shadow: 0 0 0 0.2rem rgba(0, 113, 168, 0.25);
}

/* Extent, Task 7. A 1 to 5 scale with no words behind it, so it must not
   borrow the condition palette: green on a "1" would read as good news when
   it only means a small area. Neutral until chosen, blue when chosen, the
   same treatment the app already gives a plain selection. */
.dv-cond-btn.dv-extent-btn {
    background: var(--dv-line-soft);
    color: var(--dv-text-muted);
    min-width: 3rem;
    font-variant-numeric: tabular-nums;
}

.btn-check:checked + .dv-cond-btn.dv-extent-btn {
    background: var(--dv-blue);
    color: #FFFFFF;
    box-shadow: 0 2px 10px rgba(0, 113, 168, 0.3);
}

/* Degree and Extent on a defect read as a pair, "D4 / E3", so the two badges
   sit tight together with the separator between them. */
.dv-scale-pair {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    white-space: nowrap;
}

.dv-scale-pair .dv-scale-sep {
    color: var(--dv-text-muted);
    font-size: 0.75rem;
}

/* A capability that arrives in a later release, shown so the shape of the
   screen is clear without pretending it works. */
.dv-photo-placeholder {
    border: 1px dashed var(--dv-line);
    border-radius: var(--dv-radius-sm);
    padding: var(--dv-space-4);
    text-align: center;
    font-size: 0.8rem;
    color: var(--dv-text-muted);
    background: var(--dv-canvas);
}

/* ==========================================================================
   ASSET REGISTER
   ========================================================================== */

/* Condition summary above the register. Real counts off the rows shown. */
.dv-assetbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-2);
    margin-bottom: var(--dv-space-3);
    font-size: 0.82rem;
    color: var(--dv-text-muted);
}

.dv-assetbar-count {
    margin-right: var(--dv-space-2);
}

.dv-assetbar-count strong {
    font-family: var(--dv-font-head);
    font-size: 1rem;
    color: var(--dv-navy);
}

/* A representation of the barcode affixed to the asset in the field. The bars
   are drawn from the code itself so the same asset always renders the same
   pattern. Scanning arrives with the mobile capture work. */
.dv-barcode {
    text-align: center;
    padding: var(--dv-space-3) var(--dv-space-4);
    background: var(--dv-surface);
    border: 2px solid var(--dv-navy);
    border-radius: var(--dv-radius-sm);
}

.dv-barcode-bars {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 2px;
    height: 52px;
    margin-bottom: var(--dv-space-2);
}

.dv-barcode-bar {
    display: block;
    height: 100%;
    background: var(--dv-navy);
}

.dv-barcode-w1 { width: 2px; }
.dv-barcode-w2 { width: 4px; }
.dv-barcode-w3 { width: 6px; }

.dv-barcode-value {
    font-family: Consolas, 'Courier New', monospace;
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: 3px;
    color: var(--dv-navy);
}

/* ==========================================================================
   UNDER CONSTRUCTION
   ========================================================================== */

.dv-uc {
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-6) var(--dv-space-5);
    text-align: center;
    max-width: 620px;
    margin: var(--dv-space-5) auto;
    position: relative;
    overflow: hidden;
}

.dv-uc::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: linear-gradient(90deg, var(--dv-navy), var(--dv-blue-bright) 55%, var(--dv-green));
}

.dv-uc-logo {
    display: block;
    max-width: 180px;
    width: 100%;
    height: auto;
    margin: 0 auto var(--dv-space-4);
}

.dv-uc-product {
    font-family: var(--dv-font-head);
    font-size: 0.72rem;
    font-weight: 800;
    letter-spacing: 3px;
    color: var(--dv-navy);
    margin-bottom: var(--dv-space-6);
}

.dv-uc-icon {
    width: 62px;
    height: 62px;
    border-radius: 50%;
    background: var(--dv-tint-blue);
    color: var(--dv-blue);
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto var(--dv-space-4);
}

.dv-uc-icon svg {
    width: 28px;
    height: 28px;
}

.dv-uc-group {
    font-size: 0.62rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--dv-text-muted);
    margin-bottom: var(--dv-space-2);
}

.dv-uc-name {
    font-family: var(--dv-font-head);
    font-size: 1.5rem;
    font-weight: 800;
    color: var(--dv-navy);
    letter-spacing: -0.4px;
    margin-bottom: var(--dv-space-3);
}

.dv-uc-text {
    font-size: 0.9rem;
    color: var(--dv-text-muted);
    max-width: 460px;
    margin: 0 auto var(--dv-space-5);
    line-height: 1.6;
}

/* The prototype screens this module absorbs. */
.dv-uc-includes {
    background: var(--dv-canvas);
    border-radius: var(--dv-radius-sm);
    padding: var(--dv-space-4) var(--dv-space-5);
    margin: 0 auto var(--dv-space-5);
    max-width: 460px;
    text-align: left;
}

.dv-uc-includes-head {
    font-size: 0.62rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--dv-text-muted);
    margin-bottom: var(--dv-space-3);
}

.dv-uc-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.dv-uc-list li {
    position: relative;
    padding-left: 22px;
    margin-bottom: 7px;
    font-size: 0.85rem;
    color: var(--dv-text);
}

.dv-uc-list li:last-child {
    margin-bottom: 0;
}

.dv-uc-list li::before {
    content: '';
    position: absolute;
    left: 4px;
    top: 7px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--dv-blue-bright), var(--dv-green));
}

/* ==========================================================================
   LOGIN
   ========================================================================== */

.dv-login-canvas {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--dv-space-5) var(--dv-space-3);
    background:
        radial-gradient(1200px 520px at 12% -12%, rgba(0, 137, 204, 0.14), transparent 62%),
        radial-gradient(900px 440px at 108% 112%, rgba(126, 194, 69, 0.14), transparent 62%),
        var(--dv-canvas);
}

.dv-login-card {
    width: 100%;
    max-width: 410px;
    background: var(--dv-surface);
    border: none;
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow-lg);
    padding: var(--dv-space-6) var(--dv-space-5) var(--dv-space-5);
    position: relative;
    overflow: hidden;
}

.dv-login-card::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: linear-gradient(90deg, var(--dv-navy), var(--dv-blue-bright) 55%, var(--dv-green));
}

.dv-login-logo {
    display: block;
    max-width: 220px;
    width: 100%;
    height: auto;
    margin: 0 auto var(--dv-space-5);
}

.dv-login-product {
    text-align: center;
    margin-bottom: var(--dv-space-5);
    padding-top: var(--dv-space-4);
    position: relative;
}

.dv-login-product::before {
    content: '';
    position: absolute;
    top: 0;
    left: 15%;
    right: 15%;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--dv-blue-bright), var(--dv-green), transparent);
}

.dv-login-product-name {
    display: block;
    font-family: var(--dv-font-head);
    font-size: 1.6rem;
    font-weight: 800;
    letter-spacing: 6px;
    color: var(--dv-navy);
    text-indent: 6px;
    line-height: 1.1;
}

.dv-login-product-expansion {
    display: block;
    font-size: 0.64rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    color: var(--dv-text-muted);
    margin-top: var(--dv-space-1);
}

.dv-login-title {
    font-family: var(--dv-font-head);
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 1.4px;
    color: var(--dv-text-muted);
    text-align: center;
    margin-bottom: var(--dv-space-5);
}

.dv-login-footnote {
    text-align: center;
    font-size: 0.7rem;
    color: var(--dv-text-muted);
    margin-top: var(--dv-space-4);
}

/* ==========================================================================
   ALERTS, TOASTS, MODALS
   ========================================================================== */

.alert {
    font-size: 0.875rem;
    border-radius: var(--dv-radius);
    border-width: 1px;
}

.alert-danger {
    background: var(--dv-tint-red);
    border-color: #F6BDBD;
    color: #991B1B;
}

.dv-toast-host {
    position: fixed;
    top: calc(var(--dv-appbar-h) + var(--dv-space-4));
    left: 50%;
    transform: translateX(-50%);
    z-index: 1100;
}

.toast.bg-success {
    background: var(--dv-navy) !important;
    border-left: 4px solid var(--dv-green);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow-lg);
}

.modal-content {
    border: none;
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow-lg);
    overflow: hidden;
}

.modal-header {
    background: linear-gradient(120deg, var(--dv-chrome-from), var(--dv-chrome-to));
    color: #FFFFFF;
    border-bottom: none;
    padding: var(--dv-space-4) var(--dv-space-5);
}

.modal-title {
    font-family: var(--dv-font-head);
    font-size: 0.88rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.7px;
    color: #FFFFFF;
}

.modal-header .btn-close {
    filter: invert(1) grayscale(1) brightness(2);
}

.modal-body {
    padding: var(--dv-space-5);
}

.modal-footer {
    border-top: 1px solid var(--dv-line);
    padding: var(--dv-space-3) var(--dv-space-5);
}

/* PDF preview. The viewer needs real height, so the body is sized against the
   viewport rather than growing with its content. */
.dv-pdf-modal {
    height: 88vh;
}

.dv-pdf-body {
    padding: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.dv-pdf-frame {
    flex: 1 1 auto;
    width: 100%;
    border: none;
    background: var(--dv-line-soft);
}

.dv-pdf-status {
    padding: var(--dv-space-6);
    text-align: center;
    margin: auto;
}

.dropdown-menu {
    border: none;
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow-lg);
    padding: var(--dv-space-2);
}

.dropdown-item {
    border-radius: var(--dv-radius-sm);
    font-size: 0.85rem;
    padding: 8px 10px;
}

.dropdown-item:hover {
    background: var(--dv-tint-blue);
    color: var(--dv-blue-deep);
}

/* ==========================================================================
   UTILITY
   ========================================================================== */

.dv-muted {
    color: var(--dv-text-muted);
    font-size: 0.82rem;
}

.dv-scope-note {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--dv-blue-deep);
    background: var(--dv-tint-blue);
    border-radius: var(--dv-radius-pill);
    padding: 4px 12px;
    display: inline-block;
}

.dv-warn-text {
    color: var(--dv-warning);
    font-weight: 600;
}

/* ==========================================================================
   FIELD USE - tablet friendly tap targets, high contrast
   ========================================================================== */

@media (max-width: 992px) {
    .btn,
    .form-control,
    .form-select {
        min-height: var(--dv-tap);
    }

    .dv-icon-btn {
        min-width: var(--dv-tap);
        justify-content: center;
    }

    .table > tbody td,
    .table tbody td {
        padding: 12px;
        font-size: 0.9rem;
    }

    .table-action-link {
        font-size: 0.85rem;
        display: inline-block;
        padding: 4px 0;
    }

    .dv-search,
    .dv-filter-select {
        width: 100%;
    }

    .dv-toolbar-group {
        width: 100%;
    }

    .dv-nav-item,
    .dv-sidebar-user {
        min-height: var(--dv-tap);
    }
}

@media print {
    .dv-sidebar,
    .dv-appbar,
    .dv-footer,
    .dv-toolbar {
        display: none !important;
    }

    .dv-main {
        margin-left: 0;
    }
}


/* ==========================================================================
   RELEASE 2

   Everything below belongs to the Release 2 modules: attachments, the
   notification bell, roads, the Master List, inspections, defects and
   instructions. It follows the same rules as the rest of this file - no
   literal colours, no literal spacing, tap targets at least --dv-tap on
   touch, and every wide table scrolls inside its own box rather than
   pushing the page sideways.
   ========================================================================== */


/* --------------------------------------------------------------------------
   SHARED UTILITIES
   -------------------------------------------------------------------------- */

/* Present to a screen reader, invisible on screen. */
.dv-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Money and quantities line up on the decimal, so they are read down a column
   rather than across a row. */
.dv-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Leon, 18 September 2026: a figure in a table never breaks across two lines.
   R 2 205.00 was reading as "R 2" over "205.00" in a squeezed total. */
.table td.dv-num,
.table th.dv-num {
    white-space: nowrap;
}

.dv-num-input {
    text-align: right;
    font-variant-numeric: tabular-nums;
    max-width: 7rem;
}

/* A rate or a total must never be shortened with an ellipsis - a truncated
   amount is worse than no amount. */
/* Leon, 18 September 2026: dv-no-truncate also says "never wrap", which is
   right for a figure or a reference and wrong for a sentence: a description
   then held its column open and pushed the columns after it off the edge.
   Add dv-wrap beside it for text that should keep every word but may run onto
   a second line. */
.dv-no-truncate.dv-wrap {
    white-space: normal;
}

.dv-no-truncate {
    max-width: none;
    overflow: visible;
    text-overflow: clip;
    white-space: nowrap;
}

/* The capture form wrapper. Gives every long form the same bottom room so the
   sticky action bar never covers the last field. */
.dv-capture {
    padding-bottom: var(--dv-space-6);
}

.dv-appbar-end {
    display: flex;
    align-items: center;
    gap: var(--dv-space-2);
    margin-left: auto;
}

/* A single line of reason text sitting next to the button that needs it. */
.dv-reason {
    min-width: 16rem;
    flex: 1 1 16rem;
}

/* Why something was reopened, cancelled or closed. Stated, not hidden. */
.dv-reopen-note {
    padding: var(--dv-space-2) var(--dv-space-3);
    border-left: 3px solid var(--dv-warning);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-tint-amber);
    font-size: 0.82rem;
    color: var(--dv-text);
}

.dv-section-heading {
    font-family: var(--dv-font-head);
    font-size: 0.76rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--dv-text-muted);
    margin: var(--dv-space-5) 0 var(--dv-space-3) 0;
}

/* Controls that belong on one line above a table. */
.dv-rows-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--dv-space-2);
}

.dv-checkgroup {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2) var(--dv-space-5);
}

/* Good / Fair / Poor as three large targets, not a dropdown. An inspector is
   using this one-handed on a tablet at the roadside. The buttons themselves
   are .dv-cond-btn, styled further up; this only lays them out and guarantees
   the tap height, which .dv-cond-btn's padding alone does not. */
.dv-cond-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
}

.dv-cond-row .dv-cond-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--dv-tap);
    margin: 0;
}

.dv-cond-row .btn-check:focus-visible + .dv-cond-btn {
    outline: 2px solid var(--dv-blue);
    outline-offset: 2px;
}

.dv-total-row {
    background: var(--dv-canvas);
    font-weight: 700;
}

.dv-asset-open {
    font-weight: 600;
    white-space: nowrap;
}

.dv-avatar-sm {
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--dv-tint-navy);
    color: var(--dv-navy);
    font-size: 0.66rem;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.dv-project-option {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}

.dv-edit-item {
    white-space: nowrap;
}

/* The CPA formula, shown as the client writes it. */
.dv-formula {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 0.78rem;
    background: var(--dv-canvas);
    border-radius: var(--dv-radius-sm);
    padding: var(--dv-space-2) var(--dv-space-3);
    color: var(--dv-text);
    overflow-x: auto;
}

/* A second row of tabs inside a tab. */
.dv-tabs-inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-1);
    margin-bottom: var(--dv-space-4);
    border-bottom: 1px solid var(--dv-line);
}

.dv-tabs-inner .dv-tab {
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.8rem;
    padding: 7px 14px;
}


/* --------------------------------------------------------------------------
   KPI VARIANTS AND FACT STRIPS
   -------------------------------------------------------------------------- */

.dv-kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: var(--dv-space-3);
    margin-bottom: var(--dv-space-3);
}

.dv-kpi-note {
    font-size: 0.7rem;
    color: var(--dv-text-muted);
    margin-top: var(--dv-space-1);
}

.dv-kpi-red::before   { background: linear-gradient(180deg, var(--dv-danger), #B91C1C); }
.dv-kpi-amber::before { background: linear-gradient(180deg, var(--dv-warning), #B4700A); }

.dv-kpi-red .dv-kpi-icon   { background: var(--dv-tint-red);   color: #B91C1C; }
.dv-kpi-amber .dv-kpi-icon { background: var(--dv-tint-amber); color: #B4700A; }

/* One figure on a line, for use inside a card rather than across the top. */
.dv-kpi-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--dv-space-2);
    padding: var(--dv-space-2) 0;
    border-bottom: 1px solid var(--dv-line-soft);
}

.dv-kpi-inline:last-child { border-bottom: none; }

.dv-kpi-inline-label {
    font-size: 0.78rem;
    color: var(--dv-text-muted);
}

.dv-kpi-inline-value {
    font-family: var(--dv-font-head);
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--dv-navy);
    font-variant-numeric: tabular-nums;
}

/* The facts that sit under a heading: label above, value below. */
/* The fact strip always sits INSIDE a card, so it carries no surface of its
   own. It used to be a white panel with its own shadow on a white card, which
   read as a box inside a box and put every value in 1rem heading bold. It now
   follows the asset register: a quiet grid of tinted cells, the label small and
   the value readable rather than shouted. Keep these two in step. */
.dv-fact-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: var(--dv-space-2);
    margin-bottom: var(--dv-space-3);
}

.dv-fact {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    background: var(--dv-canvas);
    border-radius: var(--dv-radius-sm);
    padding: 8px 10px;
}

.dv-fact-label {
    font-size: 0.58rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.7px;
    color: var(--dv-text-muted);
    margin-bottom: 2px;
}

/* Unlike the asset card this wraps rather than truncating. A priority reading
   "Immediate - 0 to 2 days" or a contractor's full name is the reason the fact
   is on the card at all, so it is not worth clipping to keep the rows even. */
.dv-fact-value {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--dv-navy);
    min-width: 0;
    overflow-wrap: anywhere;
}


/* --------------------------------------------------------------------------
   ITEM CARDS
   The card half of the cards-versus-list pattern, used by the inspection,
   defect and instruction registers.
   -------------------------------------------------------------------------- */

/* ==========================================================================
   CARD GRID
   Every register that shows records as cards rather than as a table.

   C54. This was defined twice, here and 1300 lines earlier as two fixed
   columns with a media query of its own. Same specificity, so this one won at
   every width and the other was dead, along with its comment promising two
   cards across. One definition now, and it says what happens.

   A card is 310px at its narrowest and as many fit as the row allows, so a
   card is the same width on every register. auto-fill rather than auto-fit on
   purpose: with auto-fit a single record would stretch its card across the
   whole page. The last row carries the remainder, which is what a grid of
   cards is meant to look like.
   ========================================================================== */

.dv-card-grid {
    display: grid;
    /* Leon, 18 September 2026: 310 was too tight once a status badge ran to
       three words. */
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    gap: var(--dv-space-4);
    align-items: stretch;   /* footers line up across a row */
}

/* See .dv-conduct: a grid item's automatic minimum lets wide content push the
   page sideways rather than being contained. */
.dv-card-grid > *,
.dv-kpi-row > *,
.dv-fact-strip > *,
.dv-sub-grid > *,
.dv-gallery-strip > *,
.dv-assign-strip > * {
    min-width: 0;
}

.dv-item-card {
    display: flex;
    flex-direction: column;
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    overflow: hidden;
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.dv-item-card:hover {
    box-shadow: var(--dv-shadow-hover);
    transform: translateY(-1px);
}

/* A card or a row that opens when you click it. Leon, 5 September 2026.

   The class is added by initClickableRows to whatever actually has somewhere to
   go, so a card with nothing to open still looks inert. The name inside it is
   coloured like a link because that is the part a person aims at, and the whole
   surface responds so they do not have to. */
.dv-item-card.dv-clickable,
table.sortable tbody tr.dv-clickable {
    cursor: pointer;
}

.dv-item-card.dv-clickable .dv-item-card-number,
table.sortable tbody tr.dv-clickable td:first-child {
    color: var(--dv-blue);
}

.dv-item-card.dv-clickable:hover .dv-item-card-number,
table.sortable tbody tr.dv-clickable:hover td:first-child {
    text-decoration: underline;
}

table.sortable tbody tr.dv-clickable:hover {
    background: var(--dv-tint-blue);
}

/* The Open link stays. It is what a keyboard reaches, and on a touch screen it
   is a target that does not depend on hitting the card between its own
   controls. */

/* Leon, 18 September 2026: a long status, such as Completed - Awaiting
   Inspection, would not shrink, so it squeezed the record number until
   RP-0006 broke over two lines. The head wraps instead: the number and its
   sub line keep their room and a badge that does not fit drops under them. */
.dv-item-card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--dv-space-2);
    padding: var(--dv-space-4) var(--dv-space-4) var(--dv-space-2) var(--dv-space-4);
}

.dv-item-card-head > div:first-child {
    flex: 1 1 11rem;
    min-width: 0;
}

.dv-item-card-head > .dv-badge {
    flex: 0 0 auto;
}

/* The record number and any badge that qualifies it - Urgent, VO, a defect
   count - sit on one line together.

   Without this the badge is an inline-block, so its bottom edge lands on the
   number's text baseline and it hangs low, while the status badge beside it is
   a flex child of the head and aligns to the top. Two alignment models on one
   line, which is why Urgent and Draft never lined up. */
.dv-item-card-number {
    font-family: var(--dv-font-head);
    font-size: 1rem;
    font-weight: 800;
    color: var(--dv-navy);
    letter-spacing: -0.2px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-2);
    /* The number itself never breaks mid reference. */
    white-space: nowrap;
    /* The row is pinned to the badge's own height: 0.65rem text at line-height
       1.5, plus its 4px padding top and bottom. That keeps the line the same
       height whatever the heading font's metrics are, so the status badge
       sitting at the head's flex-start lands on exactly the same line as the
       badges inside this one. Without it the taller of the two drives the row
       and the other drifts by a few pixels. */
    line-height: calc((0.65rem * 1.5) + 8px);
}

.dv-item-card-sub {
    font-size: 0.74rem;
    color: var(--dv-text-muted);
    margin-top: 2px;
}

.dv-item-card-body {
    flex: 1 1 auto;
    padding: 0 var(--dv-space-4) var(--dv-space-3) var(--dv-space-4);
    font-size: 0.8rem;
}

.dv-item-card-body dl,
.dv-item-card-body .dv-item-card-line {
    display: flex;
    justify-content: space-between;
    gap: var(--dv-space-2);
    margin: 0 0 var(--dv-space-1) 0;
}

.dv-item-card-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-3);
    padding: var(--dv-space-3) var(--dv-space-4);
    border-top: 1px solid var(--dv-line-soft);
    background: var(--dv-canvas);
    font-size: 0.75rem;
    color: var(--dv-text-muted);
}

.dv-item-card-open {
    margin-left: auto;
    font-weight: 700;
    font-size: 0.75rem;
    text-decoration: none;
    white-space: nowrap;
}

.dv-item-card-open::after {
    content: " \2192";
}


/* --------------------------------------------------------------------------
   ATTACHMENT GALLERY
   Photographs and documents against a record. Files live outside wwwroot and
   are served through AttachmentController, so every thumbnail here is a
   controller URL, not a static path.
   -------------------------------------------------------------------------- */

.dv-gallery {
    display: flex;
    flex-direction: column;
    gap: var(--dv-space-3);
}

.dv-gallery-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-2);
}

.dv-gallery-title {
    font-family: var(--dv-font-head);
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-gallery-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: var(--dv-radius-pill);
    background: var(--dv-tint-blue);
    color: var(--dv-blue-deep);
    font-size: 0.68rem;
    font-weight: 700;
}

.dv-gallery-strip {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(118px, 1fr));
    gap: var(--dv-space-3);
    align-items: start;
}

/* The upload form is one cell of the strip, not a button in the header. */
.dv-gallery-add {
    margin: 0;
}

/* A figure, so its default margin has to go. */
.dv-gallery-item {
    position: relative;
    display: flex;
    flex-direction: column;
    margin: 0;
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-sm);
    overflow: hidden;
    background: var(--dv-surface);
}

/* Carried by the image, by the document tile and by the add tile, so all three
   occupy the same box whatever is inside them. */
.dv-gallery-thumb {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    background: var(--dv-canvas);
}

/* A document rather than a photograph: show its extension in the tile. */
.dv-gallery-file {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    aspect-ratio: 4 / 3;
    background: var(--dv-canvas);
    text-decoration: none;
}

.dv-gallery-ext {
    font-family: var(--dv-font-head);
    font-size: 0.8rem;
    font-weight: 800;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--dv-blue-deep);
}

.dv-gallery-caption {
    padding: var(--dv-space-2);
    font-size: 0.7rem;
    color: var(--dv-text-muted);
    line-height: 1.3;
    word-break: break-word;
}

.dv-gallery-remove,
.dv-gallery-remove-btn {
    position: absolute;
    top: 4px;
    right: 4px;
}

.dv-gallery-remove-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(26, 19, 72, 0.62);
    color: #FFFFFF;
    font-size: 0.8rem;
    line-height: 1;
    cursor: pointer;
}

.dv-gallery-remove-btn:hover {
    background: var(--dv-danger);
}

/* The tile that opens the file picker. The whole tile is the tap target. */
.dv-gallery-addtile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--dv-space-1);
    border: 2px dashed var(--dv-line);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-canvas);
    color: var(--dv-text-muted);
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}

.dv-gallery-addtile svg {
    width: 22px;
    height: 22px;
}

.dv-gallery-addtile:hover {
    color: var(--dv-blue-deep);
}

.dv-gallery-addtile:hover {
    border-color: var(--dv-blue);
    background: var(--dv-tint-blue);
}

.dv-gallery-addlabel {
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--dv-text-muted);
    text-align: center;
    padding: var(--dv-space-2);
}

.dv-gallery-empty {
    padding: var(--dv-space-4);
    border: 1px dashed var(--dv-line);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-canvas);
    font-size: 0.82rem;
    color: var(--dv-text-muted);
}

.dv-gallery-help,
.dv-gallery-note {
    font-size: 0.7rem;
    color: var(--dv-text-muted);
}


/* --------------------------------------------------------------------------
   NOTIFICATION BELL
   -------------------------------------------------------------------------- */

.dv-bell {
    position: relative;
}

.dv-bell-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--dv-tap);
    height: var(--dv-tap);
    padding: 0;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: var(--dv-navy);
    cursor: pointer;
}

.dv-bell-btn:hover {
    background: var(--dv-line-soft);
}

.dv-bell-count {
    position: absolute;
    top: 4px;
    right: 2px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: var(--dv-radius-pill);
    background: var(--dv-danger);
    color: #FFFFFF;
    font-size: 0.62rem;
    font-weight: 700;
    line-height: 17px;
    text-align: center;
}

/* The separator between the parts of a notification's meta line. */
.dv-bell-dot {
    color: var(--dv-line);
    padding: 0 2px;
}

.dv-bell-menu {
    width: min(23rem, calc(100vw - var(--dv-space-4) * 2));
    padding: 0;
    border: none;
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow-lg);
    overflow: hidden;
}

.dv-bell-head {
    display: flex;
    align-items: center;
    gap: var(--dv-space-2);
    padding: var(--dv-space-3) var(--dv-space-4);
    border-bottom: 1px solid var(--dv-line);
    font-family: var(--dv-font-head);
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-bell-markall {
    margin-left: auto;
    font-size: 0.72rem;
    font-weight: 600;
}

.dv-bell-list {
    max-height: 24rem;
    overflow-y: auto;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Subject, body and meta stack down the row. */
.dv-bell-item {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--dv-space-3) var(--dv-space-4);
    border-bottom: 1px solid var(--dv-line-soft);
    text-decoration: none;
    color: var(--dv-text);
}

.dv-bell-item:last-child { border-bottom: none; }
.dv-bell-item:hover { background: var(--dv-line-soft); text-decoration: none; }

/* Unread carries a bar down its left edge rather than a different background,
   so it still reads correctly once the row is hovered. */
.dv-bell-item.unread {
    border-left: 3px solid var(--dv-blue);
    padding-left: calc(var(--dv-space-4) - 3px);
}

.dv-bell-item-subject {
    font-size: 0.8rem;
    font-weight: 700;
    color: var(--dv-navy);
    line-height: 1.3;
}

.dv-bell-item-body {
    font-size: 0.76rem;
    color: var(--dv-text);
    line-height: 1.35;
}

.dv-bell-item-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    font-size: 0.7rem;
    color: var(--dv-text-muted);
    margin-top: 2px;
}

/* Marks a notification that also went out as email through the 249Worker.
   Queued and sent must not look the same: a contractor acting late because
   they were waiting for an email that never left is a real dispute. */
.dv-bell-email {
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--dv-text-muted);
}

.dv-bell-email.sent   { color: var(--dv-green-deep); }
.dv-bell-email.queued { color: var(--dv-warning); }

.dv-bell-empty {
    padding: var(--dv-space-5) var(--dv-space-4);
    text-align: center;
    font-size: 0.82rem;
    color: var(--dv-text-muted);
}


/* --------------------------------------------------------------------------
   SUBCONTRACTORS CARD
   -------------------------------------------------------------------------- */

.dv-sub-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: var(--dv-space-3);
}

.dv-sub-card {
    padding: var(--dv-space-3);
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-surface);
}

.dv-sub-name {
    font-family: var(--dv-font-head);
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-sub-package {
    font-size: 0.74rem;
    color: var(--dv-text-muted);
    margin-top: 2px;
}

.dv-sub-line {
    display: flex;
    justify-content: space-between;
    gap: var(--dv-space-2);
    font-size: 0.78rem;
    margin-top: var(--dv-space-2);
}

.dv-sub-foot {
    margin-top: var(--dv-space-3);
    padding-top: var(--dv-space-2);
    border-top: 1px solid var(--dv-line-soft);
    font-size: 0.72rem;
    color: var(--dv-text-muted);
}


/* --------------------------------------------------------------------------
   ASSET COMMENTS
   -------------------------------------------------------------------------- */

.dv-comments {
    display: flex;
    flex-direction: column;
    gap: var(--dv-space-3);
}

.dv-comment {
    display: flex;
    gap: var(--dv-space-3);
}

.dv-comment-body {
    flex: 1 1 auto;
    min-width: 0;
    padding: var(--dv-space-3);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-canvas);
}

.dv-comment-meta {
    font-size: 0.7rem;
    color: var(--dv-text-muted);
    margin-bottom: 2px;
}

.dv-comment-text {
    font-size: 0.84rem;
    white-space: pre-wrap;
    word-break: break-word;
}

.dv-comment-form {
    display: flex;
    flex-direction: column;
    gap: var(--dv-space-2);
    padding-top: var(--dv-space-3);
    border-top: 1px solid var(--dv-line-soft);
}


/* --------------------------------------------------------------------------
   INSPECTION ASSIGNMENTS
   -------------------------------------------------------------------------- */

.dv-assign-strip {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: var(--dv-space-3);
    margin-bottom: var(--dv-space-4);
}

.dv-assign-chip {
    display: flex;
    flex-direction: column;
    gap: var(--dv-space-1);
    padding: var(--dv-space-3);
    border: 1px solid var(--dv-line);
    border-left: 3px solid var(--dv-blue);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-surface);
    text-decoration: none;
    color: var(--dv-text);
}

.dv-assign-chip:hover {
    box-shadow: var(--dv-shadow);
    text-decoration: none;
}

.dv-assign-road {
    font-family: var(--dv-font-head);
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-assign-range {
    font-size: 0.72rem;
    color: var(--dv-text-muted);
}

.dv-assign-progress {
    font-size: 0.72rem;
    font-weight: 600;
    color: var(--dv-blue-deep);
}


/* --------------------------------------------------------------------------
   CONDUCT INSPECTION
   Two panes on a tablet: the assets down the road on the left, the asset being
   recorded on the right. One above the other on a phone. This is the screen an
   inspector holds in one hand, so every target is at least --dv-tap.
   -------------------------------------------------------------------------- */

.dv-conduct {
    display: grid;
    grid-template-columns: minmax(240px, 320px) 1fr;
    gap: var(--dv-space-4);
    align-items: start;
}

/* A grid item defaults to min-width: auto, so a wide table inside the panel
   makes the panel wider than its own track and pushes the page sideways. */
.dv-conduct > * {
    min-width: 0;
}

.dv-conduct-picker {
    display: flex;
    flex-direction: column;
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    overflow: hidden;
}

.dv-picker-period {
    padding: var(--dv-space-3) var(--dv-space-4);
    border-bottom: 1px solid var(--dv-line);
    font-family: var(--dv-font-head);
    font-size: 0.76rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--dv-text-muted);
}

.dv-picker-filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
    padding: var(--dv-space-3) var(--dv-space-4);
    border-bottom: 1px solid var(--dv-line-soft);
}

.dv-picker-filters .form-select {
    flex: 1 1 8rem;
}

.dv-picker-list {
    display: flex;
    flex-direction: column;
    max-height: 32rem;
    overflow-y: auto;
}

.dv-picker-item {
    display: flex;
    align-items: center;
    gap: var(--dv-space-3);
    min-height: var(--dv-tap);
    padding: var(--dv-space-3) var(--dv-space-4);
    border-bottom: 1px solid var(--dv-line-soft);
    text-decoration: none;
    color: var(--dv-text);

    /* C53. Do not let the row be squashed.

       dv-picker-list is a column flexbox with a bounded height, so its rows are
       flex items and shrink by default. On a road with a handful of assets the
       list is shorter than that bound, nothing shrinks and the rows look right.
       On N7 Section 2, which has 301, every row was crushed to its min-height of
       42px while its content still needed up to 66px, so the asset number
       rendered on top of the description of the row below it. Every one of the
       sixty rendered rows was overflowing, the worst by 54px.

       The list already scrolls, which is the right answer to a long road. This
       stops the rows paying for it. */
    flex: 0 0 auto;
}

.dv-picker-item:last-child { border-bottom: none; }
.dv-picker-item:hover { background: var(--dv-line-soft); text-decoration: none; }

.dv-picker-item.active {
    background: var(--dv-tint-blue);
    box-shadow: inset 3px 0 0 var(--dv-blue);
}

/* The stake km, which is the order the inspector drives the road in. */
.dv-picker-km {
    flex: 0 0 3.6rem;
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--dv-blue-deep);
    font-variant-numeric: tabular-nums;
}

.dv-picker-main {
    flex: 1 1 auto;
    min-width: 0;
}

.dv-picker-number {
    display: block;
    font-family: var(--dv-font-head);
    font-size: 0.82rem;
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-picker-desc {
    display: block;
    font-size: 0.74rem;
    color: var(--dv-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dv-picker-meta {
    display: block;
    font-size: 0.68rem;
    color: var(--dv-text-muted);
}

/* Condition above outcome. The picker column is narrow, and side by side the
   two badges crowd each other and push the asset description into an ellipsis. */
.dv-picker-right {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 3px;
}

/* Already recorded on this round. */
.dv-picker-tick {
    color: var(--dv-success);
    font-weight: 700;
}

.dv-conduct-panel {
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-5);
}

.dv-conduct-back {
    display: none;
    align-items: center;
    gap: var(--dv-space-1);
    margin-bottom: var(--dv-space-3);
    font-size: 0.8rem;
    font-weight: 600;
    text-decoration: none;
}

.dv-conduct-head {
    margin-bottom: var(--dv-space-4);
    padding-bottom: var(--dv-space-3);
    border-bottom: 1px solid var(--dv-line);
}

.dv-conduct-number {
    font-family: var(--dv-font-head);
    font-size: 1.3rem;
    font-weight: 800;
    color: var(--dv-navy);
    letter-spacing: -0.3px;
    /* The asset number carries its outcome badge beside it, so the two sit on
       one line and the badge stays centred against the larger text. */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-2);
}

.dv-conduct-desc {
    font-size: 0.9rem;
    color: var(--dv-text);
    margin-top: 2px;
}

.dv-conduct-meta {
    font-size: 0.74rem;
    color: var(--dv-text-muted);
    margin-top: var(--dv-space-1);
}

.dv-conduct-form {
    display: flex;
    flex-direction: column;
    gap: var(--dv-space-4);
}

/* The three outcomes. On a phone they stack full width, because a mis-tap here
   records the wrong outcome against the asset. */
.dv-conduct-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
    padding-top: var(--dv-space-3);
    border-top: 1px solid var(--dv-line-soft);
}

.dv-conduct-actions .btn {
    min-height: var(--dv-tap);
    flex: 1 1 10rem;
}

.dv-conduct-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--dv-space-2);
    min-height: 16rem;
    padding: var(--dv-space-5);
    text-align: center;
    color: var(--dv-text-muted);
}

/* --------------------------------------------------------------------------
   TASK 5.7. THE INSPECTION SCREEN AS A FULL WIDTH LIST

   Leon's notes: "We want to see assets on the road, which will basically take
   up most of the screen." Each row leads with the km and the side, then the
   description on one line with the asset number small underneath, then the
   status at the size it has always been. A row is at least 56 pixels high,
   because it is tapped with a thumb in a vehicle, and nothing depends on hover.
   -------------------------------------------------------------------------- */

.dv-round-card { padding-bottom: var(--dv-space-3); }

/* TASK 5.9. Add Asset stays in view while the list scrolls. */
.dv-round-add {
    position: sticky;
    top: calc(var(--dv-appbar-h) + var(--dv-space-2));
    z-index: 5;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-3);
    margin-bottom: var(--dv-space-3);
    padding: var(--dv-space-2) 0;
    background: var(--dv-surface);
}

.dv-round-add .btn { min-height: var(--dv-tap); min-width: 8rem; }

/* Leon, 16 September 2026: Add Asset, the search and the filters on one row.
   The search takes the room that is left; the row wraps on a phone. */
.dv-round-filters {
    display: flex;
    flex: 1 1 32rem;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-2);
    margin: 0;
}

.dv-round-filters .form-control,
.dv-round-filters .form-select {
    min-height: var(--dv-tap);
}

.dv-round-filters .dv-round-search { flex: 2 1 14rem; }
.dv-round-filters .form-select { flex: 1 1 10rem; width: auto; }

#dvRoundList[aria-busy="true"] { opacity: 0.6; transition: opacity 0.15s; }

/* On a phone the row wraps to several lines, and held at the top it would
   cover half the list, so there it scrolls with the page. */
@media (max-width: 575.98px) {
    .dv-round-add { position: static; }
    .dv-round-add > .btn { flex: 1 1 100%; }
}

.dv-round-list {
    display: flex;
    flex-direction: column;
}

/* Leon, 19 September 2026: an inspected row ran past the headings and over the
   card's rounded edge. The rows were pulled out to the card's edges, which was
   right before the list had headings; now the headings and the rows sit in one
   framed block of the same width, and a row's colour stays inside its corners. */
.dv-round-list-body > .dv-round-head,
.dv-round-list-body > .dv-round-list {
    border: 1px solid var(--dv-line);
}

.dv-round-list-body > .dv-round-head {
    border-bottom: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-sm) var(--dv-radius-sm) 0 0;
}

.dv-round-list-body > .dv-round-list {
    border-top: none;
    border-radius: 0 0 var(--dv-radius-sm) var(--dv-radius-sm);
    overflow: hidden;
}

/* On a phone the headings are hidden, so the list rounds all four corners. */
@media (max-width: 767.98px) {
    .dv-round-list-body > .dv-round-list {
        border-top: 1px solid var(--dv-line);
        border-radius: var(--dv-radius-sm);
    }
}

.dv-round-row {
    display: flex;
    align-items: center;
    gap: var(--dv-space-4);
    min-height: 56px;
    padding: var(--dv-space-3) var(--dv-space-4);
    border-bottom: 1px solid var(--dv-line-soft);
    color: var(--dv-text);
    text-decoration: none;
    flex: 0 0 auto;
}

.dv-round-row:last-child { border-bottom: none; }
.dv-round-row:hover { text-decoration: none; color: var(--dv-text); }

.dv-round-row.active {
    background: var(--dv-tint-blue);
    box-shadow: inset 4px 0 0 var(--dv-blue);
}

.dv-round-row.done { background: var(--dv-line-soft); }
.dv-round-row.done.active { background: var(--dv-tint-blue); }

/* Leon, 19 September 2026. A line on an instruction that is for a component
   says so plainly, so the work is not read as the whole asset's. */
.dv-component-tag {
    display: inline-block;
    margin-left: var(--dv-space-2);
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--dv-tint-navy);
    color: var(--dv-navy);
    font-size: 0.78rem;
    font-weight: 700;
    white-space: nowrap;
}

/* The km and the side, which are what the inspector reads first. */
.dv-round-where {
    flex: 0 0 11.5rem;
    display: flex;
    align-items: center;
    gap: var(--dv-space-2);
}

/* Leon, 18 September 2026: "the mileage is a little bit too bold. It stands
   out a little bit too much." The asset is what the inspector is reading; the
   chainage only says where to look for it. */
.dv-round-km {
    font-family: var(--dv-font-body);
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--dv-text);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* The columns the rows line up under, in the same three widths. */
.dv-round-head {
    display: flex;
    align-items: center;
    gap: var(--dv-space-3);
    padding: var(--dv-space-2) var(--dv-space-4);
    border-bottom: 1px solid var(--dv-line);
    background: var(--dv-canvas);
    font-family: var(--dv-font-head);
    font-size: 0.68rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--dv-text-muted);
}

.dv-round-head-where { flex: 0 0 11.5rem; }
.dv-round-head-main  { flex: 1 1 auto; }
.dv-round-head-status { flex: 0 0 auto; text-align: right; }

@media (max-width: 767.98px) {
    .dv-round-head { display: none; }
}

.dv-round-side {
    display: inline-block;
    min-width: 2.8rem;
    padding: 4px 8px;
    border-radius: var(--dv-radius-pill);
    background: var(--dv-navy);
    color: #FFFFFF;
    font-family: var(--dv-font-head);
    font-size: 0.72rem;
    font-weight: 800;
    text-align: center;
    letter-spacing: 0.5px;
}

.dv-round-main {
    flex: 1 1 auto;
    min-width: 0;
}

.dv-round-desc {
    display: block;
    font-size: 0.95rem;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dv-round-sub {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
    font-size: 0.7rem;
    color: var(--dv-text-muted);
}

.dv-round-number { font-variant-numeric: tabular-nums; }

.dv-round-status {
    flex: 0 0 auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: 4px;
    max-width: 12rem;
}

/* The card that slides out: 480 pixels on a tablet or a desktop, the whole
   width on a phone. */
.dv-inspect-card.offcanvas-end {
    width: 480px;
    max-width: 100%;
}

.dv-inspect-card .offcanvas-header {
    border-bottom: 1px solid var(--dv-line);
}

.dv-inspect-card .offcanvas-title {
    font-family: var(--dv-font-head);
    font-weight: 800;
    color: var(--dv-navy);
}

.dv-inspect-card .offcanvas-header .btn { min-height: var(--dv-tap); min-width: 5.5rem; }

/* Leon, 19 September 2026: the Close button sat hard against the asset number.
   A clear gap keeps the two from reading as one. */
.dv-inspect-card .offcanvas-header { gap: var(--dv-space-4); }

.dv-inspect-card .dv-conduct-form {
    box-shadow: none;
    padding: 0;
}

/* TASK 5.10. An instructed quantity above what the asset holds. Allowed, so
   amber rather than red. */
.dv-over,
.dv-instructed-qty.dv-over {
    color: var(--dv-warning);
    font-weight: 700;
    border-color: var(--dv-warning);
}

.dv-instruction-assets-road td {
    background: var(--dv-line-soft);
    font-size: 0.78rem;
}

/* TASK 5.11. The Add Assets picker on an instruction. */
.dv-pick-list { display: flex; flex-direction: column; gap: var(--dv-space-2); }

.dv-pick-asset {
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius);
    overflow: hidden;
}

.dv-pick-row {
    display: flex;
    align-items: flex-start;
    gap: var(--dv-space-3);
    padding: var(--dv-space-2) var(--dv-space-3);
    margin: 0;
    cursor: pointer;
}

.dv-pick-component {
    padding-left: calc(var(--dv-space-3) + 2.5rem);
    border-top: 1px solid var(--dv-line-soft);
    background: var(--dv-line-soft);
}

.dv-pick-mark { flex: 0 0 auto; min-width: 1.5rem; padding-top: 2px; }
.dv-pick-mark .form-check-input { width: 1.4rem; height: 1.4rem; }
.dv-pick-km { flex: 0 0 9rem; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--dv-navy); }
.dv-pick-side { flex: 0 0 7rem; font-size: 0.8rem; }
.dv-pick-main { flex: 1 1 auto; min-width: 0; }
.dv-pick-status { flex: 0 0 auto; text-align: right; }

.dv-pick-findings {
    display: block;
    font-size: 0.75rem;
    color: var(--dv-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dv-pick-cond { font-size: 0.72rem; padding: 4px 8px; min-height: 0; }

/* TASK 5.8. What is inside the card, top to bottom. */
.dv-inspect { display: flex; flex-direction: column; gap: var(--dv-space-4); }

.dv-inspect-asset {
    padding-bottom: var(--dv-space-3);
    border-bottom: 1px solid var(--dv-line);
}

.dv-inspect-where {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-2);
}

.dv-inspect-status { margin-left: auto; }

.dv-inspect-desc {
    margin-top: var(--dv-space-2);
    font-size: 1rem;
    font-weight: 600;
}

.dv-inspect-number {
    margin-top: var(--dv-space-2);
    font-size: 0.7rem;
    color: var(--dv-text-muted);
}

.dv-inspect-components {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--dv-line-soft);
    border-radius: var(--dv-radius);
    overflow: hidden;
}

/* 18 September meeting. Each component is a button a thumb can hit: its name,
   its quantity, and a pill saying how it was rated. The one being rated is
   marked, the way the asset being recorded is marked on the list. */
.dv-inspect-component {
    display: flex;
    align-items: center;
    gap: var(--dv-space-3);
    width: 100%;
    min-height: var(--dv-tap);
    padding: var(--dv-space-2) var(--dv-space-3);
    border: none;
    border-bottom: 1px solid var(--dv-line-soft);
    background: var(--dv-surface);
    color: var(--dv-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.dv-inspect-component:last-child { border-bottom: none; }
.dv-inspect-component:hover { background: var(--dv-canvas); }

.dv-inspect-component.active {
    background: var(--dv-tint-blue);
    box-shadow: inset 4px 0 0 var(--dv-blue);
}

.dv-inspect-component-name { flex: 1 1 auto; font-weight: 600; }
.dv-inspect-component-qty { flex: 0 0 auto; white-space: nowrap; }

.dv-inspect-component-pill {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: var(--dv-space-2);
    white-space: nowrap;
}

.dv-inspect-component-extent {
    font-size: 0.75rem;
    color: var(--dv-text-muted);
}

/* Says whether the boxes below are the asset's or a component's. */
.dv-inspect-context {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--dv-space-2);
    margin-bottom: var(--dv-space-3);
    padding: var(--dv-space-2) var(--dv-space-3);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-canvas);
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-inspect-context.dv-inspect-context-component {
    background: var(--dv-tint-blue);
    box-shadow: inset 4px 0 0 var(--dv-blue);
}

.dv-inspect-last {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--dv-space-2);
    font-size: 0.8rem;
}

.dv-inspect-actions .btn { min-height: 54px; font-size: 1rem; }

.dv-inspect-empty { padding: var(--dv-space-4); }

/* TASK 5.12. The Repair Instruction step. Whole rows are tap targets. */
.dv-step-tick { cursor: pointer; min-height: var(--dv-tap); align-items: center; }
.dv-step-tick .form-check-input { width: 1.4rem; height: 1.4rem; vertical-align: middle; }

.dv-step-done {
    padding: var(--dv-space-3);
    border-radius: var(--dv-radius);
    background: var(--dv-tint-blue);
}

@media (max-width: 767.98px) {
    .dv-inspect-card.offcanvas-end { width: 100%; }

    .dv-round-row { flex-wrap: wrap; gap: var(--dv-space-2) var(--dv-space-3); }
    .dv-round-where { flex: 1 1 100%; }
    .dv-round-status { max-width: none; }
}

.dv-session-progress {
    padding-top: var(--dv-space-2);
}

/* The round total, shown under the bar only while the list is filtered. It sits
   on the dark session header, so it is white at reduced opacity. */
.dv-session-progress-note {
    margin-top: 0.4rem;
    font-size: 0.72rem;
    opacity: 0.75;
}

.dv-session-progress-head {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--dv-space-2);
    font-size: 0.78rem;
    margin-bottom: 0.3rem;
}


/* --------------------------------------------------------------------------
   PRICING THE WORKS
   The same table serves Conduct Inspection and defect capture: every line of
   the Master List with a quantity box against it. The rate and the price list
   version are stamped at save and never re-resolved.
   -------------------------------------------------------------------------- */

.dv-works-table {
    font-size: 0.8rem;
}

.dv-works-table th {
    white-space: nowrap;
}

/* The quantity box is the only thing on this table anyone types into, so it
   keeps a usable width even when the description column is squeezing it. */
.dv-works-row .dv-num-input {
    min-height: var(--dv-tap);
    min-width: 4.5rem;
}

.dv-works-table td:has(> .dv-works-qty) {
    width: 5.5rem;
}

/* A line that has been given a quantity is the one the user is working on.
   .has-qty is toggled by the recalc script on each row as it is typed. */
.dv-works-row.has-qty {
    background: var(--dv-tint-blue);
}

.dv-works-row.has-qty .dv-works-amount {
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-works-amount {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.dv-works-total {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: flex-end;
    gap: var(--dv-space-3);
    padding: var(--dv-space-3) var(--dv-space-4);
    margin-top: var(--dv-space-3);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-canvas);
}

.dv-works-total-label {
    font-size: 0.78rem;
    color: var(--dv-text-muted);
}

.dv-works-total-value {
    font-family: var(--dv-font-head);
    font-size: 1.15rem;
    font-weight: 800;
    color: var(--dv-navy);
    font-variant-numeric: tabular-nums;
}


/* --------------------------------------------------------------------------
   DEFECTS AND INSTRUCTIONS
   -------------------------------------------------------------------------- */

/* Condition and contractual defects are not mixed by default, so the switch
   between them is a deliberate, visible control rather than a filter buried in
   a dropdown. */
.dv-classswitch {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-1);
    margin-bottom: var(--dv-space-3);
    padding: 0.2rem;
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-pill);
    background: var(--dv-canvas);
    width: fit-content;
}

.dv-classswitch a {
    padding: 0.35rem 0.9rem;
    border-radius: var(--dv-radius-pill);
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--dv-text-muted);
    text-decoration: none;
}

.dv-classswitch a.active {
    background: var(--dv-navy);
    color: #FFFFFF;
}

.dv-defect-desc {
    font-size: 0.85rem;
    margin-bottom: var(--dv-space-2);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.dv-item-card.is-overdue {
    border-left: 3px solid var(--dv-danger);
}

/* The bar that appears when defects are ticked for conversion. */
.dv-convert-bar {
    position: sticky;
    top: calc(var(--dv-appbar-h) + var(--dv-space-2));
    z-index: 10;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-3);
    padding: var(--dv-space-2) var(--dv-space-3);
    margin-bottom: var(--dv-space-3);
    border: 1px solid var(--dv-blue);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-tint-blue);
    box-shadow: var(--dv-shadow);
    font-size: 0.85rem;
}

.dv-convert-check {
    flex: 0 0 auto;
}

/* --- Progress: instructed, claimed, certified, outstanding ---------------- */

.dv-progress-block {
    padding-top: var(--dv-space-2);
    border-top: 1px solid var(--dv-line-soft);
}

.dv-progress-head {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--dv-space-2);
    font-size: 0.8rem;
    margin-bottom: 0.3rem;
}

.dv-progress-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-3);
    margin-top: 0.35rem;
    font-size: 0.7rem;
    color: var(--dv-text-muted);
}

.dv-progress-legend span::before {
    content: "";
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 2px;
    margin-right: 0.3rem;
    vertical-align: middle;
}

.dv-legend-certified::before   { background: var(--dv-success); }
.dv-legend-claimed::before     { background: var(--dv-warning); }
.dv-legend-outstanding::before { background: var(--dv-line); }

/* --- Detail with a timeline down the side --------------------------------- */

.dv-detail-split {
    display: grid;
    grid-template-columns: 1fr minmax(260px, 340px);
    gap: var(--dv-space-4);
    align-items: start;
}

.dv-detail-main,
.dv-detail-side {
    min-width: 0;
}

.dv-timeline {
    list-style: none;
    margin: 0;
    padding: 0 0 0 var(--dv-space-4);
    position: relative;
}

.dv-timeline::before {
    content: "";
    position: absolute;
    left: 6px;
    top: 6px;
    bottom: 6px;
    width: 2px;
    background: var(--dv-line);
}

.dv-timeline-item {
    position: relative;
    padding-bottom: var(--dv-space-4);
}

.dv-timeline-item:last-child { padding-bottom: 0; }

.dv-timeline-item::before {
    content: "";
    position: absolute;
    left: calc(-1 * var(--dv-space-4) + 2px);
    top: 4px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--dv-blue);
    border: 2px solid var(--dv-surface);
}

/* A stage that has not happened yet. */
.dv-timeline-item.pending::before { background: var(--dv-warning); }

.dv-timeline-stage {
    font-weight: 700;
    font-size: 0.85rem;
    color: var(--dv-navy);
}

.dv-timeline-ref    { font-size: 0.8rem; }
.dv-timeline-detail { font-size: 0.78rem; color: var(--dv-text); }
.dv-timeline-when   { font-size: 0.7rem; color: var(--dv-text-muted); margin-top: 0.1rem; }

/* --- Carry-over notes ----------------------------------------------------- */

.dv-carry-note {
    padding: var(--dv-space-3);
    margin-bottom: var(--dv-space-4);
    border-left: 3px solid var(--dv-blue);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-tint-blue);
    font-size: 0.85rem;
}

.dv-carry-warn {
    border-left-color: var(--dv-warning);
    background: var(--dv-tint-amber);
}

/* --- Picking defects for an instruction ----------------------------------- */

.dv-defect-picker {
    display: flex;
    flex-direction: column;
    max-height: 20rem;
    overflow-y: auto;
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-sm);
}

.dv-defect-pick {
    display: flex;
    align-items: center;
    gap: var(--dv-space-2);
    padding: var(--dv-space-2) var(--dv-space-3);
    min-height: var(--dv-tap);
    border-bottom: 1px solid var(--dv-line-soft);
    cursor: pointer;
    margin: 0;

    /* C53, the same fault as dv-picker-item and found by looking for it rather
       than by hitting it. This list has the same shape: a column flexbox
       bounded at 20rem, so its rows shrink once there are enough of them to
       overflow. No instruction covers enough defects for it to show yet. */
    flex: 0 0 auto;
}

.dv-defect-pick:last-child { border-bottom: none; }
.dv-defect-pick:hover      { background: var(--dv-line-soft); }
.dv-defect-pick.selected   { background: var(--dv-tint-blue); }

.dv-defect-pick-main   { flex: 1 1 auto; min-width: 0; }
.dv-defect-pick-number { display: block; font-weight: 700; font-size: 0.82rem; color: var(--dv-navy); }
.dv-defect-pick-desc   { display: block; font-size: 0.75rem; color: var(--dv-text-muted); }
.dv-defect-pick-value  { flex: 0 0 auto; font-size: 0.8rem; font-weight: 600; font-variant-numeric: tabular-nums; }


/* --------------------------------------------------------------------------
   WIDE TABLES

   .table-wrapper has been on every table in the portal since Release 1 but
   never had a rule behind it, so a wide table pushed the whole page sideways
   on a phone. The table scrolls inside its own box instead; the page never
   does. -webkit-overflow-scrolling keeps the momentum scroll on iPad, which
   is the device the Consultant actually reviews on.
   -------------------------------------------------------------------------- */

.table-wrapper {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
}

@media (max-width: 767.98px) {
    /* A table that has to scroll should not also squeeze itself to nothing. */
    .table-wrapper > .table {
        min-width: 34rem;
    }

    .table-wrapper > .table th,
    .table-wrapper > .table td {
        white-space: nowrap;
    }
}


/* --------------------------------------------------------------------------
   TABLET
   -------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
    .dv-detail-split {
        grid-template-columns: 1fr;
    }

    /* The timeline moves above the detail on a narrow screen, because what
       happened to a defect is the first question asked about it. */
    .dv-detail-side {
        order: -1;
    }

    .dv-conduct {
        grid-template-columns: 1fr;
    }

    .dv-picker-list {
        max-height: 20rem;
    }

    .dv-classswitch {
        width: 100%;
    }

    .dv-classswitch a {
        flex: 1 1 auto;
        text-align: center;
    }
}


/* --------------------------------------------------------------------------
   PHONE
   -------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
    /* One asset at a time. The picker shows until an asset is chosen, then the
       panel takes the screen and a back link returns to the list. Driven by
       .has-open on the wrapper, which the view sets server side from whether an
       asset is selected, so it works with JavaScript off. */
    .dv-conduct:not(.has-open) .dv-conduct-panel { display: none; }
    .dv-conduct.has-open .dv-conduct-picker { display: none; }
    .dv-conduct.has-open .dv-conduct-back { display: flex; }

    .dv-conduct-panel {
        padding: var(--dv-space-4);
    }

    .dv-conduct-actions .btn {
        flex: 1 1 100%;
    }

    .dv-fact-strip {
        grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
        padding: var(--dv-space-3);
        gap: var(--dv-space-3);
    }

    .dv-card-grid,
    .dv-kpi-row {
        grid-template-columns: 1fr;
    }

    .dv-gallery-strip {
        grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    }

    .dv-bell-menu {
        position: fixed;
        left: var(--dv-space-2);
        right: var(--dv-space-2);
        width: auto;
    }

    .dv-convert-bar {
        position: fixed;
        left: var(--dv-space-2);
        right: var(--dv-space-2);
        bottom: var(--dv-space-2);
        top: auto;
        margin: 0;
        z-index: 1030;
    }

    .dv-num-input {
        max-width: none;
    }

    .dv-reason {
        min-width: 0;
        flex: 1 1 100%;
    }
}


/* --------------------------------------------------------------------------
   REPAIRS, EVIDENCE AND VERIFICATION
   -------------------------------------------------------------------------- */

/* One defect covered by a repair, with its own outcome. Five of eight potholes
   fixed means five of these say Completed and three say Not Started. */
.dv-defect-outcome {
    padding: var(--dv-space-3);
    margin-bottom: var(--dv-space-3);
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-surface);
}

.dv-defect-outcome:last-child { margin-bottom: 0; }

.dv-defect-outcome-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--dv-space-2);
    margin-bottom: var(--dv-space-2);
}

/* A line claiming more than was instructed. Allowed, and said out loud. */
.dv-works-row.is-overclaim,
.dv-verify-row.is-overclaim,
tr.is-overclaim {
    background: var(--dv-tint-amber);
}

/* --- The evidence gate ---------------------------------------------------- */

.dv-evidence-pane {
    padding-top: var(--dv-space-3);
}

.dv-evidence-tick {
    color: var(--dv-success);
    font-weight: 700;
    margin-left: 0.2rem;
}

.dv-evidence-check {
    margin-top: var(--dv-space-4);
    padding: var(--dv-space-3);
    border-left: 3px solid var(--dv-warning);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-tint-amber);
    font-size: 0.85rem;
}

.dv-evidence-check.is-complete {
    border-left-color: var(--dv-success);
    background: var(--dv-tint-green);
}

.dv-evidence-check-head {
    font-weight: 600;
    margin-bottom: var(--dv-space-2);
}

.dv-evidence-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.dv-evidence-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--dv-space-2);
    padding: 0.15rem 0 0.15rem 1.4rem;
    position: relative;
    font-size: 0.82rem;
}

/* A tick or an empty box, so the state reads at a glance rather than by
   colour alone. */
.dv-evidence-list li::before {
    position: absolute;
    left: 0;
    font-weight: 700;
}

.dv-evidence-list li.done::before {
    content: "\2713";
    color: var(--dv-green-deep);
}

.dv-evidence-list li.open::before {
    content: "\25A2";
    color: #B4700A;
}

.dv-evidence-list li.open {
    font-weight: 600;
}

/* --- Verification --------------------------------------------------------- */

.dv-verify {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: var(--dv-space-4);
    align-items: start;
}

.dv-verify-left,
.dv-verify-right {
    min-width: 0;
}

/* Before and after, side by side. This comparison is the whole screen. */
.dv-beforeafter {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--dv-space-4);
}

.dv-beforeafter-side {
    min-width: 0;
}

.dv-beforeafter-label {
    font-family: var(--dv-font-head);
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--dv-text-muted);
    margin-bottom: var(--dv-space-2);
}

.dv-verify-table {
    font-size: 0.8rem;
}

.dv-verify-row .dv-verify-qty {
    min-height: var(--dv-tap);
    min-width: 4.5rem;
}

.dv-verify-table td:has(> .dv-verify-qty) {
    width: 6rem;
}

.dv-verify-amount {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Certified differs from claimed, so the line owes a reason. The reason box is
   marked required by the script at the same moment. */
.dv-verify-row.is-varied {
    background: var(--dv-tint-blue);
}

.dv-verify-row.is-varied .dv-verify-reason {
    border-color: var(--dv-blue);
}

/* Leon, 18 September 2026: wide enough that the hint and what is typed can
   both be read, rather than "Required if chang". */
.dv-verify-reason {
    min-width: 12rem;
}


/* --------------------------------------------------------------------------
   REPAIRS AND VERIFICATION - TABLET
   -------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
    /* Verification is done on site. One column, in the order the inspector
       works: photographs, then workmanship, then the items, then the decision. */
    .dv-verify {
        grid-template-columns: 1fr;
    }
}


/* --------------------------------------------------------------------------
   REPAIRS AND VERIFICATION - PHONE
   -------------------------------------------------------------------------- */

@media (max-width: 767.98px) {
    /* The supporting document tabs become one stacked list of upload blocks,
       so the contractor can see every requirement without hunting through
       tabs for the one that is missing. */
    .dv-tabs-inner {
        display: none;
    }

    .dv-evidence-pane.d-none {
        display: block !important;
    }

    .dv-evidence-pane {
        padding-bottom: var(--dv-space-3);
        border-bottom: 1px solid var(--dv-line-soft);
    }

    .dv-beforeafter {
        grid-template-columns: 1fr;
    }

    .dv-defect-outcome .dv-cond-row .dv-cond-btn {
        flex: 1 1 100%;
    }

    .dv-verify-reason {
        min-width: 0;
    }
}


/* --------------------------------------------------------------------------
   PAYMENTS
   -------------------------------------------------------------------------- */

/* The contract summary beside the spend chart. This is the block a Service
   Manager looks at first, so the two halves sit together at desk width. */
.dv-summary-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
    gap: var(--dv-space-4);
    align-items: start;
    margin-bottom: var(--dv-space-4);
}

.dv-summary-split > * {
    min-width: 0;
    margin-bottom: 0;
}

/* On Payments the split is the whole body of the contract summary modal, so it
   should not carry the trailing margin it needs when it sits on a page. */
.modal-body > .dv-summary-split {
    margin-bottom: 0;
}

/* The total line on a certificate card, set apart from the workings above it. */
.dv-item-card-total {
    margin-top: var(--dv-space-2);
    padding-top: var(--dv-space-2);
    border-top: 1px solid var(--dv-line);
    font-size: 0.85rem;
}

/* The calculation, in the order the W1 form sets it out. */
.dv-cert-totals {
    max-width: 34rem;
}

/* The amount actually being paid. It ends the list, so it is set apart. */
.dv-cert-grand {
    margin-top: var(--dv-space-2);
    padding: var(--dv-space-2) var(--dv-space-3);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-navy);
    border-bottom: none;
}

.dv-cert-grand .dv-kpi-inline-label {
    color: rgba(255, 255, 255, 0.75);
    font-weight: 600;
}

.dv-cert-grand .dv-kpi-inline-value {
    color: #FFFFFF;
    font-size: 1.2rem;
}

.dv-section-total {
    font-variant-numeric: tabular-nums;
}

/* A button that has to sit inside a form but read as a table action link. */
.dv-linkbutton {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    cursor: pointer;
}

@media (max-width: 991.98px) {
    .dv-summary-split {
        grid-template-columns: 1fr;
    }

    /* The wrange is a desk document. Each W section keeps its own card and its
       table scrolls inside the wrapper, rather than being reflowed into
       something that is no longer recognisable as a W form. */
    .dv-cert-totals {
        max-width: none;
    }
}


/* --------------------------------------------------------------------------
   REVIEWS AND DOCUMENTS
   -------------------------------------------------------------------------- */

/* The approval chain, as a stepper. The order is Client, then Service Manager,
   then Construction Manager, and the state of each is readable at a glance. */
.dv-stepper {
    list-style: none;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: var(--dv-space-3);
    margin: 0;
    padding: 0;
    counter-reset: dv-step;
}

.dv-step {
    position: relative;
    padding: var(--dv-space-3) var(--dv-space-3) var(--dv-space-3) 2.4rem;
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-surface);
    counter-increment: dv-step;
}

/* The step number, so the order is not something to work out. */
.dv-step::before {
    content: counter(dv-step);
    position: absolute;
    left: var(--dv-space-3);
    top: var(--dv-space-3);
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--dv-line);
    color: var(--dv-text-muted);
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 20px;
    text-align: center;
}

.dv-step-done      { border-color: var(--dv-success); background: var(--dv-tint-green); }
.dv-step-current   { border-color: var(--dv-blue); background: var(--dv-tint-blue); }
.dv-step-rejected  { border-color: var(--dv-danger); background: var(--dv-tint-red); }

.dv-step-done::before     { background: var(--dv-green-deep); color: #FFFFFF; content: "\2713"; }
.dv-step-current::before  { background: var(--dv-blue); color: #FFFFFF; }
.dv-step-rejected::before { background: var(--dv-danger); color: #FFFFFF; content: "\00D7"; }

.dv-step-name {
    font-family: var(--dv-font-head);
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-step-who    { font-size: 0.78rem; color: var(--dv-text); }
.dv-step-state  { font-size: 0.72rem; color: var(--dv-text-muted); margin-top: 2px; }

.dv-step-comment {
    font-size: 0.75rem;
    color: var(--dv-text);
    margin-top: var(--dv-space-2);
    padding-top: var(--dv-space-2);
    border-top: 1px solid var(--dv-line-soft);
}

/* --- The contractor's review, three columns ------------------------------- */

/* One column per question the contractor actually has: what am I accepting,
   what is my decision, what do I still owe. */
/* C54. A main column and a sidebar, not three equal columns.

   This was three equal columns holding four blocks of content, so the fourth
   wrapped onto a second row and left two cells empty, and the blocks ranged
   from 190px to 902px tall. Measured on the live portal: two empty cells and
   712px between the shortest column and the tallest. It read as cards scattered
   at random with a hole in the middle of the page.

   Named areas rather than a wrapper div, so the reading order in the markup is
   left alone: what am I accepting, what is my decision, what do I still owe,
   and the reference material beside all three. */
.dv-review {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
    grid-template-areas:
        "summary  side"
        "decision side"
        "owing    side";
    gap: var(--dv-space-4);
    align-items: start;
}

.dv-review-col {
    min-width: 0;
}

.dv-review-summary  { grid-area: summary; }
.dv-review-decision { grid-area: decision; }
.dv-review-side     { grid-area: side; }
.dv-review-owing    { grid-area: owing; }

/* Accept or reject, as two large targets. A mis-tap here signs off money. */
.dv-decision-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-3);
}

.dv-decision-btn {
    flex: 1 1 10rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 56px;
    padding: var(--dv-space-3);
    border: 2px solid var(--dv-line);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-surface);
    font-family: var(--dv-font-head);
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--dv-text-muted);
    text-align: center;
    cursor: pointer;
    transition: all 0.15s ease;
}

.dv-decision-note {
    font-family: var(--dv-font-body);
    font-size: 0.68rem;
    font-weight: 400;
    color: var(--dv-text-muted);
}

.dv-decision-btn:hover { border-color: var(--dv-blue-light); }

.btn-check:checked + .dv-decision-accept {
    border-color: var(--dv-green-deep);
    background: var(--dv-tint-green);
    color: var(--dv-green-deep);
}

.btn-check:checked + .dv-decision-reject {
    border-color: var(--dv-danger);
    background: var(--dv-tint-red);
    color: #B91C1C;
}

.btn-check:focus-visible + .dv-decision-btn {
    outline: 2px solid var(--dv-blue);
    outline-offset: 2px;
}

/* --- Compliance documents ------------------------------------------------- */

.dv-compliance {
    padding: var(--dv-space-3) 0;
    border-bottom: 1px solid var(--dv-line-soft);
}

.dv-compliance:last-of-type { border-bottom: none; }

.dv-compliance-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--dv-space-2);
}

.dv-compliance-type {
    font-weight: 600;
    font-size: 0.85rem;
    color: var(--dv-navy);
}

.dv-compliance-meta {
    font-size: 0.75rem;
    color: var(--dv-text-muted);
    margin-top: 2px;
}

/* A certificate card in Reviews that is not this contractor's turn yet. */
.dv-item-card.is-waiting {
    opacity: 0.82;
}


/* --------------------------------------------------------------------------
   REVIEWS AND DOCUMENTS - TABLET AND PHONE
   -------------------------------------------------------------------------- */

@media (max-width: 991.98px) {
    /* One column, and the reference material goes to the bottom rather than
       sitting between the decision and what is still owed. Named areas have to
       be restated here, or the two column definition above still applies. */
    .dv-review {
        grid-template-columns: 1fr;
        grid-template-areas:
            "summary"
            "decision"
            "owing"
            "side";
    }


    .dv-stepper {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 767.98px) {
    /* Full width, and comfortably over the 56px the decision needs. */
    .dv-decision-btn {
        flex: 1 1 100%;
    }
}


/* --------------------------------------------------------------------------
   REPORTS
   -------------------------------------------------------------------------- */

/* Two charts across on a desktop and a tablet, one on a phone, with the
   underlying table beneath each chart rather than beside it. A report that
   cannot be read on a tablet in a site meeting is not much use. */
/* Two across, never four. auto-fit put all four report cards on one row on a
   wide screen, which made each one narrow and tall: the donuts filled their
   card and the short charts sat in a lot of empty space, because the grid
   stretches every card to the tallest. Two columns give each chart room to be
   read and keep the cards a similar height. */
/* Two cards across, the same height, whatever is in them.

   Leon, 5 September 2026: "the right-hand one is half the size of the
   left-hand one. Just make sure that these are all aligned and look
   professional."

   It was align-items: start, so each card was as tall as its own contents and
   a two bar chart sat in a card half the height of the donut beside it. Stretch
   makes the row decide, and the card becomes a column so the chart can grow
   into the space and the caption can sit on the bottom edge. */
.dv-report-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--dv-space-4);
    margin-bottom: var(--dv-space-4);
    align-items: stretch;
}

.dv-report-grid > .dv-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    margin-bottom: 0;
}

/* A chart in a stretched card gets the leftover height rather than leaving it
   blank under the drawing. */
.dv-report-grid > .dv-card > .dv-chart-figure,
.dv-report-grid > .dv-card > .dv-chart-empty {
    flex: 1 1 auto;
}

/* One card across below a laptop. Two would leave a donut about 340px wide,
   which is smaller than it reads well at. This has to sit after the rule above
   rather than up with the other tablet rules, because they share a specificity
   and the later one wins. */
@media (max-width: 991.98px) {
    .dv-report-grid {
        grid-template-columns: 1fr;
    }
}

/* The chart text lives in SVG user units, so it is sized here rather than
   inheriting whatever the browser defaults to. Charts.LabelChars is measured
   against .dv-chart-label, so the two move together. */
.dv-chart-label {
    font-size: 13px;
    fill: var(--dv-text-muted);
}

.dv-chart-value {
    font-size: 13px;
    font-weight: 700;
    fill: var(--dv-navy);
}

/* C37. The numbers on the value axis. Smaller and lighter than a data label:
   a scale is there to be referred to, not read. */
.dv-chart-tick {
    font-size: 11px;
    fill: var(--dv-text-muted);
}

/* Charts.cs emits these four as well, and none of them had a rule. An SVG
   line with no stroke draws nothing, so every axis and gridline in the portal
   was invisible and the charts read as bars floating on a blank card. */
.dv-chart-axis {
    stroke: var(--dv-line);
    stroke-width: 1;
}

.dv-chart-grid {
    stroke: var(--dv-line-soft);
    stroke-width: 1;
    stroke-dasharray: 3 4;
}

.dv-chart-centre-value {
    font-family: var(--dv-font-head);
    font-size: 30px;
    font-weight: 800;
    fill: var(--dv-navy);
}

/* The stand-in when a chart has nothing to draw. It sits where the chart would
   have been, so a card with no data is the same shape as one with data. */
.dv-chart-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 140px;
    padding: var(--dv-space-4);
    border: 1px dashed var(--dv-line);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-canvas);
    color: var(--dv-text-muted);
    font-size: 0.85rem;
    text-align: center;
}

/* C54. An empty chart holds 140px open only where it is holding a card's shape,
   which is the report grid: there the cards sit side by side and one of them
   collapsing would leave the row uneven. Anywhere else an empty chart is a
   sentence, and 140px of dashed box is not.

   This was worst on an instruction with nothing instructed yet. The progress
   block sits in the navy header, so the box arrived with a dashed border and a
   pale panel on a dark ground, 140px tall, and pushed the header past half the
   screen on a laptop. One rule, so every progress block reads the same: the
   record header, and the cards on the register. */
.dv-progress-block .dv-chart-empty {
    min-height: 0;
    padding: var(--dv-space-2) 0 0;
    border: 0;
    background: none;
    justify-content: flex-start;
    text-align: left;
    font-size: 0.78rem;
}



.dv-report-grid > * {
    min-width: 0;
    margin-bottom: 0;
}

/* A KPI tile that is also the link to its detail report. */
.dv-kpi-link {
    text-decoration: none;
    transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.dv-kpi-link:hover {
    box-shadow: var(--dv-shadow-hover);
    transform: translateY(-1px);
    text-decoration: none;
}

.dv-kpi-link::after {
    content: "\2192";
    position: absolute;
    right: var(--dv-space-3);
    bottom: var(--dv-space-3);
    font-size: 0.9rem;
    color: var(--dv-text-muted);
}

/* What the period control resolved to, said in words beside the inputs so
   nobody has to work out what "quarter" meant. */
.dv-period-note {
    align-self: center;
    font-size: 0.78rem;
    color: var(--dv-text-muted);
    white-space: nowrap;
}

@media (max-width: 767.98px) {
    .dv-period-note {
        white-space: normal;
        width: 100%;
    }
}


/* --------------------------------------------------------------------------
   DASHBOARD ALERTS
   -------------------------------------------------------------------------- */

/* An alert carries a title and a line of explanation, so the badge sits at the
   top of the row rather than centred against two lines of text. */
.dv-row-stack {
    align-items: flex-start;
}

.dv-row-stack .dv-badge {
    flex: 0 0 auto;
    margin-top: 0.1rem;
}

/* The positive empty state. An empty alert list is good news and should read
   like it, rather than as a card that failed to load. */
.dv-alert-clear {
    display: flex;
    align-items: flex-start;
    gap: var(--dv-space-3);
}

.dv-alert-clear > svg {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    color: var(--dv-green);
}

.dv-alert-clear > div {
    min-width: 0;
}

.dv-alert-clear-title {
    font-weight: 600;
    color: var(--dv-text);
    margin-bottom: 0.15rem;
}


/* --------------------------------------------------------------------------
   INLINE SVG CHARTS

   Charts.cs writes its SVGs with width="100%" and no height. Without a rule
   here the browser falls back to the default replaced-element height of 150px,
   which turned the round progress bar into a green slab half a screen tall.
   -------------------------------------------------------------------------- */

/* Every chart is an inline SVG at width 100%, so its height comes from its own
   aspect ratio and it grows without limit as its card gets wider. A square
   donut in a 700px card was rendering 700px tall. The cap applies everywhere,
   because there is nowhere a chart should be free to do that; with the
   preserveAspectRatio="xMidYMid meet" the SVGs already carry, the drawing
   scales down inside the box and centres itself. */
.dv-chart {
    display: block;
    max-width: 100%;
    max-height: 280px;
    margin-inline: auto;
}

/* C37. A chart, its key and the sentence that says what it counts, as one
   figure. The figure grows to fill its card so two charts side by side line
   up whatever their natural heights are, and the caption sits at the foot of
   both rather than halfway up one of them. */
.dv-chart-figure {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    margin: 0;
    min-width: 0;
}

.dv-chart-figure > .dv-chart {
    flex: 0 0 auto;
    /* Two bars in a card as tall as a donut used to sit at the top with the
       space below them, which looks like something failed to load. Centred, the
       extra height reads as deliberate. */
    margin-block: auto;
}

.dv-chart-note {
    margin: var(--dv-space-3) 0 0;
    padding-top: var(--dv-space-3);
    border-top: 1px solid var(--dv-line-soft);
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--dv-text-muted);
}

/* The caption is the last thing in the card, so it holds the bottom edge and
   two cards of different content still finish level. */
.dv-chart-figure > .dv-chart-note {
    margin-top: auto;
}

/* The progress bar carries preserveAspectRatio="none" so it stretches to
   whatever height it is given. It is a bar, not a chart: keep it thin. */
.dv-progress {
    height: 10px;
}


/* --------------------------------------------------------------------------
   CALLOUT

   A block that explains an unusual view before the reader wonders what they
   are looking at. Follows the same amber treatment as .dv-reopen-note, with
   room for a heading and more than one line.
   -------------------------------------------------------------------------- */

.dv-callout {
    padding: var(--dv-space-3);
    border-left: 3px solid var(--dv-line);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-surface);
    font-size: 0.85rem;
    color: var(--dv-text);
}

.dv-callout-title {
    font-weight: 600;
    margin-bottom: 0.35rem;
}

.dv-callout-warning {
    border-left-color: var(--dv-warning);
    background: var(--dv-tint-amber);
}


/* --------------------------------------------------------------------------
   HEADING PAIRED WITH ITS CONTROLS

   A small heading sitting on one line with the badge or button that acts on it.

   This is not .dv-detail-head-row. That row aligns to the top, which is right
   for a large page title above a stack of buttons, but leaves a short uppercase
   heading floating above a badge that is centred inside its own taller pill.
   The heading styles also carry their own spacing: .dv-card-heading has a
   bottom padding and a rule, which as a flex item underlines only the width of
   the heading text, and .dv-section-heading has a top margin that pushes it
   down while the button beside it stays put. Both are stripped here.
   -------------------------------------------------------------------------- */

.dv-head-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--dv-space-3);
    margin-bottom: var(--dv-space-3);
}

.dv-head-inline > .dv-card-heading,
.dv-head-inline > .dv-section-heading {
    margin: 0;
    padding-bottom: 0;
    border-bottom: 0;
}

/* ==========================================================================
   HELP AND SUPPORT

   A contents tree that stays put beside a long document. Two columns on a
   laptop, stacked below that, with the contents collapsing to a plain list
   rather than a sidebar nobody can see past.
   ========================================================================== */

.dv-help-search {
    display: flex;
    align-items: center;
    gap: var(--dv-space-2);
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-3) var(--dv-space-4);
    margin-bottom: var(--dv-space-3);
}

.dv-help-search > svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--dv-text-muted);
}

.dv-help-search input {
    border: none;
    box-shadow: none;
    padding-left: 0;
    font-size: 0.95rem;
}

.dv-help-search input:focus {
    border: none;
    box-shadow: none;
    outline: none;
}

.dv-help-count {
    flex-shrink: 0;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    color: var(--dv-text-muted);
    white-space: nowrap;
}

.dv-help {
    display: grid;
    grid-template-columns: 268px minmax(0, 1fr);
    gap: var(--dv-space-4);
    align-items: start;
}

/* The contents follow you down the page. The offset clears the app bar. */
.dv-help-index {
    position: sticky;
    top: var(--dv-space-4);
    max-height: calc(100vh - 7rem);
    overflow-y: auto;
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-3);
}

.dv-help-index-head {
    font-size: 0.68rem;
    font-weight: 800;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    color: var(--dv-text-muted);
    padding: 0 var(--dv-space-2) var(--dv-space-2);
}

.dv-help-index-section { margin-bottom: var(--dv-space-2); }

.dv-help-index a {
    display: block;
    text-decoration: none;
    border-radius: var(--dv-radius-sm);
    line-height: 1.35;
}

.dv-help-index-1 {
    padding: 6px var(--dv-space-2);
    font-size: 0.84rem;
    font-weight: 700;
    color: var(--dv-navy);
}

.dv-help-index-2 {
    padding: 4px var(--dv-space-2) 4px 18px;
    font-size: 0.8rem;
    color: var(--dv-text-muted);
}

.dv-help-index-3 {
    padding: 3px var(--dv-space-2) 3px 32px;
    font-size: 0.76rem;
    color: var(--dv-text-muted);
}

.dv-help-index a:hover {
    background: var(--dv-tint-blue);
    color: var(--dv-blue);
}

.dv-help-body > .dv-help-section + .dv-help-section { margin-top: var(--dv-space-5); }

.dv-help-intro {
    margin-bottom: var(--dv-space-4);
    border-left: 3px solid var(--dv-blue);
}

.dv-help-section {
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-5);
}

.dv-help-h1 {
    font-family: var(--dv-font-head);
    font-size: 1.35rem;
    font-weight: 800;
    color: var(--dv-navy);
    letter-spacing: -0.4px;
    margin: 0;
}

.dv-help-h2 {
    font-size: 1.02rem;
    font-weight: 800;
    color: var(--dv-navy);
    margin: 0;
}

.dv-help-h3 {
    font-size: 0.92rem;
    font-weight: 700;
    color: var(--dv-navy);
    margin: 0;
}

/* The one line under a heading. Set apart so it reads as the answer in
   brief, with the paragraphs under it as the detail. */
.dv-help-summary {
    font-size: 0.86rem;
    font-weight: 600;
    color: var(--dv-blue);
    margin: 4px 0 var(--dv-space-3);
}

.dv-help-section > p,
.dv-help-topic > p,
.dv-help-sub > p {
    font-size: 0.9rem;
    line-height: 1.62;
    margin-bottom: var(--dv-space-3);
}

.dv-help-topic {
    margin-top: var(--dv-space-4);
    padding-top: var(--dv-space-4);
    border-top: 1px solid var(--dv-line);
    scroll-margin-top: var(--dv-space-4);
}

.dv-help-sub {
    margin: var(--dv-space-3) 0 0 0;
    padding: var(--dv-space-3) var(--dv-space-4);
    background: var(--dv-canvas);
    border-radius: var(--dv-radius-sm);
    scroll-margin-top: var(--dv-space-4);
}

.dv-help-section { scroll-margin-top: var(--dv-space-4); }

/* Where the search found something, and where a link landed you. */
.dv-help-hit > .dv-help-h2,
.dv-help-hit > .dv-help-h3 { color: var(--dv-blue); }

.dv-help-target {
    box-shadow: inset 3px 0 0 var(--dv-blue);
    padding-left: var(--dv-space-3);
}

.dv-help-empty {
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
    padding: var(--dv-space-6);
    text-align: center;
    margin-bottom: var(--dv-space-4);
}

@media (max-width: 991.98px) {
    .dv-help {
        grid-template-columns: 1fr;
    }

    /* Stacked, the contents is a list at the top rather than something
       sticky that would sit over the text. */
    .dv-help-index {
        position: static;
        max-height: none;
    }

    .dv-help-section { padding: var(--dv-space-4); }
}

/* ==========================================================================
   PAGER

   Sits under a paged list. The count on the left is what people actually read,
   so it comes first and stays readable when the links wrap under it.
   ========================================================================== */

.dv-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--dv-space-3);
    margin-top: var(--dv-space-4);
    padding: var(--dv-space-3) var(--dv-space-4);
    background: var(--dv-surface);
    border-radius: var(--dv-radius);
    box-shadow: var(--dv-shadow);
}

.dv-pager-count {
    font-size: 0.82rem;
    color: var(--dv-text-muted);
}

.dv-pager-count strong {
    color: var(--dv-navy);
    font-weight: 700;
}

.dv-pager-links {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
}

.dv-pager-step,
.dv-pager-num {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-width: 32px;
    height: 32px;
    padding: 0 10px;
    justify-content: center;
    border-radius: var(--dv-radius-sm);
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--dv-navy);
    text-decoration: none;
    border: 1px solid var(--dv-line);
    background: var(--dv-surface);
}

.dv-pager-step svg {
    width: 15px;
    height: 15px;
}

.dv-pager-step:hover,
.dv-pager-num:hover {
    background: var(--dv-tint-blue);
    border-color: var(--dv-blue);
    color: var(--dv-blue);
}

.dv-pager-num.active {
    background: var(--dv-blue);
    border-color: var(--dv-blue);
    color: #FFFFFF;
}

.dv-pager-num.active:hover {
    background: var(--dv-blue);
    color: #FFFFFF;
}

/* Shown rather than removed, so the control does not change width as you
   reach either end of the register. */
.dv-pager-step.disabled {
    opacity: 0.4;
    border-color: var(--dv-line-soft);
    cursor: default;
}

.dv-pager-gap {
    padding: 0 2px;
    color: var(--dv-text-muted);
}

@media (max-width: 575.98px) {
    .dv-pager {
        justify-content: center;
        text-align: center;
    }

    /* The numbers go on a phone. Previous and Next are what gets tapped. */
    .dv-pager-num:not(.active) {
        display: none;
    }
}

/* =====================================================================
   Task 5, MT-8. The collapsing detail header.

   Leon, in the review: "you seeing all that information is not needed all the
   time ... the top bit of a project like this, like, we don't, this is not
   relevant. We can have like a little hide and expand." Dewlann made the same
   point about density.

   One implementation, applied by a class, rather than four copies across
   Project, Instruction, Repair and Defect.

   The record number and the status badge stay visible when collapsed. Those
   two are the reason anybody looks at the header at all, so hiding them would
   defeat the point of having one.
   ===================================================================== */
.dv-detail-meta.is-collapsed > span:not(.dv-keep) {
    display: none;
}

.dv-meta-toggle {
    background: none;
    border: 0;
    padding: 0 0.35rem;
    color: var(--dv-text-muted);
    font-size: 0.8rem;
    cursor: pointer;
    line-height: 1;
}

.dv-meta-toggle:hover {
    color: var(--dv-blue);
}

/* A list of external references, e.g. the Stats SA releases on the CPA tab. */
.dv-link-list {
    list-style: none;
    padding: 0;
    margin: 0.25rem 0 0 0;
}

.dv-link-list li {
    padding: 0.15rem 0;
    font-size: 0.85rem;
}

.dv-link-list strong {
    display: inline-block;
    min-width: 5.5rem;
}

.dv-link-list .dv-muted {
    font-size: 0.8rem;
}

/* =====================================================================
   Task 36, MT-5. Field mode.

   Dewlann, in the review: "the moment we're on site, we need bigger icons, we
   need legacy options. We just need to focus on getting the data on the sheet."

   The reviewer of the Inspector login confirmed the responsive work is already
   sound, so this is not a responsiveness problem. It is a DENSITY problem: the
   same screen, correctly laid out for a tablet, still shows more than somebody
   standing on a road needs.

   So it is a class on <body> and nothing else. No parallel views: two copies of
   the conduct screen would diverge within a month.

   What it never does is hide something that cannot be reached another way, or
   change what a save does. It is the same system, quieter.
   ===================================================================== */
body.dv-field-mode {
    /* The theme routes every touch target through --dv-tap, and the FIELD USE
       block above already applies it below 992px. So field mode raises the
       variable rather than restating a list of selectors, which would fall out
       of date the first time a new control is added.

       42px to 54px. Measured, not guessed: at 42px the buttons were already
       reachable and field mode changed almost nothing, which is worse than not
       having it. */
    --dv-tap: 54px;
    font-size: 1.1rem;
}

/* And it has to hold above 992px as well. A tablet in landscape is 1024 wide
   and is still somebody standing next to a road. */
body.dv-field-mode .btn,
body.dv-field-mode .form-control,
body.dv-field-mode .form-select,
body.dv-field-mode .dv-nav-item,
body.dv-field-mode .dv-sidebar-user {
    min-height: var(--dv-tap);
}

body.dv-field-mode .btn,
body.dv-field-mode .form-control,
body.dv-field-mode .form-select {
    font-size: 1.05rem;
}

body.dv-field-mode .dv-icon-btn {
    min-width: var(--dv-tap);
}

body.dv-field-mode .dv-cond-btn {
    min-height: var(--dv-tap);
    min-width: 4.5rem;
    font-size: 1.1rem;
    padding: 0.6rem 1rem;
}

/* A tick box a gloved thumb can actually hit. */
body.dv-field-mode .form-check-input {
    width: 1.75rem;
    height: 1.75rem;
}

/* The KPI strip is a management view. On a road it is noise. */
body.dv-field-mode .dv-kpi,
body.dv-field-mode .dv-tab-count {
    display: none;
}

/* The detail header collapses to the number and the status. */
body.dv-field-mode .dv-detail-meta > span:not(.dv-keep) {
    display: none;
}

/* A table on a tablet becomes a stack of cards, because a horizontal scroll
   with one hand on a steering wheel is not a table. */
@media (max-width: 991.98px) {
    body.dv-field-mode .table-wrapper table thead {
        display: none;
    }

    body.dv-field-mode .table-wrapper table,
    body.dv-field-mode .table-wrapper tbody,
    body.dv-field-mode .table-wrapper tr,
    body.dv-field-mode .table-wrapper td {
        display: block;
        width: 100%;
    }

    /* Leon, 20 September 2026, on a phone: the stacked cards were wider than
       the screen, so the values sat off the right edge and a card of six
       figures looked empty. A table that has to scroll sideways keeps its
       34rem, but a table that has become a stack of cards is not scrolling any
       more and must fit the screen it is on. */
    body.dv-field-mode .table-wrapper > .table {
        min-width: 0;
    }

    body.dv-field-mode .table-wrapper tr {
        border: 1px solid var(--dv-line);
        border-radius: 0.5rem;
        margin-bottom: 0.6rem;
        padding: 0.5rem 0.75rem;
    }

    body.dv-field-mode .table-wrapper td {
        border: 0;
        padding: 0.25rem 0;
    }

    /* A stacked card with no labels is a column of orphan values: "Inspection",
       "RP-0002", "N9", "2026-08-20" tells a reader nothing. The heading the
       cell belongs under is carried on data-label by the field mode script and
       printed here, because the thead itself is hidden above. */
    body.dv-field-mode .table-wrapper td[data-label]::before {
        content: attr(data-label);
        display: block;
        font-size: 0.72rem;
        letter-spacing: 0.04em;
        text-transform: uppercase;
        color: var(--dv-text-muted);
        margin-bottom: 0.1rem;
    }

    /* An empty cell would otherwise show its label with nothing under it. */
    body.dv-field-mode .table-wrapper td:empty {
        display: none;
    }
}

/* The toggle itself, in the app bar. */
.dv-field-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.8rem;
    color: var(--dv-text-muted);
    white-space: nowrap;
}


/* =====================================================================
   TASK 7, their ref M3. The primary section summary.

   Three levels in one table. The indent and the weight are what make it
   readable as a tree; nothing here carries a colour of its own, it reads
   the same tokens every other table does.
   ===================================================================== */
.dv-summary-level-1 > td:first-child {
    font-weight: 600;
}

.dv-summary-level-1 > td {
    background: var(--dv-surface-muted, #f6f7f9);
}

.dv-summary-level-2 > td:nth-child(2) {
    padding-left: 1.25rem;
}

.dv-summary-level-3 > td:nth-child(3) {
    padding-left: 2.5rem;
    color: var(--dv-text-muted);
}

/* =====================================================================
   MAPS (Task 16)

   The container is sized by the page, not by the script, so nothing
   reflows when the tiles arrive and the written panel that stands in
   for a map occupies exactly the space the map would have.

   Every state a map can be in other than a map is .dv-map-empty, and it
   is a sentence a person can act on. Never a grey rectangle.
   ===================================================================== */
.dv-map-wrap {
    margin: 0;
}

.dv-map {
    width: 100%;
    min-height: 200px;
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius);
    overflow: hidden;
    background: var(--dv-line-soft);
}

.dv-map-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--dv-line-soft);
    padding: var(--dv-space-5);
}

.dv-map-empty-inner {
    max-width: 34rem;
    text-align: center;
}

.dv-map-empty-heading {
    font-weight: 600;
    color: var(--dv-text);
    margin-bottom: var(--dv-space-2);
}

.dv-map-empty-detail {
    margin: 0;
    color: var(--dv-text-muted);
    font-size: 0.875rem;
    line-height: 1.5;
}

/* The pin is drawn, not fetched: a map costs no extra requests and the
   colour comes from this file rather than from the script. */
.dv-map-pin {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50% 50% 50% 0;
    transform: rotate(-45deg);
    border: 2px solid #FFFFFF;
    box-shadow: var(--dv-shadow);
    color: #FFFFFF;
    font-size: 0.6875rem;
    font-weight: 700;
    line-height: 1;
}

/* The label rides upright inside a pin that is rotated. */
.dv-map-pin::first-line {
    line-height: 1;
}

.dv-map-bubble {
    min-width: 12rem;
    max-width: 18rem;
    font-size: 0.875rem;
    line-height: 1.45;
}

.dv-map-bubble-title {
    font-weight: 600;
    color: var(--dv-text);
    margin-bottom: 0.15rem;
}

.dv-map-bubble-detail {
    color: var(--dv-text-muted);
}

.dv-map-bubble-link {
    display: inline-block;
    margin-top: var(--dv-space-2);
    font-weight: 600;
}

.dv-map-missing {
    margin: var(--dv-space-2) 0 0;
    color: var(--dv-text-muted);
    font-size: 0.875rem;
}

/* Field mode: the map is the screen. The strip stays, everything else
   below it gives way, because an inspector holding a tablet at arm's
   length in daylight needs the map and not the chrome around it. */
.dv-field-mode .dv-map {
    min-height: 60vh;
}

@media (max-width: 575.98px) {
    .dv-map-bubble {
        max-width: 14rem;
    }
}

/* Task 14. Where a photograph was taken, on its tile. Small and quiet:
   it is a fact about the photograph, not a heading. */
.dv-gallery-where {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    margin-top: 0.15rem;
    font-size: 0.75rem;
    color: var(--dv-text-muted);
    white-space: nowrap;
}

.dv-gallery-where svg {
    width: 0.8rem;
    height: 0.8rem;
    flex: 0 0 auto;
}

/* =====================================================================
   THE LIVE ROUTE PANEL (Task 15)

   The wireframe drew this dark, against the rest of the screen. Leon,
   18 September 2026: "That dark block in the route position and the light
   one in the roads driven doesn't work. Make the route position the same
   colour." Side by side with Roads Driven the contrast read as a fault
   rather than as emphasis, so it is an ordinary card now and the reading
   itself carries the weight.
   ===================================================================== */
.dv-route-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--dv-space-3);
}

.dv-route-live {
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--dv-green);
    background: rgba(126, 194, 69, 0.18);
    border: 1px solid rgba(126, 194, 69, 0.45);
    border-radius: var(--dv-radius-pill);
    padding: 0.15rem 0.55rem;
}

/* The pulse says the reading is live without a second word for it. */
.dv-route-live::before {
    content: "\25CF";
    margin-right: 0.35rem;
    animation: dv-route-pulse 2s ease-in-out infinite;
}

@keyframes dv-route-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.25; }
}

@media (prefers-reduced-motion: reduce) {
    .dv-route-live::before { animation: none; }
}

.dv-route-km {
    text-align: center;
    margin-bottom: var(--dv-space-4);
}

.dv-route-km-label {
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--dv-text-muted);
}

.dv-route-km-value {
    font-size: 2.75rem;
    font-weight: 800;
    line-height: 1.05;
    color: var(--dv-blue);
    font-variant-numeric: tabular-nums;
}

.dv-route-km-unit {
    font-size: 0.8125rem;
    font-weight: 700;
    color: var(--dv-text-muted);
}

.dv-route-strip-wrap {
    margin-bottom: var(--dv-space-4);
}

.dv-route-strip-ends {
    display: flex;
    justify-content: space-between;
    gap: var(--dv-space-2);
    font-size: 0.6875rem;
    color: var(--dv-text-muted);
    margin-bottom: 0.35rem;
}

.dv-route-strip {
    position: relative;
    height: 16px;
    background: var(--dv-canvas);
    border: 1px solid var(--dv-line);
    border-radius: 8px;
}

.dv-route-done {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 0;
    background: linear-gradient(90deg, var(--dv-green), var(--dv-blue-bright));
    border-radius: 8px 0 0 8px;
    transition: width 0.6s ease;
}

.dv-route-marker {
    position: absolute;
    top: 50%;
    left: 0;
    transform: translate(-50%, -50%);
    width: 20px;
    height: 20px;
    background: #FFFFFF;
    border: 3px solid var(--dv-blue-bright);
    border-radius: 50%;
    box-shadow: 0 0 10px rgba(0, 137, 204, 0.7);
    transition: left 0.6s ease;
}

.dv-route-stats {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--dv-space-2);
    margin-bottom: var(--dv-space-3);
}

.dv-route-stat {
    background: var(--dv-canvas);
    border-radius: var(--dv-radius-sm);
    padding: var(--dv-space-2);
    text-align: center;
}

.dv-route-stat-label {
    font-size: 0.6875rem;
    color: var(--dv-text-muted);
}

.dv-route-stat-value {
    font-size: 1.0625rem;
    font-weight: 800;
    color: var(--dv-green);
    font-variant-numeric: tabular-nums;
}

.dv-route-stat-unit {
    font-size: 0.625rem;
    color: var(--dv-text-muted);
}

/* The coordinates AND how the chainage above was arrived at. The second
   half is the honest one and is why this is a sentence, not a readout. */
.dv-route-coords {
    background: var(--dv-canvas);
    border-radius: var(--dv-radius-sm);
    padding: var(--dv-space-2) var(--dv-space-3);
    font-size: 0.75rem;
    line-height: 1.45;
    color: var(--dv-text-muted);
}

.dv-route-note {
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--dv-text-muted);
    margin-top: var(--dv-space-2);
}

@media (max-width: 575.98px) {
    .dv-route-stats { grid-template-columns: repeat(2, 1fr); }
    .dv-route-km-value { font-size: 2.25rem; }
}

/* Task 17. The map key. Reading a coloured pin without one is guessing,
   and the dots read the same custom properties the pins do, so the key
   and the map cannot disagree. */
.dv-map-key {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-3);
    margin: var(--dv-space-3) 0;
    font-size: 0.8125rem;
    color: var(--dv-text-muted);
}

.dv-map-key-item {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    white-space: nowrap;
}

.dv-map-key-dot {
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.85);
    box-shadow: 0 0 0 1px var(--dv-line);
    flex: 0 0 auto;
}

/* =====================================================================
   INSTALL RADAR (Task 26)

   Shown only where the browser has said it can install, so it never
   appears somewhere it cannot be acted on. Quiet: it is an offer, not
   an alert, and it does not come back once it is dismissed.
   ===================================================================== */
.dv-install {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-3);
    align-items: center;
    justify-content: space-between;
    background: var(--dv-tint-blue);
    border: 1px solid var(--dv-blue-light);
    border-radius: var(--dv-radius);
    padding: var(--dv-space-3) var(--dv-space-4);
    margin-bottom: var(--dv-space-4);
}

.dv-install-body {
    flex: 1 1 22rem;
    min-width: 0;
}

.dv-install-title {
    font-weight: 600;
    color: var(--dv-blue-deep);
    margin-bottom: 0.15rem;
}

.dv-install-text {
    margin: 0;
    font-size: 0.875rem;
    line-height: 1.5;
    color: var(--dv-text);
}

.dv-install-actions {
    display: flex;
    gap: var(--dv-space-2);
    flex: 0 0 auto;
}

@media (max-width: 575.98px) {
    .dv-install-actions { width: 100%; }
    .dv-install-actions .btn { flex: 1 1 auto; }
}

/* =====================================================================
   TASK 4.1. The update banner.

   Shown when a new version of RADAR has installed and is waiting. It is a
   line across the top of the screen with one button on it, in the portal's
   own colours, rather than a browser prompt people close without reading.

   It sits above everything because it is the one message that is about the
   application rather than about the work.
   ===================================================================== */
.dv-update-banner {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1080;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--dv-space-4);
    flex-wrap: wrap;
    padding: var(--dv-space-3) var(--dv-space-4);
    background: var(--dv-navy);
    color: #FFFFFF;
    font-size: 0.95rem;
    box-shadow: 0 -2px 10px rgba(28, 35, 51, 0.18);
}

.dv-update-banner .btn {
    min-height: var(--dv-tap);
    padding-left: var(--dv-space-5);
    padding-right: var(--dv-space-5);
}

@media (max-width: 575.98px) {
    .dv-update-banner { flex-direction: column; text-align: center; }
    .dv-update-banner .btn { width: 100%; }
}

/* =====================================================================
   TASK 4.3. Make available offline.

   One block per assignment, drawn by dv-takeoffline.js once it has asked
   the device what it is carrying.
   ===================================================================== */
.dv-offline-block {
    border-top: 1px solid var(--dv-line);
    margin-top: var(--dv-space-3);
    padding-top: var(--dv-space-3);
}

.dv-offline-held {
    font-size: 0.9rem;
    color: var(--dv-text);
    margin-bottom: var(--dv-space-2);
}

.dv-offline-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
}

.dv-offline-actions .btn { min-height: var(--dv-tap); }

.dv-offline-progress { margin-top: var(--dv-space-2); }

.dv-held-list { padding-left: var(--dv-space-5); }
.dv-held-list li { margin-bottom: var(--dv-space-1); }

/* =====================================================================
   TASKS 4.4 to 4.8. Capturing with no signal.

   The same controls as the online conduct screen, so nothing has to be
   learned twice. Every touch target goes through --dv-tap, which field
   mode raises from 42px to 54px, and that is the whole of the field mode
   rule on this screen.
   ===================================================================== */
.dv-offline-banner {
    background: var(--dv-tint-navy);
    border-left: 4px solid var(--dv-navy);
    padding: var(--dv-space-3) var(--dv-space-4);
    margin-bottom: var(--dv-space-4);
    font-size: 0.95rem;
}

.dv-offline-capture .btn,
.dv-offline-capture .form-control,
.dv-offline-capture .form-select {
    min-height: var(--dv-tap);
}

.dv-offline-capture .dv-cond-btn,
.dv-offline-capture .dv-picker-item,
.dv-offline-capture .dv-road-chip {
    min-height: var(--dv-tap);
}

.dv-priced-row {
    display: flex;
    gap: var(--dv-space-2);
    margin-bottom: var(--dv-space-2);
}

.dv-priced-row select { flex: 3 1 auto; }
.dv-priced-row input { flex: 1 1 120px; }

.dv-photo-strip {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
    margin-top: var(--dv-space-2);
}

.dv-photo-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--dv-space-2);
    background: var(--dv-line-soft);
    border-radius: 6px;
    padding: var(--dv-space-2) var(--dv-space-3);
    font-size: 0.85rem;
}

/* Offline capture draws its road chips as buttons. Leon, 18 September 2026:
   these two rules had no element selector, so the round's own chip, a span
   wrapping a button and its x, took a square border around a rounded pill. */
button.dv-road-chip { cursor: pointer; border: 1px solid var(--dv-line); background: transparent; }
button.dv-road-chip.active { border-color: var(--dv-navy); }

/* =====================================================================
   TASK 4.13. The sync indicator and the waiting list.

   Words, not a symbol. The colour is a second way of saying the same
   thing rather than the only way, because a person standing in the sun
   with the screen at an angle reads the word.
   ===================================================================== */
.dv-sync-pill {
    border: 1px solid transparent;
    border-radius: 999px;
    padding: 6px var(--dv-space-3);
    font-size: 0.8rem;
    font-weight: 600;
    min-height: var(--dv-tap);
    line-height: 1.2;
    cursor: pointer;
    white-space: nowrap;
}

.dv-sync-online {
    background: var(--dv-tint-green);
    border-color: var(--dv-green-deep);
    color: var(--dv-green-deep);
}

.dv-sync-offline {
    background: var(--dv-line-soft);
    border-color: var(--dv-grey);
    color: var(--dv-text);
}

.dv-sync-sending {
    background: var(--dv-tint-blue);
    border-color: var(--dv-blue);
    color: var(--dv-blue-deep);
}

.dv-sync-waiting {
    background: #FFF6DA;
    border-color: #B8892B;
    color: #7A5A14;
}

.dv-sync-attention {
    background: #FCE8E6;
    border-color: #A3261A;
    color: #A3261A;
}

.dv-waiting-item {
    border: 1px solid var(--dv-line);
    border-radius: 8px;
    padding: var(--dv-space-3);
    margin-bottom: var(--dv-space-3);
}

.dv-waiting-attention {
    border-color: #A3261A;
    background: #FCE8E6;
}

.dv-waiting-what { font-weight: 600; }
.dv-waiting-when, .dv-waiting-tries { font-size: 0.9rem; color: var(--dv-text-muted); }
.dv-waiting-todo { font-size: 0.9rem; margin-top: var(--dv-space-2); }
.dv-waiting-item .btn { margin-top: var(--dv-space-2); min-height: var(--dv-tap); }

/* On a phone the app bar is tight, so the pill drops its padding rather
   than its word. */
@media (max-width: 575.98px) {
    .dv-sync-pill { padding: 4px var(--dv-space-2); font-size: 0.75rem; }
}

/* TASK 5.15. The roads driven on a trip, in order. Large buttons to tap, and
   the order above them as numbered chips that are tapped to take out. */
.dv-trip-order {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
    min-height: var(--dv-tap);
    align-items: center;
    padding: var(--dv-space-2);
    margin-bottom: var(--dv-space-3);
    border: 1px dashed var(--dv-line);
    border-radius: 8px;
}

.dv-trip-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 36px;
    padding: 4px 12px 4px 4px;
    border: none;
    border-radius: var(--dv-radius-pill);
    background: var(--dv-navy);
    color: #FFFFFF;
    font-family: var(--dv-font-head);
    font-weight: 700;
}

.dv-trip-chip-number {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 28px;
    height: 28px;
    border-radius: var(--dv-radius-pill);
    background: #FFFFFF;
    color: var(--dv-navy);
    font-size: 0.8rem;
}

.dv-trip-roads {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
    gap: var(--dv-space-2);
    margin-bottom: var(--dv-space-2);
}

.dv-trip-road {
    position: relative;
    min-height: 56px;
    padding: var(--dv-space-2);
    border: 1px solid var(--dv-line);
    border-radius: 8px;
    background: #FFFFFF;
    color: var(--dv-navy);
    font-family: var(--dv-font-head);
    font-size: 1.05rem;
    font-weight: 800;
}

.dv-trip-road.chosen {
    border-color: var(--dv-blue);
    background: var(--dv-tint-blue);
}

.dv-trip-road-count {
    position: absolute;
    top: 4px;
    right: 6px;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: var(--dv-radius-pill);
    background: var(--dv-blue);
    color: #FFFFFF;
    font-size: 0.72rem;
    line-height: 20px;
}

/* ---------------------------------------------------------------------------
   19 September 2026. Starting, checkpointing and ending a trip, and the month
   page rebuilt along the wireframe.
   --------------------------------------------------------------------------- */

/* What a captured trip comes to, as three figures rather than a line of text. */
.dv-trip-sum {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--dv-space-3);
    padding: var(--dv-space-3) var(--dv-space-4);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-canvas);
}

.dv-trip-sum-value {
    font-family: var(--dv-font-head);
    font-size: 1.15rem;
    font-weight: 800;
    color: var(--dv-navy);
    white-space: nowrap;
}

.dv-trip-sum-money { color: var(--dv-green-deep); }

.dv-trip-sum-label {
    font-size: 0.68rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--dv-text-muted);
}

/* The odometer is the reading somebody signs for, so its box is large. */
.dv-trip-odometer {
    font-size: 1.35rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Where the device says it is, with a button to take it again. */
.dv-trip-gps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--dv-space-2);
    padding: var(--dv-space-2) var(--dv-space-3);
    border: 1px solid var(--dv-line-soft);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-canvas);
}

.dv-trip-gps-text { font-size: 0.85rem; }

/* A trip still open, which must not be forgotten. */
.dv-trip-open-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--dv-space-3);
    margin-bottom: var(--dv-space-4);
    padding: var(--dv-space-3) var(--dv-space-4);
    border-radius: var(--dv-radius);
    background: var(--dv-tint-amber);
    box-shadow: inset 4px 0 0 #E8930C;
    color: var(--dv-text);
}

a.dv-trip-open-link { text-decoration: none; }
a.dv-trip-open-link:hover { color: var(--dv-text); box-shadow: inset 4px 0 0 #E8930C, var(--dv-shadow); }

.dv-trip-open-title {
    font-family: var(--dv-font-head);
    font-weight: 800;
    color: var(--dv-navy);
}

.dv-trip-open-text { font-size: 0.88rem; }

/* The trip step by step: start, checkpoints, end. */
.dv-trip-steps {
    list-style: none;
    margin: 0;
    padding: 0;
    position: relative;
}

.dv-trip-step {
    display: flex;
    gap: var(--dv-space-3);
    padding: var(--dv-space-2) 0 var(--dv-space-2) var(--dv-space-4);
    border-left: 3px solid var(--dv-line);
    position: relative;
}

.dv-trip-step::before {
    content: "";
    position: absolute;
    left: -8px;
    top: 14px;
    width: 13px;
    height: 13px;
    border-radius: 50%;
    background: var(--dv-surface);
    border: 3px solid var(--dv-blue);
}

.dv-trip-step-start::before { border-color: var(--dv-green); }
.dv-trip-step-end::before { border-color: var(--dv-navy); background: var(--dv-navy); }
.dv-trip-step-waiting::before { border-color: #E8930C; border-style: dashed; }

/* The step's own pin, the same letter or number and colour as on the map. */
.dv-trip-pin {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    margin-right: 6px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--dv-pin-default);
    color: #FFFFFF;
    font-size: 0.72rem;
    font-weight: 800;
    vertical-align: 1px;
}

.dv-trip-pin-start { background: var(--dv-pin-good); }
.dv-trip-pin-end { background: var(--dv-pin-alert); }

.dv-trip-step-time {
    flex: 0 0 3.5rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--dv-navy);
}

/* The month page: the person, then the tools. */
.dv-trip-person {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--dv-space-3);
    margin-bottom: var(--dv-space-4);
}

.dv-trip-person-who,
.dv-trip-person-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--dv-space-3);
}

.dv-trip-person-tools .form-select { min-width: 10rem; }

.dv-trip-person-name {
    font-family: var(--dv-font-head);
    font-size: 1.3rem;
    font-weight: 800;
    color: var(--dv-navy);
    line-height: 1.2;
}

.dv-avatar.dv-avatar-lg {
    width: 56px;
    height: 56px;
    font-size: 1.1rem;
    background: var(--dv-navy);
}

.dv-trip-kpis { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.dv-trip-money { color: var(--dv-green-deep); }

/* The month's trips as cards, as the wireframe has them. */
.dv-trip-cards {
    display: flex;
    flex-direction: column;
    gap: var(--dv-space-3);
}

.dv-trip-card {
    display: block;
    padding: var(--dv-space-3) var(--dv-space-4);
    border-radius: var(--dv-radius);
    background: var(--dv-surface);
    box-shadow: var(--dv-shadow);
    color: var(--dv-text);
    text-decoration: none;
    transition: box-shadow 0.2s ease, transform 0.2s ease;
}

.dv-trip-card:hover {
    color: var(--dv-text);
    box-shadow: var(--dv-shadow-hover);
    transform: translateY(-2px);
}

.dv-trip-card.is-open { box-shadow: inset 4px 0 0 #E8930C, var(--dv-shadow); }

.dv-trip-card-top {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--dv-space-3);
}

.dv-trip-card-date {
    font-family: var(--dv-font-head);
    font-weight: 800;
    color: var(--dv-navy);
}

.dv-trip-card-km {
    font-family: var(--dv-font-head);
    font-size: 1.1rem;
    font-weight: 800;
    color: var(--dv-blue);
    white-space: nowrap;
}

.dv-trip-card-project {
    margin-top: 2px;
    font-size: 0.85rem;
    font-weight: 700;
    color: var(--dv-blue);
}

.dv-trip-card-meta,
.dv-trip-card-foot {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--dv-space-3);
    margin-top: 4px;
    font-size: 0.84rem;
    color: var(--dv-text-muted);
}

.dv-trip-card-meta span + span::before,
.dv-trip-card-foot span + span::before {
    content: "\00B7";
    margin-right: var(--dv-space-3);
    color: var(--dv-line);
}

.dv-trip-card-money {
    margin-left: auto;
    font-weight: 700;
    color: var(--dv-green-deep);
}

.dv-trip-card-foot .dv-trip-card-money::before { content: none; }

/* Kilometres day by day: one slim column a day, the future faint. */
.dv-daybars {
    display: flex;
    align-items: stretch;
    gap: 3px;
    height: 190px;
    padding-top: var(--dv-space-2);
}

.dv-daybar {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-width: 0;
}

.dv-daybar-fill {
    display: block;
    width: 100%;
    min-height: 2px;
    border-radius: 4px 4px 1px 1px;
    background: var(--dv-line-soft);
}

.dv-daybar.has-km .dv-daybar-fill { background: var(--dv-blue-bright); }
.dv-daybar.has-km:hover .dv-daybar-fill { background: var(--dv-blue); }
.dv-daybar.is-future .dv-daybar-fill { background: var(--dv-canvas); }

.dv-daybar-day {
    height: 1.1rem;
    margin-top: 4px;
    font-size: 0.66rem;
    text-align: center;
    color: var(--dv-text-muted);
    white-space: nowrap;
}

/* Kilometres by project, each with its share as a bar. */
.dv-trip-split-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 4px var(--dv-space-3);
    padding: var(--dv-space-2) 0;
    border-bottom: 1px solid var(--dv-line-soft);
}

.dv-trip-split-row:last-child { border-bottom: none; }

.dv-trip-split-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dv-trip-split-name .dv-muted { margin-left: var(--dv-space-2); font-size: 0.85rem; }

.dv-trip-split-figure {
    font-weight: 700;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.dv-trip-split-bar {
    grid-column: 1 / -1;
    height: 6px;
    border-radius: 3px;
    background: var(--dv-line-soft);
    overflow: hidden;
}

.dv-trip-split-bar span {
    display: block;
    height: 100%;
    border-radius: 3px;
    background: var(--dv-blue-bright);
}

/* Leon, 17 September 2026: the top of a round takes far less room. How many
   assets are recorded is a pill in the header's action row, not a bar. */
.dv-session-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.3rem 0.75rem;
    border-radius: var(--dv-radius-pill);
    background: rgba(255, 255, 255, 0.14);
    border: 1px solid rgba(255, 255, 255, 0.28);
    color: #FFFFFF;
    font-size: 0.8rem;
    white-space: nowrap;
}

.dv-route-head { gap: var(--dv-space-2); }
.dv-route .dv-route-note { margin-top: 0; }

/* A road on the round with a small x to take it off, where it may come off. */
.dv-road-chip {
    display: inline-flex;
    align-items: center;
}

.dv-road-chip > .btn:not(:last-child) {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

/* The x reads as the right hand end of the chip, one control with it. */
.dv-road-chip-remove {
    align-self: stretch;
    min-width: 2.25rem;
    padding: 0 0.5rem;
    border: 1px solid var(--dv-field-line);
    border-left: none;
    border-radius: 0 var(--dv-radius-sm) var(--dv-radius-sm) 0;
    background: #FFFFFF;
    color: var(--dv-text-muted);
    font-size: 1.1rem;
    line-height: 1;
}

.dv-road-chip-remove:hover { color: #A3261A; background: #FCE8E6; }

.dv-road-add > summary {
    cursor: pointer;
    font-size: 0.85rem;
    color: var(--dv-blue);
}

/* Leon, 17 September 2026. A card folded away until it is wanted, such as
   the map and the late completion terms on an instruction. */
.dv-fold > summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: var(--dv-space-2);
}

.dv-fold > summary::-webkit-details-marker {
    display: none;
}

/* The heading keeps its accent bar before it; the fold's arrow sits at the
   far end of the heading. */
.dv-fold > summary::after {
    content: "\25B8";
    font-size: 1rem;
    margin-left: auto;
    color: var(--dv-text-muted);
    transition: transform 0.15s ease;
}

.dv-fold[open] > summary::after {
    transform: rotate(90deg);
}

/* Leon, 18 September 2026. Columns that hold a figure or a unit take only the
   room they need, so the description and the asset get the rest. The widths are
   a hint, not a cage: a wide table still scrolls inside its own box. */
.dv-col-tight {
    width: 6rem;
}

.dv-col-where {
    width: 8.5rem;
}

.dv-col-rate {
    width: 7.5rem;
}

.dv-col-money {
    width: 8.5rem;
}

/* Wide enough to read the reason somebody typed, not just its first word. */
.dv-col-reason {
    width: 16rem;
}

/* The description takes the room that is left, but not so much that the
   columns after it are pushed off the edge. */
.dv-col-desc {
    width: 22rem;
}

/* A line's priced items open in a pop-up over the table. A table inside a
   table row broke the columns of the table around it. */
.dv-work-open::before {
    content: "\25B8\00a0";
}

/* The priced items under a line open in place. */
.dv-priced-items > summary {
    cursor: pointer;
    color: var(--dv-blue);
}


/* ---------------------------------------------------------------------------
   LEON, 20 SEPTEMBER 2026. THE MENU AND THE TOOLBARS ON A PHONE
   --------------------------------------------------------------------------- */

/* Help and Support, the Asset Map and Asset Approvals used to be anchored at
   the foot with Configure, which left a short scrolling list above them and
   made the menu awkward to move under a thumb. They are the last group of the
   scrolling list now, under a hairline so the grouping still reads. */
.dv-nav-extra {
    margin-top: var(--dv-space-3);
    padding-top: var(--dv-space-3);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.dv-shell.dv-rail .dv-nav-extra {
    margin-top: var(--dv-space-2);
    padding-top: var(--dv-space-2);
}

/* A row of buttons at the top of a screen: where to go and what to do. It
   wraps on its own down to a tablet, which reads well. On a phone it used to
   break into a ragged stack of pills of different widths, so there the two
   halves become their own full width rows: the months as one segmented strip,
   the actions two to a row, each a full sized target. */
.dv-page-tools {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dv-space-2);
    align-items: center;
}

.dv-page-tools .dv-page-tools-spacer {
    margin-left: auto;
}

@media (max-width: 575.98px) {
    .dv-page-tools {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: var(--dv-space-2);
    }

    .dv-page-tools .dv-page-tools-spacer {
        display: none;
    }

    /* The months read as one control across the top. */
    .dv-page-tools .dv-tools-period {
        grid-column: 1 / -1;
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 0;
    }

    .dv-page-tools .dv-tools-period > .btn {
        border-radius: 0;
        border-right-width: 0;
    }

    .dv-page-tools .dv-tools-period > .btn:first-child {
        border-top-left-radius: var(--dv-radius-sm);
        border-bottom-left-radius: var(--dv-radius-sm);
    }

    .dv-page-tools .dv-tools-period > .btn:last-child {
        border-top-right-radius: var(--dv-radius-sm);
        border-bottom-right-radius: var(--dv-radius-sm);
        border-right-width: 1px;
    }

    /* Every action is the same size, side by side, two to a row. */
    .dv-page-tools > .btn,
    .dv-page-tools > form,
    .dv-page-tools > .btn-group,
    .dv-page-tools .dv-tools-period > .btn {
        width: 100%;
        min-height: var(--dv-tap);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        white-space: nowrap;
    }

    /* A set of buttons that belong together, such as the months, takes the
       whole row and splits it evenly rather than spilling out of half of it. */
    .dv-page-tools > .btn-group {
        grid-column: 1 / -1;
    }

    .dv-page-tools > .btn-group > .btn {
        flex: 1 1 0;
        min-width: 0;
        min-height: var(--dv-tap);
    }

    /* Something that must not be cut in half, such as a project picker, takes
       the whole row. */
    .dv-page-tools > .dv-tools-wide,
    .dv-page-tools > form.dv-tools-wide {
        grid-column: 1 / -1;
    }

    .dv-page-tools > form.dv-tools-wide .form-select {
        width: 100%;
        min-height: var(--dv-tap);
    }
}


/* ---------------------------------------------------------------------------
   LEON, 20 SEPTEMBER 2026. THE APP BAR ON A PHONE

   On an iPhone SE the project chip is 113 pixels wide, but the project's
   status badge ran from 220 to 278 and the caret to 303, so both slid out of
   the chip and under the Online pill, which starts at 237. Three things fit
   across the top of a phone: what screen you are on, which project you are in,
   and whether you are online. The project's long name and its status are both
   in the picker the moment it opens, so on a phone the chip carries its number
   and nothing else. Nothing is hidden that cannot be reached.
   --------------------------------------------------------------------------- */
@media (max-width: 575.98px) {
    .dv-appbar .dv-project-toggle {
        padding-left: 10px;
        padding-right: 8px;
        gap: 6px;
        overflow: hidden;
    }

    /* The folder, the long name and the status all give way to the number. */
    .dv-appbar .dv-project-toggle > svg:first-of-type,
    .dv-appbar .dv-project-toggle .dv-project-name,
    .dv-appbar .dv-project-toggle .dv-badge {
        display: none;
    }

    .dv-appbar .dv-project-number {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* The caret says it opens, so it is the one thing that never shrinks. */
    .dv-appbar .dv-project-toggle > svg:last-of-type {
        flex: 0 0 auto;
    }

    /* The chip takes exactly what its number needs, and the screen name
       shrinks around it rather than the other way about. */
    .dv-appbar-centre {
        flex: 0 0 auto;
    }

    .dv-appbar .dv-project-number {
        flex: 0 0 auto;
    }
}

/* Patch 5.3. The distress window, in the shape of Dewlann's drawing. Extent
   and Degree are two rows of five buttons, big enough for a thumb on a
   tablet. */
.dv-distress-scale {
    display: flex;
    gap: 8px;
}

.dv-distress-scale-btn {
    flex: 1 1 0;
    min-width: 0;
    max-width: 64px;
    min-height: var(--dv-tap, 44px);
    border: 1px solid var(--dv-line);
    border-radius: var(--dv-radius-pill);
    background: var(--dv-surface);
    color: var(--dv-text);
    font-weight: 700;
    font-size: 1.05rem;
}

.dv-distress-scale-btn:hover { background: var(--dv-tint-blue); }

.dv-distress-scale-btn.active {
    background: var(--dv-navy);
    border-color: var(--dv-navy);
    color: #FFFFFF;
}

.dv-distress-group {
    border-top: 1px solid var(--dv-line-soft);
    padding: 8px 0;
}

.dv-distress-group-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.dv-distress-group-name { font-weight: 700; }

.dv-distress-comp {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 6px 0 0 12px;
}

.dv-distress-comp-main { flex: 1 1 auto; min-width: 0; }
.dv-distress-comp-amount { flex: 0 0 auto; white-space: nowrap; }
.dv-distress-view [data-dv-comp-remove] { display: none; }

.dv-distress-total {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    border-top: 1px solid var(--dv-line);
    padding-top: 8px;
}

.dv-distress-chosen {
    border: 1px solid var(--dv-line-soft);
    border-radius: var(--dv-radius-sm);
    background: var(--dv-canvas);
    padding: 10px 12px;
}

.dv-distress-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 16px;
}

/* A distress in the round's list, told apart from the assets. */
.dv-round-row.dv-round-distress { border-left: 4px solid var(--dv-danger); }
