﻿/* ==========================================================================
   Marriage Registry — stylesheet

   Written by hand rather than pulled from a CDN. Two reasons, both practical:
   an on-premises deployment cannot rely on reaching a CDN (§2.2), and the
   Content Security Policy permits no external origins, so anything fetched
   from one would silently fail to load.

   Accessibility is designed in rather than retrofitted (§25):
     - every colour pair below meets WCAG 2.1 AA contrast at its intended size;
     - focus is always visible, and never removed;
     - nothing conveys meaning by colour alone — status carries a label;
     - the layout reflows to 320px without horizontal scrolling;
     - prefers-reduced-motion is honoured.
   ========================================================================== */

:root {
    /* Greens: registry identity. --green-700 on white is 9.5:1.
       --green-900 was #06301f, which gave the gold primary button's text 4.45:1 —
       under the 4.5:1 WCAG 2.1 minimum for normal text. #06241b gives 5.06:1, and is
       the shade BrandColours.Darken derives from the primary, so a registry's own
       colours are checked against the same relationship. */
    --green-900: #06241b;
    --green-700: #0e4f3c;
    --green-500: #1a7a5e;
    --green-100: #e6f2ed;
    --green-050: #f4faf7;

    --gold-600: #8a6410;
    --gold-400: #b8860b;
    --gold-100: #f7efd9;

    --ink-900: #16181a;
    --ink-700: #33383d;
    /* --ink-500 on white is 4.6:1 — AA for body text. */
    --ink-500: #5b6269;
    --ink-300: #9aa1a8;
    --ink-200: #d5d9dd;
    --ink-100: #e9ecef;
    --ink-050: #f5f7f8;
    --white: #ffffff;

    --danger-700: #8a1c1c;
    --danger-100: #fbeaea;
    --warning-700: #7a5200;
    --warning-100: #fdf3e0;
    --info-700: #14507a;
    --info-100: #e6f1f8;

    --radius: 6px;
    --radius-lg: 10px;
    --shadow-sm: 0 1px 2px rgba(16, 24, 32, .06), 0 1px 3px rgba(16, 24, 32, .08);
    --shadow-md: 0 4px 12px rgba(16, 24, 32, .09);

    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-display: Georgia, "Times New Roman", serif;

    --measure: 68ch;
    --container: 1120px;
}

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

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

body {
    margin: 0;
    font-family: var(--font);
    font-size: 16px;
    line-height: 1.6;
    color: var(--ink-900);
    background: var(--white);
}

/* --- Skip link: the first thing a keyboard user reaches ------------------ */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    background: var(--green-700);
    color: var(--white);
    padding: 12px 20px;
    z-index: 100;
    text-decoration: none;
}
.skip-link:focus { left: 0; }

/* --- Focus: visible everywhere, never removed --------------------------- */
:focus-visible {
    outline: 3px solid var(--gold-400);
    outline-offset: 2px;
}

a { color: var(--green-700); text-underline-offset: 2px; }
a:hover { color: var(--green-900); }

h1, h2, h3, h4 { line-height: 1.25; margin: 0 0 .5em; font-weight: 600; }
h1 { font-size: 2rem; }
h2 { font-size: 1.5rem; }
h3 { font-size: 1.175rem; }
p { margin: 0 0 1rem; max-width: var(--measure); }

.container {
    max-width: var(--container);
    margin: 0 auto;
    padding: 0 20px;
}
.container--narrow { max-width: 760px; }

/* --- Header ------------------------------------------------------------- */
.site-header {
    background: var(--green-700);
    color: var(--white);
    border-bottom: 4px solid var(--gold-400);
}
.site-header__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    min-height: 68px;
    flex-wrap: wrap;
}
.site-header a { color: var(--white); }

.brand { display: flex; align-items: center; gap: 12px; text-decoration: none; padding: 10px 0; }
.brand__mark { flex: 0 0 auto; }
/* A registry's uploaded logo. Bounded so an unusually wide image cannot push the navigation off the row. */
.brand__logo { flex: 0 0 auto; height: 40px; width: auto; max-width: 160px; object-fit: contain; }
.brand__logo-preview { max-height: 120px; max-width: 100%; height: auto; }
.admin-bar__logo { height: 28px; width: auto; max-width: 120px; margin-right: 10px; object-fit: contain; }
.brand__name { font-family: var(--font-display); font-size: 1.2rem; font-weight: 600; line-height: 1.2; }
.brand__tagline { font-size: .75rem; opacity: .85; letter-spacing: .04em; text-transform: uppercase; }

.nav { display: flex; gap: 4px; flex-wrap: wrap; align-items: center; }
.nav a {
    padding: 8px 12px;
    border-radius: var(--radius);
    text-decoration: none;
    font-size: .925rem;
}
.nav a:hover { background: rgba(255, 255, 255, .12); }
.nav a[aria-current="page"] { background: rgba(255, 255, 255, .18); font-weight: 600; }

/* --- Hero ---------------------------------------------------------------
   The illustration is an inline SVG drawn for this project. It is not a
   photograph, and that is deliberate: §24 requires properly licensed imagery
   and warns against depicting a particular ethnic group, government body or
   ceremony without a genuine right to. An abstract commissioned illustration
   avoids both the licensing question and the misrepresentation.
   See docs/BRANDING-AND-IMAGERY.md.
   ------------------------------------------------------------------------- */
.hero {
    background: linear-gradient(160deg, var(--green-900) 0%, var(--green-700) 55%, var(--green-500) 100%);
    color: var(--white);
    position: relative;
    overflow: hidden;
}
.hero__inner {
    display: grid;
    grid-template-columns: 1.15fr .85fr;
    gap: 40px;
    align-items: center;
    padding: 64px 20px 72px;
    max-width: var(--container);
    margin: 0 auto;
}
.hero h1 {
    font-family: var(--font-display);
    font-size: clamp(2rem, 4.5vw, 3rem);
    margin-bottom: .4em;
}
.hero p { font-size: 1.075rem; opacity: .94; max-width: 46ch; }
.hero__actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 28px; }
.hero__art { display: flex; justify-content: center; }
.hero__art svg { width: 100%; max-width: 340px; height: auto; }

@media (max-width: 800px) {
    .hero__inner { grid-template-columns: 1fr; padding: 44px 20px 52px; }
    .hero__art { order: -1; }
    .hero__art svg { max-width: 220px; }
}

/* --- Buttons ------------------------------------------------------------ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 11px 20px;
    border-radius: var(--radius);
    border: 1px solid transparent;
    font: inherit;
    font-weight: 600;
    font-size: .95rem;
    cursor: pointer;
    text-decoration: none;
    /* 44px minimum target: WCAG 2.5.5 */
    min-height: 44px;
}
.btn--primary { background: var(--gold-400); color: var(--green-900); border-color: var(--gold-600); }
.btn--primary:hover { background: #c99512; color: var(--green-900); }
.btn--secondary { background: var(--green-700); color: var(--white); }
.btn--secondary:hover { background: var(--green-900); color: var(--white); }
.btn--ghost { background: transparent; color: var(--white); border-color: rgba(255, 255, 255, .55); }
.btn--ghost:hover { background: rgba(255, 255, 255, .12); color: var(--white); }
.btn--outline { background: var(--white); color: var(--green-700); border-color: var(--ink-200); }
.btn--outline:hover { background: var(--green-050); }
.btn--danger { background: var(--danger-700); color: var(--white); }
.btn--danger:hover { background: #6d1616; color: var(--white); }
.btn--small { padding: 7px 12px; min-height: 36px; font-size: .875rem; }
.btn[disabled], .btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }

/* --- Layout blocks ------------------------------------------------------ */
main { min-height: 55vh; }
.section { padding: 48px 0; }
.section--tint { background: var(--ink-050); }

.page-head { padding: 28px 0 8px; border-bottom: 1px solid var(--ink-100); margin-bottom: 28px; }
.page-head h1 { margin-bottom: .15em; }
.page-head__meta { color: var(--ink-500); font-size: .925rem; }

.card {
    background: var(--white);
    border: 1px solid var(--ink-100);
    border-radius: var(--radius-lg);
    padding: 24px;
    box-shadow: var(--shadow-sm);
}
.card + .card { margin-top: 20px; }
.card__title { margin-top: 0; }

.grid { display: grid; gap: 20px; }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

/* --- Steps -------------------------------------------------------------- */
.steps { list-style: none; margin: 0 0 32px; padding: 0; display: flex; flex-wrap: wrap; gap: 4px; }
.steps li {
    flex: 1 1 120px;
    padding: 10px 12px;
    border-top: 4px solid var(--ink-200);
    font-size: .8rem;
    color: var(--ink-500);
}
.steps li[data-state="done"] { border-top-color: var(--green-500); color: var(--green-700); }
.steps li[data-state="current"] { border-top-color: var(--gold-400); color: var(--ink-900); font-weight: 600; }
.steps__number { display: block; font-size: .7rem; letter-spacing: .06em; text-transform: uppercase; }

/* --- Forms --------------------------------------------------------------
   Hint text sits BELOW its control.

   The markup keeps the reading order label -> hint -> control, which is what
   aria-describedby announces and what a screen-reader user wants: the guidance
   before the field, not after they have typed. Visually the control is lifted
   above the hint with flex ordering.

   Why it matters. A hint above a control pushes that control down by however
   many lines the hint happens to run to. In a `grid grid--3` row the fields are
   side by side, so one long hint left every other control in the row sitting at
   a different height — worst on the settings page, where "Payment window" and
   "Duplicate photograph sensitivity" sank well below their neighbours. Ordering
   here rather than moving thirty spans in eighteen views means the next hint
   somebody adds cannot reintroduce it (§51).

   Safe as a blanket rule because every .field in the application holds exactly
   one control; a checkbox nested inside its own label is untouched, being a
   descendant of the label rather than a sibling of it.
   ------------------------------------------------------------------------- */
.field { display: flex; flex-direction: column; margin-bottom: 20px; }
.field label { order: 1; display: block; font-weight: 600; margin-bottom: 6px; font-size: .925rem; }
.field > input, .field > select, .field > textarea { order: 2; }
/*  Below the control, so margin-top rather than the margin-bottom this had
    when it sat above one. */
.field .hint { order: 3; display: block; color: var(--ink-500); font-size: .85rem; margin: 6px 0 0; }
/*  Last, so that an error appearing does not shove the hint down the page. */
.field .field-error { order: 4; }

/* --- Field rows that line up ----------------------------------------------
   Opt-in: `grid grid--fields grid--N`.

   Ordering the hint below its control stops a long HINT pushing a control
   down. It does nothing for a long LABEL: in a row of four narrow columns a
   label such as "…or type the place of birth, if it is not listed" wraps to two
   lines, and its text box sat a line lower than the three beside it.

   So each field in the row shares the row's horizontal lines, through
   subgrid. Every label occupies track 1, every control track 2, and a track is
   as tall as its tallest occupant -- so all the controls in a row start at the
   same height however many lines any one label runs to. The same holds for
   hints and errors:

     1 label      3 hint           5 error
     2 control    4 second hint    6 second error      7 spacer

   A second hint is the identification number's format hint beside its
   "never shown again" hint; a second error is the gender field's warning
   beside its validation message.

   A checkbox inside its own label has no separate control, so that label is
   moved down to track 2 and sits level with the controls beside it, rather
   than on the line of their labels.

   Details that matter:
   - A subgrid cannot create rows of its own, so every row a field uses is
     declared, the last an empty 20px spacer drawn by ::after. The spacer does
     what margin-bottom did; margin on a subgrid is only carried by items in
     its edge row, and that row is usually empty.
   - The parent's row-gap is zero, or its gutters would open up between label,
     control and hint -- including under tracks that hold nothing.
   - Hidden inputs are display:none and take no track. A field hidden with the
     `hidden` attribute takes no column, as before.
   - Opt-in rather than blanket: a field holding anything beyond the seven
     parts above would be squeezed into a track it shares. FormLayoutTests
     renders every form that uses the class and fails if one does.
   - Behind @supports, so a browser without subgrid keeps the layout above.
     The checkbox rule uses :has() and is its own rule, so a browser without
     :has() drops that one rule and leaves the checkbox on the label line.
   ------------------------------------------------------------------------- */
@supports (grid-template-rows: subgrid) {
    .grid--fields { row-gap: 0; }

    .grid--fields > .field {
        display: grid;
        grid-row: span 7;
        grid-template-rows: subgrid;
        grid-template-columns: minmax(0, 1fr);
        align-content: start;
        margin-bottom: 0;
        min-width: 0;
    }

    .grid--fields > .field > label { grid-row: 1; align-self: end; }
    .grid--fields > .field > input,
    .grid--fields > .field > select,
    .grid--fields > .field > textarea { grid-row: 2; align-self: start; }
    .grid--fields > .field > .hint { grid-row: 3; }
    .grid--fields > .field > .hint ~ .hint { grid-row: 4; }
    .grid--fields > .field > .field-error { grid-row: 5; }
    .grid--fields > .field > .field-error ~ .field-error { grid-row: 6; }
    .grid--fields > .field::after { content: ""; grid-row: 7; height: 20px; }

    .grid--fields > .field > label:has(> input[type="checkbox"]) {
        grid-row: 2;
        align-self: center;
        margin-bottom: 0;
    }
}

input[type=text], input[type=email], input[type=password], input[type=date],
input[type=tel], input[type=number], input[type=search], input[type=time],
select, textarea {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--ink-300);
    border-radius: var(--radius);
    font: inherit;
    color: var(--ink-900);
    background: var(--white);
    min-height: 44px;
}
textarea { min-height: 120px; resize: vertical; }
input:focus, select:focus, textarea:focus { border-color: var(--green-500); }

input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
    border-color: var(--danger-700);
    border-width: 2px;
}

.field-error { color: var(--danger-700); font-size: .875rem; margin-top: 6px; display: block; font-weight: 500; }

fieldset { border: 1px solid var(--ink-100); border-radius: var(--radius); padding: 20px; margin: 0 0 24px; }
/*  Stated rather than left to the browser: a fieldset carries display: block
    from the user-agent stylesheet, and a section the server or the page has
    hidden must actually be gone. */
fieldset[hidden] { display: none; }
legend { font-weight: 600; padding: 0 8px; }

/*  The emblem and header mark follow the registry's colours. The SVGs carry
    the default colours as attributes, for a browser without CSS; these
    properties override them with whatever the theme stylesheet has set. */
.emblem-accent { stroke: var(--gold-400); }
.emblem-accent-stop { stop-color: var(--gold-400); }
.emblem-main-stop { stop-color: var(--green-700); }

/*  A brand colour chosen with the browser's picker (_ColourField.cshtml). */
.colour-field { margin: 0 0 20px; }
.colour-field .hint { display: block; color: var(--ink-500); font-size: .85rem; margin: 0 0 10px; }
.colour-field__toggle { display: flex; align-items: center; gap: 8px; font-weight: 500; margin-bottom: 10px; }
.colour-field__picker { display: flex; align-items: center; gap: 12px; }
.colour-field__picker label { font-weight: 600; font-size: .925rem; }
.colour-field input[type="color"] {
    width: 56px; height: 40px; padding: 2px; cursor: pointer;
    border: 1px solid var(--ink-300); border-radius: var(--radius); background: #fff;
}
.colour-field input[type="color"]:disabled { cursor: not-allowed; opacity: .45; }
.colour-field__value { font-family: ui-monospace, Consolas, monospace; font-size: .925rem; }

.form-actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 28px; }

/* --- Alerts -------------------------------------------------------------
   Each carries an icon and a visible label, so meaning never rests on colour.
   ------------------------------------------------------------------------- */
.alert {
    padding: 14px 16px;
    border-radius: var(--radius);
    border-left: 4px solid;
    margin-bottom: 20px;
    display: flex;
    gap: 12px;
    align-items: flex-start;
}
.alert p:last-child { margin-bottom: 0; }
.alert--error { background: var(--danger-100); border-color: var(--danger-700); color: #5d1212; }
.alert--warning { background: var(--warning-100); border-color: var(--warning-700); color: #563a00; }
.alert--success { background: var(--green-100); border-color: var(--green-500); color: var(--green-900); }
.alert--info { background: var(--info-100); border-color: var(--info-700); color: #0e3a58; }
.alert__label { font-weight: 700; }

/* --- Status pills ------------------------------------------------------- */
.pill {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 600;
    border: 1px solid;
    white-space: nowrap;
}
.pill--neutral { background: var(--ink-050); color: var(--ink-700); border-color: var(--ink-200); }
.pill--progress { background: var(--info-100); color: var(--info-700); border-color: #b6d4e8; }
.pill--success { background: var(--green-100); color: var(--green-900); border-color: #a9d6c5; }
.pill--warning { background: var(--warning-100); color: var(--warning-700); border-color: #e8ce93; }
.pill--danger { background: var(--danger-100); color: var(--danger-700); border-color: #e6b3b3; }

/* --- Tables ------------------------------------------------------------- */
.table-wrap { overflow-x: auto; border: 1px solid var(--ink-100); border-radius: var(--radius-lg); }
table { width: 100%; border-collapse: collapse; font-size: .925rem; }
caption { text-align: left; padding: 12px 16px; font-weight: 600; color: var(--ink-700); }
th, td { padding: 11px 16px; text-align: left; border-bottom: 1px solid var(--ink-100); vertical-align: top; }
thead th { background: var(--ink-050); font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-700); }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--green-050); }
.table__numeric { text-align: right; font-variant-numeric: tabular-nums; }

/* --- Definition lists --------------------------------------------------- */
.details { display: grid; grid-template-columns: minmax(140px, 220px) 1fr; gap: 10px 20px; margin: 0; }
/*  The label column takes at most 40% of the list.

    A 140-220px label column is grown to its full 220px before the value column
    gets anything. Once values were allowed to shrink (below), a narrow list --
    a notice card is about 265px -- left its values a few pixels wide, and they
    broke one character per line. Where the list is 550px or wider this is the
    same 220px as before. The declaration above stays as the fallback for a
    browser without min(). */
.details { grid-template-columns: minmax(0, min(220px, 40%)) minmax(0, 1fr); }
.details dt { color: var(--ink-500); font-size: .875rem; }
/*  min-width: 0 and overflow-wrap: anywhere so an unbroken value -- a reference,
    an email address, a user name -- wraps inside its column instead of
    widening the list, and with it whatever card or page holds it. break-word
    comes first as the fallback for a browser that does not know `anywhere`,
    which drops only that declaration. */
.details dd { margin: 0; min-width: 0; overflow-wrap: break-word; overflow-wrap: anywhere; }
@media (max-width: 560px) { .details { grid-template-columns: 1fr; gap: 2px 0; } .details dd { margin-bottom: 12px; } }

/*  In a notice card the list is always narrow, so each label sits above its
    value and the value has the card's full width. Side by side, even at 40%, a
    notice reference or "Wednesday 14 October 2026" had to break mid-word. */
.notice-card__body .details { grid-template-columns: minmax(0, 1fr); gap: 0; }
.notice-card__body .details dt { margin-top: 10px; }
.notice-card__body .details dt:first-child { margin-top: 0; }
.notice-card__body .details dd { margin-bottom: 0; }

/* --- Reference display -------------------------------------------------- */
.reference {
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 1.35rem;
    font-weight: 600;
    letter-spacing: .05em;
    color: var(--green-700);
}
.reference-box {
    background: var(--green-050);
    border: 1px solid #b8ded0;
    border-radius: var(--radius-lg);
    padding: 20px;
    text-align: center;
    margin: 24px 0;
}
.reference-box__label { font-size: .75rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-500); }

/* --- Notices ------------------------------------------------------------
   A card is two parts: the photographs, and a body holding everything else.

   Everything after the photographs must be inside .notice-card__body. Left
   as separate flex items, the names, status pills, details and link were
   laid side by side in one row, and the back-office Objections and
   Publications lists ran several hundred pixels past the window.
   NoticeCardTests holds every view to the two-part structure.

   The body sits beside the photographs when the card has room and drops
   below them when it does not. With 96px photographs it needs a card about
   440px wide to sit beside them, so in the three-column lists below it is
   under the photographs at every ordinary width, and only a very wide
   back-office screen puts it alongside. min-width: 0 lets its contents wrap
   rather than widen the card.

   .grid--cards: at most three columns, each at least 280px, so a narrower
   screen gets two and then one. The track minimum is the larger of 280px and
   a third of the row less its two 20px gaps, which is what caps the count at
   three. The first declaration is the fallback for a browser without max():
   the same 280px minimum, without the cap.
   ------------------------------------------------------------------------- */
.grid--cards { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid--cards { grid-template-columns: repeat(auto-fit, minmax(max(280px, calc((100% - 40px) / 3)), 1fr)); }
.notice-card { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; }
.notice-card__photos { display: flex; gap: 8px; flex: 0 0 auto; }
.notice-card__body { flex: 1 1 220px; min-width: 0; }
.notice-card__photos img,
.notice-card__photos .photo-placeholder {
    width: 96px; height: 96px; object-fit: cover;
    border-radius: var(--radius); border: 1px solid var(--ink-200);
}
.photo-placeholder {
    display: flex; align-items: center; justify-content: center;
    background: var(--ink-050); color: var(--ink-300); font-size: .7rem; text-align: center;
}

/*  A party's photograph on the public notice itself. The photograph is what
    lets someone recognise the couple, so it takes the width of its column up
    to 280px -- the photographs are stored at 400-1200px, so this is still a
    reduction -- and stays square. The two columns stack on a phone, where each
    photograph still gets up to 280px. A missing photograph gets the same box. */
.notice-portrait {
    display: block; width: 100%; max-width: 280px; height: auto; aspect-ratio: 1 / 1;
    object-fit: cover; border-radius: var(--radius); border: 1px solid var(--ink-200);
}
.notice-portrait.photo-placeholder { font-size: .875rem; }

/*  The party summary on the two review screens: the applicant's "Check your
    answers" page and the officer's application screen.

    The photograph is 200px, large enough to recognise a face, which is what
    both readers are there to check. The details sit beside it while they have
    about 360px, and drop below it when they do not. Beside a photograph in a
    half-width officer card they were 287px wide, and an email address took
    three lines and the witness nine. On the officer's screen the face-check
    note sits in the photograph's column, under the picture it describes. */
.party-summary { display: flex; flex-wrap: wrap; gap: 16px 20px; align-items: flex-start; }
.party-summary__photo { flex: 0 0 auto; width: 200px; max-width: 100%; }
.party-summary__details { flex: 1 1 360px; min-width: 0; }
.review-photo {
    display: block; width: 200px; max-width: 100%; height: auto; aspect-ratio: 1 / 1;
    object-fit: cover; border-radius: var(--radius); border: 1px solid var(--ink-200);
}
.review-photo.photo-placeholder { font-size: .875rem; }
.notice-card__names { font-size: 1.05rem; font-weight: 600; margin: 0 0 4px; }

/* --- Photograph preview -------------------------------------------------
   Shown once a file is chosen, before it is uploaded. Hidden by [hidden]
   until then, and never shown at all without scripting.                   */
.photo-preview {
    display: flex; gap: 14px; align-items: flex-start;
    margin: 12px 0 0; padding: 12px;
    background: var(--ink-050); border: 1px solid var(--ink-100);
    border-radius: var(--radius);
}
.photo-preview[hidden] { display: none; }
/*  200px, the size the photograph is shown at on the review pages, so the
    applicant judges the picture at the size an officer will. On a narrow
    screen the advice wraps under it rather than into a sliver beside it. */
.photo-preview { flex-wrap: wrap; }
.photo-preview__advice { flex: 1 1 200px; }
.photo-preview img {
    width: 200px; max-width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover; flex: 0 0 auto;
    border-radius: var(--radius); border: 1px solid var(--ink-200);
    background: var(--white);
}
.photo-preview__advice { margin: 0; color: var(--ink-500); }
.photo-preview__advice--warn { color: var(--warning-700); }

/* --- Dashboard tiles ---------------------------------------------------- */
.tile {
    background: var(--white); border: 1px solid var(--ink-100);
    border-radius: var(--radius-lg); padding: 18px 20px; box-shadow: var(--shadow-sm);
    display: block; text-decoration: none; color: inherit;
}
a.tile:hover { border-color: var(--green-500); box-shadow: var(--shadow-md); }
.tile__value { font-size: 2rem; font-weight: 700; line-height: 1.1; color: var(--green-700); }
.tile__label { font-size: .875rem; color: var(--ink-500); margin-top: 2px; }
.tile--attention .tile__value { color: var(--danger-700); }

/* --- Admin shell --------------------------------------------------------
   The main column is minmax(0, 1fr), not 1fr. A 1fr track will not shrink
   below its widest content, so one wide table -- the audit trail, with a
   long user name in it -- made the whole main column wider than the window,
   and the table's own .table-wrap never got the chance to scroll.
   ------------------------------------------------------------------------- */
.admin-shell { display: grid; grid-template-columns: 236px minmax(0, 1fr); min-height: 100vh; }
.admin-nav { background: var(--green-900); color: var(--white); padding: 20px 0; }
.admin-nav a {
    display: block; color: rgba(255, 255, 255, .88);
    padding: 9px 20px; text-decoration: none; font-size: .925rem;
    border-left: 3px solid transparent;
}
.admin-nav a:hover { background: rgba(255, 255, 255, .08); color: var(--white); }
.admin-nav a[aria-current="page"] { background: rgba(255, 255, 255, .13); border-left-color: var(--gold-400); color: var(--white); font-weight: 600; }
.admin-nav__group { padding: 16px 20px 6px; font-size: .7rem; text-transform: uppercase; letter-spacing: .08em; opacity: .6; }
.admin-main { padding: 0 28px 56px; background: var(--ink-050); }
.admin-bar {
    display: flex; justify-content: space-between; align-items: center; gap: 16px;
    padding: 14px 0; border-bottom: 1px solid var(--ink-100); margin-bottom: 24px; flex-wrap: wrap;
}

@media (max-width: 900px) {
    .admin-shell { grid-template-columns: minmax(0, 1fr); }
    .admin-nav { padding: 8px 0; }
    .admin-nav__links { display: flex; flex-wrap: wrap; }
    .admin-nav a { border-left: none; border-bottom: 3px solid transparent; }
    .admin-nav a[aria-current="page"] { border-left: none; border-bottom-color: var(--gold-400); }
    .admin-nav__group { display: none; }
    .admin-main { padding: 0 16px 40px; }
}

/* --- Pagination --------------------------------------------------------- */
.pagination { display: flex; gap: 6px; align-items: center; margin-top: 20px; flex-wrap: wrap; }
.pagination a, .pagination span {
    padding: 8px 12px; border: 1px solid var(--ink-200); border-radius: var(--radius);
    text-decoration: none; min-width: 40px; text-align: center;
}
.pagination [aria-current="page"] { background: var(--green-700); color: var(--white); border-color: var(--green-700); }
.pagination__summary { color: var(--ink-500); font-size: .875rem; margin-left: auto; }

/* --- Footer ------------------------------------------------------------- */
.site-footer {
    background: var(--green-900); color: rgba(255, 255, 255, .82);
    padding: 36px 0 28px; margin-top: 56px; font-size: .9rem;
}
.site-footer a { color: var(--white); }
.site-footer__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 28px; }
.site-footer h2 { font-size: .8rem; text-transform: uppercase; letter-spacing: .07em; opacity: .7; }
.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li { margin-bottom: 6px; }
.site-footer__legal { border-top: 1px solid rgba(255, 255, 255, .16); margin-top: 28px; padding-top: 18px; font-size: .82rem; opacity: .75; }

/* --- Utilities ---------------------------------------------------------- */
.visually-hidden {
    position: absolute !important; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.text-muted { color: var(--ink-500); }
.text-small { font-size: .875rem; }
.text-right { text-align: right; }
.text-center { text-align: center; }
.mt-0 { margin-top: 0; } .mt-1 { margin-top: 8px; } .mt-2 { margin-top: 16px; } .mt-3 { margin-top: 24px; }
.mb-0 { margin-bottom: 0; } .mb-1 { margin-bottom: 8px; } .mb-2 { margin-bottom: 16px; } .mb-3 { margin-bottom: 24px; }
.flex { display: flex; } .flex-gap { gap: 12px; } .flex-wrap { flex-wrap: wrap; }
.items-center { align-items: center; } .justify-between { justify-content: space-between; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }
}

@media print {
    .site-header, .site-footer, .admin-nav, .no-print, .form-actions { display: none !important; }
    body { font-size: 12pt; color: #000; }
    .card { border: 1px solid #999; box-shadow: none; }
    .admin-shell { grid-template-columns: 1fr; }
    a[href]::after { content: ""; }
}
