/*
 * AQJ Order Portal — stylesheet.
 *
 * Written fresh in this repository from the values in
 * docs/spec/AQJ_PORTAL_DESIGN_SPECIFICATION.md. Master Brief §1.2 permits AQJ's brand
 * identity to be reused and requires stylesheets to be implemented fresh; no previous
 * stylesheet was sought, requested, opened or copied.
 *
 * Nothing here loads from another origin. Design Specification §9 forbids any third-party
 * font, icon or CSS CDN, and the Content-Security-Policy in src/Http/SecurityHeaders.php
 * enforces the same rule independently.
 *
 * Phase D implements the tokens, the shell, the header, the left rail and the error surfaces.
 * Components arrive with the surfaces that need them, in Phases F to H.
 */

/* ------------------------------------------------------------------- 0. Brand fonts */
/*
 * Source Sans 3, SELF-HOSTED. Design Specification §9 forbids any third-party font, icon or
 * CSS CDN, and the Content-Security-Policy enforces the same rule independently — so these
 * two files are served from this origin and there is no fallback that could reach out.
 *
 * Delivered by Ahmad and verified before placement, on all three properties A47 records:
 *
 *   SourceSans3-Regular.woff2   138,912 bytes  1136b494f8dc6731bc31ef587eb060aa6a5ef5d423d822a59a1d98e0b7f832a5
 *   SourceSans3-Semibold.woff2  138,236 bytes  5464eac6222281bea6810148fa1c52653df4f121181efe98c1010636aac172ec
 *
 * `font-display: swap` deliberately. The system stack below renders immediately and is
 * replaced when the file arrives; `block` would leave a customer looking at invisible text
 * on a slow connection, which is worse than a moment of the fallback face.
 *
 * Two weights only, because two are what AQJ supplied. Nothing here asks for an italic or a
 * weight that does not exist — a browser asked for a missing weight synthesises one, and a
 * synthesised bold is not AQJ's typeface.
 */
@font-face {
    font-family: "Source Sans 3";
    src: url("fonts/SourceSans3-Regular.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Source Sans 3";
    src: url("fonts/SourceSans3-Semibold.woff2") format("woff2");
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}
/* ---------------------------------------------------------------- 1. Colour tokens */
/* Design Specification §1. The names are advisory; the values are not. */
:root {
    --navy: #0c2942;
    --navy-deep: #071d2f;
    --blue: #087fba;
    --blue-strong: #006fae;
    --blue-hover: #005f95;
    --slate: #526473;      /* corrected value: an earlier lighter grey failed AA at 2.797:1 */
    --mist: #eef2f4;
    --marble: #f4f5f4;
    --white: #fff;
    --ink: #162a39;
    --line: #d8e0e4;
    --line-strong: #9fb2bf;
    --gold: #b89438;
    --focus: #edb83f;
    --success: #155f35;
    --warning: #8a5a00;
    --error: #a32424;
    --info-tint: #eef6fa;
    --success-tint: #eaf7ef;
    --warning-tint: #fff6df;
    --error-tint: #fff0f0;

    /* §2 spacing scale. Use scale steps only; no arbitrary values. */
    --s4: 4px;   --s8: 8px;   --s12: 12px; --s16: 16px;
    --s20: 20px; --s24: 24px; --s32: 32px; --s40: 40px;
    --s48: 48px; --s64: 64px; --s80: 80px;

    /* §3 shape, depth, motion */
    --radius: 4px;
    --radius-pill: 999px;
    --shadow-card: 0 14px 30px rgba(12, 41, 66, .08);
    --shadow-elevated: 0 20px 50px rgba(12, 41, 66, .08);
    --transition: 180ms ease;
}

/* ------------------------------------------------------------------ 2. Typography */
/*
 * §4. Source Sans 3 is self-hosted with font-display: swap once Ahmad supplies the .woff2
 * files at Phase F (§8). Until then the system stack stands in — deliberately, rather than
 * substituting a different webfont, which §8 forbids.
 *
 * The serif/sans pairing is the portal's signature and does not change: Georgia headings,
 * Source Sans 3 body.
 */
:root {
    --font-body: "Source Sans 3", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, sans-serif;
    --font-heading: Georgia, "Times New Roman", serif;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--marble);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 15px;
    line-height: 1.5;
}

h1, h2, h3 {
    font-family: var(--font-heading);
    font-weight: 400;
    color: var(--navy);
    margin: 0 0 var(--s16);
}

h1 { font-size: 46px; line-height: 1.1; }
h2 { font-size: 30px; line-height: 1.15; }
h3 { font-size: 21px; line-height: 1.2; }

p { margin: 0 0 var(--s16); }

/* WCAG AA, and this was found by axe in Phase I rather than by reading the palette.
   --blue #087fba is 4.416:1 on white. AA needs 4.5:1 for normal text, so a plain body-size
   link on white FAILED — by 0.084. --blue-strong #006fae is 5.405:1 and was already in the
   approved palette, so no new colour is introduced here.
   The precedent is --slate, whose earlier lighter value failed at 2.797:1 and was corrected
   the same way. --blue is unchanged and still used for the border at .filter-chip. */
a { color: var(--blue-strong); }

.muted { color: var(--slate); font-size: 13px; line-height: 1.45; }

/* §9: the focus outline is never removed or reduced, on any interactive element. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
    outline: 3px solid var(--focus);
    outline-offset: 3px;
}

/* §3: respect prefers-reduced-motion. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        transition: none !important;
        animation: none !important;
        scroll-behavior: auto !important;
    }
}

/* ---------------------------------------------------------------- 3. Page structure */
/* §5. Centred shell, maximum width 1440px. */
.shell {
    width: 100%;
    max-width: 1440px;
    margin: 0 auto;
    padding: 0 var(--s24);
}

/* §5.1 navy utility strip, ABOVE the header. */
.utility {
    background: var(--navy);
    color: var(--white);
    font-size: 13px;
}

.utility .shell {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s16);
    min-height: 36px;
}

.utility a { color: var(--white); }

/*
 * §5.2 main header. WHITE background, navy text, one `line` bottom border, no shadow, no
 * radius. This is deliberate and is called out as a non-negotiable in §9: the navy appears
 * only in the utility strip above.
 */
.masthead {
    background: var(--white);
    border-bottom: 1px solid var(--line);
    box-shadow: none;
    border-radius: 0;
}

.masthead .shell {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s24);
    min-height: 96px;
}

.brand { display: flex; align-items: center; gap: var(--s16); }

/*
 * The AQJ logo. §8's 126x70 box, with `object-fit: contain` so the image is fitted rather
 * than stretched: the artwork is 1000x470 (ratio 2.13) and the box is 1.80, so it letterboxes
 * inside the box instead of distorting.
 *
 * `max-width: 100%` and the intrinsic-size attributes on the element stop it overflowing a
 * narrow header and reserve its space before it loads, so the masthead does not jump.
 *
 * NO VECTOR MASTER EXISTS — 1000x470 is the largest asset AQJ holds. No surface may be
 * designed that needs it larger, and it must never be upscaled to fill one.
 */
/*
 * The AQJ logo lockup — Amendment 20, A81. The globe above the words "AQJ Trading Limited",
 * and the ONLY brand element in the header: the duplicated text beside it is removed.
 *
 * ## The size is measured, not chosen
 *
 * A81 asks for the wordmark to be legible rather than merely present, judged against the
 * rendered result and with no number specified. This build cannot look at a screen, so the
 * artwork was measured instead — the alpha channel decoded and scanned row by row:
 *
 *   globe        rows   0-295   (296 px, 31% of the width, centred)
 *   wordmark     rows 374-452   (79 px tall, spanning the FULL 1000 px width)
 *   fine detail  rows 454-469   (16 px, decorative)
 *
 * The wordmark is 79/470 of the image height, so it renders at 0.168 x the logo height:
 *
 *   80 px logo -> 13.4 px wordmark    <- chosen; 8 px clear in the 96 px header
 *   68 px logo -> 11.4 px             <- 900 px breakpoint, 84 px header
 *   60 px logo -> 10.1 px             <- 620 px breakpoint, 72 px header
 *
 * The previous 126x70 box rendered the wordmark at 9.9 px, which is the fault A61 recorded on
 * the Catalyst sign-in page: present, and not readable.
 *
 * **A81 says the wordmark occupies "roughly the lower third".** Measured, it is 20.4% — the
 * globe takes 63% and a clear 78 px band separates them. The ruling is unaffected; the figure
 * is corrected here because the sizing arithmetic depends on it.
 *
 * Aspect ratio is held at the artwork's own 2.128 at every breakpoint, so `object-fit: contain`
 * has nothing to letterbox. NO VECTOR MASTER EXISTS — 1000x470 is the largest AQJ holds, so no
 * surface may need it larger and it must never be upscaled to fill one.
 */
.brand-logo {
    width: 170px;
    height: 80px;
    max-width: 100%;
    object-fit: contain;
}


/* §5.2: header actions hold only the drawer toggle and sign-out. */
.masthead-actions { display: flex; align-items: center; gap: var(--s16); }

.masthead-actions a,
.masthead-actions button {
    color: var(--navy);
    font-size: 14px;
    font-weight: 600;
    background: transparent;
    border: 0;
    font-family: inherit;
    cursor: pointer;
    text-decoration: none;
}

.masthead-actions a:hover,
.masthead-actions button:hover { text-decoration: underline; }

/* §6: the drawer toggle appears only at ≤900px. */
.drawer-toggle {
    display: none;
    min-width: 44px;
    min-height: 44px;
    background: var(--white);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    color: var(--navy);
    font-weight: 600;
}

.drawer-toggle:hover { background: var(--mist); }

/* --------------------------------------------------------- 4. Layout and left rail */
/* §5.3: two-column flex, 32px gap, 24px top padding. Rail left, main right. */
.layout {
    display: flex;
    gap: var(--s32);
    padding-top: var(--s24);
    padding-bottom: var(--s48);
}

.layout > main {
    flex: 1 1 auto;
    min-width: 0; /* §5.3: main content must be allowed to shrink */
}

/*
 * §5.3: the rail is THE primary navigation. 250px, sticky at 16px, white, 1px solid line,
 * radius 4, card shadow, 20px padding, grid with 20px between groups.
 */
.rail {
    flex: 0 0 250px;
    width: 250px;
    align-self: flex-start;
    position: sticky;
    top: var(--s16);
    background: var(--white);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    padding: var(--s20);
    display: grid;
    gap: var(--s20);
}

.rail-group-label {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .16em;
    color: var(--slate);
    margin-bottom: var(--s4);
}

.rail ul { list-style: none; margin: 0; padding: 0; }

.rail a {
    display: flex;
    align-items: center;
    min-height: 44px;              /* §5.3 touch target */
    padding: var(--s8) var(--s12);
    border-radius: var(--radius);
    color: var(--ink);
    text-decoration: none;
    transition: background var(--transition), color var(--transition);
}

.rail a:hover { background: var(--mist); color: var(--navy); }

.rail a[aria-current="page"] {
    background: var(--info-tint);
    color: var(--navy);
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--blue-strong);
}

/* ------------------------------------------------------------------ 5. Components */
.card {
    background: var(--white);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-card);
    padding: var(--s24);
}

.notice {
    border: 1px solid var(--blue);
    border-radius: var(--radius);
    padding: var(--s16) var(--s20);
    margin-bottom: var(--s24);
    background: var(--info-tint);
}

.notice-success { background: var(--success-tint); border-color: var(--success); color: var(--success); }
.notice-warning { background: var(--warning-tint); border-color: var(--warning); color: var(--warning); }
.notice-error   { background: var(--error-tint);   border-color: var(--error);   color: var(--error); }

.chip {
    display: inline-block;
    border-radius: var(--radius-pill);
    padding: var(--s4) var(--s12);
    font-size: 12px;
    font-weight: 600;
    background: var(--mist);
    color: var(--navy);
}

.chip-secure { background: var(--success-tint); color: var(--success); }

.button {
    display: inline-block;
    background: var(--blue-strong);
    color: var(--white);
    border: 0;
    border-radius: var(--radius);
    padding: var(--s12) var(--s24);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    transition: background var(--transition);
}

.button:hover { background: var(--blue-hover); }

.field { display: grid; gap: var(--s4); margin-bottom: var(--s16); }

.field input {
    font: inherit;
    padding: var(--s12);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--white);
    color: var(--ink);
}

/*
 * Visually hidden but PRESENT in the accessibility tree.
 * Design Specification §6 requires the phone table header row to be offscreen-clipped and
 * explicitly forbids display:none, which would remove the column semantics screen readers
 * rely on. The same utility is used for skip links and field hints.
 */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    background: var(--white);
    color: var(--navy);
    padding: var(--s12) var(--s16);
    z-index: 10;
}

.skip-link:focus { left: var(--s16); }

/* ------------------------------------------- 5b. Ordering surfaces (Phase F) */
/*
 * Design Specification §7's catalogue, line items, checkout and document sheet, plus the
 * table rules §6 requires. Written from the specification's values; no previous stylesheet
 * was consulted or requested (§1.2, CLAUDE.md §11).
 */

/* §7: tables are white cards with the standard border and radius. */
table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: var(--s24);
}

thead th {
    text-align: left;
    font-family: var(--font-body);
    font-size: 13px;
    font-weight: 600;
    color: var(--slate);
    padding: var(--s12) var(--s16);
    border-bottom: 1px solid var(--line);
}

tbody td {
    padding: var(--s12) var(--s16);
    border-bottom: 1px solid var(--line);
    vertical-align: top;
}

tbody tr:last-child td { border-bottom: 0; }

/* §7: a two-column product grid on desktop. */
.product-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s24);
    margin-bottom: var(--s24);
}

.product-name { font-size: 22px; margin: 0 0 var(--s4); }

.product-name a { color: var(--navy); }

.product-unit { color: var(--slate); font-size: 13px; margin: 0 0 var(--s8); }

.product-price {
    font-family: var(--font-heading);
    font-size: 24px;
    color: var(--navy);
    margin: 0 0 var(--s8);
}

.product-available { color: var(--success); font-size: 13px; font-weight: 600; margin: 0 0 var(--s16); }

.product-unavailable { color: var(--warning); font-size: 13px; font-weight: 600; margin: 0 0 var(--s16); }

.add-to-basket { display: flex; align-items: center; gap: var(--s8); }

.add-to-basket input[type="number"] {
    width: 84px;
    padding: var(--s8) var(--s12);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--white);
    color: var(--ink);
    font: inherit;
}

/*
 * §7 line items: description and quantity left, rate and total right.
 */
.basket-line {
    display: grid;
    grid-template-columns: minmax(0, 2fr) auto auto auto auto;
    align-items: center;
    gap: var(--s16);
    margin-bottom: var(--s16);
}

.basket-description { display: grid; gap: var(--s4); }

.basket-quantity { display: flex; align-items: center; gap: var(--s8); }

.basket-quantity input[type="number"] {
    width: 72px;
    padding: var(--s8);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--white);
    color: var(--ink);
    font: inherit;
    text-align: center;
}

.basket-rate, .basket-total { font-family: var(--font-heading); font-size: 18px; text-align: right; }

.basket-blocked { font-family: var(--font-body); font-size: 13px; color: var(--warning); font-weight: 600; }

.basket-summary p, .checkout-summary p {
    display: flex;
    justify-content: space-between;
    gap: var(--s16);
    margin: 0 0 var(--s8);
}

.checkout-total, .sheet-total {
    border-top: 1px solid var(--line);
    padding-top: var(--s12);
    font-family: var(--font-heading);
    font-size: 20px;
}

/* A quieter action for in-line controls, so Update and Remove do not compete with Place order. */
.button-quiet {
    background: var(--white);
    color: var(--navy);
    border: 1px solid var(--line-strong);
}

.button-quiet:hover { background: var(--mist); }

/* §7 fulfilment options. */
.fulfilment-options { display: grid; gap: var(--s16); margin-bottom: var(--s24); }

.fulfilment-option { display: flex; align-items: flex-start; gap: var(--s12); margin: 0; }

.fulfilment-option label { display: grid; gap: var(--s4); cursor: pointer; }

/* §7 checkout: main column with a sticky summary aside on desktop. */
.checkout-summary {
    position: sticky;
    top: var(--s16);
    display: grid;
    gap: var(--s8);
}

/* §7 document sheets: white, 32px padding, elevated shadow. */
.sheet {
    background: var(--white);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-elevated);
    padding: var(--s32);
    margin-bottom: var(--s24);
}

.sheet-head {
    display: flex;
    justify-content: space-between;
    gap: var(--s24);
    border-bottom: 2px solid var(--navy);
    padding-bottom: var(--s16);
    margin-bottom: var(--s24);
}

.sheet-issuer { display: grid; gap: var(--s4); }

.sheet-identity { text-align: right; display: grid; gap: var(--s4); }

.sheet-reference { font-family: var(--font-heading); font-size: 20px; color: var(--navy); }

.sheet-totals { display: grid; gap: var(--s8); margin-top: var(--s16); }

.sheet-totals p { display: flex; justify-content: space-between; gap: var(--s16); margin: 0; }

/*
 * A238.1's Linked row and A238.3's related invoice or order.
 *
 * Separated from the totals by a rule rather than sitting inside them: a document number
 * in a column of money reads as an amount, and the one thing this block must not do is
 * look like a figure the customer owes.
 */
.sheet-related {
    display: grid;
    gap: var(--s8);
    margin-top: var(--s16);
    padding-top: var(--s16);
    border-top: 1px solid var(--line);
}

.sheet-related p { display: flex; justify-content: space-between; gap: var(--s16); margin: 0; }

.sheet-related span { color: var(--slate); }

/* --------------------------------------------------------------- 6. Responsive */
/*
 * §6, four breakpoints: 1120px, 900px, 620px, 420px.
 *
 * IMPLEMENTATION WARNING, from a real defect recorded in §6: each header height belongs to
 * EXACTLY ONE breakpoint block. 84px was once written into the 620px block as well,
 * contradicting that tier's 72px rule and producing a 135px header on a 390px screen.
 * A regression test asserts this (tests/Unit/Http/StylesheetTest.php).
 */

@media (max-width: 1120px) {
    .layout { gap: var(--s24); }
}

@media (max-width: 900px) {
    .masthead .shell { min-height: 84px; }

    .brand-logo { width: 145px; height: 68px; }

    .masthead-actions { gap: var(--s8); justify-content: flex-start; }

    .drawer-toggle { display: inline-flex; align-items: center; justify-content: center; }

    h1 { font-size: 38px; }

    /*
     * §6, progressive enhancement, and it matters: the rail leaves the sidebar and returns
     * to normal flow ABOVE the main content. With scripting unavailable it simply stays
     * here and every destination remains reachable.
     */
    .layout { flex-direction: column; gap: var(--s20); }

    .rail {
        position: static;
        flex: 1 1 auto;
        width: 100%;
    }

    /*
     * `is-drawer` is added BY JAVASCRIPT. Collapsing is therefore an enhancement on top of a
     * working page, never a prerequisite for one. Inverting this — hiding the rail in CSS and
     * revealing it with script — would make the portal unnavigable without JavaScript, which
     * §6 explicitly forbids.
     */
    .rail.is-drawer[hidden] { display: none; }

    /* §7: the catalogue grid and the checkout summary become single column. */
    .product-grid { grid-template-columns: minmax(0, 1fr); }

    .checkout-summary { position: static; }

    /* §7 line items reflow: rate and quantity left, total and remove right. */
    .basket-line { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
}

@media (max-width: 620px) {
    body { font-size: 16px; }

    .shell { width: calc(100% - 30px); padding: 0; }

    .masthead .shell { min-height: 72px; padding: var(--s16) 0; }

    .brand-logo { width: 128px; height: 60px; }

    .utility { font-size: 12px; }

    .masthead-actions { flex-wrap: nowrap; }

    .rail { padding: var(--s16); }

    .layout { padding-top: var(--s16); }

    /* §6: every actionable control spans the full width. */
    .button, .field input { width: 100%; }

    /* §6: the add-to-basket form stacks vertically with full-width inputs. */
    .add-to-basket { flex-direction: column; align-items: stretch; }

    .add-to-basket input[type="number"] { width: 100%; }

    /* §6: the quantity stepper becomes a full-width 52px / flexible / 52px grid. */
    .basket-quantity {
        display: grid;
        grid-template-columns: 52px minmax(0, 1fr) 52px;
        width: 100%;
    }

    .basket-quantity input[type="number"] { width: 100%; grid-column: 1 / -1; }

    /* §6: basket lines collapse to one column, 20px vertical padding. */
    .basket-line {
        grid-template-columns: minmax(0, 1fr);
        padding-top: var(--s20);
        padding-bottom: var(--s20);
    }

    .basket-rate, .basket-total { text-align: left; }

    /* §7: the sheet head stacks vertically, left-aligned, padding drops to 20px. */
    .sheet { padding: var(--s20); }

    .sheet-head { flex-direction: column; }

    .sheet-identity { text-align: left; }

    /*
     * §6: TABLES BECOME CARDS.
     *
     * The header row is offscreen-clipped and explicitly NOT display:none, so screen
     * readers keep the column semantics. Each cell is a two-column grid whose label
     * comes from its data-label attribute — which is why every <td> the portal renders
     * must carry one, and why a route test counts them rather than trusting review.
     */
    table, thead, tbody, tr, td { display: block; }

    thead {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        clip-path: inset(50%);
        white-space: nowrap;
    }

    tbody tr {
        background: var(--white);
        border: 1px solid var(--line);
        border-radius: var(--radius);
        padding: var(--s16);
        margin-bottom: var(--s16);
    }

    tbody td {
        display: grid;
        grid-template-columns: minmax(92px, 40%) minmax(0, 1fr);
        gap: var(--s8);
        padding: var(--s4) 0;
        border-bottom: 0;
    }

    tbody td::before {
        content: attr(data-label);
        color: var(--slate);
        font-weight: 600;
    }
}

@media (max-width: 420px) {
    h1 { font-size: 34px; }

    .masthead .shell { gap: var(--s8); }

    .utility-text { display: none; }

    .utility .shell { justify-content: flex-end; }

    /* §6: the table cards go single column, so each label sits above its value. */
    tbody td { grid-template-columns: minmax(0, 1fr); gap: 0; }
}
