/* MicroMed - application styles layered over Bootstrap 5.
 *
 * Palette and typography are taken from the public site,
 * https://micromedlaboratory.com/ - that site is the source of truth for the
 * brand, not the logo file. Navy carries everything structural. The green is
 * an accent only: at its brand value it is 2.80:1 on white and fails AA both
 * as text and as a fill behind white text, so it is confined to hairlines and
 * marks. --mm-green-ink is the same hue walked down to 4.59:1 for the rare
 * case where a label has to be green.
 */

/* Self-hosted: a lab workstation may be on a LAN or offline, where a CDN link
   renders the whole application in a fallback face. Both are variable fonts,
   so one file per family covers every weight used. */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url('../fonts/inter-latin-var.woff2') format('woff2');
}

@font-face {
    font-family: 'Sora';
    font-style: normal;
    font-weight: 600 700;
    font-display: swap;
    src: url('../fonts/sora-latin-var.woff2') format('woff2');
}

:root {
    --mm-navy:        #133a6b;   /* 11.38:1 on white */
    --mm-navy-mid:    #0d2a4f;   /* 14.37:1 - hover */
    --mm-navy-deep:   #091f3b;   /* 16.53:1 - active, gradient foot */
    --mm-navy-tint:   #eef3f9;   /* hover wash, selected rows */
    --mm-green:       #4caf3f;   /* accent: hairlines and marks, never text */
    --mm-green-light: #6cc04a;
    --mm-green-ink:   #3a8530;   /* 4.59:1 - green that may carry text */

    --mm-ink:         #1a2230;
    --mm-muted:       #6b7684;
    --mm-line:        #e5e7eb;

    --mm-font-body:    'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --mm-font-display: 'Sora', var(--mm-font-body);

    /* Alias: rules and views written against the old token still resolve. */
    --ml-brand: var(--mm-navy);

    --bs-body-font-family: var(--mm-font-body);
    --bs-body-color: var(--mm-ink);
}

body {
    font-family: var(--mm-font-body);
    color: var(--mm-ink);
}

/* Sora is the display face on the site - headings only. Body copy, tables and
   form controls stay on Inter, which is built for dense UI at small sizes. */
h1, h2, h3, h4, h5, h6,
.navbar-brand,
.auth-title,
.card-title {
    font-family: var(--mm-font-display);
    font-weight: 600;
    letter-spacing: -.015em;
    color: var(--mm-navy);
}

/* The 'o' of "Micro", green as the logo draws it. --mm-green-ink rather than
   the logo's own #4caf3f: the brand green is 2.80:1 on white, under even the
   3:1 large-text floor, and this is the line naming the laboratory. Same hue,
   4.59:1. Inherits the surrounding weight and size - only the colour changes. */
.brand-o {
    color: var(--mm-green-ink);
}

/* Numeric columns line up when the figures share a width. */
.table td,
.table th {
    font-variant-numeric: tabular-nums;
}

/* --- Bootstrap overrides -------------------------------------------------
   5.3 compiles .btn-primary with literal hex into its own --bs-btn-* custom
   properties, so overriding --bs-primary alone would never reach a button.
   Only the three primary utilities the views actually use are redefined;
   success / danger / warning are semantic, not brand, and stay as they are. */

.btn-primary {
    --bs-btn-bg: var(--mm-navy);
    --bs-btn-border-color: var(--mm-navy);
    --bs-btn-hover-bg: var(--mm-navy-mid);
    --bs-btn-hover-border-color: var(--mm-navy-mid);
    --bs-btn-active-bg: var(--mm-navy-deep);
    --bs-btn-active-border-color: var(--mm-navy-deep);
    --bs-btn-disabled-bg: var(--mm-navy);
    --bs-btn-disabled-border-color: var(--mm-navy);
    --bs-btn-focus-shadow-rgb: 19, 58, 107;
}

.btn-outline-primary {
    --bs-btn-color: var(--mm-navy);
    --bs-btn-border-color: var(--mm-navy);
    --bs-btn-hover-bg: var(--mm-navy);
    --bs-btn-hover-border-color: var(--mm-navy);
    --bs-btn-active-bg: var(--mm-navy-deep);
    --bs-btn-active-border-color: var(--mm-navy-deep);
    --bs-btn-disabled-color: var(--mm-navy);
    --bs-btn-disabled-border-color: var(--mm-navy);
    --bs-btn-focus-shadow-rgb: 19, 58, 107;
}

.text-bg-primary {
    background-color: var(--mm-navy) !important;
    color: #fff !important;
}

.btn-link,
.link-primary,
a:not([class]) {
    color: var(--mm-navy);
}

/* Focus lands on the brand rather than Bootstrap's stock blue. */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    border-color: #8aa9c9;
    box-shadow: 0 0 0 .25rem rgba(19, 58, 107, .15);
}

.form-check-input:checked {
    background-color: var(--mm-navy);
    border-color: var(--mm-navy);
}

/* --- Top bar -------------------------------------------------------------
   Light rather than the old bg-dark: the logo is drawn for a white ground,
   and a white bar with a navy wordmark is what makes the sidebar below it
   read as one surface. */

.app-navbar {
    background: #fff;
    /* The one place the green appears on screen, as a hairline. Decoration
       only - nothing about the bar depends on it being seen. */
    border-bottom: 2px solid var(--mm-green);
    box-shadow: 0 1px 3px rgba(16, 32, 56, .06);
}

.app-navbar .navbar-brand {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    color: var(--mm-navy);
    font-weight: 600;
    letter-spacing: -.01em;
}

.app-brand-mark {
    flex: 0 0 auto;
    width: 2.1rem;
    height: 2.1rem;
    object-fit: contain;
}

/* Hamburger. Only ever shown below lg, where the sidebar is a collapse. */
.app-sidebar-toggle {
    padding: .35rem .55rem;
    border: 1px solid var(--mm-line);
    border-radius: .5rem;
    background: #fff;
    color: var(--mm-navy);
    line-height: 0;
}

.app-sidebar-toggle:focus-visible {
    outline: 2px solid var(--mm-navy);
    outline-offset: 2px;
}

/* --- Application shell -------------------------------------------------- */

.app-shell {
    display: flex;
    align-items: stretch;
    min-height: calc(100vh - 58px);
}

.app-sidebar {
    flex: 0 0 250px;
    padding: .5rem .75rem 1.5rem;
    background: #fff;
    border-right: 1px solid var(--mm-line);
}

/* Quiet group label - "Operations", "Masters". Sets the groups apart without
   competing with the entries under it. */
.app-sidebar-heading {
    padding: 1rem .75rem .4rem;
    color: var(--mm-muted);
    font-size: .75rem;
    font-weight: 500;
}

/* A rule between groups, never above the first one. */
.app-sidebar-group + .app-sidebar-group {
    margin-top: .35rem;
    border-top: 1px solid var(--mm-line);
}

.app-sidebar-link {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .55rem .75rem;
    border-radius: .6rem;
    color: var(--mm-ink);
    font-size: .925rem;
    font-weight: 500;
    text-decoration: none;
}

.app-sidebar-link + .app-sidebar-link {
    margin-top: .1rem;
}

/* currentColor throughout, so the icon follows the label into hover and
   active without a rule of its own for each state. */
.app-sidebar-icon {
    flex: 0 0 auto;
    width: 1.15rem;
    height: 1.15rem;
    color: var(--mm-muted);
}

/* Hover, on everything that is not already the active pill.

   Excluding .active here, rather than restating white for the pill further
   down, is what stops the two rules racing. :not() contributes its argument's
   weight, so :hover:not(.disabled) outranked .app-sidebar-link.active and
   repainted the pill's icon navy - on a navy background, which read as the
   icon disappearing on hover. The label escaped only because it had an
   explicit .active:hover of its own to tie on and win by source order.
   Making the two states mutually exclusive settles it without either rule
   depending on where it sits in the file. */
.app-sidebar-link:hover:not(.disabled):not(.active) {
    background: var(--mm-navy-tint);
    color: var(--mm-navy);
}

.app-sidebar-link:hover:not(.disabled):not(.active) .app-sidebar-icon {
    color: var(--mm-navy);
}

.app-sidebar-link:focus-visible {
    outline: 2px solid var(--mm-navy);
    outline-offset: 1px;
}

/* The filled pill from the reference, in brand navy. White on navy is 12:1,
   so the label survives the solid fill with room to spare. Hover is no longer
   named here: the rule above now steps aside for .active, so the pill holds
   this appearance through hover on its own. */
.app-sidebar-link.active {
    background: var(--mm-navy);
    color: #fff;
    font-weight: 600;
}

.app-sidebar-link.active .app-sidebar-icon {
    color: #fff;
}

.app-sidebar-link.disabled {
    color: var(--mm-muted);
    cursor: default;
    opacity: .65;
}

.app-main {
    flex: 1 1 auto;
    min-width: 0;    /* lets wide tables scroll instead of stretching the page */
    padding: 1.5rem;
}

/* Below lg the sidebar stops being furniture and becomes a disclosure: 20-odd
   entries as a permanent strip would push every page a screenful down. */
@media (max-width: 991.98px) {
    .app-shell {
        display: block;
    }

    .app-sidebar {
        padding-bottom: .75rem;
        border-right: 0;
        border-bottom: 1px solid var(--mm-line);
    }

    /* Comfortable target once there is no pointer to aim with. */
    .app-sidebar-link {
        padding: .7rem .75rem;
    }

    .app-main {
        padding: 1rem;
    }
}

/* Bootstrap's .collapse sets display:none. Above lg the sidebar is permanent,
   so the hidden state is overridden here rather than toggled off in script -
   the same element is a collapse on a phone and a fixture on a desktop. */
@media (min-width: 992px) {
    .app-sidebar.collapse:not(.show) {
        display: block;
    }
}

/* --- Sign-in ------------------------------------------------------------- */

.auth-body {
    background: linear-gradient(160deg, #1d5091 0%, var(--mm-navy) 45%, var(--mm-navy-deep) 100%);
}

.auth-shell {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 1.5rem;
}

.auth-card {
    width: 100%;
    max-width: 390px;
    padding: 2.25rem 2rem;
    border-radius: .9rem;
    background: #fff;
    box-shadow: 0 1rem 3rem rgba(9, 31, 59, .38);
}

/* The card is white and so is the logo's ground, so it sits flush - no plate
   behind it. Width and height are set on the element to reserve the space
   before the image lands and stop the card jumping. */
.auth-logo {
    width: 88px;
    height: 88px;
    object-fit: contain;
}

.auth-title {
    color: var(--mm-navy);
    font-weight: 600;
    letter-spacing: -.01em;
}

@media (max-width: 400px) {
    .auth-card {
        padding: 1.75rem 1.25rem;
    }

    .auth-logo {
        width: 72px;
        height: 72px;
    }
}

/* Desktop is the primary target: this is a workstation application. */
@media (min-width: 1400px) {
    .app-main {
        padding: 1.75rem 2rem;
    }
}

/*
 * Barcode preview on the sample detail screen. The label sheet has its own
 * stylesheet; this is only the on-screen copy, sized in mm so what you see
 * is roughly what the printer produces.
 */
.sample-barcode {
    max-width: 60mm;
}

.sample-barcode svg {
    display: block;
    width: 100%;
    height: 22mm;
}

/* Print rules. Report-specific A4 layout arrives with Milestone 10. */
@media print {
    .d-print-none {
        display: none !important;
    }

    body {
        background: #fff !important;
    }

    .card {
        border: 0 !important;
        box-shadow: none !important;
    }
}

/* --- List toolbars -------------------------------------------------------
   Was an inline min-width:260px on the field. At 320px that plus the row's
   padding overflowed the viewport, and an inline style cannot be reined back
   in by a media query - hence the class. */

.toolbar-search {
    min-width: 260px;
}

/* The lab picker on the allotment screen, for the same reason: 16rem as an
   inline style put the button beside it off the edge of a small phone. */
.outsource-lab-select {
    min-width: 16rem;
}

@media (max-width: 575.98px) {
    /* Below this the field takes the row to itself and the controls under it
       wrap as a group, rather than every control fighting for the same line. */
    .toolbar-search,
    .outsource-lab-select {
        flex: 1 1 100%;
        min-width: 0;
        width: 100%;
    }
}

/* --- List tables on a phone ----------------------------------------------
   Below md a nine-column table cannot be read across the screen. It fits -
   .table-responsive scrolls it - but the scroll is a drag nobody makes, so
   more than half the columns may as well not be there. Each row becomes a
   labelled block instead, and every column comes back into view.

   The captions are the table's own headings, stamped onto the cells by
   responsive-tables.js. Only a table that script has prepared carries
   .table-cards, so with scripting off nothing is transformed and the table
   scrolls sideways as before - the fallback is today's behaviour.

   Desktop is untouched: everything here is inside the query. */

@media (max-width: 767.98px) {
    .table-cards,
    .table-cards > tbody,
    .table-cards > tfoot,
    .table-cards > tbody > tr,
    .table-cards > tbody > tr > td {
        display: block;
        width: auto;
    }

    /* The headings are on the cells now. */
    .table-cards > thead {
        display: none;
    }

    .table-cards > tbody > tr,
    .table-cards > tfoot > tr {
        padding: .55rem .8rem;
        border: 1px solid var(--mm-line);
        border-radius: .6rem;
    }

    .table-cards > tbody > tr + tr,
    .table-cards > tfoot > tr {
        margin-top: .5rem;
    }

    /* A totals row is a summary, not a record: one bold line that wraps,
       rather than a card of captioned figures. */
    .table-cards > tfoot > tr {
        display: flex;
        flex-wrap: wrap;
        gap: .15rem 1rem;
        font-weight: 600;
    }

    .table-cards > tfoot > tr > th,
    .table-cards > tfoot > tr > td {
        padding: 0;
        border: 0;
        text-align: left !important;
    }

    /* The caption sits in a gutter rather than as a flex sibling of the
       content. Cells here routinely hold several elements - a name with the
       patient code under it, a badge with a second badge beneath - and as flex
       items those get scattered by the alignment and refuse to shrink, which
       pushed long values straight out of the card. In a padded box the content
       simply flows, whatever shape it is. */
    .table-cards > tbody > tr > td {
        position: relative;
        padding: .2rem 0 .2rem 7rem;
        border: 0;
        /* A column set flush right reads as a stray indent once it is a row in
           a card rather than the foot of a column of figures. */
        text-align: left !important;
        /* A stored filename or a hash has no space to break at. */
        overflow-wrap: anywhere;
    }

    .table-cards td[data-label]::before {
        content: attr(data-label);
        position: absolute;
        top: .2rem;
        left: 0;
        width: 6.4rem;
        color: var(--mm-muted);
        font-size: .78rem;
        font-weight: 500;
        line-height: 1.5;
    }

    /* The tick-box and actions columns have no heading, so there is no gutter
       to leave room for. */
    .table-cards > tbody > tr > td:not([data-label]) {
        padding-left: 0;
    }

    /* An empty state, or the strip of actions under a dispatch row: one wide
       cell that describes the card above rather than being a card itself. No
       top border and no gap, so the two read as one block. */
    .table-cards > tbody > tr.table-card-note {
        margin-top: -.5rem;
        border-top: 0;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
    }

    .table-cards > tbody > tr.table-card-note > td {
        padding-left: 0;
    }

    /* An empty state standing on its own has no card above to join. */
    .table-cards > tbody > tr.table-card-note:first-child {
        margin-top: 0;
        border-top: 1px solid var(--mm-line);
        border-radius: .6rem;
    }

    /* 16px is a fine target for a mouse and a poor one for a thumb. The
       sidebar links were given the same treatment where the pointer stops
       being a pointer. */
    .table-cards .form-check-input {
        width: 1.35rem;
        height: 1.35rem;
    }

    .table-cards-host {
        overflow-x: visible;
    }
}

/* --- Catalogue type-ahead ------------------------------------------------
   Shared by the billing search box and by the Tests, Test groups and
   Packages list screens, so a suggestion looks the same wherever it
   appears. ---------------------------------------------------------- */

.catalogue-search {
    position: relative;
}

.catalogue-suggestions {
    position: absolute;
    z-index: 1050;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    /* A list screen's filter field sits in a row of dropdowns, and at its
       width a catalogue name wraps to four lines and cannot be scanned - so
       the panel is allowed to spill past it. Expressed as width rather than
       min-width on purpose: min-width outranks max-width, so a floor could
       not be reined back in on a narrow screen. Billing's box is already
       wider than the floor, so nothing changes there. */
    width: max(100%, 26rem);
    max-height: 24rem;
    overflow-y: auto;
    background: #fff;
    border: 1px solid #ced4da;
    border-radius: .375rem;
    box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .12);
}

.catalogue-item {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    width: 100%;
    padding: .35rem .6rem;
    border: 0;
    border-bottom: 1px solid #f1f3f5;
    background: transparent;
    font-size: .82rem;
    text-align: left;
}

.catalogue-item:last-child {
    border-bottom: 0;
}

/* One highlight class for both hover and keyboard, so the two cannot
   disagree about which row is selected. */
.catalogue-item:hover,
.catalogue-item.is-active {
    background: #eef4ff;
}

/* Already on the group or package: shown, so it is clear the search found
   it, but not offered a second time. */
.catalogue-item[aria-disabled="true"] {
    opacity: .55;
    cursor: default;
}

.catalogue-item[aria-disabled="true"]:hover,
.catalogue-item[aria-disabled="true"].is-active {
    background: transparent;
}

.catalogue-code {
    flex: 0 0 4.5rem;
    color: #666;
    font-size: .75rem;
}

.catalogue-main {
    flex: 1 1 auto;
    min-width: 0;
}

.catalogue-tag {
    display: inline-block;
    margin-right: .3rem;
    padding: 0 .3rem;
    border: 1px solid #ced4da;
    border-radius: .2rem;
    color: #555;
    font-size: .65rem;
    letter-spacing: .03em;
}

.catalogue-meta {
    display: block;
    color: #777;
    font-size: .72rem;
}

.catalogue-price {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
}

/* A zero-priced catalogue row is how the legacy import bills nothing
   without anyone noticing, so it is called out rather than shown as 0.00. */
.catalogue-price-missing {
    color: #997404;
    font-size: .72rem;
}

.catalogue-empty {
    padding: .6rem;
    color: #777;
    font-size: .8rem;
}

/* --- Attachment viewer ---------------------------------------------------
   The modal that shows a machine printout or an outsourced lab's sheet
   without making anyone download it first. ------------------------------ */

/* Tall enough to read a page of A4 without scrolling the modal itself, and
   expressed against the viewport so it still fits on a laptop. */
.attachment-frame {
    display: block;
    width: 100%;
    height: 78vh;
    border: 0;
    background: #fff;
}

.attachment-image {
    display: block;
    max-width: 100%;
    margin: 0 auto;
    background: #fff;
}

/* Phone. The panel is pinned to both edges of the field it hangs from, so
   it can no longer reach past the side of the screen whatever the field's
   width or position. Where the row wraps - the list toolbars - the field
   takes the whole row first, which gives the panel back its width. */
@media (max-width: 575.98px) {
    .catalogue-search {
        flex: 1 1 100%;
    }

    .catalogue-suggestions {
        width: auto;
    }

    /* Room is too tight for code, name and price on one line, so the code
       and price share the top line and the name runs full width beneath.
       order reshuffles them without touching the markup, which keeps the
       reading order - code, name, price - intact for a screen reader. */
    .catalogue-item {
        flex-wrap: wrap;
        row-gap: .15rem;
    }

    .catalogue-code {
        flex: 0 1 auto;
    }

    .catalogue-price {
        order: 1;
        margin-left: auto;
    }

    .catalogue-main {
        order: 2;
        flex: 1 1 100%;
    }
}
