/* Shared styling for the public portal pages.

   The portal is a UAVLAS page that happens to live on another host, so it
   borrows the marketing site's brand rather than inventing one: Montserrat,
   the navy of the logo, and the blue the site uses for anything clickable.
   The source of truth for those values is website/assets/scss/_variables.scss.

   The variable names and the `body.embedded` behaviour match
   frontend/web/contact_form.html, so a page embedded from uavlas.com looks
   like the contact form already does — change a colour in one, change both. */

:root {
    /* Brand. Navy is the logo's own colour and belongs to chrome and
       headings; blue is the interactive colour. --focus-color is a shade
       darker than the site's #3b82f6 because here it also sets link text,
       which needs 4.5:1 against white and does not get it at #3b82f6. */
    --brand-navy: #2d4255;
    --brand-blue: #3b82f6;
    --brand-blue-strong: #1e3a8a;

    --page-bg: #eef2f7;
    --card-bg: #ffffff;
    --surface-sunken: #f4f7fb;
    --text-color: #111827;
    --heading-color: #16263a;
    --muted-text: #5b6880;
    --border-color: #dde4ed;
    --hairline: rgba(45, 66, 85, .09);
    --focus-color: #2563eb;
    --focus-ring: rgba(37, 99, 235, .18);
    --focus-soft: rgba(37, 99, 235, .08);
    --success-color: #047857;
    --warning-color: #b45309;
    --error-color: #b91c1c;

    --font-brand: "Montserrat", -apple-system, BlinkMacSystemFont, "Segoe UI",
        Roboto, Helvetica, Arial, sans-serif;
    --font-ui: var(--font-brand);

    --card-radius: 16px;
    --control-radius: 10px;
    --card-shadow: 0 1px 2px rgba(16, 24, 40, .04),
        0 16px 40px -20px rgba(16, 24, 40, .28);
    --page-padding: 32px 16px 48px;
    --card-padding: 40px 40px 32px;
    --card-max-width: 560px;
    --bar-height: 60px;
}

*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

body {
    font-family: var(--font-ui);
    background: var(--page-bg);
    color: var(--text-color);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

/* The one piece of chrome that is not ours: a focus ring that is visible on
   every surface, including the navy bar and the tinted buttons. */
:focus-visible {
    outline: 2px solid var(--focus-color);
    outline-offset: 2px;
    border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        transition-duration: .01ms !important;
        animation-duration: .01ms !important;
    }
}

/* ---------------------------------------------------------------------------
   Site chrome.

   The portal used to be a bare form on a grey page, which read as somebody
   else's infrastructure. The bar and the footer say whose documents these are
   and put the rest of uavlas.com one click away — which matters here, because
   a visitor arriving from an emailed link has no other way back.
   --------------------------------------------------------------------------- */

.site-bar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: color-mix(in srgb, var(--card-bg) 86%, transparent);
    backdrop-filter: saturate(1.6) blur(12px);
    border-bottom: 1px solid var(--hairline);
}

.site-bar-inner {
    width: min(1200px, 100%);
    margin: 0 auto;
    min-height: var(--bar-height);
    padding: 8px 16px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

.site-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    text-decoration: none;
    color: inherit;
    min-width: 0;
}

.site-brand img {
    height: 22px;
    width: auto;
    display: block;
    flex: 0 0 auto;
}

.site-brand-text {
    display: flex;
    align-items: baseline;
    gap: 10px;
    min-width: 0;
}

.site-brand-text strong {
    font-family: var(--font-brand);
    font-weight: 600;
    font-size: 15px;
    letter-spacing: .14em;
    color: var(--heading-color);
}

/* The page's own name, hung off the wordmark by a hairline. Dropped on a
   phone, where the <h1> below is already saying it. */
.site-brand-text span {
    font-size: 13px;
    color: var(--muted-text);
    padding-left: 10px;
    border-left: 1px solid var(--border-color);
    white-space: nowrap;
}

.site-nav {
    display: flex;
    align-items: center;
    gap: 4px;
}

.site-nav a {
    font-size: 13.5px;
    font-weight: 500;
    color: var(--muted-text);
    text-decoration: none;
    padding: 6px 10px;
    border-radius: 8px;
    white-space: nowrap;
    transition: color .15s ease-out, background-color .15s ease-out;
}

.site-nav a:hover {
    color: var(--focus-color);
    background: var(--focus-soft);
}

.site-foot {
    margin-top: auto;
    border-top: 1px solid var(--hairline);
    background: var(--card-bg);
}

.site-foot-inner {
    width: min(1200px, 100%);
    margin: 0 auto;
    padding: 32px 16px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
}

.site-foot-brand img {
    /* Wide enough that the lockup's strapline is still readable; below about
       260px it turns into a grey smear. */
    width: 280px;
    max-width: 100%;
    height: auto;
    display: block;
}

.site-foot-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 20px;
}

.site-foot-nav a {
    font-size: 13.5px;
    font-weight: 500;
    color: var(--muted-text);
    text-decoration: none;
}

.site-foot-nav a:hover {
    color: var(--focus-color);
}

.site-foot-legal {
    flex: 1 0 100%;
    font-size: 12.5px;
    color: var(--muted-text);
    padding-top: 20px;
    border-top: 1px solid var(--hairline);
}

@media (max-width: 860px) {
    .site-brand-text span {
        display: none;
    }

    /* Only the two a visitor on a phone plausibly wants from here. */
    .site-nav a:nth-child(2),
    .site-nav a:nth-child(4) {
        display: none;
    }
}

@media (max-width: 520px) {
    .site-nav a:nth-child(1) {
        display: none;
    }

    .site-foot-inner {
        flex-direction: column;
        align-items: flex-start;
    }
}

/* ---------------------------------------------------------------------------
   The card
   --------------------------------------------------------------------------- */

.card {
    background: var(--card-bg);
    border: 1px solid var(--hairline);
    border-radius: var(--card-radius);
    box-shadow: var(--card-shadow);
    padding: var(--card-padding);
    width: min(var(--card-max-width), 100% - 32px);
    margin: 32px auto;
}

/* Embedded from uavlas.com: the host page supplies the chrome, so everything
   that would repeat it is dropped. */
body.embedded {
    background: transparent;
    min-height: auto;
    display: block;
}

body.embedded .site-bar,
body.embedded .site-foot {
    display: none;
}

body.embedded .card {
    width: 100%;
    max-width: none;
    margin: 0;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
    background: transparent;
}

h1 {
    font-family: var(--font-brand);
    font-size: clamp(22px, 1.1rem + 1vw, 28px);
    font-weight: 700;
    letter-spacing: -.01em;
    line-height: 1.2;
    color: var(--heading-color);
    margin-bottom: 8px;
}

.subtitle {
    color: var(--muted-text);
    font-size: 15px;
    margin-bottom: 24px;
}

label {
    display: block;
    font-size: 13.5px;
    font-weight: 600;
    margin-bottom: 6px;
}

.field {
    margin-bottom: 16px;
}

input,
select {
    width: 100%;
    font: inherit;
    padding: 11px 13px;
    border: 1px solid var(--border-color);
    border-radius: var(--control-radius);
    background: var(--card-bg);
    color: inherit;
    transition: border-color .15s ease-out, box-shadow .15s ease-out;
}

input:focus,
select:focus {
    outline: none;
    border-color: var(--focus-color);
    box-shadow: 0 0 0 3px var(--focus-ring);
}

button {
    font: inherit;
    font-weight: 600;
    width: 100%;
    padding: 12px 18px;
    border: 1px solid transparent;
    border-radius: var(--control-radius);
    background: var(--focus-color);
    color: #fff;
    cursor: pointer;
    transition: background-color .15s ease-out, border-color .15s ease-out,
        box-shadow .15s ease-out, transform .1s ease-out;
}

button:hover:not(:disabled) {
    background: color-mix(in srgb, var(--focus-color) 88%, #000);
    box-shadow: 0 6px 16px -8px var(--focus-color);
}

button:active:not(:disabled) {
    transform: translateY(1px);
}

button:disabled {
    opacity: .5;
    cursor: not-allowed;
}

.turnstile-holder {
    margin-bottom: 16px;
}

/* The six-digit sign-in code. Wide tracking and a tabular face, because
   reading a code back off the screen to check it against an email is the
   thing people actually do with this field. */
.code-input {
    font-size: 24px;
    font-weight: 600;
    letter-spacing: .32em;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.note {
    font-size: 13px;
    color: var(--muted-text);
    margin-top: 16px;
}

.note a {
    color: var(--focus-color);
    font-weight: 600;
}

.message {
    padding: 12px 14px;
    border-radius: var(--control-radius);
    font-size: 14px;
    margin-bottom: 16px;
    border: 1px solid transparent;
}

.message.success {
    background: #ecfdf5;
    border-color: rgba(4, 120, 87, .22);
    color: var(--success-color);
}

.message.error {
    background: #fef2f2;
    border-color: rgba(185, 28, 28, .22);
    color: var(--error-color);
}

/* A third tone, because "your download started but two files were dropped" is
   neither a success nor an error, and saying it in red reads as a failure. */
.message.warn {
    background: #fffbeb;
    border-color: rgba(180, 83, 9, .22);
    color: var(--warning-color);
}

.file-list {
    list-style: none;
    border: 1px solid var(--border-color);
    border-radius: var(--control-radius);
    overflow: hidden;
}

.file-list li {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--border-color);
}

.file-list li:last-child {
    border-bottom: 0;
}

.file-meta {
    flex: 1;
    min-width: 0;
}

.file-name {
    font-weight: 600;
    font-size: 14px;
    overflow-wrap: anywhere;
}

.file-sub {
    font-size: 12px;
    color: var(--muted-text);
}

.file-list a {
    flex-shrink: 0;
    text-decoration: none;
    font-size: 14px;
    font-weight: 600;
    color: var(--focus-color);
    padding: 7px 14px;
    border: 1px solid var(--border-color);
    border-radius: 8px;
    transition: background-color .15s ease-out, border-color .15s ease-out,
        color .15s ease-out;
}

.file-list a:hover {
    background: var(--focus-color);
    border-color: var(--focus-color);
    color: #fff;
}

.hidden {
    display: none !important;
}

@media (max-width: 480px) {
    :root {
        --card-padding: 24px 20px 20px;
    }

    .card {
        width: calc(100% - 24px);
        margin: 16px auto;
    }
}

/* ---------------------------------------------------------------------------
   Dark mode.

   Pure variable overrides, so every rule above and every page keeps working.
   contact_form.html shares the light palette but deliberately not this block:
   it is embedded in a uavlas.com page that is light whatever the visitor's
   system says, and a dark form inside a white page reads as a broken one.
   --------------------------------------------------------------------------- */

@media (prefers-color-scheme: dark) {
    :root {
        --page-bg: #0c1420;
        --card-bg: #141d2b;
        --surface-sunken: #0f1826;
        --text-color: #e6ecf5;
        --heading-color: #f2f6fb;
        --muted-text: #9aa8bd;
        --border-color: #27354a;
        --hairline: rgba(255, 255, 255, .08);
        --focus-color: #60a5fa;
        --focus-ring: rgba(96, 165, 250, .24);
        --focus-soft: rgba(96, 165, 250, .12);
        --success-color: #34d399;
        --warning-color: #fbbf24;
        --error-color: #f87171;
        --card-shadow: 0 1px 2px rgba(0, 0, 0, .3),
            0 16px 40px -20px rgba(0, 0, 0, .7);
    }

    input,
    select {
        background: var(--surface-sunken);
    }

    button:hover:not(:disabled) {
        background: color-mix(in srgb, var(--focus-color) 88%, #fff);
    }

    .message.success {
        background: rgba(52, 211, 153, .12);
        border-color: rgba(52, 211, 153, .24);
    }

    .message.error {
        background: rgba(248, 113, 113, .12);
        border-color: rgba(248, 113, 113, .24);
    }

    .message.warn {
        background: rgba(251, 191, 36, .12);
        border-color: rgba(251, 191, 36, .24);
    }
}

/* ---------------------------------------------------------------------------
   The library.
   --------------------------------------------------------------------------- */

:root {
    --card-max-width-wide: 1200px;
    --row-hover: rgba(45, 66, 85, .045);
}

@media (prefers-color-scheme: dark) {
    :root {
        --row-hover: rgba(255, 255, 255, .045);
    }
}

.card.wide {
    max-width: var(--card-max-width-wide);
    width: min(var(--card-max-width-wide), 100% - 32px);
}

/* The masthead: a tinted band rather than a heading floating on white, so the
   search and the selection button read as one control surface. */
.portal-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 24px;
    padding: 24px;
    border-radius: var(--card-radius);
    background:
        radial-gradient(120% 140% at 0% 0%, var(--focus-soft) 0%, transparent 60%),
        var(--surface-sunken);
    border: 1px solid var(--hairline);
}

.portal-head .subtitle {
    margin-bottom: 0;
    max-width: 62ch;
}

.portal-head-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Buttons default to full width for the gate's single-column form; inside the
   library they sit next to each other. */
.portal-head-actions button,
.toolbar button,
.row-actions button,
.drawer-actions button,
.doc-sub button,
.pager button {
    width: auto;
}

.button-quiet {
    background: transparent;
    color: var(--focus-color);
    border: 1px solid var(--border-color);
}

.button-quiet:hover:not(:disabled) {
    background: var(--focus-soft);
    border-color: var(--focus-color);
    box-shadow: none;
}

.toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
}

.toolbar .search {
    flex: 1 1 260px;
}

.toolbar select {
    width: auto;
    flex: 0 0 auto;
}

/* The type chips. A pill row rather than a select: on a phone it is the one
   filter worth a tap instead of two, and it wraps to as many lines as the
   catalogue needs. */
.type-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
}

.type-bar button {
    width: auto;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    font-size: 13px;
    border-radius: 999px;
    border: 1px solid var(--border-color);
    background: var(--surface-sunken);
    color: inherit;
}

.type-bar button:hover:not(:disabled) {
    background: var(--focus-soft);
    border-color: var(--focus-color);
    box-shadow: none;
}

/* The page background as the label colour, not white: --focus-color is a pale
   blue in dark mode, where white text on it reads at about 2.5:1. */
.type-bar button[aria-pressed="true"] {
    background: var(--focus-color);
    border-color: var(--focus-color);
    color: var(--page-bg);
}

.type-bar button[aria-pressed="true"] .facet-count {
    color: inherit;
    opacity: .75;
}

.portal-layout {
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: 24px;
    align-items: start;
}

.facet-disclosure summary {
    /* Hidden at desktop width, where the rail is always open. */
    display: none;
}

@media (max-width: 860px) {
    .portal-layout {
        grid-template-columns: 1fr;
    }

    .facet-disclosure {
        border: 1px solid var(--border-color);
        border-radius: var(--control-radius);
        margin-bottom: 16px;
    }

    .facet-disclosure summary {
        display: list-item;
        padding: 12px 14px;
        font-weight: 600;
        font-size: 14px;
        cursor: pointer;
    }

    .facet-disclosure[open] summary {
        border-bottom: 1px solid var(--border-color);
    }

    .facets {
        padding: 12px 14px;
    }

    .facet-group {
        margin-bottom: 16px;
    }

    .facet-group:last-child {
        margin-bottom: 0;
    }
}

.facet-group {
    margin-bottom: 24px;
}

.facet-group h2,
h2.facet-group {
    font-size: 11.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--muted-text);
    margin-bottom: 8px;
}

/* The same label, used as a section heading in the detail panel rather than as
   the head of a filter group. */
h2.facet-group {
    margin-top: 24px;
}

.facet-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.facet-list button {
    width: 100%;
    text-align: left;
    background: transparent;
    color: inherit;
    font-weight: 500;
    font-size: 14px;
    padding: 7px 10px;
    border-radius: 8px;
    display: flex;
    justify-content: space-between;
    gap: 8px;
}

.facet-list button:hover:not(:disabled) {
    background: var(--row-hover);
    box-shadow: none;
}

.facet-list button[aria-pressed="true"] {
    background: var(--focus-color);
    color: #fff;
}

.facet-count {
    color: var(--muted-text);
    font-variant-numeric: tabular-nums;
}

.facet-list button[aria-pressed="true"] .facet-count {
    color: inherit;
    opacity: .8;
}

/* Rows. A list, not a table: each row is a label wrapping its checkbox, so the
   whole row is one click target and screen readers announce the title. */
.doc-list {
    list-style: none;
    border: 1px solid var(--border-color);
    border-radius: var(--control-radius);
    overflow: hidden;
}

.doc-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--border-color);
    transition: background-color .12s ease-out;
}

.doc-row:last-child {
    border-bottom: 0;
}

.doc-row:hover {
    background: var(--row-hover);
}

/* A ticked row should be visible without hunting for the checkbox. */
.doc-row:has(input:checked) {
    background: var(--focus-soft);
}

.doc-row.is-locked {
    opacity: .72;
}

.doc-pick {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: 1;
    min-width: 0;
    cursor: pointer;
}

.doc-row.is-locked .doc-pick {
    cursor: default;
}

.doc-pick input[type="checkbox"] {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
    accent-color: var(--focus-color);
}

.doc-main {
    min-width: 0;
}

.doc-title {
    font-weight: 600;
    font-size: 14px;
    overflow-wrap: anywhere;
}

.doc-sub {
    font-size: 12px;
    color: var(--muted-text);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 8px;
    margin-top: 3px;
}

.row-actions {
    flex: 0 0 auto;
    display: flex;
    gap: 8px;
}

/* A phone is 360px wide, and Details in its own column left the title about
   two words a line. Below this width the row wraps and the button sits under
   the text, lined up with the title rather than the checkbox. */
@media (max-width: 520px) {
    .doc-row {
        flex-wrap: wrap;
        row-gap: 8px;
    }

    .doc-pick,
    .row-actions {
        flex: 1 1 100%;
    }

    .row-actions {
        /* The 18px checkbox plus the 12px gap beside it. */
        padding-left: 30px;
    }

    /* A locked row has no checkbox to line up with. */
    .doc-row.is-locked .row-actions {
        padding-left: 0;
    }
}

.badge {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .02em;
    padding: 2px 8px;
    border-radius: 999px;
    border: 1px solid var(--border-color);
    background: var(--surface-sunken);
    color: var(--muted-text);
    white-space: nowrap;
}

.badge.confidential {
    border-color: color-mix(in srgb, var(--error-color) 35%, transparent);
    background: color-mix(in srgb, var(--error-color) 10%, transparent);
    color: var(--error-color);
}

.badge.locked {
    border-color: var(--border-color);
}

/* Not the red of .badge.confidential: this is an invitation, not a warning.
   The row is here because somebody decided to advertise it. */
.badge.register {
    border-color: color-mix(in srgb, var(--focus-color) 35%, transparent);
    background: color-mix(in srgb, var(--focus-color) 10%, transparent);
    color: var(--focus-color);
}

.pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-top: 16px;
    font-size: 14px;
    color: var(--muted-text);
}

.empty {
    border: 1px dashed var(--border-color);
    border-radius: var(--control-radius);
    padding: 32px 16px;
    text-align: center;
    color: var(--muted-text);
    background: var(--surface-sunken);
}

/* The basket. A <dialog> so Escape, focus trapping and the backdrop come from
   the platform rather than from us. */
dialog {
    border: 1px solid var(--hairline);
    border-radius: var(--card-radius);
    background: var(--card-bg);
    color: var(--text-color);
    box-shadow: var(--card-shadow);
    padding: 24px;
    width: min(560px, calc(100vw - 32px));
    max-height: calc(100vh - 64px);
    overflow: auto;
    margin: auto;
}

dialog.detail {
    width: min(720px, calc(100vw - 32px));
}

dialog::backdrop {
    background: rgba(12, 20, 32, .55);
    backdrop-filter: blur(2px);
}

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

.drawer-head h2 {
    font-family: var(--font-brand);
    font-size: 19px;
    font-weight: 700;
    color: var(--heading-color);
}

.icon-button {
    width: auto;
    background: transparent;
    color: var(--muted-text);
    font-size: 20px;
    line-height: 1;
    padding: 4px 8px;
}

.icon-button:hover:not(:disabled) {
    background: var(--row-hover);
    color: var(--text-color);
    box-shadow: none;
}

/* The basket's form opens straight under the size meter; without this they
   read as one run-on block. */
dialog form {
    margin-top: 16px;
}

.drawer-actions {
    display: flex;
    gap: 8px;
    margin-top: 16px;
}

.drawer-actions button:first-child {
    flex: 1;
}

/* "Copy link": the selection's /d/ link, to send on rather than download. */
.share-panel {
    margin-top: 16px;
    padding: 16px;
    border: 1px solid var(--border-color);
    border-radius: var(--control-radius);
    background: var(--surface-sunken);
}

.share-row {
    display: flex;
    gap: 8px;
}

.share-row input {
    flex: 1;
    min-width: 0;
    font-size: 13px;
}

.share-row button {
    width: auto;
}

.share-terms {
    font-size: 13px;
    color: var(--muted-text);
    margin: 8px 0 16px;
}

.share-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* Links dressed as the quiet button, since the base button styles are on the
   element and a mailto: or wa.me target has to be an anchor. */
.share-actions a.button-quiet,
.share-actions button {
    display: inline-flex;
    align-items: center;
    width: auto;
    padding: 8px 16px;
    border-radius: var(--control-radius);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
}

.meter {
    height: 6px;
    border-radius: 999px;
    background: var(--border-color);
    overflow: hidden;
    margin: 8px 0 4px;
}

.meter span {
    display: block;
    height: 100%;
    background: var(--focus-color);
    transition: width .15s ease-out;
}

.meter.over span {
    background: var(--error-color);
}

.basket-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 40vh;
    overflow: auto;
}

.basket-list li {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    padding: 6px 0;
    border-bottom: 1px solid var(--border-color);
}

.basket-list li:last-child {
    border-bottom: 0;
}

.basket-list .basket-name {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
}

.basket-list .icon-button {
    font-size: 16px;
}

.spec {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px 16px;
    font-size: 14px;
    margin-bottom: 24px;
}

.spec dt {
    color: var(--muted-text);
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   The product gallery.

   The library's front page. A customer arrives knowing their hardware, not our
   document numbers, so the first thing on screen is the hardware — a picture,
   what state it is in, and what paperwork exists for it. Expanding a product
   is one click and needs no account: a visitor sees everything that is there
   before being asked for anything.
   --------------------------------------------------------------------------- */

.product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
    gap: 16px;
    /* Each card is its own height. Stretching looks tidier until one card is
       opened, at which point every card beside it grows to match and the row
       becomes mostly empty space. */
    align-items: start;
}

/* An opened product takes the full width of the grid. A document list squeezed
   into one column of three wraps every row onto four lines; given the width it
   reads like a table. */
.product-card.is-open {
    grid-column: 1 / -1;
}

.product-card.is-open {
    border-color: var(--focus-color);
    box-shadow: var(--card-shadow);
}

.product-card.is-open .product-head {
    border-bottom: 1px solid var(--border-color);
}

.product-card {
    border: 1px solid var(--border-color);
    border-radius: var(--card-radius);
    overflow: hidden;
    background: var(--card-bg);
    transition: border-color .15s ease-out, box-shadow .15s ease-out,
        transform .15s ease-out;
    display: flex;
    flex-direction: column;
}

.product-card:hover {
    border-color: var(--focus-color);
    box-shadow: 0 10px 28px -16px rgba(16, 24, 40, .45);
}

.product-card:not(.is-open):hover {
    transform: translateY(-2px);
}

/* The whole header is the control, so the hit target is the card and not a
   caret somebody has to aim at. */
.product-head {
    width: 100%;
    flex: 1;
    display: flex;
    align-items: flex-start;
    gap: 16px;
    padding: 16px;
    background: transparent;
    color: inherit;
    text-align: left;
    font: inherit;
    border: 0;
    border-radius: 0;
    cursor: pointer;
}

.product-head:hover:not(:disabled) {
    background: var(--row-hover);
    box-shadow: none;
}

.product-head:active:not(:disabled) {
    transform: none;
}

.product-media {
    flex: 0 0 auto;
    width: 88px;
    height: 88px;
    border-radius: 12px;
    object-fit: contain;
    background: var(--surface-sunken);
    border: 1px solid var(--hairline);
    padding: 6px;
}

/* Product icons from UAVLAS/docs assets/icons/products/ are white on
   transparent, so the tile behind them carries the colour, in both themes. */
img.product-media {
    background: var(--brand-navy);
    border-color: transparent;
    padding: 10px;
}

.product-media.placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-brand);
    font-size: 22px;
    font-weight: 700;
    color: var(--muted-text);
    letter-spacing: -.03em;
}

.product-body {
    flex: 1;
    min-width: 0;
}

.product-body h2 {
    font-family: var(--font-brand);
    font-size: 17px;
    font-weight: 700;
    line-height: 1.25;
    color: var(--heading-color);
    overflow-wrap: anywhere;
}

.product-meta,
.product-stats {
    font-size: 13px;
    color: var(--muted-text);
    margin-top: 2px;
}

.product-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin-top: 8px;
}

.product-toggle {
    flex: 0 0 auto;
    align-self: flex-start;
    white-space: nowrap;
    color: var(--focus-color);
    font-size: 13px;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 6px;
}

.product-toggle::after {
    content: "\25BE";
    transition: transform .15s ease-out;
}

.product-head[aria-expanded="true"] .product-toggle::after {
    transform: rotate(180deg);
}

.product-docs {
    padding: 16px;
    background: var(--surface-sunken);
}

.doc-group+.doc-group {
    margin-top: 16px;
}

.doc-group-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 8px;
}

.doc-group-head h3 {
    font-size: 11.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--muted-text);
}

.product-docs .doc-list {
    background: var(--card-bg);
}

.link-button {
    width: auto;
    background: transparent;
    color: var(--focus-color);
    font-size: 13px;
    font-weight: 600;
    padding: 2px 6px;
    border-radius: 6px;
}

.link-button:hover:not(:disabled) {
    background: var(--focus-soft);
    box-shadow: none;
}

.badge.status-production {
    border-color: color-mix(in srgb, var(--success-color) 35%, transparent);
    background: color-mix(in srgb, var(--success-color) 10%, transparent);
    color: var(--success-color);
}

.badge.status-preproduction,
.badge.status-development {
    border-color: color-mix(in srgb, var(--warning-color) 38%, transparent);
    background: color-mix(in srgb, var(--warning-color) 12%, transparent);
    color: var(--warning-color);
}

.visitor-badge {
    font-size: 13px;
    color: var(--muted-text);
    text-align: right;
    line-height: 1.3;
}

.visitor-badge strong {
    color: var(--text-color);
    display: block;
    font-size: 13px;
}

@media (max-width: 480px) {
    .product-grid {
        grid-template-columns: 1fr;
    }

    .product-media {
        width: 64px;
        height: 64px;
    }

    .portal-head {
        padding: 20px 16px;
    }
}
/* ---------------------------------------------------------------------------
   One file from its row, for a signed-in account.
   --------------------------------------------------------------------------- */

.file-button {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    white-space: nowrap;
}

.file-button svg {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
}

/* ---------------------------------------------------------------------------
   The selection bar: pinned to the bottom while anything is ticked, so the
   way to the download is on screen wherever the visitor has scrolled to.
   --------------------------------------------------------------------------- */

.selection-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 20;
    padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
    background: var(--card-bg);
    border-top: 1px solid var(--border-color);
    box-shadow: 0 -16px 32px -24px rgba(16, 24, 40, .45);
}

.selection-bar.hidden {
    display: none;
}

.selection-bar-inner {
    max-width: var(--card-max-width-wide);
    margin: 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
}

.selection-summary {
    font-size: 14px;
    color: var(--muted-text);
}

.selection-summary strong {
    color: var(--text-color);
}

.selection-actions {
    display: flex;
    gap: 8px;
}

.selection-actions button {
    width: auto;
}

/* Room under the footer for the bar, so nothing ends up behind it. */
body.has-selection {
    padding-bottom: 80px;
}

@media (max-width: 520px) {
    .selection-bar-inner {
        flex-wrap: wrap;
        gap: 8px;
    }

    .selection-actions {
        flex: 1 1 100%;
    }

    .selection-actions button:last-child {
        flex: 1;
    }

    body.has-selection {
        padding-bottom: 128px;
    }
}

/* ---------------------------------------------------------------------------
   Phone widths.

   A grid track is never narrower than its content's minimum, so one row whose
   buttons could not shrink widened the whole page past the screen. The
   results column may shrink; what is inside it wraps instead.
   --------------------------------------------------------------------------- */

.portal-layout > * {
    min-width: 0;
}

@media (max-width: 520px) {
    .row-actions {
        flex-wrap: wrap;
    }

    .row-actions button {
        flex: 1 1 auto;
        justify-content: center;
        padding: 8px 16px;
    }

    /* Signed in, the address, Sign out and Download did not fit one line and
       pushed Download off the card. The address takes its own line and the
       two buttons share the one under it. */
    .portal-head-actions {
        flex-wrap: wrap;
        width: 100%;
    }

    .portal-head-actions .visitor-badge {
        flex: 1 1 100%;
        text-align: left;
    }

    .portal-head-actions button {
        flex: 1 1 auto;
    }
}

/* At the narrowest phones the wordmark and two nav links overlap; Contact
   stays, and Documentation is still in the footer. */
@media (max-width: 360px) {
    .site-nav a:nth-child(3) {
        display: none;
    }
}
