/* ═════════════════════════════════════════════════════════════════════════════
   JD design system — the house page style.

   Approved on the /account redesign (PR #167) and lifted here the moment a
   second page (/portal/trip) adopted it, so there is ONE copy rather than three.
   Anything visual and reusable belongs here; page-specific rules stay in that
   page's scoped `.razor.css`.

   Deliberately GLOBAL, not a scoped `.razor.css`:
     · shared components (CollapsibleCard) and pages both need these classes, and
     · Blazor CSS isolation only stamps its b-xxxx attribute on elements a single
       .razor renders — a scoped rule could never reach both.
   Every class is `jd-`-prefixed and only ever applied to elements we own, so it
   cannot collide with MudBlazor's own styles.

   ── Colour rule, read before touching a hex value ──────────────────────────
   MudThemeProvider swaps the --mud-palette-* values for dark/light and leaves
   NO class or attribute to branch on, so a stylesheet cannot ask which theme is
   active. Brand amber #FFB300 is only ~2.6:1 on white, so as *text* it fails AA
   on the light theme.

   Fix: mix the accent toward --mud-palette-text-primary, which is near-black on
   light and near-white on dark. One expression, both themes:
       --jd-accent         52% amber → 4.6:1 on light, pale amber on dark  (TEXT)
       --jd-accent-strong  68% amber → 3.3:1 on light, bright amber on dark (ICONS)
   Raw --jd-amber is for surfaces, borders, gradients and focus rings only,
   where contrast ratios don't apply.
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
    --jd-amber: #FFB300;
    --jd-amber-deep: #ff8f00;
    --jd-accent: color-mix(in srgb, #FFB300 52%, var(--mud-palette-text-primary));
    --jd-accent-strong: color-mix(in srgb, #FFB300 68%, var(--mud-palette-text-primary));
    --jd-ok: color-mix(in srgb, var(--mud-palette-success) 60%, var(--mud-palette-text-primary));
    /* 56%, not 60%: at 60% this measured 4.48:1 on the light theme — just under AA. */
    --jd-warn: color-mix(in srgb, var(--mud-palette-warning) 56%, var(--mud-palette-text-primary));
    --jd-info: color-mix(in srgb, var(--mud-palette-info) 60%, var(--mud-palette-text-primary));
    --jd-danger: color-mix(in srgb, var(--mud-palette-error) 60%, var(--mud-palette-text-primary));
    --jd-line: color-mix(in srgb, var(--mud-palette-lines-default) 85%, transparent);
    --jd-tint: color-mix(in srgb, var(--mud-palette-text-primary) 4%, transparent);
    --jd-tint-strong: color-mix(in srgb, var(--mud-palette-text-primary) 7%, transparent);
    --jd-radius: 20px;
    --jd-radius-sm: 12px;
    /* Widest the content column ever gets. One number — see "content width
       cap" below for what reads it and why it isn't a MudBlazor MaxWidth. */
    --jd-content-max: 1440px;
}

/* ── content width cap ───────────────────────────────────────────────────────
   A 32" monitor is ~2560 CSS px wide. PortalLayout used to hand its container
   MudBlazor's MaxWidth.ExtraLarge, which is 1920px — wide enough that a
   two-column card puts its two halves at opposite ends of the desk and the eye
   has to travel to pair them up. This caps it instead.

   MudBlazor's MaxWidth enum cannot express the number we want: its rungs jump
   1280px (Large) → 1920px (ExtraLarge) with nothing between, so the container
   asks for MaxWidth.False and is capped here, where the value is one token.

   The app bar is capped to the SAME width, so the logo lines up with the left
   edge of the content and the account controls with its right edge. That only
   works because .mud-container--gutters and .mud-toolbar-gutters carry the
   identical 16px/24px padding — the two columns agree to the pixel.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-shell {
    max-width: var(--jd-content-max);
}

/* `> .mud-toolbar` is the single div MudAppBar renders inside itself. The class
   goes on the MudAppBar we author, so this reaches nothing we don't own. */
.jd-appbar > .mud-toolbar {
    width: 100%;
    max-width: var(--jd-content-max);
    margin-inline: auto;
}

/* ── page shell ──────────────────────────────────────────────────────────────
   Ambient amber wash behind the panels. A background, not an element, so it
   can't introduce a scroll container or overflow.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-page {
    position: relative;
    background: radial-gradient(ellipse 70% 45% at 50% 0%, rgba(255, 179, 0, 0.07), transparent 72%);
}

/* ── page header ─────────────────────────────────────────────────────────────
   Eyebrow + title, with an optional intro paragraph. Lifted here after five
   pages had grown their own identical copy of it.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-page-head {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    flex-wrap: wrap;
    margin-bottom: 0.5rem;
}

.jd-page-headings { min-width: 0; flex: 1 1 auto; }

/* Circular back affordance beside a page title. */
.jd-back-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    color: var(--mud-palette-text-secondary);
    background: var(--jd-tint);
    border: 1px solid var(--jd-line);
    text-decoration: none;
    transition: color .16s ease, border-color .16s ease, transform .16s ease;
}

.jd-back-btn:hover {
    color: var(--mud-palette-text-primary);
    border-color: rgba(255, 179, 0, 0.45);
    transform: translateX(-2px);
}

.jd-back-btn:focus-visible { outline: 2px solid var(--jd-amber); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
    .jd-back-btn { transition: none; }
    .jd-back-btn:hover { transform: none; }
}

.jd-page-title {
    margin: 0;
    font-size: 1.9rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.15;
    color: var(--mud-palette-text-primary);
}

.jd-page-intro {
    margin: 0 0 1.35rem;
    max-width: 84ch;
    font-size: 0.85rem;
    line-height: 1.6;
    color: var(--mud-palette-text-secondary);
}

.jd-page-intro b { color: var(--mud-palette-text-primary); font-weight: 600; }

.jd-page-intro a {
    color: var(--jd-accent);
    text-decoration: none;
    border-bottom: 1px solid color-mix(in srgb, var(--jd-accent) 45%, transparent);
}

.jd-page-intro a:hover { border-bottom-color: var(--jd-accent); }

@media (max-width: 560px) {
    .jd-page-title { font-size: 1.5rem; }
}

/* ── panel ───────────────────────────────────────────────────────────────── */

.jd-panel {
    position: relative;
    margin-bottom: 1.15rem;
    border-radius: var(--jd-radius);
    overflow: hidden;
    background: color-mix(in srgb, var(--mud-palette-surface) 82%, transparent);
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    border: 1px solid var(--jd-line);
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.04);
}

/* Staggered entrance. Set `style="--d:.07s"` per panel to cascade them. */
.jd-rise { animation: jd-rise 0.6s cubic-bezier(0.16, 1, 0.3, 1) var(--d, 0s) backwards; }

@keyframes jd-rise {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: translateY(0); }
}

.jd-panel-body { padding: 1.35rem 1.5rem; }
.jd-panel > .jd-panel-head + .jd-panel-body { padding-top: 0; }

/* An ambient amber orb, clipped by the panel. For hero/feature panels only —
   it is a decoration, so it never carries meaning or affects layout. */
.jd-panel-orb::before {
    content: "";
    position: absolute;
    top: -90px;
    right: -70px;
    width: 260px;
    height: 260px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(255, 179, 0, 0.32), transparent 70%);
    filter: blur(52px);
    opacity: 0.5;
    pointer-events: none;
}

.jd-panel-orb > * { position: relative; }

/* ── panel header: badge + title + sub, affixes pushed right ─────────────── */

.jd-panel-head {
    position: relative;
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 1.15rem 1.5rem;
}

.jd-badge {
    flex: 0 0 auto;
    width: 42px;
    height: 42px;
    border-radius: 13px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--jd-accent-strong);
    background: linear-gradient(135deg, rgba(255, 179, 0, 0.20), rgba(255, 143, 0, 0.05));
    border: 1px solid rgba(255, 179, 0, 0.30);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

.jd-badge-sm { width: 34px; height: 34px; border-radius: 11px; }

.jd-panel-headings { min-width: 0; flex: 1; }

.jd-panel-title {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.3;
    color: var(--mud-palette-text-primary);
}

.jd-panel-sub {
    margin: 0.3rem 0 0;
    font-size: 0.82rem;
    line-height: 1.5;
    color: var(--mud-palette-text-secondary);
    max-width: 64ch;
}

/* The noun inside a .jd-count pill — "12 TOTAL". A bare number beside a page title is
   ambiguous, and four pages had grown their own copy of this rule. */
.jd-count-noun {
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    opacity: 0.7;
}

.jd-eyebrow {
    margin: 0 0 0.2rem;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--jd-accent);
}

/* count / total pill, e.g. "10/10" */
.jd-count {
    flex: 0 0 auto;
    align-self: center;
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    font-size: 0.9rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--jd-accent);
    background: rgba(255, 179, 0, 0.10);
    border: 1px solid rgba(255, 179, 0, 0.28);
}

/* ── collapsible panel header ────────────────────────────────────────────────
   The whole bar toggles, but the toggle is a real <button> so it is reachable
   and announced. Its ::before stretches over the bar to keep the big click
   target; header affixes sit above it on the z-axis and stop propagation, so a
   control in the header (an edit pencil, an "add" button) acts without also
   collapsing the panel underneath it.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-panel-toggle {
    display: flex;
    align-items: center;
    /* A <button> centres its content by UA default, and that survives display:flex —
       so without this the badge + title float in the middle of the header bar while
       a non-button header (FlightStatusPanel) sits correctly at the left. */
    justify-content: flex-start;
    gap: 0.85rem;
    flex: 1;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.jd-panel-toggle::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--jd-radius) var(--jd-radius) 0 0;
}

.jd-panel-toggle:focus-visible { outline: none; }
.jd-panel-toggle:focus-visible::before {
    outline: 2px solid var(--jd-amber);
    outline-offset: -3px;
}

.jd-panel-head:hover .jd-badge { border-color: rgba(255, 179, 0, 0.5); }

/* affixes: above the toggle overlay so their own clicks land */
.jd-panel-affix {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    flex: 0 0 auto;
}

.jd-chevron {
    position: relative;
    z-index: 1;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: none;
    color: var(--mud-palette-text-secondary);
    cursor: pointer;
    transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1), background 160ms ease, color 160ms ease;
}

.jd-chevron:hover { background: var(--jd-tint); color: var(--mud-palette-text-primary); }
.jd-chevron:focus-visible { outline: 2px solid var(--jd-amber); outline-offset: 2px; }
.jd-chevron.is-open { transform: rotate(180deg); }

/* ── chips ───────────────────────────────────────────────────────────────── */

.jd-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.22rem 0.6rem 0.22rem 0.45rem;
    border-radius: 999px;
    font-size: 0.74rem;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
    background: var(--jd-tint);
    border: 1px solid var(--jd-line);
    white-space: nowrap;
}

.jd-chip .mud-icon-root { font-size: 0.95rem; }

/* An inline annotation riding inside a line of caption text ("live" beside a flight time),
   rather than a chip standing on its own in a row. */
.jd-chip-xs {
    padding: 0.05rem 0.4rem;
    font-size: 0.65rem;
    vertical-align: middle;
}

.jd-chip.is-ok {
    color: var(--jd-ok);
    background: color-mix(in srgb, var(--mud-palette-success) 12%, transparent);
    border-color: color-mix(in srgb, var(--mud-palette-success) 35%, transparent);
}

.jd-chip.is-warn {
    color: var(--jd-warn);
    background: color-mix(in srgb, var(--mud-palette-warning) 12%, transparent);
    border-color: color-mix(in srgb, var(--mud-palette-warning) 35%, transparent);
}

.jd-chip.is-danger {
    color: var(--jd-danger);
    background: color-mix(in srgb, var(--mud-palette-error) 12%, transparent);
    border-color: color-mix(in srgb, var(--mud-palette-error) 35%, transparent);
}

.jd-chip.is-info {
    color: var(--jd-info);
    background: color-mix(in srgb, var(--mud-palette-info) 12%, transparent);
    border-color: color-mix(in srgb, var(--mud-palette-info) 35%, transparent);
}

.jd-chip.is-accent {
    color: var(--jd-accent);
    background: rgba(255, 179, 0, 0.10);
    border-color: rgba(255, 179, 0, 0.28);
}

/* ── inline card link ────────────────────────────────────────────────────────
   A name in running text that opens its own record — a customer card, a profile.
   It is a <button>, not an <a>, because those records are dialogs rather than
   URLs; everything below is the reset that makes a button read as text. It takes
   the type scale of whatever it sits in (`font: inherit`), so the same class
   works on an h6 name and on a body2 caption without a size variant.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-cardlink {
    display: inline;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    text-align: inherit;
    cursor: pointer;
    /* max-width because Blink blockifies a <button> back to inline-block whatever
       `display` says: without it a long name is one unbreakable box that overflows
       a narrow column. text-decoration, not border-bottom, for the same reason —
       a border draws once under the whole box, an underline follows each wrapped
       line. Underline only, no colour change: the name keeps the weight of the
       heading it sits in, so linking it doesn't make it shout over the real
       actions on the page. */
    max-width: 100%;
    text-decoration: underline;
    text-decoration-color: color-mix(in srgb, var(--jd-accent) 45%, transparent);
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
}

.jd-cardlink:hover { color: var(--jd-accent-strong); text-decoration-color: var(--jd-accent); }
.jd-cardlink:focus-visible { outline: 2px solid var(--jd-amber); outline-offset: 3px; border-radius: 3px; }

/* ── buttons ─────────────────────────────────────────────────────────────────
   `.jd-ghost` works as a <button> AND as a <label for> (file inputs), which is
   why it is not built on MudButton.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-ghost {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.36rem 0.75rem;
    border-radius: 10px;
    font-family: inherit;
    font-size: 0.78rem;
    font-weight: 600;
    cursor: pointer;
    color: var(--mud-palette-text-primary);
    background: var(--jd-tint);
    border: 1px solid var(--jd-line);
    transition: background 0.16s ease, border-color 0.16s ease, color 0.16s ease, transform 0.12s ease;
}

.jd-ghost:hover:not(:disabled):not(.is-busy) {
    border-color: rgba(255, 179, 0, 0.45);
    color: var(--jd-accent);
    transform: translateY(-1px);
}

.jd-ghost:focus-visible { outline: 2px solid var(--jd-amber); outline-offset: 2px; }
.jd-ghost:disabled, .jd-ghost.is-busy { opacity: 0.45; cursor: not-allowed; }

/* It also works as an <a> (a link out to a map, a document). Anchors arrive underlined and
   in the link colour, which would fight the ghost's quiet border. */
a.jd-ghost { text-decoration: none; }

.jd-ghost.is-danger:hover:not(:disabled) {
    color: var(--mud-palette-error);
    border-color: color-mix(in srgb, var(--mud-palette-error) 45%, transparent);
}

.jd-ghost .mud-icon-root { font-size: 1rem; }

/* Amber primary. Applied to a MudButton via Class= — it has to out-specify
   MudBlazor's own filled-button rules, hence the !important. */
.jd-primary {
    border-radius: var(--jd-radius-sm) !important;
    padding: 0.45rem 1.35rem !important;
    font-weight: 700 !important;
    text-transform: none !important;
    letter-spacing: 0.01em !important;
    color: #1a1a1a !important;
    background: linear-gradient(135deg, var(--jd-amber) 0%, var(--jd-amber-deep) 100%) !important;
    box-shadow: 0 8px 22px rgba(255, 179, 0, 0.22) !important;
    transition: box-shadow 0.2s ease, transform 0.18s ease !important;
}

.jd-primary:hover {
    transform: translateY(-1px);
    box-shadow: 0 12px 28px rgba(255, 179, 0, 0.32) !important;
}

/* Disabled. `:disabled` FIRST, because that is the only one of these that actually matches:
   MudBlazor 9 renders a disabled MudButton as a real disabled <button> and adds no
   `mud-button-disabled` class at all, so this rule silently stopped applying — leaving a
   disabled amber button at full brightness, carrying MudBlazor's own 26%-opacity white
   label. It looked pressable and its text was unreadable. The class selectors stay for any
   MudBlazor version that does emit them. */
.jd-primary:disabled,
.jd-primary[disabled],
.jd-primary.mud-button-disabled,
.jd-primary.mud-disabled {
    background: var(--jd-tint) !important;
    color: var(--mud-palette-text-disabled) !important;
    box-shadow: none !important;
    transform: none !important;
}

/* …and it must not lift on hover either, or it still reads as live. */
.jd-primary:disabled:hover,
.jd-primary[disabled]:hover {
    transform: none !important;
    box-shadow: none !important;
}

/* ── card actions bar ────────────────────────────────────────────────────────
   The tinted strip under a divider at the foot of a full-width row card, holding
   that record's sub-pages and its destructive action. Buttons, links and a
   right-aligned tail all read the same.

   Lifted out of VehicleRowCard's scoped CSS the moment a second card (the fleet
   entry) needed the identical bar. Global, not scoped, for the usual reason: the
   MudIcon inside a button is rendered by MudIcon and never carries a caller's
   isolation attribute.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-cardactions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding: 11px 16px;
    border-top: 1px solid var(--jd-line);
    background: var(--jd-tint);
}

/* Pushes itself and everything after it to the right-hand end of the bar. */
.jd-cardactions-gap { flex: 1 1 auto; }

.jd-cardaction {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 0.42rem 0.85rem;
    border-radius: 10px;
    font-family: inherit;
    font-size: 0.79rem;
    font-weight: 600;
    line-height: 1.4;
    text-decoration: none;
    cursor: pointer;
    color: var(--mud-palette-text-primary);
    background: color-mix(in srgb, var(--mud-palette-surface) 72%, transparent);
    border: 1px solid var(--jd-line);
    transition: border-color 0.16s ease, color 0.16s ease, transform 0.12s ease, background 0.16s ease;
}

.jd-cardaction:hover:not(:disabled) {
    border-color: rgba(255, 179, 0, 0.45);
    color: var(--jd-accent);
    transform: translateY(-1px);
}

.jd-cardaction:focus-visible { outline: 2px solid var(--jd-amber); outline-offset: 2px; }

.jd-cardaction:disabled { opacity: 0.45; cursor: not-allowed; }

.jd-cardaction .mud-icon-root { font-size: 1.15rem; }

.jd-cardaction-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 19px;
    height: 19px;
    padding: 0 5px;
    border-radius: 999px;
    font-size: 0.68rem;
    font-weight: 700;
    color: var(--jd-accent);
    background: rgba(255, 179, 0, 0.16);
}

.jd-cardaction-danger:hover:not(:disabled) {
    color: var(--mud-palette-error);
    border-color: color-mix(in srgb, var(--mud-palette-error) 45%, transparent);
}

@media (max-width: 560px) {
    .jd-cardactions-gap { display: none; }

    .jd-cardaction { flex: 1 1 calc(50% - 8px); justify-content: center; }

    /* Everything else pairs up two-to-a-row; the destructive one does not stretch to fill
       the row it lands on, because the widest, boldest control on a phone should not be
       the one that ends something. */
    .jd-cardaction-danger { flex: 0 1 auto; margin-left: auto; }
}

@media (prefers-reduced-motion: reduce) {
    .jd-cardaction { transition: none; }
    .jd-cardaction:hover { transform: none; }
}

/* ── selectable tiles ────────────────────────────────────────────────────────
   Two of these replace an ambiguous boolean switch: each states its own
   consequence, so the unselected option's cost is visible too.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 0.7rem;
}

.jd-tile {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    width: 100%;
    padding: 0.85rem 0.9rem;
    text-align: left;
    font-family: inherit;
    cursor: pointer;
    border-radius: 14px;
    border: 1px solid var(--jd-line);
    background: var(--jd-tint);
    color: var(--mud-palette-text-primary);
    -webkit-appearance: none;
    appearance: none;
    transition: border-color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease, transform 0.12s ease;
}

.jd-tile:hover:not(:disabled) { border-color: rgba(255, 179, 0, 0.40); }
.jd-tile:active:not(:disabled) { transform: scale(0.995); }
.jd-tile:focus-visible { outline: 2px solid var(--jd-amber); outline-offset: 2px; }
.jd-tile:disabled { opacity: 0.6; cursor: progress; }

.jd-tile.selected {
    border-color: rgba(255, 179, 0, 0.55);
    background: linear-gradient(135deg, rgba(255, 179, 0, 0.14), rgba(255, 143, 0, 0.04));
    box-shadow: 0 8px 24px rgba(255, 179, 0, 0.12);
}

.jd-tile-icon {
    flex: 0 0 auto;
    width: 38px;
    height: 38px;
    border-radius: 11px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--mud-palette-text-secondary);
    background: var(--jd-tint);
    transition: color 0.18s ease, background 0.18s ease;
}

.jd-tile.selected .jd-tile-icon { color: var(--jd-accent-strong); background: rgba(255, 179, 0, 0.16); }

.jd-tile-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.jd-tile-title { font-size: 0.92rem; font-weight: 600; line-height: 1.3; }

.jd-tile-caption {
    margin-top: 2px;
    font-size: 0.77rem;
    line-height: 1.4;
    color: var(--mud-palette-text-secondary);
}

.jd-tile-check { flex: 0 0 auto; color: var(--jd-accent-strong); line-height: 0; margin-top: 2px; }

/* ── line rows inside a panel ────────────────────────────────────────────────
   A repeated item within a card — an extra, a fee, a purchase. Goes on the MudPaper
   those cards already render, so it only restyles what is there: MudBlazor's 4px
   corners inside a 20px panel read as photos dropped onto a card, and a full border
   around every row turned one panel into a stack of little ones. A tint and the
   house radius instead. Loaded after MudBlazor.min.css, so no !important is needed.

   Below 600px the row's own content is given the first line to itself
   (`.jd-linerow-main`) and the controls flow underneath, rather than five competing
   flex children squeezing a label into three wrapped lines.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-linerow {
    border-radius: var(--jd-radius-sm);
    border-color: var(--jd-line);
    background: var(--jd-tint);
    transition: border-color .16s ease, background .16s ease;
}

.jd-linerow:hover { border-color: color-mix(in srgb, var(--jd-amber) 32%, var(--jd-line)); }

@media (max-width: 599px) {
    /* MudStack renders utility classes (d-flex flex-row …), not a .mud-stack hook — hence
       the direct-child selector rather than a component one. */
    .jd-linerow > .d-flex { flex-wrap: wrap; }
    .jd-linerow .jd-linerow-main { flex: 1 0 100%; min-width: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .jd-linerow { transition: none; }
}

/* ── semantic accents ────────────────────────────────────────────────────────
   A surface that colour-codes itself (launcher tiles, status rails) needs TWO
   values per meaning, because the contrast bar differs:

     --accent       saturated — rails, icons, borders. Graphics, so 3:1.
     --accent-text  mixed toward text-primary — labels, meta. Small text, 4.5:1.

   Set both together via one of these classes, then use var(--accent) /
   var(--accent-text) inside. Never hard-code a hex here: a fixed #66BB6A is
   picked for the dark theme and drops to ~2.2:1 as text on the light one.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-accent-brand     { --accent: var(--jd-amber);              --accent-text: var(--jd-accent); }
.jd-accent-success   { --accent: var(--mud-palette-success);   --accent-text: var(--jd-ok); }
.jd-accent-warning   { --accent: var(--mud-palette-warning);   --accent-text: var(--jd-warn); }
.jd-accent-info      { --accent: var(--mud-palette-info);      --accent-text: var(--jd-info); }
.jd-accent-danger    { --accent: var(--mud-palette-error);     --accent-text: var(--jd-danger); }
.jd-accent-neutral   { --accent: var(--mud-palette-text-secondary); --accent-text: var(--mud-palette-text-secondary); }

/* ── KPI / stat cards ────────────────────────────────────────────────────────
   The dashboard headline-number card. Lifted here because the operator and the
   admin dashboard each carried their own copy of it in scoped CSS.

   Give a card a jd-accent-* class to colour its icon and headline number.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
    gap: 18px;
}

.jd-kpi {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 132px;
    padding: 18px 20px;
    border-radius: 18px;
    color: var(--mud-palette-text-primary);
    background: color-mix(in srgb, var(--mud-palette-surface) 82%, transparent);
    backdrop-filter: blur(20px) saturate(140%);
    -webkit-backdrop-filter: blur(20px) saturate(140%);
    border: 1px solid var(--jd-line);
    box-shadow: 0 14px 40px rgba(0, 0, 0, 0.16), inset 0 1px 0 rgba(255, 255, 255, 0.04);
    transition: border-color 0.18s ease, background 0.18s ease, box-shadow 0.18s ease, transform 0.12s ease;
    animation: jd-rise 0.5s cubic-bezier(0.16, 1, 0.3, 1) var(--d, 0s) backwards;
}

.jd-kpi-clickable { cursor: pointer; user-select: none; }

.jd-kpi-clickable:hover {
    border-color: color-mix(in srgb, var(--accent-text, var(--jd-amber)) 55%, transparent);
    background: color-mix(in srgb, var(--accent, var(--jd-amber)) 5%, var(--mud-palette-surface));
    box-shadow: 0 20px 46px rgba(0, 0, 0, 0.24);
    transform: translateY(-3px);
}

.jd-kpi-clickable:focus-visible {
    outline: 2px solid var(--accent-text, var(--jd-amber));
    outline-offset: 2px;
}

.jd-kpi-clickable:active { transform: translateY(0); }

.jd-kpi-head { display: flex; align-items: center; gap: 8px; }

.jd-kpi-icon { color: var(--accent-text, var(--mud-palette-text-secondary)); }

.jd-kpi-label {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--mud-palette-text-secondary);
}

.jd-kpi-value {
    font-size: 2.4rem;
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: var(--accent-text, var(--mud-palette-text-primary));
}

.jd-kpi-value-unit {
    font-size: 1rem;
    font-weight: 500;
    color: var(--mud-palette-text-secondary);
    margin-left: 0.3rem;
    letter-spacing: 0;
}

/* A zero reads as "all clear" — mute it so attention goes to the live numbers. */
.jd-kpi-value.is-zero { color: var(--mud-palette-text-secondary); opacity: 0.7; }

.jd-kpi-sub {
    font-size: 0.82rem;
    line-height: 1.4;
    color: var(--mud-palette-text-secondary);
    margin-top: auto;
}

/* four-up breakdown inside a KPI card (today / week / month / year) */
.jd-kpi-subgrid { display: flex; justify-content: space-between; gap: 8px; margin-top: auto; }
.jd-kpi-substat { text-align: center; }

.jd-kpi-subvalue {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}

.jd-kpi-sublabel {
    font-size: 0.66rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--mud-palette-text-secondary);
}

/* label / value rows inside a panel */
.jd-stat-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    padding: 7px 0;
    border-bottom: 1px solid color-mix(in srgb, var(--mud-palette-lines-default) 45%, transparent);
    font-size: 0.88rem;
}

.jd-stat-row:last-child { border-bottom: none; }
.jd-stat-row > span:first-child { color: var(--mud-palette-text-secondary); }

@media (prefers-reduced-motion: reduce) {
    .jd-kpi { animation: none; transition: none; }
    .jd-kpi-clickable:hover { transform: none; }
}

/* ── table inside a panel ────────────────────────────────────────────────────
   MudTable brings its own surface and dividers; strip them so it reads as part
   of the panel rather than a card inside a card. Put .jd-table on the wrapper
   (usually the .jd-panel-body) around the MudTable.

   Global, so it reaches MudBlazor's own markup without ::deep. MudTable's
   responsive mode (Breakpoint=…) hides the head and switches cells to flex with
   a ::before DataLabel — none of this fights that, it just restyles the head.
   ─────────────────────────────────────────────────────────────────────────── */

.jd-table .mud-table-root,
.jd-table .mud-table-container { background: transparent; }

.jd-table .mud-table-head .mud-table-cell {
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    border-bottom: 1px solid var(--jd-line);
}

.jd-table .mud-table-body .mud-table-cell {
    border-bottom: 1px solid color-mix(in srgb, var(--mud-palette-lines-default) 45%, transparent);
}

.jd-table .mud-table-body .mud-table-row:last-child .mud-table-cell { border-bottom: 0; }
.jd-table .mud-table-body .mud-table-row:hover { background: var(--jd-tint) !important; }

/* Striped tables: keep the banding subtle enough not to fight the panel tint. */
.jd-table .mud-table-body .mud-table-striped .mud-table-row:nth-of-type(odd),
.jd-table .mud-table-striped .mud-table-body .mud-table-row:nth-of-type(odd) {
    background: color-mix(in srgb, var(--mud-palette-text-primary) 2.5%, transparent);
}

/* ── misc text ───────────────────────────────────────────────────────────── */

/* Text for assistive tech only. For the half of a label that would be noise on screen but
   is the whole meaning to a screen reader — "(opens in a new tab)" after a link's name.
   Not `display:none`, which removes it from the a11y tree along with everything else. */
.jd-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.jd-foot {
    margin: 0.85rem 0 0;
    font-size: 0.75rem;
    line-height: 1.45;
    color: var(--mud-palette-text-secondary);
    opacity: 0.85;
}

/* ── loading skeleton ────────────────────────────────────────────────────── */

.jd-skeleton { display: flex; flex-direction: column; gap: 1.15rem; }

.jd-skeleton-block {
    border-radius: var(--jd-radius);
    border: 1px solid var(--jd-line);
    background: linear-gradient(100deg, var(--jd-tint) 30%, rgba(255, 179, 0, 0.07) 50%, var(--jd-tint) 70%);
    background-size: 220% 100%;
    animation: jd-shimmer 1.4s ease-in-out infinite;
}

@keyframes jd-shimmer {
    from { background-position: 140% 0; }
    to   { background-position: -40% 0; }
}

/* ── narrow screens ──────────────────────────────────────────────────────── */

@media (max-width: 560px) {
    .jd-panel { border-radius: 16px; }
    .jd-panel-body { padding: 1rem; }

    /* Both gaps, or the titles drift: a plain header spaces its own children while a
       collapsible one spaces them inside .jd-panel-toggle, so tightening only the
       first leaves button-headed panels 3px further right than the rest. */
    .jd-panel-head,
    .jd-panel-head .jd-panel-toggle { gap: 0.7rem; }
    .jd-panel-head { padding: 0.9rem 1rem; }

    /* A header carrying its own affix — a button, a count, a flight number — ran out of
       room and broke the TITLE instead ("Flight / status", "VA / 45"). Letting the line
       wrap drops the affixes to a second row and stops every item being squeezed, which
       is what forced the title to break mid-phrase.

       Deliberately NOT paired with nowrap/ellipsis on .jd-panel-title: titles elsewhere in
       the app run long ("Fleet, roster and where you operate"), and truncating those loses
       meaning. A genuinely long title should wrap over two lines; a short one no longer
       has to. */
    .jd-panel-head { flex-wrap: wrap; }

    .jd-badge, .jd-badge-sm { width: 36px; height: 36px; border-radius: 11px; }
}

/* ── motion ──────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
    .jd-rise,
    .jd-skeleton-block { animation: none; }

    .jd-chevron,
    .jd-ghost,
    .jd-tile,
    .jd-primary { transition: none !important; }

    .jd-ghost:hover,
    .jd-primary:hover,
    .jd-tile:active { transform: none !important; }
}

/* ── a frozen plan body, as a granted reader sees it ─────────────────────── */

/* The markdown of a published SmpVersion, rendered at /portal/smp/published. It lives HERE rather
   than in a scoped .razor.css because the content arrives as a MarkupString: scoped CSS works by
   stamping an attribute on elements the component renders, and nothing inside a MarkupString gets
   one — the headings and tables below are produced by Markdig, not by Razor.

   Read as a DOCUMENT, not as a dashboard. No cards, no chips, no accent colour: the operator's own
   words carry the page, and JollyDrivers styling on top of a statutory document would start to look
   like JollyDrivers endorsing it. */

.jd-smp-document { color: var(--mud-palette-text-primary); line-height: 1.65; }

.jd-smp-document > *:first-child { margin-top: 0; }
.jd-smp-document > *:last-child { margin-bottom: 0; }

.jd-smp-document h1,
.jd-smp-document h2,
.jd-smp-document h3,
.jd-smp-document h4 {
    margin: 1.6rem 0 0.6rem;
    line-height: 1.3;
    font-weight: 650;
}

.jd-smp-document h1 { font-size: 1.4rem; }
.jd-smp-document h2 { font-size: 1.2rem; }
.jd-smp-document h3 { font-size: 1.05rem; }
.jd-smp-document h4 { font-size: 0.98rem; }

.jd-smp-document p,
.jd-smp-document ul,
.jd-smp-document ol { margin: 0 0 0.9rem; }

.jd-smp-document li { margin-bottom: 0.3rem; }

.jd-smp-document blockquote {
    margin: 0 0 0.9rem;
    padding: 0.1rem 0 0.1rem 1rem;
    border-left: 3px solid var(--mud-palette-divider);
    color: var(--mud-palette-text-secondary);
}

/* Tables are the shape most of a safety management plan actually is — the hazard register, the
   version history, the administrative details. Scrolls inside itself: a wide register must not make
   the whole page scroll sideways on a phone, which is where a driver reads this. */
.jd-smp-document table {
    display: block;
    width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
    margin: 0 0 1rem;
    font-size: 0.92rem;
}

.jd-smp-document th,
.jd-smp-document td {
    border: 1px solid var(--mud-palette-divider);
    padding: 0.45rem 0.6rem;
    text-align: left;
    vertical-align: top;
}

.jd-smp-document th { font-weight: 650; background: var(--mud-palette-background-grey); }

.jd-smp-document code {
    font-size: 0.88em;
    padding: 0.1rem 0.3rem;
    border-radius: 4px;
    background: var(--mud-palette-background-grey);
}

.jd-smp-document hr {
    border: none;
    border-top: 1px solid var(--mud-palette-divider);
    margin: 1.4rem 0;
}

.jd-smp-document img { max-width: 100%; height: auto; }

/* ── driver earnings (docs/operator-collects-earnings-design.md S1) ───────────
   Built mobile-first and deliberately WITHOUT a table. A five-column table of
   money is the one layout that cannot be made to work on a 375px screen: it
   either scrolls sideways (so the total — the number the driver opened the app
   for — is off-screen) or it squeezes the route into three wrapped lines.

   One layout at every width instead of a table/card pair behind MudHidden.
   Two markup trees for one screen is two things to keep in sync, and the money
   one is the worst candidate for drift.
   ───────────────────────────────────────────────────────────────────────── */

.jd-earn-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 1rem;
    padding: 0.35rem 0.4rem 0.35rem 0.9rem;
    border: 1px solid var(--jd-line);
    border-radius: var(--jd-radius-sm);
    background: var(--jd-tint);
}

.jd-earn-week {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.25rem;
    flex: 1 1 auto;
    min-width: 0;
}

.jd-earn-week-label { text-align: center; min-width: 0; flex: 1 1 auto; }

.jd-earn-week-range {
    display: block;
    font-weight: 600;
    font-size: 0.95rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.jd-earn-week-sub {
    display: block;
    font-size: 0.78rem;
    color: var(--mud-palette-text-secondary);
}

/* 44px is the floor for a thumb. The nav arrows and the mask toggle are the only
   controls on this page, and all three are used one-handed on a phone. */
.jd-earn-tap { min-width: 44px; min-height: 44px; }

.jd-earn-hero {
    padding: 1.15rem 1.25rem 1.25rem;
    margin-bottom: 1rem;
    border: 1px solid var(--jd-line);
    border-radius: var(--jd-radius);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--jd-amber) 7%, var(--mud-palette-surface)),
        var(--mud-palette-surface));
}

.jd-earn-hero-label {
    margin: 0;
    font-size: 0.72rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

/* Fluid rather than a fixed size: this is the number the page exists to show, and
   it should be the biggest thing on a 320px screen and on a 27" monitor alike. */
.jd-earn-hero-value {
    margin: 0.2rem 0 0;
    font-size: clamp(2.1rem, 11vw, 3rem);
    font-weight: 700;
    line-height: 1.05;
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}

.jd-earn-hero-note {
    margin: 0.7rem 0 0;
    font-size: 0.86rem;
    line-height: 1.45;
    color: var(--mud-palette-text-secondary);
}

.jd-earn-op { margin-bottom: 1rem; }

.jd-earn-op-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0 0.15rem 0.55rem;
}

.jd-earn-op-name { margin: 0; font-size: 1.02rem; font-weight: 600; }
.jd-earn-op-count { font-size: 0.78rem; color: var(--mud-palette-text-secondary); }

.jd-earn-op-total {
    font-size: 1.15rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.jd-earn-jobs { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.6rem; }

/* The whole row is one grid: identity on the left, the earned figure on the
   right, and the breakdown spanning both underneath. At >=720px the breakdown
   moves inline, which is the only thing that changes between phone and desktop. */
/* Explicit areas rather than source order + `order`. The three pieces move between two
   arrangements and auto-placement got it wrong: the amount fell BELOW the breakdown
   instead of beside it. Named areas make each arrangement readable in one glance. */
.jd-earn-job {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
        "main      amount"
        "breakdown breakdown"
        "note      note";
    align-items: baseline;
    gap: 0.15rem 0.75rem;
    padding: 0.8rem 0.9rem;
    border: 1px solid var(--jd-line);
    border-radius: var(--jd-radius-sm);
    background: var(--mud-palette-surface);
}

.jd-earn-job-main      { grid-area: main; }
.jd-earn-job-amount    { grid-area: amount; }
.jd-earn-job-breakdown { grid-area: breakdown; }
.jd-earn-job-note      { grid-area: note; }

.jd-earn-job-main { min-width: 0; text-decoration: none; color: inherit; }
.jd-earn-job-main:hover .jd-earn-job-date { text-decoration: underline; }

.jd-earn-job-date { display: block; font-weight: 600; font-size: 0.92rem; }

.jd-earn-job-route {
    display: block;
    font-size: 0.82rem;
    color: var(--mud-palette-text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.jd-earn-job-amount {
    font-size: 1.05rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    text-align: right;
}

.jd-earn-job-breakdown {
    display: flex;
    flex-wrap: wrap;
    gap: 0.15rem 0.9rem;
    margin: 0.5rem 0 0;
    padding-top: 0.5rem;
    border-top: 1px dashed var(--jd-line);
    font-size: 0.79rem;
    color: var(--mud-palette-text-secondary);
}

.jd-earn-job-breakdown > div { display: flex; gap: 0.3rem; }
.jd-earn-job-breakdown dt { margin: 0; }
.jd-earn-job-breakdown dd { margin: 0; font-variant-numeric: tabular-nums; color: var(--mud-palette-text-primary); }

.jd-earn-job-note {
    margin: 0.45rem 0 0;
    font-size: 0.78rem;
    line-height: 1.4;
    color: var(--mud-palette-text-secondary);
}

.jd-earn-empty {
    padding: 2.2rem 1rem;
    text-align: center;
    border: 1px dashed var(--jd-line);
    border-radius: var(--jd-radius);
}

.jd-earn-foot {
    margin: 0.4rem 0.15rem 0;
    font-size: 0.76rem;
    color: var(--mud-palette-text-secondary);
}

/* Masked figures keep the row geometry so toggling privacy on does not reflow the
   page under the reader's thumb. */
.jd-earn-masked { letter-spacing: 0.06em; }

/* Wider than a phone, the breakdown tucks under the route and the earned figure sits
   centred against both — one row of identity, one of detail, one number beside them. */
@media (min-width: 720px) {
    .jd-earn-job {
        grid-template-areas:
            "main      amount"
            "breakdown amount"
            "note      note";
        align-items: center;
        gap: 0.15rem 1.4rem;
        padding: 0.9rem 1.1rem;
    }
    .jd-earn-job-breakdown {
        margin: 0.35rem 0 0;
        padding-top: 0;
        border-top: none;
    }
    .jd-earn-job-amount {
        align-self: center;
        min-width: 6.5rem;
        font-size: 1.15rem;
    }
}

@media (max-width: 380px) {
    .jd-earn-hero { padding: 1rem; }
    .jd-earn-job { padding: 0.7rem 0.75rem; }
    .jd-earn-job-breakdown { gap: 0.15rem 0.7rem; }
}

/* ── earned vs paid (design §4.2) ─────────────────────────────────────────────
   Two figures side by side and the difference between them, never summed into
   one headline. The hero keeps EARNED as the largest thing on the screen; these
   sit under it as a subordinate pair, because "what you earned" is the question
   the driver opened the app with and "what your operator has recorded paying"
   is the follow-up.
   ───────────────────────────────────────────────────────────────────────── */

.jd-earn-split {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.6rem;
    margin-top: 0.85rem;
    padding-top: 0.85rem;
    border-top: 1px solid var(--jd-line);
}

.jd-earn-split-item { min-width: 0; }

.jd-earn-split-label {
    display: block;
    font-size: 0.7rem;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

.jd-earn-split-value {
    display: block;
    font-size: 1.15rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Deliberately NOT coloured as a warning. An operator who has not yet recorded a
   payout has not done anything wrong, and an amber number on a driver's screen
   would say they had. */
.jd-earn-split-outstanding .jd-earn-split-value { color: var(--mud-palette-text-primary); }

.jd-earn-paid-chip {
    display: inline-block;
    margin-top: 0.3rem;
    padding: 0.08rem 0.42rem;
    border: 1px solid color-mix(in srgb, var(--jd-ok) 45%, transparent);
    border-radius: 999px;
    font-size: 0.7rem;
    color: var(--jd-ok);
    white-space: nowrap;
}

/* ── the operator's payout picker ─────────────────────────────────────────── */

.jd-payout-pick {
    grid-template-areas:
        "check main   amount"
        "check note   note";
    grid-template-columns: auto minmax(0, 1fr) auto;
    cursor: default;
}

.jd-payout-check { grid-area: check; align-self: center; margin: 0; }

.jd-payout-picked {
    border-color: color-mix(in srgb, var(--jd-amber) 45%, var(--jd-line));
    background: color-mix(in srgb, var(--jd-amber) 5%, var(--mud-palette-surface));
}

/* Withdrawn rows stay on the list rather than disappearing — a payment recorded and then withdrawn
   is itself something an operator may need to explain. Struck rather than hidden. */
.jd-payout-voided { opacity: 0.55; }
.jd-payout-voided .jd-earn-job-amount { text-decoration: line-through; }

/* ── driving hours (docs/fatigue-reporting-dashboard-design.md §4.1) ──────────
   Same bones as the earnings page: a stepper toolbar, panels, a footnote. The
   one new piece is the tap strip under the chart — MudChart's bars select the
   SERIES on click, not the column, so the day/week buttons ARE the chart's tap
   targets, at the 44px floor. The cap line is drawn into the chart's own SVG
   through CustomGraphics; its colour is the warning tone so it reads as a
   reference line and not as data. */
.jd-hours-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 1rem;
    padding: 0.35rem 0.4rem 0.35rem 0.4rem;
    border: 1px solid var(--jd-line);
    border-radius: var(--jd-radius-sm);
    background: var(--jd-tint);
}

.jd-hours-range {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.25rem;
    flex: 1 1 auto;
    min-width: 0;
}

.jd-hours-range-label { text-align: center; min-width: 0; flex: 1 1 auto; }

.jd-hours-range-text {
    display: block;
    font-weight: 600;
    font-size: 0.95rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.jd-hours-range-sub {
    display: block;
    font-size: 0.78rem;
    color: var(--mud-palette-text-secondary);
}

.jd-hours-tap { min-width: 44px; min-height: 44px; }

.jd-hours-chart { width: 100%; overflow: hidden; }

.jd-hours-strip {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 4px;
    margin-top: 0.5rem;
}

.jd-hours-strip-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 44px;
    padding: 4px 2px;
    border: 1px solid transparent;
    border-radius: var(--jd-radius-sm);
    background: transparent;
    color: var(--mud-palette-text-secondary);
    font: inherit;
    cursor: pointer;
}

.jd-hours-strip-btn.is-selected {
    border-color: var(--jd-line);
    background: var(--jd-tint);
    color: var(--mud-palette-text-primary);
}

.jd-hours-strip-btn:focus-visible { outline: 2px solid var(--jd-amber); outline-offset: 1px; }

.jd-hours-strip-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em; }
.jd-hours-strip-value { font-size: 0.9rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.jd-hours-strip-recorded { font-size: 0.68rem; color: var(--mud-palette-text-secondary); font-variant-numeric: tabular-nums; }

.jd-hours-caption {
    margin: 0.6rem 0 0;
    font-size: 0.9rem;
    color: var(--mud-palette-text-secondary);
}

.jd-hours-empty {
    padding: 2.2rem 1rem;
    text-align: center;
    border: 1px dashed var(--jd-line);
    border-radius: var(--jd-radius);
}

.jd-hours-blocks { list-style: none; margin: 0; padding: 0; }

.jd-hours-block {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 0.1rem 0.75rem;
    padding: 0.6rem 0;
    border-top: 1px solid var(--jd-line);
    font-size: 0.9rem;
}

.jd-hours-block:first-child { border-top: 0; padding-top: 0; }

.jd-hours-block-when { grid-column: 1; }
.jd-hours-block-hours { grid-column: 2; grid-row: 1 / span 2; align-self: center; font-weight: 700; font-variant-numeric: tabular-nums; }
.jd-hours-block-trips { grid-column: 1; font-size: 0.78rem; color: var(--mud-palette-text-secondary); }

.jd-hours-foot {
    margin: 0.4rem 0.15rem 0;
    font-size: 0.76rem;
    color: var(--mud-palette-text-secondary);
}

/* Fleet driving hours: one tappable row per driver, the whole row the target. */
.jd-hours-drivers { list-style: none; margin: 0; padding: 0; }

.jd-hours-driver {
    display: grid;
    grid-template-columns: 1fr auto auto;
    grid-template-areas: "name hours chev" "sub hours chev";
    gap: 0.1rem 0.75rem;
    align-items: center;
    min-height: 56px;
    padding: 0.6rem 0;
    border-top: 1px solid var(--jd-line);
    color: inherit;
    text-decoration: none;
}

.jd-hours-drivers li:first-child .jd-hours-driver { border-top: 0; padding-top: 0; }
.jd-hours-driver:focus-visible { outline: 2px solid var(--jd-amber); outline-offset: 2px; border-radius: var(--jd-radius-sm); }

.jd-hours-driver-name { grid-area: name; font-weight: 600; }
.jd-hours-driver-hours { grid-area: hours; font-weight: 700; font-variant-numeric: tabular-nums; }
.jd-hours-driver-sub { grid-area: sub; font-size: 0.78rem; color: var(--mud-palette-text-secondary); }
.jd-hours-driver-chevron { grid-area: chev; color: var(--mud-palette-text-secondary); }

.jd-hours-note { color: var(--mud-palette-text-primary); }

/* Pattern grid (S3, redrawn in HTML). Fill strength is --w, set per cell from hours over the
   busiest cell; an empty cell keeps only a faint outline. Every cell is a 44px-tall target and
   says its hours; the last column and row are totals. */
.jd-hours-cells {
    display: grid;
    grid-template-columns: auto repeat(6, minmax(0, 1fr)) auto;
    gap: 4px;
    align-items: stretch;
    width: 100%;
    font-variant-numeric: tabular-nums;
}

.jd-hours-cells-corner { }

.jd-hours-cells-col, .jd-hours-cells-row {
    display: flex;
    align-items: center;
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

.jd-hours-cells-col { justify-content: center; padding-bottom: 2px; white-space: nowrap; }
.jd-hours-cells-row { justify-content: flex-end; padding-right: 0.35rem; }

.jd-hours-cell {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    border-radius: 6px;
    border: 1px solid color-mix(in srgb, var(--jd-amber) 22%, transparent);
    background: color-mix(in srgb, var(--jd-amber) calc(var(--w, 0) * 100%), transparent);
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}

.jd-hours-cell.is-empty { border-color: var(--jd-line); }

/* Text stays light on the faint and middling cells and flips to dark only on the strongest
   (≥ 70% of the busiest), which are near-solid amber. A continuous blend went muddy in between. */
.jd-hours-cell.is-strong span { color: #1a1200; }

.jd-hours-cell.is-peak { outline: 2px solid var(--mud-palette-text-primary); outline-offset: -2px; }

.jd-hours-cells-total { border-color: transparent; background: var(--jd-tint); font-weight: 700; }
.jd-hours-cells-row.jd-hours-cells-total, .jd-hours-cells-col.jd-hours-cells-total { text-transform: none; letter-spacing: 0; }
.jd-hours-cells-grand { background: color-mix(in srgb, var(--jd-tint) 60%, var(--jd-amber) 12%); }

@media (max-width: 420px) {
    .jd-hours-cells { gap: 3px; }
    .jd-hours-cell { font-size: 0.72rem; min-height: 40px; }
    .jd-hours-cells-col, .jd-hours-cells-row { font-size: 0.62rem; letter-spacing: 0.02em; }
}
