/*
 * ePMR component layer.
 *
 * Logical properties only (constitution I2) — one stylesheet serves LTR and RTL.
 * Every colour is a token; no literal hex values below this line.
 */

/* =============================================================================
   StatusPill — colour NEVER travels alone (NFR-U4)
   Every status carries colour + icon + label. A colour-blind nurse under ward
   lighting must never depend on hue.
   ============================================================================= */

.status-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding-block: 0.25rem;
    padding-inline: 0.625rem;
    border-radius: var(--radius-pill);
    font-size: var(--font-size-label);
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
    border: var(--border-width) solid currentColor;
}

.status-pill__icon {
    flex: 0 0 auto;
    font-style: normal;
    /* The icon is decorative only when the label is present, which it always is. */
}

.status-pill--success { color: var(--status-success); }
.status-pill--info    { color: var(--status-info); }
.status-pill--warning { color: var(--status-warning); }
.status-pill--danger  { color: var(--status-danger); }
.status-pill--neutral { color: var(--status-neutral); }

/* =============================================================================
   JourneyBadge — status is POSITION on the medication loop (PRD §13.0.7)
   ============================================================================= */

.journey-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: var(--font-size-label);
    font-weight: 600;
}

.journey-badge__dot {
    inline-size: 0.625rem;
    block-size: 0.625rem;
    border-radius: var(--radius-pill);
    flex: 0 0 auto;
}

.journey-badge--ordered      { color: var(--journey-ordered); }
.journey-badge--prepared     { color: var(--journey-prepared); }
.journey-badge--in-transit   { color: var(--journey-in-transit); }
.journey-badge--at-ward      { color: var(--journey-at-ward); }
.journey-badge--administered { color: var(--journey-administered); }

.journey-badge__dot { background-color: currentColor; }

/* A card's leading edge carries its journey colour, so status is read peripherally. */
.journey-edge {
    border-inline-start: 4px solid transparent;
    padding-inline-start: calc(var(--card-padding) - 4px);
}

.journey-edge--ordered      { border-inline-start-color: var(--journey-ordered); }
.journey-edge--prepared     { border-inline-start-color: var(--journey-prepared); }
.journey-edge--in-transit   { border-inline-start-color: var(--journey-in-transit); }
.journey-edge--at-ward      { border-inline-start-color: var(--journey-at-ward); }
.journey-edge--administered { border-inline-start-color: var(--journey-administered); }

/* =============================================================================
   SafetySpine — ambient safety state on the inline-start edge (PRD §13.0.12)
   Peripheral vision, not foreground. A nurse never reads it; they notice when
   it changes. RTL-safe by construction: it uses the logical inline-start edge.
   ============================================================================= */

.safety-spine {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    inline-size: 4px;
    z-index: 1030;
    transition: background-color var(--duration-base) var(--ease);
}

.safety-spine--normal      { background-color: var(--journey-in-transit); }
.safety-spine--provisional { background-color: var(--status-warning); animation: spine-pulse 2s ease-in-out infinite; }
.safety-spine--blocked     { background-color: var(--status-danger); }

@keyframes spine-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
    .safety-spine--provisional { animation: none; opacity: 1; }
}

/* =============================================================================
   VerificationGate — the screen IS the answer (PRD §13.0.10)
   The moment the whole product exists for. Full screen, never a toast.
   ============================================================================= */

.gate {
    position: fixed;
    inset: 0;
    z-index: 1080;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--screen-padding);
    text-align: center;
}

/* The block fill uses danger-EMPHASIS, not danger: the plain danger token is tuned to read as
   TEXT on a surface, and in dark theme it fails AA behind white text (3.30:1). */
.gate--pass  { background-color: var(--bg); color: var(--text); }
.gate--block { background-color: var(--status-danger-emphasis); color: var(--on-emphasis); }

.gate__symbol {
    font-size: 4rem;
    line-height: 1;
    margin-block-end: 1rem;
}

.gate__headline {
    font-family: var(--font-display);
    font-size: 2rem;
    font-weight: 700;
    margin-block-end: 0.75rem;
}

/* The block screen states ONE failure, readable at a metre. */
.gate--block .gate__headline {
    font-size: 2.5rem;
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.gate__detail {
    font-size: var(--font-size-body);
    max-inline-size: 32rem;
    margin-block-end: 1.5rem;
}

/* The seven-check cascade. Showing WHAT was verified builds trust and teaches the
   rights by repetition — a green tick alone proves nothing. */
.gate__checks {
    list-style: none;
    padding: 0;
    margin: 0 0 2rem;
    text-align: start;
    max-inline-size: 26rem;
    inline-size: 100%;
}

.gate__check {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    padding-block: 0.375rem;
    opacity: 0;
    animation: gate-check-in var(--duration-base) var(--ease-out) forwards;
}

.gate__check-mark  { color: var(--status-success); font-weight: 700; }
.gate__check-label { flex: 0 0 9rem; color: var(--text-2); }
.gate__check-value { font-weight: 600; }

@keyframes gate-check-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: none; }
}

/* Stagger, ~50ms apart, total ~400ms. */
.gate__check:nth-child(1) { animation-delay: 0ms; }
.gate__check:nth-child(2) { animation-delay: 50ms; }
.gate__check:nth-child(3) { animation-delay: 100ms; }
.gate__check:nth-child(4) { animation-delay: 150ms; }
.gate__check:nth-child(5) { animation-delay: 200ms; }
.gate__check:nth-child(6) { animation-delay: 250ms; }
.gate__check:nth-child(7) { animation-delay: 300ms; }
.gate__check:nth-child(8) { animation-delay: 350ms; }

/* The information must NEVER depend on the animation. */
@media (prefers-reduced-motion: reduce) {
    .gate__check {
        opacity: 1;
        animation: none;
    }
}

/* =============================================================================
   HoldToConfirm — the final commit is deliberate (PRD §13.0.10)
   Cannot fire from a brushed screen or a glove catching the display.
   ============================================================================= */

.hold-confirm {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-block-size: var(--touch-target);
    min-inline-size: 16rem;
    padding-inline: 1.5rem;
    border: 2px solid var(--primary);
    border-radius: var(--radius-pill);
    background-color: transparent;
    color: var(--primary);
    font-size: var(--font-size-body);
    font-weight: 600;
    cursor: pointer;
    overflow: hidden;
    user-select: none;
    -webkit-user-select: none;
    touch-action: manipulation;
}

.hold-confirm__fill {
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    inline-size: 0;
    background-color: var(--primary-soft);
    transition: inline-size linear;
    pointer-events: none;
}

.hold-confirm__label {
    position: relative;
    z-index: 1;
}

.hold-confirm--holding .hold-confirm__fill {
    inline-size: 100%;
    transition-duration: var(--hold-to-confirm);
}

.hold-confirm--complete {
    background-color: var(--status-success);
    border-color: var(--status-success);
    color: var(--on-primary);
}

/* =============================================================================
   WristbandCard — identity confirmation is picture-to-picture (PRD §13.0.11)
   Echoes the physical band so the nurse compares a picture to a picture, not
   text to text. Skim-reading a field list is exactly how misidentification happens.
   ============================================================================= */

.wristband-card {
    border: var(--border-width) solid var(--border-2);
    border-radius: var(--radius-md);
    padding: var(--card-padding);
    background-color: var(--surface);
    max-inline-size: 26rem;
}

.wristband-card__name-ar {
    font-family: var(--font-display-arabic);
    font-size: 1.5rem;
    font-weight: 700;
    line-height: var(--line-height-arabic);
}

.wristband-card__name-latin {
    font-family: var(--font-display);
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--text-2);
}

.wristband-card__details {
    margin-block-start: 0.5rem;
    color: var(--text-2);
    font-size: var(--font-size-label);
}

.wristband-card__location {
    margin-block-start: 0.25rem;
    font-weight: 600;
}

/* =============================================================================
   SkeletonLoader — loading is a skeleton, never a spinner (NFR-P10)
   Matches the final layout so nothing reflows when data arrives (NFR-P15).
   ============================================================================= */

.skeleton {
    background-color: var(--surface-2);
    border-radius: var(--radius-sm);
    position: relative;
    overflow: hidden;
}

.skeleton::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, var(--bg-tint), transparent);
    animation: skeleton-sweep 1.4s ease-in-out infinite;
}

.skeleton--text  { block-size: 1em; margin-block: 0.25em; }
.skeleton--title { block-size: 1.5em; inline-size: 40%; }
.skeleton--line  { block-size: 1em; inline-size: 100%; }
.skeleton--short { block-size: 1em; inline-size: 60%; }
.skeleton--card  { block-size: 6rem; }

@keyframes skeleton-sweep {
    from { transform: translateX(-100%); }
    to   { transform: translateX(100%); }
}

[dir="rtl"] .skeleton::after {
    animation-direction: reverse;
}

@media (prefers-reduced-motion: reduce) {
    .skeleton::after { animation: none; }
}

/* =============================================================================
   EmptyState — an empty list teaches (P3)
   Says what belongs here and how to put it there.
   ============================================================================= */

.empty-state {
    text-align: center;
    padding-block: 3rem;
    padding-inline: var(--screen-padding);
    color: var(--text-2);
}

.empty-state__icon    { font-size: 2.5rem; opacity: 0.5; margin-block-end: 0.75rem; }
.empty-state__title   { font-family: var(--font-display); font-size: var(--font-size-heading); color: var(--text); }
.empty-state__message { margin-block-start: 0.5rem; max-inline-size: 26rem; margin-inline: auto; }
.empty-state__action  { margin-block-start: 1.25rem; }

/* An "all clear" empty state must look intentional, not like a failed load. */
.empty-state--all-clear .empty-state__icon { color: var(--status-success); opacity: 1; }

/* =============================================================================
   Cards and layout rhythm — geometry from PRD §13.0.3
   ============================================================================= */

.epmr-card {
    background-color: var(--surface);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-lg);
    padding: var(--card-padding);
    box-shadow: var(--shadow-sm);
}

/* Elevation is reserved for the hero and floating bars (PRD §13.0.3) — flat cards elsewhere. */
.epmr-card--elevated { box-shadow: var(--shadow-md); }
.epmr-hero          { box-shadow: var(--shadow-lg); }

.epmr-card + .epmr-card { margin-block-start: var(--card-gap); }

.epmr-section + .epmr-section { margin-block-start: var(--section-gap); }

/* High-alert products: border and hatch, never colour alone. */
.high-alert {
    border: 2px solid var(--status-warning);
    background-image: repeating-linear-gradient(
        45deg,
        transparent,
        transparent 6px,
        var(--amber-soft) 6px,
        var(--amber-soft) 12px
    );
}

/* =============================================================================
   App shell — the web mirror of the mobile bottom nav (PRD §13.0.6)
   Same vocabulary, laid out wider: bottom nav becomes a fixed sidebar, the hero
   card becomes a page header band. A pharmacist moving between tablet and
   desktop should feel one product, not two.
   ============================================================================= */

.app-shell {
    display: grid;
    grid-template-columns: auto 1fr;
    min-block-size: 100vh;
}

.app-sidebar {
    inline-size: 15rem;

    /* Blue, and raised. See --sidebar-bg: a white menu beside white cards on a near-white page made
       the whole shell read as one sheet, which is most of what "it all feels white" meant. */
    background-color: var(--sidebar-bg);
    border-inline-end: var(--border-width) solid var(--sidebar-border);
    box-shadow: var(--sidebar-shadow);
    z-index: 2;
    padding-block: var(--card-padding);
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.app-sidebar__brand {
    font-family: var(--font-display);
    font-size: var(--font-size-heading);
    font-weight: 700;
    padding-inline: var(--card-padding);
    padding-block-end: var(--card-padding);
}

/* Mirrors the mobile nav item: icon, label, filled pill when active. */
.app-nav-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-block-size: var(--touch-target);
    padding-inline: var(--card-padding);
    color: var(--text-2);
    text-decoration: none;
    border-radius: var(--radius-md);
    margin-inline: 0.5rem;
}

.app-nav-item:hover { background-color: var(--sidebar-hover); color: var(--text); }

.app-nav-item--active {
    /* A solid surface, not a teal tint: `--primary-soft` is itself a pale blue-green and vanished
       against the blue menu. White in light, deep navy in dark — the active item now reads as a
       raised chip on the menu rather than a slightly different blue. */
    background-color: var(--surface);
    color: var(--primary);
    font-weight: 600;
    box-shadow: 0 1px 3px rgba(6, 21, 43, .12);
}

.app-nav-item__icon { flex: 0 0 1.5rem; text-align: center; font-style: normal; }

/* Page header band — the desktop equivalent of the mobile hero card. */
.app-header-band {
    padding-block: var(--card-padding);
    padding-inline: var(--screen-padding);
    border-block-end: var(--border-width) solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.app-header-band__title {
    font-family: var(--font-display);
    font-size: var(--font-size-display);
    font-weight: 700;
    margin: 0;
}

.app-content { padding: var(--screen-padding); }

/* Dashboard grid — the desktop equivalent of the mobile feature tiles. */
.app-tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
    gap: var(--card-gap);
}

/* Superseded by the fuller breakpoint block at the end of this file, which also handles the group
   labels and the brand. Kept narrow here so the two cannot disagree about the shell itself. */
@media (max-inline-size: 48rem) {
}

/* ---- Internal-identifier badge -------------------------------------------------------------
   An internal key looks exactly like a GS1-registered one, so the badge is the ONLY signal that
   a number is not valid outside this hospital. Neutral rather than a warning colour: internal
   mode is a supported way to run, not a fault. Direction-agnostic spacing (constitution I2). */
.identifier-badge {
    display: inline-block;
    padding: 0.05rem 0.4rem;
    margin-inline-start: 0.4rem;
    border: 1px solid var(--status-neutral);
    border-radius: 0.25rem;
    color: var(--status-neutral);
    font-size: var(--font-size-label);
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    vertical-align: middle;
    white-space: nowrap;
}

/* ---- Priority card (reprint worklist) ------------------------------------------------------
   The four progress cards double as the priority filter, so they need to read as pressable and
   to show which one is active. The selected state uses a border rather than a fill: the numbers
   inside must stay legible, and a filled card in dark mode loses its contrast. */
.priority-card {
    border: 1px solid var(--border);
    transition: border-color 120ms ease, transform 120ms ease;
}

.priority-card:hover,
.priority-card:focus-visible {
    border-color: var(--journey-in-transit);
}

.priority-card--selected {
    border-color: var(--journey-in-transit);
    border-width: 2px;
    /* Compensate for the extra border so the row does not shift when a card is selected. */
    padding: calc(1rem - 1px) !important;
}

@media (prefers-reduced-motion: reduce) {
    .priority-card { transition: none; }
}

/* ---- Location tree --------------------------------------------------------------------------
   Indentation is nesting plus logical padding, never a physical margin, so the tree mirrors
   correctly in Arabic without a second stylesheet (constitution I2). */
.location-tree { max-height: 70vh; overflow-y: auto; }

.location-tree__list {
    list-style: none;
    padding-inline-start: 1.1rem;
    margin: 0;
}

.location-tree__row {
    display: flex;
    align-items: center;
    gap: .25rem;
    border-radius: .25rem;
    padding: .15rem .25rem;
}

.location-tree__row:hover { background-color: var(--surface-2); }

.location-tree__row--selected {
    background-color: var(--surface-2);
    box-shadow: inset 3px 0 0 0 var(--journey-in-transit);
}

.location-tree__toggle {
    flex: 0 0 1.25rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 0;
    padding: 0;
    color: var(--text-2);
    cursor: pointer;
}

.location-tree__toggle--leaf { cursor: default; }

/* Rotates toward the reading direction, so it points "into" the branch in both scripts. */
.location-tree__chevron {
    display: inline-block;
    transition: transform 120ms ease;
}

[dir="rtl"] .location-tree__chevron { transform: rotate(180deg); }

.location-tree__toggle--open .location-tree__chevron { transform: rotate(90deg); }

[dir="rtl"] .location-tree__toggle--open .location-tree__chevron { transform: rotate(90deg); }

.location-tree__link {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    text-decoration: none;
    color: inherit;
    min-width: 0;
}

.location-tree__code {
    font-family: var(--font-mono, monospace);
    font-size: var(--font-size-label);
    color: var(--text-2);
    flex: 0 0 auto;
}

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

.location-tree__retired {
    font-size: var(--font-size-label);
    color: var(--status-neutral);
    border: 1px solid var(--status-neutral);
    border-radius: .2rem;
    padding: 0 .3rem;
}

.location-tree__error {
    font-size: var(--font-size-label);
    color: var(--status-danger-emphasis);
    padding-inline-start: 1.1rem;
}

/* Holds the space the rows will occupy, so the tree does not jump when they arrive. */
.location-tree__skeleton {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding-inline-start: 1.35rem;
    padding-block: .25rem;
}

.location-tree__skeleton span {
    height: .85rem;
    border-radius: .2rem;
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--border) 50%, var(--surface-2) 75%);
    background-size: 200% 100%;
    animation: location-skeleton 1.2s ease-in-out infinite;
}

.location-tree__skeleton span:nth-child(1) { width: 70%; }
.location-tree__skeleton span:nth-child(2) { width: 55%; }
.location-tree__skeleton span:nth-child(3) { width: 62%; }

@keyframes location-skeleton {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .location-tree__skeleton span { animation: none; }
    .location-tree__chevron { transition: none; }
}

/* ---- Safety-relevant setting marker ---------------------------------------------------------
   Warning colour, not danger: these settings are not wrong, they are consequential. An
   administrator should look twice, not be frightened off. */
.config-safety {
    display: inline-block;
    padding: 0.05rem 0.4rem;
    margin-inline-start: 0.4rem;
    border: 1px solid var(--status-warning);
    border-radius: 0.25rem;
    color: var(--status-warning);
    font-size: var(--font-size-label);
    font-weight: 600;
    text-transform: uppercase;
    vertical-align: middle;
    white-space: nowrap;
}

/* ---- Deployment preset badge in the header -------------------------------------------------
   Which preset a site runs is the first question in any support conversation, so it is visible
   on every screen rather than buried in a settings page. */
.preset-badge {
    font-size: var(--font-size-label);
    color: var(--text-2);
    white-space: nowrap;
}

.preset-badge__customised {
    display: inline-block;
    margin-inline-start: 0.3rem;
    padding: 0 0.3rem;
    border: 1px solid var(--status-warning);
    border-radius: 0.2rem;
    color: var(--status-warning);
}

/* ---- Order type and status tokens (slice 029) ------------------------------------------------
   Colour AND icon AND label, on every one (constitution Q4).

   Colour alone is not differentiation: roughly one man in twelve cannot separate the refill green
   from the new-medication blue, and a pharmacy workstation in afternoon daylight washes out the
   difference for everybody else. So each type carries a glyph as well, and the label is always
   rendered beside it — a pharmacist scanning two hundred cards for today's refills is reading
   shape and text, not hue.

   The glyphs come from the accent map in PRD 13.0.4 and are set in CSS rather than in the view so
   the markup stays a token name and the design system stays the one place that decides how a type
   looks. */
.order-type {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    border: 1px solid currentcolor;
    background: transparent;
    font-weight: 600;
}

.order-type-icon::before {
    font-weight: 700;

    /* Overridden per type below. A token with no glyph would silently degrade to colour only,
       which is the failure this whole block exists to prevent. */
    content: "\2022";
}

.order-type-newmedication {
    color: var(--brand-blue-700);
}

.order-type-newmedication .order-type-icon::before {
    content: "\002B"; /* plus — something new */
}

.order-type-refill {
    color: var(--brand-green-500);
}

.order-type-refill .order-type-icon::before {
    content: "\21BB"; /* clockwise arrow — the daily round */
}

.order-type-takehome {
    color: var(--brand-teal-500);
}

.order-type-takehome .order-type-icon::before {
    content: "\2302"; /* house — leaving with it */
}

.order-type-floorstock {
    color: var(--brand-amber-600);
}

.order-type-floorstock .order-type-icon::before {
    content: "\25A6"; /* filled box — ward stock */
}

/* Status uses the journey gradient, so the five stages stay visually sequential rather than
   arbitrary (slice 004, D-35g). Position on the gradient is the information. */
.order-status {
    border: 1px solid currentcolor;
    background: transparent;
    font-weight: 600;
}

.order-status-pending,
.order-status-pendingapproval {
    color: var(--journey-ordered);
}

.order-status-preparing {
    color: var(--journey-prepared);
}

.order-status-reviewed {
    color: var(--journey-at-ward);
}

.order-status-completed {
    color: var(--journey-administered);
}

.order-status-cancelled {
    color: var(--status-neutral);
    text-decoration: line-through;
}

/* ---- The pharmacy bench (slice 030, PRD 13.0.15) ----------------------------------------------
   One job: remove ambiguity about which line was just confirmed.

   The scanned line rises to full contrast with its detail open; every other line recedes to 55%
   opacity with its detail collapsed. This is not decoration — with eight medications in front of
   somebody, confusion about where they are is the single most common source of preparation error,
   and the requirement documents call it out by name.

   Opacity AND elevation AND detail, three signals rather than one, for the same reason the order
   type badges carry colour and glyph and label: a washed-out screen in daylight defeats any one of
   them on its own. */
.bench-line {
    opacity: 0.55;
    transition: opacity 120ms ease-in-out;
}

.bench-line-active {
    opacity: 1;
    border-inline-start: 4px solid var(--brand-teal-500);
    box-shadow: 0 2px 8px rgb(0 0 0 / 18%);
}

.bench-line-complete {
    /* Done is not the same as inactive. A finished line stays readable so a pharmacist can see how
       much of the order is behind them without scanning it again. */
    opacity: 0.8;
}

.bench-line-detail {
    /* Batches and expiries, shown only on the active line. */
    color: var(--text-2);
}

/* A GS1 value is never reversed by an Arabic layout (constitution I5). A batch rendered
   right-to-left is a batch that will not match the box in somebody's hand. */
.gs1-value {
    direction: ltr;
    unicode-bidi: isolate;
}

/* ---- Sidebar grouping, and the shell's main column -----------------------------------------
   Added when the layout finally adopted the shell that slice 004 drew. The shell CSS above was
   written for a seven-item mock; a real deployment has twenty-four entries, which is what makes
   the grouping structural rather than decorative. */

.app-main {
    display: flex;
    flex-direction: column;
    min-inline-size: 0; /* lets the content column shrink instead of forcing the page to scroll */
}

/*
    On a page with no sidebar — sign-in, and anything else reached before authentication — the
    shell is one column, not two.

    Without this the grid's `auto 1fr` puts the only child in the `auto` track, which sizes to its
    content and squeezed the whole sign-in page into a narrow strip against the leading edge.
*/
.app-shell:not(:has(.app-sidebar)) {
    grid-template-columns: 1fr;
}

.app-sidebar {
    overflow-y: auto;
    max-block-size: 100vh;
    position: sticky;
    inset-block-start: 0;
}

.app-nav-group + .app-nav-group {
    margin-block-start: 1.25rem;
}

/* A quiet label. It is a signpost, not content — if it competes with the items beneath it, the
   grouping has made the menu busier rather than calmer. */
.app-nav-group__label {
    padding-inline: var(--card-padding);
    padding-block-end: 0.35rem;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-3);
}

/*
    The collapsed rail.

    Everything that makes the menu wide is hidden — the wordmark, the item labels, the group
    headings, the preset footer — and what identifies a destination at a glance, the icon, stays. The
    width is a touch target plus its padding, not an arbitrary number, so a gloved tap still lands
    (NFR-U6).

    Written as `.app-sidebar--rail X` rather than by re-declaring the components: the rail is a state
    of the menu, not a different menu, and a second set of nav-item rules would be a second thing to
    keep in step with the first.

    The chevron is flipped with `scale`, not swapped for a different icon class, so it mirrors
    correctly in Arabic along with everything else (I2).
*/
.app-sidebar--rail {
    inline-size: calc(var(--touch-target) + var(--card-padding));
}

.app-sidebar--rail .app-sidebar__wordmark,
.app-sidebar--rail .app-nav-group__text,
.app-sidebar--rail .app-nav-group__chevron,
.app-sidebar--rail .app-sidebar__preset {
    display: none;
}

/*
    A rail link is still a link with a name.

    `display: none` on the label took it out of the accessibility tree as well as off the screen, so
    a screen reader was left with "link, /Orders" and an icon — which is exactly the icon-alone case
    Q4 forbids, arrived at by hiding text rather than by never writing it. Clipped instead: the label
    is unreadable to the eye and intact to everything else. Sighted users get the same words from the
    `title` the markup carries.
*/
.app-sidebar--rail .app-nav-item span {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.app-sidebar--rail .app-sidebar__brand {
    justify-content: center;
    padding-inline: 0;
}

/* A group heading in the rail would be a coloured bar with nothing beside it. The items stay; only
   the heading goes, and the accent rule is what keeps the groups visually separated. */
.app-sidebar--rail .app-nav-group__toggle {
    display: none;
}

.app-sidebar--rail .app-nav-group + .app-nav-group {
    margin-block-start: 0.5rem;
    border-block-start: var(--border-width) solid var(--sidebar-border);
    padding-block-start: 0.5rem;
}

/* Hidden groups must not stay hidden in the rail: with no heading to click there would be no way to
   get them back, and the items would simply be gone. */
.app-sidebar--rail .app-nav-group__items {
    display: block !important;
}

.app-sidebar--rail .app-nav-item {
    justify-content: center;
    padding-inline: 0;
}

/*
    The chevron points the way the menu will move.

    `scaleX(-1)` keyed on the rail state alone was wrong in Arabic, where the sidebar sits on the
    right: expanded it pointed away from the edge it collapses toward, and collapsed it pointed away
    from the content. Direction is a second axis, so it needs a second rule — the two flips cancel,
    which is why the RTL rail resets to none.
*/
.app-sidebar--rail .app-sidebar__rail-icon {
    transform: scaleX(-1);
}

[dir="rtl"] .app-sidebar__rail-icon {
    transform: scaleX(-1);
}

[dir="rtl"] .app-sidebar--rail .app-sidebar__rail-icon {
    transform: none;
}

.app-sidebar__brand {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.app-sidebar__wordmark { flex: 1; min-inline-size: 0; }

.app-sidebar__rail-toggle {
    flex: 0 0 auto;
    inline-size: 2rem;
    block-size: 2rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--text-2);
    cursor: pointer;
}

.app-sidebar__rail-toggle:hover { background-color: var(--sidebar-hover); color: var(--text); }

.app-sidebar__rail-toggle:focus-visible {
    outline: 3px solid var(--primary-soft);
    outline-offset: 2px;
}

.app-sidebar__preset {
    margin-block-start: auto;
    padding-inline: var(--card-padding);
    padding-block-start: 1rem;
    font-size: 0.75rem;
    color: var(--text-3);
}

.app-header-band__actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* The EN/ع toggle from the reference language: one control with two states, not two buttons. */
.segmented {
    display: inline-flex;
    margin: 0;
    border: var(--border-width) solid var(--border-2);
    border-radius: var(--radius-pill);
    overflow: hidden;
}

.segmented__option {
    border: 0;
    background: transparent;
    color: var(--text-2);
    padding-block: 0.25rem;
    padding-inline: 0.9rem;
    font-size: var(--font-size-label);
}

.segmented__option--on {
    background: var(--primary);
    color: var(--on-primary);
    font-weight: 600;
}

/*
    Below the tablet breakpoint the sidebar becomes a horizontal strip along the top.

    Not a hamburger: a nurse holding a handheld in one hand should not have to open a menu to move
    between the four screens she uses. The group labels disappear here — at this width the strip is
    short enough not to need them, and they would cost a line of vertical space that the round
    itself needs.
*/
@media (max-width: 48rem) {
    .app-shell {
        grid-template-columns: 1fr;
    }

    .app-sidebar {
        inline-size: 100%;
        max-block-size: none;
        position: static;
        flex-direction: row;
        overflow-x: auto;
        gap: 0.25rem;
        padding-block: 0.5rem;
        border-inline-end: 0;
        border-block-end: var(--border-width) solid var(--border);
    }

    .app-sidebar__brand,
    .app-nav-group__label,
    .app-sidebar__preset {
        display: none;
    }

    .app-nav-group {
        display: flex;
        gap: 0.25rem;
    }

    .app-nav-group + .app-nav-group {
        margin-block-start: 0;
    }

    .app-nav-item {
        white-space: nowrap;
    }
}

/* ---- Landing tiles -------------------------------------------------------------------------
   The icon-tile grid from the reference design: a soft-tinted rounded square holding the icon,
   the label beneath, the whole tile a link. */

.feature-tile {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    text-decoration: none;
    color: var(--text);
    transition: border-color var(--duration-fast) var(--ease);
}

.feature-tile:hover {
    border-color: var(--primary);
    color: var(--text);
}

.feature-tile__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 3rem;
    block-size: 3rem;
    border-radius: var(--radius-md);

    /* Tinted, not filled. The icon is a signpost; a solid brand-coloured block on every tile would
       make the grid shout and leave nothing to draw the eye to what actually needs attention. */
    background: var(--primary-soft);
    color: var(--primary);
    font-size: 1.5rem;
}

.feature-tile__label {
    font-weight: 600;
}

.epmr-section__heading {
    font-family: var(--font-display);
    font-size: var(--font-size-heading);
    margin-block-end: var(--card-gap);
}


/* ---- Landing tiles, continued ---------------------------------------------------------------
   The grid stretches its rows by default, but a card whose content is shorter than its neighbour
   still needs to fill the track — otherwise a one-word tile sits half the height of a two-word one
   and the grid reads as broken rather than as varied. */

.app-tile-grid > .feature-tile {
    block-size: 100%;
    justify-content: flex-start;
}

/* ---- The hero -------------------------------------------------------------------------------
   The gradient banner from the reference design. Journey colours, left to right, so the one piece
   of decoration in the product is still saying something: this is the path a dose travels. */

.epmr-hero {
    padding-block: 2rem;
    padding-inline: var(--card-padding);
    border-radius: var(--radius-xl);
    margin-block-end: var(--section-gap);
    background: linear-gradient(
        120deg,
        var(--journey-prepared) 0%,
        var(--journey-in-transit) 55%,
        var(--journey-at-ward) 100%);
    color: var(--on-journey);
}

.epmr-hero__title {
    font-family: var(--font-display);
    font-size: var(--font-size-display);
    font-weight: 700;
    margin: 0;

    /* Explicit, not inherited from `.epmr-hero`. Bootstrap 5.3 gives every heading
       `color: var(--bs-heading-color)`, which is the body ink for the theme — so the one element on
       the page that does NOT sit on a body background was taking the body's colour, and the 32px
       title on the landing page came out near-white over a bright teal gradient in dark mode. */
    color: var(--on-journey);
}

.epmr-hero__subtitle {
    margin-block-start: 0.5rem;
    margin-block-end: 0;
    font-size: var(--font-size-body);

    /* Not --text-2: this sits on the gradient, not on a surface, so it needs its own value or it
       inherits a colour tuned for a different background and disappears in one of the themes.
       Not a literal white either, for the same reason the title is not: the gradient is deep in the
       light theme and bright in the dark one, so the ink has to follow it. */
    color: var(--on-journey);
    opacity: .88;
}


/*
    Inside a grid, cards are laid out by the grid — the stacking margin must not apply.

    `.epmr-card + .epmr-card` gives every adjacent card a top margin, which is right when they stack
    down a page and wrong in a tile grid: it pushed every tile after the first down by a gap, so a
    row of four read as one tall tile and three short ones. It looked like broken heights and was
    actually a margin.
*/
.app-tile-grid > .epmr-card + .epmr-card {
    margin-block-start: 0;
}

/* ---- Ground, surface and accent ---------------------------------------------------------------

   "It feels like all white only except the buttons."

   That was three near-identical values stacked on top of each other. The sidebar painted
   `--surface` (#FFFFFF), the content ground painted `--bg` (#F7FAFC) and every card painted
   `--surface` again — so a white sidebar sat beside a white-ish page holding white cards, and the
   only thing with any colour in it was a button. The dark theme had the same problem one shade
   down: #06152B, #0A1D33 and #0C2340 are not far enough apart to read as separate planes.

   Two changes. The content ground drops to `--bg-tint`, which is the token that already existed for
   exactly this and was used nowhere, so a white card now sits ON something. And the seven
   per-function accents from PRD §13.0.4 — defined per theme in `_tokens.css`, used by the Flutter
   app since slice 034 and by the web never — finally reach the screen.

   `color-mix` rather than seven more soft tokens per theme: the tint is always the accent at 14%,
   which is the figure §13.0.4 gives, and deriving it means the tint cannot drift from the colour it
   is supposed to be a tint of. The plain accent is declared first as the fallback for anything that
   cannot mix. */

.app-main {
    background-color: var(--bg-tint);
}

/* ---- group accents ----------------------------------------------------------------------------
   A bar, not a coloured label: the label is a signpost and must not compete with the items under it
   (which is why it is `--text-3` and stays that way). The bar is 3px of the area's own colour, so
   the eye learns "teal band = the ward's work" without anything shouting. */

.app-nav-group__label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.app-nav-group__label::before {
    content: "";
    inline-size: 3px;
    block-size: 0.85em;
    border-radius: var(--radius-pill);
    background-color: var(--accent-admin);
}

.app-nav-group--pharmacy .app-nav-group__label::before { background-color: var(--accent-pharmacy); }
.app-nav-group--administration .app-nav-group__label::before { background-color: var(--accent-administration); }
.app-nav-group--returns .app-nav-group__label::before { background-color: var(--accent-returns); }
.app-nav-group--scanning .app-nav-group__label::before { background-color: var(--accent-scanning); }
.app-nav-group--admin .app-nav-group__label::before { background-color: var(--accent-admin); }

/* ---- tile accents -----------------------------------------------------------------------------
   One accent per function, on the icon's tinted square — the reference pattern, and the thing that
   makes a grid of twenty tiles scannable instead of uniform. */

.epmr-section--pharmacy .feature-tile__icon {
    background: var(--accent-pharmacy);
    background: color-mix(in srgb, var(--accent-pharmacy) 14%, transparent);
    color: var(--accent-pharmacy);
}

.epmr-section--administration .feature-tile__icon {
    background: var(--accent-administration);
    background: color-mix(in srgb, var(--accent-administration) 14%, transparent);
    color: var(--accent-administration);
}

.epmr-section--returns .feature-tile__icon {
    background: var(--accent-returns);
    background: color-mix(in srgb, var(--accent-returns) 14%, transparent);
    color: var(--accent-returns);
}

.epmr-section--admin .feature-tile__icon {
    background: var(--accent-admin);
    background: color-mix(in srgb, var(--accent-admin) 14%, transparent);
    color: var(--accent-admin);
}

/* A tile borrows its area's colour on hover too, rather than every tile on the page turning the
   same brand teal and undoing the distinction the grid just made. */
.epmr-section--pharmacy .feature-tile:hover { border-color: var(--accent-pharmacy); }
.epmr-section--administration .feature-tile:hover { border-color: var(--accent-administration); }
.epmr-section--returns .feature-tile:hover { border-color: var(--accent-returns); }
.epmr-section--admin .feature-tile:hover { border-color: var(--accent-admin); }

/* ---- section headings -------------------------------------------------------------------------
   A rule under the heading in the area's accent, so the page reads as bands of work rather than as
   one continuous list of cards. */

.epmr-section__heading {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.epmr-section__heading::after {
    content: "";
    flex: 1;
    block-size: var(--border-width);
    background-color: var(--border);
}

.epmr-section--pharmacy .epmr-section__heading::after { background-color: var(--accent-pharmacy); opacity: .35; }
.epmr-section--administration .epmr-section__heading::after { background-color: var(--accent-administration); opacity: .35; }
.epmr-section--returns .epmr-section__heading::after { background-color: var(--accent-returns); opacity: .35; }
.epmr-section--admin .epmr-section__heading::after { background-color: var(--accent-admin); opacity: .35; }

/* ---- scrollbars -------------------------------------------------------------------------------
   The sidebar and the content column scroll independently, and the platform default draws a wide
   grey trough over both. Thin, in the theme's own colours, so a scrolling list does not have a
   strip of Windows grey down the side of it.

   `scrollbar-color` is the standard property; the `::-webkit-` block is what Chrome and Edge
   actually honour today. Both are set, neither is required for the page to work. */

.app-sidebar,
.app-main,
.table-responsive,
.epmr-scroll {
    scrollbar-width: thin;
    scrollbar-color: var(--border-2) transparent;
}

.app-sidebar::-webkit-scrollbar,
.app-main::-webkit-scrollbar,
.table-responsive::-webkit-scrollbar,
.epmr-scroll::-webkit-scrollbar {
    inline-size: 10px;
    block-size: 10px;
}

.app-sidebar::-webkit-scrollbar-track,
.app-main::-webkit-scrollbar-track,
.table-responsive::-webkit-scrollbar-track,
.epmr-scroll::-webkit-scrollbar-track {
    background: transparent;
}

.app-sidebar::-webkit-scrollbar-thumb,
.app-main::-webkit-scrollbar-thumb,
.table-responsive::-webkit-scrollbar-thumb,
.epmr-scroll::-webkit-scrollbar-thumb {
    background-color: var(--border-2);
    border-radius: var(--radius-pill);
    border: 2px solid transparent;
    background-clip: content-box;
}

.app-sidebar::-webkit-scrollbar-thumb:hover,
.app-main::-webkit-scrollbar-thumb:hover,
.table-responsive::-webkit-scrollbar-thumb:hover,
.epmr-scroll::-webkit-scrollbar-thumb:hover {
    background-color: var(--text-3);
}

/* ---- Collapsing nav groups --------------------------------------------------------------------
   The label became a button so the group can be operated from a keyboard and announced to a screen
   reader. It has to keep looking like a label: a signpost that competes with the items beneath it
   makes the menu busier, which is the opposite of what grouping was for. So: no button chrome, the
   same muted colour, and the only added ink is a chevron that says which way it goes. */

.app-nav-group__toggle {
    inline-size: 100%;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    background: none;
    border: 0;
    text-align: start;
    cursor: pointer;

    /* Comfortably past the 44px this needs on a tablet without becoming a row in its own right. */
    min-block-size: 2.75rem;
    color: var(--text-3);
}

.app-nav-group__toggle:hover { color: var(--text-2); }

.app-nav-group__toggle:focus-visible {
    outline: var(--focus-ring-width, 3px) solid var(--primary-soft);
    outline-offset: -2px;
    border-radius: var(--radius-sm);
}

.app-nav-group__text { flex: 1; }

/* Rotates rather than swapping glyphs, so the state change is a movement the eye follows instead of
   a substitution it has to notice. `matchTextDirection` is not a CSS thing — a chevron pointing
   down means the same in both scripts, which is why this is a down/up chevron and not a caret. */
.app-nav-group__chevron {
    font-size: 0.7rem;
    transition: transform var(--duration-fast) var(--ease);
}

.app-nav-group--collapsed .app-nav-group__chevron {
    transform: rotate(-90deg);
}

/* Colour is never the only signal (Q4): the chevron's rotation is the state, `aria-expanded`
   carries it to assistive technology, and the items are genuinely removed from the tree with
   `hidden` rather than merely being invisible. */
.app-nav-group__items[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
    .app-nav-group__chevron { transition: none; }
}

/* ---- The content column scrolls on its own ----------------------------------------------------
   Not the window. With the sidebar sticky and the page scrolling as one, a long table dragged the
   whole shell up and the menu went with it. Giving the content its own scroll container is also
   what makes "the sidebar keeps its position when you click an item" true by construction rather
   than by restoring an offset afterwards. */

.app-shell {
    block-size: 100vh;
    overflow: hidden;
}

.app-main {
    overflow-y: auto;
    block-size: 100vh;
}

.app-sidebar {
    block-size: 100vh;
}

/* The header band stays put while the content under it scrolls — on a long list the page title and
   the working location are exactly what you need to still be able to see. */
.app-header-band {
    position: sticky;
    inset-block-start: 0;
    z-index: 3;
    background-color: var(--bg-tint);
}

/* ---- The header band, painted -----------------------------------------------------------------
   PRD §13.0.1's fixed navy gradient header. It existed as a layout element and painted nothing, so
   the page opened with white chrome above a white-ish ground holding white cards — which is what
   the owner was describing. The Flutter app has had this exact band since slice 034; this is the
   web catching up to it, and §13.0.6 says the two must feel like one product.

   The band does not follow light/dark. Chrome anchors the brand; content adapts. That is also why
   its own ink is `--on-band` rather than anything theme-derived. */

.app-header-band {
    background: var(--gradient-header);
    border-block-end: 0;
    color: var(--on-band);
}

.app-header-band__title { color: var(--on-band); }

/* Every control that sits in the band is on navy in both themes, so it takes the band's own ink
   rather than the theme's. Scoped to the band so the same components elsewhere are untouched. */

.app-header-band .btn-outline-secondary {
    --bs-btn-color: var(--on-band-quiet);
    --bs-btn-border-color: var(--on-band-quiet);
    --bs-btn-hover-bg: var(--on-band-fill);
    --bs-btn-hover-border-color: var(--on-band);
    --bs-btn-hover-color: var(--on-band);
    --bs-btn-active-bg: var(--on-band-fill);
    --bs-btn-active-border-color: var(--on-band);
    --bs-btn-active-color: var(--on-band);
}

/* A real fill, not the translucent white the other band controls take.
   There is exactly one primary action in this band — "where are you working?" — and until it is
   answered every scoped list on the site is showing less than it could. Giving it the same
   translucent treatment as the theme toggle made it indistinguishable from the location pill it
   replaces, which is the opposite of what LocationContext's own comment asks for. Teal on navy
   reads clearly in both themes, and the navy ink on it is fixed rather than theme-derived. */
.app-header-band .btn-primary {
    --bs-btn-bg: var(--brand-teal-400);
    --bs-btn-border-color: var(--brand-teal-400);
    --bs-btn-color: var(--brand-navy-900);
    --bs-btn-hover-bg: var(--on-band);
    --bs-btn-hover-border-color: var(--on-band);
    --bs-btn-hover-color: var(--brand-navy-900);
    --bs-btn-active-bg: var(--on-band);
    --bs-btn-active-color: var(--brand-navy-900);
}

.app-header-band .segmented {
    border-color: var(--on-band-quiet);
}

.app-header-band .segmented__option {
    color: var(--on-band-quiet);
}

.app-header-band .segmented__option--on {
    background-color: var(--on-band-fill);
    color: var(--on-band);
}

/* The working location is the one control in the band that must stay the most legible thing in it:
   a nurse who believes she is in Ward 3 while every list is filtered to Ward 7 reads results that
   look entirely plausible and are wrong. */
.app-header-band .badge {
    background-color: var(--on-band-fill) !important;
    color: var(--on-band) !important;
}

/* ---- Dashboard: the ward strip and the day's figures -------------------------------------------
   The verdict and attention rows reuse `.ops-*` from operations.css unchanged. These two blocks are
   what the landing page adds on top of them. */

.ward-strip {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: var(--card-gap);
}

.ward-strip__item {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.ward-strip__name {
    font-weight: 600;
    font-size: var(--font-size-body);
}

.ward-strip__sub {
    color: var(--text-2);
    font-size: var(--font-size-label);
}

/* The journey gradient, because the bar is literally how far today's doses have travelled along it
   — at ward on the left, administered on the right. Decoration that is still saying something. */
.ward-strip__bar {
    block-size: 0.5rem;
    border-radius: var(--radius-pill);
    background-color: var(--surface-2);
    overflow: hidden;
}

.ward-strip__bar > span {
    display: block;
    block-size: 100%;
    background: linear-gradient(
        to inline-end,
        var(--journey-at-ward),
        var(--journey-administered));
}

.ward-strip__figures {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    font-size: var(--font-size-label);
    color: var(--text-2);
}

/* A ward with nothing due gets a sentence, not an empty bar. Zero percent complete and nothing to
   complete are different facts and the first one is an accusation. */
.ward-strip__quiet {
    margin: 0;
    color: var(--text-3);
    font-size: var(--font-size-label);
}

.ward-strip__flags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
}

.stat-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--card-gap);
}

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

.stat-card__figure {
    font-family: var(--font-display);
    font-size: var(--font-size-display);
    font-weight: 700;
    line-height: 1.1;
}

.stat-card__label {
    color: var(--text-2);
    font-size: var(--font-size-label);
    margin-block-start: 0.25rem;
}

/* A short field that sits inline with the button that acts on it — a revoke reason beside Revoke.
   In the stylesheet rather than a `style=` attribute so the width is one decision rather than one
   per row, and so it can change with the density scale later. */
.field-inline { inline-size: 10rem; }

/* ---- Disclosure ------------------------------------------------------------------------------
   A native <details>. No JavaScript, keyboard-operable and correctly announced for free, mirrors in
   Arabic without a second rule, and it still opens if a script fails to load — which for the screen
   that switches modules off for a whole hospital is the failure mode to want. */

.epmr-disclosure__summary {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    cursor: pointer;
    min-block-size: var(--touch-target);

    /* The default marker is a small black triangle that sits outside the flex layout and points the
       wrong way in Arabic. The chevron replaces it. */
    list-style: none;
}

.epmr-disclosure__summary::-webkit-details-marker { display: none; }

.epmr-disclosure__summary:focus-visible {
    outline: 3px solid var(--primary-soft);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* The count carries the state in words, so a shut group is still a statement (Q4). */
.epmr-disclosure__count {
    margin-inline-start: auto;
    color: var(--text-2);
    font-size: var(--font-size-label);
}

.epmr-disclosure__chevron {
    color: var(--text-3);
    transition: transform var(--duration-fast) var(--ease);
}

.epmr-disclosure[open] .epmr-disclosure__chevron { transform: rotate(180deg); }

@media (prefers-reduced-motion: reduce) {
    .epmr-disclosure__chevron { transition: none; }
}

/* ---- Scrolling ---------------------------------------------------------------------------------
   Sticky table headings were tried here and taken out again, which is worth writing down so the
   next person does not re-add them the same way.

   Twenty-five of the thirty-three tables sit inside `.table-responsive`, whose `overflow-x: auto`
   makes IT the scroll container — so a sticky `<th>` sticks to the top of a box that never scrolls
   vertically and nothing happens. The eight that are not wrapped stick to the top of the content
   column, which is underneath the header band, so they are invisible instead.

   Doing it properly means giving the band a known height at each density and offsetting the heading
   by it, and the band's height changes when its actions wrap — which they do in Arabic. That is a
   real piece of work rather than a declaration, and shipping a rule that silently does nothing is
   worse than not shipping it: it looks done. */

/* A flick of the sidebar's own scroll does not carry on into the page behind it. */
.app-sidebar { overscroll-behavior: contain; }

/* The mark beside the wordmark. `alt=""` and aria-hidden because the word next to it already says
   the name — a screen reader announcing "ePMR ePMR" is worse than one that says it once. */
.app-sidebar__brand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.app-sidebar__mark {
    inline-size: 1.75rem;
    block-size: 1.75rem;
    flex: 0 0 auto;
}

/* ---- Summary tiles above a listing -------------------------------------------------------------
   The counts that sit over a table: how many there are, how many are active, how many need
   attention. Tinted rather than white, because a white card on a white card on a tinted page is
   three surfaces saying nothing about each other — and because the point of these is to be seen
   before the table is read.

   The tint carries meaning and never carries it alone (Q4): every tile has its number and its label,
   and the colour only tells you which KIND of count it is. `--stat-*` are aliases of the status
   palette rather than new colours, so a warning tile is the same amber as a warning badge. */

.stat-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--card-gap);
    margin-block-end: var(--section-gap);
}

.stat-tile {
    padding: var(--card-padding);
    border-radius: var(--radius-lg);
    border: var(--border-width) solid transparent;
}

.stat-tile__figure {
    font-family: var(--font-display);
    font-size: var(--font-size-heading);
    font-weight: 700;
    line-height: 1.1;
}

.stat-tile__label {
    font-size: var(--font-size-label);
    margin-block-start: 0.15rem;
}

.stat-tile--neutral {
    background-color: var(--primary-soft);
    border-color: var(--primary);
    color: var(--text);
}

.stat-tile--good {
    background-color: var(--green-soft);
    border-color: var(--status-success);
    color: var(--text);
}

.stat-tile--warn {
    background-color: var(--amber-soft);
    border-color: var(--status-warning);
    color: var(--text);
}

.stat-tile--quiet {
    background-color: var(--surface-2);
    border-color: var(--border-2);
    color: var(--text-2);
}

/* ---- Listing tables ----------------------------------------------------------------------------
   Row actions sit at the inline end and stay on one line, so a table does not grow a column of
   wrapped buttons. `text-align: end` rather than `right` — this table is read the other way round
   in Arabic (I2). */

.table-actions {
    text-align: end;
    white-space: nowrap;
}

.table-actions .btn + .btn,
.table-actions .btn + form,
.table-actions form + .btn {
    margin-inline-start: 0.35rem;
}

.table-actions form {
    display: inline;
}

/* The identity column: the Arabic name leads, the Latin name and the code sit under it. One cell
   rather than three columns, because they are one fact about one thing. */
.cell-identity__primary { font-weight: 600; }

.cell-identity__secondary {
    color: var(--text-2);
    font-size: var(--font-size-label);
}

.cell-identity__code {
    font-family: var(--font-mono);
    color: var(--text-3);
    font-size: var(--font-size-label);
}

/* ---- The profile block, at the foot of the menu -------------------------------------------------
   Who you are and how to leave. It sits in the sidebar rather than the header band on purpose: the
   sidebar is outside the region a menu click replaces, so this survives every navigation without
   re-initialising, and the band is left for the working location — the one thing up there that
   changes what the screen is showing. */

.app-profile {
    margin-block-start: auto;
    padding-inline: 0.5rem;
    border-block-start: var(--border-width) solid var(--sidebar-border);
    padding-block-start: 0.5rem;
}

.app-profile__summary {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    cursor: pointer;
    list-style: none;
    min-block-size: var(--touch-target);
    padding-inline: 0.5rem;
    border-radius: var(--radius-md);
}

.app-profile__summary::-webkit-details-marker { display: none; }
.app-profile__summary:hover { background-color: var(--sidebar-hover); }

.app-profile__summary:focus-visible {
    outline: 3px solid var(--primary-soft);
    outline-offset: 2px;
}

.app-profile__avatar {
    inline-size: 2rem;
    block-size: 2rem;
    flex: 0 0 auto;
    border-radius: var(--radius-pill);
    background-color: var(--primary);
    color: var(--on-primary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: var(--font-size-label);
}

.app-profile__who {
    display: flex;
    flex-direction: column;
    min-inline-size: 0;
    flex: 1;
}

.app-profile__name {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-profile__role {
    font-size: var(--font-size-label);
    color: var(--text-2);
}

.app-profile__chevron {
    color: var(--text-3);
    transition: transform var(--duration-fast) var(--ease);
}

.app-profile[open] .app-profile__chevron { transform: rotate(180deg); }

.app-profile__body { padding: 0.5rem; }

/*
    The same component in the header band.

    Only the placement differs, so the modifier changes placement and nothing else: no second avatar
    style, no second set of labels. In the sidebar it is a footer that pushes the panel open
    downwards; in the header it must float ABOVE the page, or opening it would grow the band and
    shove the content down every time somebody checked whose session they were in.

    `inset-inline-end: 0` rather than `right`, so it hangs from the correct edge in Arabic without a
    second rule (I2).
*/
.app-profile--header {
    position: relative;
    margin-block-start: 0;
    padding-block-start: 0;
    padding-inline: 0;
    border-block-start: none;
    flex: 0 0 auto;
}

/* Two letters, and only as wide as two letters need. The spelled-out language names were the
   widest thing in the header band and said nothing the abbreviation does not. */
.segmented--compact .segmented__option {
    padding-inline: 0.6rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
}

.app-profile--header .app-profile__summary {
    padding-inline: 0.5rem;
    border: var(--border-width) solid var(--border);
    background-color: var(--surface);
    max-inline-size: 15rem;

    /* One line, not two. The role used to sit under the name, which made this the tallest thing in
       the band for a fact the person already knows and the profile screen repeats. */
    min-block-size: 2.5rem;

    /* The band paints its text white, for white-on-navy. This pill is a white surface sitting ON
       that band, so the inherited colour made the username white on white — invisible in light
       mode at 1:1 contrast, while the role line below it stayed readable because it sets its own
       colour. Anything that changes its background must restate its foreground. */
    color: var(--text);
}

.app-profile--header .app-profile__body {
    position: absolute;
    inset-block-start: calc(100% + 0.35rem);
    inset-inline-end: 0;
    z-index: 30;
    min-inline-size: 15rem;
    background-color: var(--surface);
    border: var(--border-width) solid var(--border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

/* On a narrow screen the name and role are the first thing to go — the avatar still identifies the
   session, and the band has to leave room for the page title. */
@media (max-inline-size: 48rem) {
    .app-profile--header .app-profile__who,
    .app-profile--header .app-profile__chevron { display: none; }
}

.app-profile__label {
    font-size: var(--font-size-label);
    color: var(--text-3);
    margin-block: 0.6rem 0.25rem;
}

@media (prefers-reduced-motion: reduce) {
    .app-profile__chevron { transition: none; }
}

/* A link in the profile menu — the menu is a way to somewhere now, not a settings panel. */
.app-profile__link {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-block-size: var(--touch-target);
    padding-inline: 0.5rem;
    border-radius: var(--radius-md);
    color: var(--text);
    text-decoration: none;
}

.app-profile__link:hover { background-color: var(--surface-2); color: var(--text); }

.app-profile__link:focus-visible {
    outline: 3px solid var(--primary-soft);
    outline-offset: 2px;
}

/* A full-width segmented control splits evenly.

   `.segmented` is `inline-flex` and its options are sized to their text, so stretching the control
   with `w-100` on the settings screen left "English" as a small pill beside a wide empty strip that
   read as a text field rather than the other half of a choice. */
.segmented.w-100 { display: flex; }
.segmented.w-100 .segmented__option { flex: 1; text-align: center; }

/* Both halves are targets, so both clear the floor a gloved tap needs (NFR-U6). */
.segmented.w-100 .segmented__option { min-block-size: var(--touch-target); }

/*
    The landing page, a little smaller.

    Only this screen. It is the one page that is mostly large tiles and headline figures, and at full
    size it read as shouting next to every other screen in the product — the density tokens are tuned
    for tables and forms, not for a wall of cards. Everything else keeps its size, because ward
    screens are sized for arm's length on purpose (NFR-U1) and shrinking those would undo it.

    `zoom` rather than `transform: scale()`: scale would leave the original box behind and the page
    would keep the taller scrollbar of the size it no longer is.
*/
.app-main--home .app-content {
    zoom: 0.9;
}


/* ---- Panel headers -------------------------------------------------------------------------------
   A panel's header names the panel, and a white strip on a white card names nothing: on the order
   creation screen the "Patient" and "Medications" captions read as stray sentences rather than as
   the tops of two boxes. The tint is `--primary-soft` and the weight is 700, so the header is a
   band in both themes without a hardcoded colour — the dark palette redefines the token, which is
   what the theme-dependent status badge bug taught (see the review notes in HANDOFF).

   `.card > .card-header` rather than `.card-header`, so a header nested in something that is not a
   card keeps whatever that component decided. */
.card > .card-header {
    background-color: var(--primary-soft);
    color: var(--text);
    font-weight: 700;
    border-block-end-color: var(--border);
}

/* ---- Whole-row targets -----------------------------------------------------------------------
   A row whose only target is a small button at its inline end is a row most people miss. The whole
   row becomes the target by stretching the link that was already there — `.stretched-link` covers
   the nearest positioned ancestor — rather than by hanging a click handler on the container.

   That distinction is the accessibility of it: the thing being activated is still the same <a> or
   <button>, so it keeps its place in the tab order, its role and its accessible name. A `<tr>` or a
   `<div>` with an onclick has none of those, and there is no keyboard route to it at all.

   Anything in the row that is ALSO a target has to sit above the stretched overlay, or the overlay
   swallows it. */
.row-target {
    position: relative;
}

.row-target :is(a, button, input, select, label):not(.stretched-link) {
    position: relative;
    z-index: 2;
}

/* ---- The profile block in the menu ---------------------------------------------------------------
   The same component the header band uses, at the foot of the sidebar. `.app-profile`'s base rules
   were written for exactly this placement — a footer that pushes its panel open downwards — so the
   modifier carries only what the menu's two shapes need.

   In the rail the words go and the avatar stays, which is the same trade every menu item makes: the
   `title` attribute keeps the name for a pointer and the button text keeps it for a screen reader. */
.app-sidebar--rail .app-profile--menu .app-profile__name,
.app-sidebar--rail .app-profile--menu .app-profile__chevron {
    display: none;
}

.app-sidebar--rail .app-profile--menu .app-profile__body {
    padding-inline: 0.1rem;
}

/* Only ONE thing in a flex column may claim the free space. `.app-sidebar__preset` and
   `.app-profile` both set `margin-block-start: auto`, and flexbox splits the slack between every
   auto margin it finds — which left the preset label floating in the middle of the sidebar with a
   gap above and below it. The sibling selector keeps the no-preset case (a deployment with no
   preset renders no label) working, where the profile block still needs the auto margin. */
.app-sidebar__preset + .app-profile--menu {
    margin-block-start: 0;
}

/* Below 48rem the sidebar becomes a horizontally scrolling strip, and a panel that opens downwards
   inside it has nowhere to go. The header band carries the same pair on every screen width, so
   nothing is lost by hiding this copy. */
@media (max-inline-size: 48rem) {
    .app-profile--menu {
        display: none;
    }
}
