/* The brand's free stand-ins, self-hosted (#340): Figtree for text, Outfit
   for headings. Each subset is its own face, so a browser fetches Latin
   Extended only for a page that uses it, as for a macron in a Māori name.
   The weight ranges and unicode-ranges are @fontsource-variable 5.3.0's,
   the release the files came from. */
@font-face {
    font-family: "Figtree";
    font-style: normal;
    font-weight: 300 900;
    font-display: swap;
    src: url("fonts/figtree/figtree-latin-wght-normal.202cfb54d4e6.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Figtree";
    font-style: normal;
    font-weight: 300 900;
    font-display: swap;
    src: url("fonts/figtree/figtree-latin-ext-wght-normal.eca8d8b337ff.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Outfit";
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("fonts/outfit/outfit-latin-wght-normal.fdf9c509a809.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "Outfit";
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url("fonts/outfit/outfit-latin-ext-wght-normal.6a2414e384c3.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

* { box-sizing: border-box; }

/* The `hidden` attribute is only `display: none` in the user-agent stylesheet,
   so ANY author rule setting `display` on the same element silently defeats it
   — which is how the board's outcome dialog came to show the Lost reason
   selector on a Won close (#83): `.field` below sets `display: flex`.
   `!important` is required, not lazy: `[hidden]` and `.field` have equal
   specificity (0,1,0), so without it the winner is source order, which would
   work today and break the next time a `display` rule lands below this one. */
[hidden] { display: none !important; }

/* The palette. Every colour in this stylesheet is a token here and is read
   through var(--…) everywhere else, so a colour pass is an edit to this block
   alone; core.tests.test_static_assets fails if a literal appears outside it.
   Names say what a colour is for, not what it looks like, so a redesign can
   change a value without the name becoming a lie. */
:root {
    --ground: #fafafa;
    --surface: #fff;
    --ink: #1a1a1a;
    --muted: #4a4a4a;
    --faint: #666;
    --border: #e0e0e0;
    --border-strong: #bfbfbf;
    --control-border: #8c8c8c;
    --table-head: #f5f5f5;
    --hover: #f0f0f0;
    --accent: #197b30;
    --accent-hover: #13632a;
    --accent-tint: #eaf5ec;
    --accent-tint-border: #c4e2cb;
    /* The brand green, for accent fills. White text on it fails AA, so
       text on it is --ink (the production job's type fill); --accent is its
       partner behind white text. */
    --brand: #3eb54a;
    /* Two stronger greens for the work item cards' type stripes (issue #432
       R-12), each a mark on --surface that must stand at 3:1 against it.
       --brand-strong is --brand with each channel scaled by 0.85, and
       --accent-deep is --accent-hover with each channel scaled by 0.75. */
    --brand-strong: #359a3f;
    --accent-deep: #0e4a20;
    /* The Authorisation request's stripe (issue #519): --brand with each
       channel scaled by 0.7, distinct from the four stripes already taken. */
    --brand-deep: #2b7f34;
    /* Text and marks drawn on a filled colour: the accent, the ink, a filled
       chip or a stepper number. */
    --on-fill: #fff;
    --ok-bg: #e3f2ef;
    --ok-fg: #0b6b5a;
    --info-bg: #e7f1fb;
    --info-fg: #1c4f8a;
    --warning-bg: #fff4d4;
    --warning-fg: #7a5400;
    --error-bg: #fce4e4;
    --error-fg: #8a1c1c;
    --neutral-bg: #eef0f2;
    --neutral-fg: #5a636b;
    --step-bg: #f1f2f4;
    --step-num: #d9dde1;
    --count-bg: #e4e7ea;
    --bubble: #f1f3f5;
    --panel: #f7f8f9;
    --panel-ink: #24292f;
    --backdrop: rgb(26 26 26 / 45%);
    /* The initials badge's palette (issue #669), one per member of
       core.badges.BadgeColour, each behind --on-fill text.
       core.tests.test_badge_palette is the authority on these values: their
       contrast with the text, and their distance from each other and from
       every other colour above. */
    --badge-red: #d12a2a;
    --badge-orange: #b35000;
    --badge-olive: #5f6b12;
    --badge-teal: #007f8f;
    --badge-blue: #1d5fc9;
    --badge-indigo: #2a2a8f;
    --badge-purple: #8a2fb8;
    --badge-magenta: #a3166b;
    --radius: 3px;
    --font-body: "Figtree", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --font-heading: "Outfit", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

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

a { color: var(--accent); }
a:hover { text-decoration: underline; }

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.skip-link {
    position: absolute;
    top: -100px;
    left: 0;
    background: var(--ink);
    color: var(--on-fill);
    padding: 0.5rem 1rem;
    z-index: 1000;
}
.skip-link:focus { top: 0; }

/* The header is a band of the accent with white text (#340). */
.site-header {
    background: var(--accent);
    color: var(--on-fill);
    padding: 0.75rem 1.5rem;
    display: flex;
    align-items: center;
    gap: 1.5rem;
    flex-wrap: wrap;
}
/* Outranks the page-wide focus rule, whose accent ring would vanish on the band. */
.site-header :focus-visible { outline: 2px solid var(--on-fill); }

.site-brand a { display: block; color: var(--on-fill); }
.site-mark { display: block; height: 36px; width: auto; }

.site-nav ul {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}

.site-nav a {
    color: var(--on-fill);
    text-decoration: none;
}
.site-nav a:hover { text-decoration: underline; }
/* Marked by weight and a bar, not by colour: every header link is white. */
.site-nav a[aria-current="page"] {
    font-weight: 700;
    border-bottom: 3px solid var(--on-fill);
    padding-bottom: 0.15rem;
}
/* The divider after the Tasks entry (issue #431 R-1): a thin rule in the
   links' own colour, holding no text. */
.site-nav-divider {
    align-self: stretch;
    width: 1px;
    background: var(--on-fill);
    opacity: 0.5;
}

.user-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.user-display {
    color: var(--on-fill);
    font-size: 0.9em;
}

.logout-form { display: inline; margin: 0; }

.logout-button {
    background: none;
    border: 1px solid var(--on-fill);
    color: var(--on-fill);
    padding: 0.25rem 0.75rem;
    border-radius: var(--radius);
    cursor: pointer;
    font: inherit;
}
.logout-button:hover { background: var(--accent-hover); }

main {
    padding: 1.5rem;
    max-width: 1200px;
    margin: 0 auto;
}

/* The Enquiries board opts out of the reading measure (#93). Six 200px columns
   need 1280px (#109 lowered the floor from 260px), so inside the 1152px content
   area five of six fit and the pipeline's two ends can never be on screen
   together. Any board page opts out the same way, through core/base.html's
   main_class block; a template that leaves the block empty renders class=""
   and keeps the reading measure. */
main.main-wide { max-width: none; }

h1, h2, h3 { margin-top: 0; font-family: var(--font-heading); font-weight: 700; }

.messages ul {
    list-style: none;
    margin: 1rem 1.5rem 0;
    padding: 0;
    max-width: 1200px;
}

/* .form-errors and the library's .banner are messages under other names:
   listed here rather than restyled, so the three boxes cannot drift. */
.message,
.form-errors,
.banner {
    padding: 0.75rem 1rem;
    border-radius: var(--radius);
    margin-bottom: 0.5rem;
}
.message-success { background: var(--ok-bg); color: var(--ok-fg); }
.message-info    { background: var(--info-bg); color: var(--info-fg); }
.message-warning,
.banner-warning { background: var(--warning-bg); color: var(--warning-fg); }
.message-error,
.form-errors     { background: var(--error-bg); color: var(--error-fg); }

table {
    border-collapse: collapse;
    width: 100%;
}
th, td,
.card dt, .card dd {
    text-align: left;
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid var(--border);
}
th, .card dt { background: var(--table-head); font-weight: 600; }

label { display: inline-block; margin-bottom: 0.25rem; }

input[type="text"],
input[type="number"],
input[type="email"],
input[type="date"],
input[type="datetime-local"],
input[type="password"],
input[type="url"],
input[type="search"],
input[type="tel"],
select,
textarea {
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--control-border);
    border-radius: var(--radius);
    font: inherit;
    /* Set explicitly so a select does not keep the user agent's grey beside
       white text inputs, which is the one visible difference between them. */
    background: var(--surface);
    color: var(--ink);
}

img { max-width: 100%; height: auto; }

button.primary,
input[type="submit"] {
    padding: 0.5rem 1rem;
    background: var(--accent);
    color: var(--on-fill);
    border: 0;
    border-radius: var(--radius);
    cursor: pointer;
    font: inherit;
}
button.primary:hover,
input[type="submit"]:hover { background: var(--accent-hover); }

ul.errorlist {
    list-style: none;
    margin: 0.25rem 0;
    padding: 0;
    color: var(--error-fg);
}

.signin-button {
    display: inline-block;
    padding: 0.5rem 1rem;
    background: var(--accent);
    color: var(--on-fill);
    text-decoration: none;
    border-radius: var(--radius);
}
.signin-button:hover {
    background: var(--accent-hover);
    text-decoration: none;
}
/* The sign-in page is one centred block, so the stacked logo sits centred
   over the sign-in action as well as on the page (#340). */
.signin { text-align: center; }
.signin-logo { margin-bottom: 1.5rem; }
.signin-logo img { width: 9rem; }

/* Layout utilities */
.stack   { display: flex; flex-direction: column; gap: 1rem; }
.row, .model-machine, .manufacturer-model { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.spread  { justify-content: space-between; }
.narrow  { max-width: 640px; }
.subhead { margin: 0; font-size: 1.15rem; }
.list-reset { list-style: none; margin: 0; padding: 0; }

/* Page header */
.page-header { margin-bottom: 1.5rem; }
.page-header .eyebrow {
    text-transform: uppercase; letter-spacing: 0.08em;
    font-size: 0.72rem; font-weight: 600; color: var(--faint); margin: 0 0 0.25rem;
}
.page-header h1 { font-size: 1.75rem; margin: 0; text-wrap: balance; }
.page-meta { margin-top: 0.5rem; color: var(--muted); font-size: 0.9rem; }
/* A record's name is free text with no spaces guaranteed, so the header wraps
   it anywhere rather than scroll a phone sideways (#310 R-11). */
.page-header h1, .page-meta { overflow-wrap: anywhere; }

/* Cards */
/* A review-gallery .image-card is a card: the figure gets the shared surface. */
.card,
.image-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1.1rem 1.25rem;
}
.card h2, .card h3 { margin: 0 0 0.5rem; }
.card h2 { font-size: 1.2rem; }
.card h3 { font-size: 1rem; }
.card form + form { margin-top: 0.75rem; }
.card-grid, .image-gallery, .machine-cards { display: grid; gap: 1rem; }
.card-title, .machine-card-holder { font-size: 1.05rem; font-weight: 600; }
.card-sub,
.machine-card-site, .machine-card-application, .machine-card-status, .machine-card-film-supply,
.model-machine-status, .manufacturer-model-name { color: var(--muted); font-size: 0.9rem; }
.card-highlight { border-left: 3px solid var(--brand); }

/* Key/value detail table */
.kv th { width: 38%; }
/* A value can be a name at its column's full width with no space in it, such
   as a work item's company (issue #294 R-11); like `.card dd` below, only the
   value may break inside a word. */
.kv td { overflow-wrap: anywhere; }
/* The Owner row's label is its heading (issue #606); the label's own bottom
   margin would lift it 2px off the row's middle. */
.kv th label { margin: 0; }
/* The machine register's <dl> reads as a .kv table: dt and dd join the th
   and td rules above, and this sets the same label column. Grid rather than a breakpoint, so it
   needs no entry in the mobile collapse list (#112): at phone width the
   label column holds at 8.5rem, wide enough for "Configuration notes" to wrap
   between words, and the value keeps the rest. Only the value may break
   inside a word, because only it carries free text. */
.card dl {
    display: grid; grid-template-columns: minmax(8.5rem, 38%) minmax(0, 1fr);
    margin: 0;
}
.card dt, .card dd { margin: 0; }
.card dd { overflow-wrap: anywhere; }

/* Chips — reuse the message palette. Two treatments share one box (#165
   ruling 1): a state chip fills it, the type chip outlines it. The border
   slot lives on .chip so the two sit at the same height side by side; a
   filled chip leaves it transparent and .chip-type leaves the background
   transparent instead. Structural rather than a colour rule — the chip's
   text is the identity — so a further card type needs no rule here. */
/* A machine card's attention .cue is a warning chip under its own name. */
.chip,
.cue {
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.1rem 0.55rem; border-radius: 999px;
    border: 1px solid transparent;
    font-size: 0.72rem; font-weight: 600; letter-spacing: 0.03em;
    text-transform: uppercase; white-space: nowrap;
}
.chip::before,
.cue::before {
    content: ""; width: 0.45rem; height: 0.45rem;
    border-radius: 50%; background: currentColor; opacity: 0.7;
}
.chip-neutral { background: var(--neutral-bg); color: var(--neutral-fg); }
.chip-active  { background: var(--info-bg); color: var(--info-fg); }
.chip-done    { background: var(--ok-bg); color: var(--ok-fg); }
.chip-warning,
.cue          { background: var(--warning-bg); color: var(--warning-fg); }
/* The outlined chips: the type marker (issue #165) and a relationship's kind
   on the person page (issue #614), which is not a work item's type and so does
   not wear .chip-type, the one template issue #165's sweep allows it. */
.chip-type,
.chip-relationship { background: transparent; border-color: currentColor; color: var(--muted); }
/* A Related to row's End control sits on the row's line (issue #626 R-1). */
.relationship-end { display: inline; margin: 0; }
/* An annotation (issue #175): a marker the spine computes for a card, which is
   neither its identity nor its state, so it is both filled and bordered, which
   neither #165 treatment is. Red from the error message palette, and an
   exclamation mark in place of the dot; the text still carries the meaning.
   After .chip-type so source order settles the equal-specificity overrides. */
.chip-annotation { background: var(--error-bg); border-color: var(--error-fg); color: var(--error-fg); }
.chip-annotation::before {
    content: "!"; width: auto; height: auto;
    border-radius: 0; background: none; opacity: 1;
}

/* Buttons — including link-styled-as-button */
.btn, a.btn, .pagination a {
    display: inline-flex; align-items: center; gap: 0.4rem;
    padding: 0.45rem 0.9rem; border-radius: var(--radius);
    font: inherit; font-weight: 600; cursor: pointer; text-decoration: none;
    border: 1px solid var(--border-strong); background: var(--surface); color: var(--ink);
}
.btn:hover, a.btn:hover, .pagination a:hover { background: var(--hover); text-decoration: none; }
.btn-primary, a.btn-primary { background: var(--accent); color: var(--on-fill); border-color: var(--accent); }
.btn-primary:hover, a.btn-primary:hover { background: var(--accent-hover); color: var(--on-fill); }
.btn-row { display: flex; gap: 0.6rem; flex-wrap: wrap; }
.btn[disabled] { opacity: 0.6; cursor: not-allowed; }

/* Flow stepper — the document lifecycle */
.stepper { display: flex; flex-wrap: wrap; gap: 0.4rem; list-style: none; margin: 0 0 1.5rem; padding: 0; }
.stepper li {
    display: flex; align-items: center; gap: 0.45rem;
    padding: 0.35rem 0.75rem; border-radius: var(--radius);
    font-size: 0.85rem; background: var(--step-bg); color: var(--faint);
}
.stepper .num {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.25rem; height: 1.25rem; border-radius: 50%;
    font-size: 0.72rem; font-weight: 600; background: var(--step-num); color: var(--on-fill);
}
.stepper .done { background: var(--ok-bg); color: var(--ok-fg); }
.stepper .done .num { background: var(--ok-fg); }
.stepper .current { background: var(--accent-tint); color: var(--accent-hover); font-weight: 600; }
.stepper .current .num { background: var(--accent); }

/* Questionnaire: chat transcript */
.chat-layout { display: grid; grid-template-columns: 1fr 260px; gap: 1.25rem; align-items: start; }
.transcript { display: flex; flex-direction: column; gap: 0.75rem; }
.msg { max-width: 78%; padding: 0.6rem 0.85rem; border-radius: 10px; }
.msg .who { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--faint); margin-bottom: 0.2rem; }
.msg-assistant { align-self: flex-start; background: var(--bubble); border-bottom-left-radius: 2px; }
.msg-operator  { align-self: flex-end; background: var(--accent-tint); border-bottom-right-radius: 2px; }
.chat-input { margin-top: 1rem; display: flex; flex-direction: column; gap: 0.6rem; }
.chat-input textarea { width: 100%; min-height: 4.5rem; resize: vertical; }

/* Coverage sidebar */
.coverage { display: flex; flex-direction: column; gap: 0.5rem; }
.coverage li { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; font-size: 0.9rem; }

/* Monospace draft / preview panel */
.draft-panel {
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 1rem 1.1rem; font-family: var(--mono); font-size: 0.85rem; line-height: 1.55;
    white-space: pre-wrap; overflow-x: auto; margin: 0; color: var(--panel-ink);
}

/* Section review: two-column content + progress list */
.review-layout { display: grid; grid-template-columns: 240px 1fr; gap: 1.25rem; align-items: start; }
.section-list { display: flex; flex-direction: column; gap: 0.35rem; }
.section-list li a {
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
    padding: 0.5rem 0.7rem; border-radius: var(--radius); text-decoration: none;
    color: var(--ink); border: 1px solid transparent;
}
.section-list li a:hover { background: var(--hover); text-decoration: none; }
.section-list li a.current { background: var(--accent-tint); border-color: var(--accent-tint-border); font-weight: 600; }
.section-list li .plain { padding: 0.5rem 0.7rem; display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }

/* Form field stacking */
.field { display: flex; flex-direction: column; gap: 0.25rem; margin-bottom: 0.9rem; }
.field label { font-weight: 600; margin: 0; }
/* Checkboxes and radios keep their own size rather than spanning the field. A
   select in a `.kv` value cell, the Owner row's list (issue #606), spans it
   too, or its widest option would push the page sideways at phone width. */
.field input:not([type="checkbox"]):not([type="radio"]), .field textarea, .field select, .kv td select { width: 100%; }
/* A .stack's gap alone separates its fields; the margin is for fields outside one. */
.stack > .field { margin-bottom: 0; }

/* Coming-soon disabled list */
.coming-soon { list-style: none; margin: 0.5rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.coming-soon li {
    display: flex; align-items: center; justify-content: space-between;
    padding: 0.5rem 0.7rem; border: 1px dashed var(--border-strong); border-radius: var(--radius);
    color: var(--faint); font-size: 0.9rem;
}

/* Empty state */
.empty,
.empty-state { text-align: center; color: var(--faint); padding: 2rem 1rem; border: 1px dashed var(--border-strong); border-radius: var(--radius); }

/* Feedback thread inside a section */
.feedback { display: flex; flex-direction: column; gap: 0.6rem; margin: 0.75rem 0; }
.feedback .turn { font-size: 0.9rem; }
.feedback .turn .lbl { font-weight: 600; }

/* Success receipt */
.receipt-icon {
    width: 2.5rem; height: 2.5rem; border-radius: 50%; background: var(--ok-bg); color: var(--ok-fg);
    display: inline-flex; align-items: center; justify-content: center; font-size: 1.3rem; font-weight: 700;
}

/* Accessible visually-hidden label (kept in the a11y tree) */
.visually-hidden {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; border: 0;
    clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden;
    white-space: nowrap;
}

/* Small button variant — compact card controls */
.btn-small, a.btn-small, .pagination a { padding: 0.3rem 0.6rem; font-size: 0.82rem; }

/* ============================================================
   Enquiries board (Sales) — a lightweight kanban.
   The column row is the ONLY horizontal-scroll container; the
   page body never scrolls sideways (PRD desktop/mobile NFR).
   ============================================================ */
.board {
    display: flex; gap: 1rem; align-items: flex-start;
    overflow-x: auto; padding-bottom: 0.75rem;
    /* position: relative is load-bearing (#117): it makes the board the
       containing block for absolutely positioned descendants, so their
       layout overflow lands in this scroll container instead of escaping its
       clip to the root element, where a card in a far column widens the page
       itself. #117's case was a visually-hidden label on each card, gone with
       the card's controls (#411); the guard stays for whatever comes next. */
    position: relative;
}
.board-column {
    flex: 1 0 200px; min-width: 0; max-width: 480px;
    background: var(--ground); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 0.75rem;
}
.board-column-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 0.5rem; margin: 0 0 0.75rem; font-size: 0.95rem;
}
.board-column-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.5rem; padding: 0 0.4rem; border-radius: 999px;
    font-size: 0.72rem; font-weight: 600; background: var(--count-bg); color: var(--muted);
}
.board-column-body { display: flex; flex-direction: column; gap: 0.6rem; min-height: 2.5rem; }
.board-column-empty { color: var(--faint); font-size: 0.85rem; margin: 0.25rem 0; }
/* Sales renders the placeholder ALWAYS, so a column emptied by a drag shows it
   again with no JavaScript. The master board renders it only when the column
   is empty, so this rule does the opposite job there: it hides the stale
   placeholder a card DROPPED into an empty column now sits beside (#102 D7).
   One selector list rather than two rules, because the mechanism is the same
   and the two card families must not drift. */
.board-column-body:has(.enquiry-card) .board-column-empty,
.board-column-body:has(.work-item-card) .board-column-empty,
.board-column-body:has(.task-card) .board-column-empty { display: none; }

/* The card box is shared with the master board's card (#147): same surface,
   border, radius and column flow. Listed together rather than copied, so the
   two card families cannot drift. Everything below these rules is
   enquiry-specific.

   A card is one link, a child of the card element (#411; decisions log,
   3 October 2026): the link takes the card's padding and `flex: 1`, so every
   point inside the border is the link, and it carries the column flow the
   card used to. Decoration is suppressed in BOTH states, because the global
   `a:hover` rule would otherwise underline the whole card; only the title
   underlines, on hovering the card. The link's own `pointer` is the cursor on
   every board, dragging or not. */
.enquiry-card,
.work-item-card,
.task-card {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 0; padding: 0;
    display: flex; flex-direction: column;
}
/* A work item card is square (issue #432 R-11); a task card's corners are
   round (issue #431 R-7). */
.task-card { border-radius: 10px; }
/* A work item card's left edge is its card type's stripe (issue #432 R-12),
   one rule per `WorkItem.TYPE_STRIPES` entry. Colour never carries the type
   alone: the card keeps its type marker, or sits on its type's own board. */
.type-stripe-brand-strong { border-left: 4px solid var(--brand-strong); }
.type-stripe-accent { border-left: 4px solid var(--accent); }
.type-stripe-accent-deep { border-left: 4px solid var(--accent-deep); }
.type-stripe-accent-hover { border-left: 4px solid var(--accent-hover); }
.type-stripe-brand-deep { border-left: 4px solid var(--brand-deep); }
.card-link {
    flex: 1; padding: 0.7rem 0.8rem;
    display: flex; flex-direction: column; gap: 0.5rem;
    color: inherit;
}
.card-link,
.card-link:hover { text-decoration: none; }
.card-link:hover .work-item-card-title,
.card-link:hover .task-card-title,
.card-link:hover .enquiry-card-customer { text-decoration: underline; }
/* The card degrades by wrapping, with no breakpoint of any kind (#109). A flex
   container collects items into lines by each item's HYPOTHETICAL main size,
   then shrinks what is on each line, so `flex-wrap` degrades on max-content
   while `overflow-wrap` lowers the min-content floor shrinking cannot pass:
   wrap decides whether a row splits, breakability whether the split row fits.
   Both engage from the content, so a wide board is untouched and a ten-stage
   board (#103) needs no number revisited. The single "narrowest still-readable
   field" width is 7rem, used twice below and nowhere else. */
.enquiry-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.5rem; flex-wrap: wrap; }
/* `flex: 1 1 7rem` is what keeps the head row a CARD-width rule: `flex-wrap`
   alone wraps whenever the name's max-content plus the value does not fit,
   which restyles wide cards that are fine today. The basis says "wrap when the
   name cannot keep 112px" instead. When the value does drop to its own line it
   lands at main-start, left-aligned under the name — `justify-content:
   space-between` places a lone item there, so no declaration is added for it. */
.enquiry-card-customer { font-weight: 700; color: var(--ink); flex: 1 1 7rem; overflow-wrap: anywhere; }
.enquiry-card-value { font-size: 0.85rem; font-weight: 600; color: var(--muted); white-space: nowrap; }
/* `anywhere`, not `break-word`: only `anywhere` lowers the element's
   min-content size, which is the floor a flex item cannot shrink past.
   `break-word` breaks glyphs at paint time and leaves intrinsic size alone, so
   an unbreakable token would still push the row wide. Applied to the two
   free-text render targets only — the display name above and
   `product_type_display` here, both at most CharField(max_length=255). */
.enquiry-card-product { margin: 0; font-size: 0.88rem; color: var(--muted); display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; overflow-wrap: anywhere; }
/* The dt labels are single unbreakable words ("RECEIVED" is 62px), so without
   the wrap the meta row escapes the card by 9.28px at a 200px column even with
   both free-text fields breakable. */
.enquiry-card-meta { margin: 0; display: flex; gap: 1rem; flex-wrap: wrap; }
.enquiry-card-meta div { display: flex; flex-direction: column; }
.enquiry-card-meta dt { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--faint); margin: 0; }
.enquiry-card-meta dd { margin: 0; font-size: 0.85rem; }
/* Drag states (#71). */
.enquiry-card.is-busy { opacity: 0.55; cursor: progress; }
.enquiry-card-ghost { opacity: 0.4; border-style: dashed; border-color: var(--accent); }

/* Modal dialogs: the Sales board's outcome dialog (Won / Lost) and the master
   board's move-refusal dialog (#102). One box for both, by selector list
   rather than by copy — the same reason the card box is shared below. A copy
   had already drifted by a `.card-sub` margin before this was merged. */
.outcome-dialog,
.move-refusal-dialog {
    width: min(28rem, calc(100vw - 2rem));
    border: 1px solid var(--border-strong); border-radius: var(--radius);
    background: var(--surface); color: var(--ink); padding: 1.25rem;
}
.outcome-dialog::backdrop,
.move-refusal-dialog::backdrop { background: var(--backdrop); }
.outcome-dialog h2,
.move-refusal-dialog h2 { margin-bottom: 0.25rem; }
.outcome-dialog .card-sub,
.move-refusal-dialog .card-sub { margin: 0 0 1rem; }
.outcome-dialog .field:last-of-type { margin-bottom: 1.1rem; }
.field-hint,
.help-text { margin: 0; color: var(--faint); font-size: 0.82rem; }

/* ============================================================
   Enquiry detail (Sales) — the record beside its activity log.
   The .activity-* rules style WorkItemNote entries, a workitems concept,
   so they are not sales-owned despite living under this header.
   ============================================================ */
.detail-layout {
    display: grid; gap: 1rem; align-items: start;
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
}
.detail-text { margin: 0; overflow-wrap: anywhere; }
/* Machine detail's three blocks (issue #407 R-6). */
.machine-block {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.75rem;
    gap: 0.75rem;
}
.activity-entry,
.history-row { border-left: 3px solid var(--border-strong); padding-left: 0.7rem; }
/* A machine's location row names its site and company, each up to its
   column's full width with no space in it (issue #294 R-11). */
.history-row { overflow-wrap: anywhere; }
.activity-meta { margin: 0 0 0.15rem; font-size: 0.78rem; color: var(--faint); }

@media (max-width: 760px) {
    .chat-layout, .review-layout, .detail-layout { grid-template-columns: 1fr; }
}

/* ============================================================
   The General board (once the master board) — #147, and #102's drag.
   Reuses the .board / .board-column / .board-column-head /
   -count / -body / -empty layout above, and shares the card box
   with .enquiry-card. New here: the filter row, the Waiting
   disclosure, the card's own text rules, and (since #102) the
   card's drag states and its refusal dialog.
   Read-only until #102; a card is now draggable and clickable.
   ============================================================ */
.board-filters {
    display: flex; gap: 1rem; align-items: flex-end; flex-wrap: wrap;
    margin-bottom: 1rem;
}
/* Only what differs from the shared .field: the row lays its controls out
   side by side, so the checkbox and its label sit on one line. */
.board-filters .field-inline {
    flex-direction: row; align-items: center; gap: 0.4rem;
}
.board-filters label, .filters .field { font-size: 0.82rem; color: var(--muted); }
.board-filters select, .filters select, .filters input { font-size: 0.85rem; padding: 0.3rem 0.4rem; }

.board-column-title { margin: 0; font-size: 0.95rem; font-weight: 700; }
/* The disclosure triangle is the only affordance telling a user the column
   opens, and a <summary> only draws it while it stays display: list-item. So
   the shared flex head goes on a wrapper INSIDE the summary rather than on the
   summary itself, and no rule here has to undo the shared one. */
.board-column-disclosure > summary { cursor: pointer; }
/* A column's stack of archived cards (issue #594 K-1), at its foot below the
   drop list. Its titles are free text, so they wrap `anywhere` for the reason
   the card title below gives. */
.board-stack { margin-top: 0.75rem; font-size: 0.85rem; color: var(--muted); }
.board-stack > summary { cursor: pointer; }
.board-stack-list { margin: 0.5rem 0 0; padding-left: 1.1rem; }
.board-stack-list a { overflow-wrap: anywhere; }

/* `anywhere`, not `break-word`, for the same reason .enquiry-card-customer
   gives above: only `anywhere` lowers the element's min-content size, which is
   the floor a flex item cannot shrink past. Every field on this card is free
   text or a name, so every one of them can carry an unbreakable token. */
.work-item-card-title { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
.work-item-card-meta {
    margin: 0; display: flex; align-items: center; gap: 0.4rem;
    flex-wrap: wrap; font-size: 0.85rem; color: var(--muted);
    overflow-wrap: anywhere;
}
.work-item-card-owner {
    margin: 0; font-size: 0.85rem; color: var(--muted);
    overflow-wrap: anywhere;
}

/* Drag states (#102). Deliberately the same rules the enquiry card carries;
   the two are not merged into one selector list because each is set by a
   different script on a different board. */
.work-item-card.is-busy,
.task-card.is-busy { opacity: 0.55; cursor: progress; }
.work-item-card-ghost { opacity: 0.4; border-style: dashed; border-color: var(--accent); }

/* The task card (issue #431 R-6). Its chip is filled with its work item's
   card type's colour, one rule per `WorkItem.TYPE_FILLS` entry, each a green
   token with a text token meeting 4.5:1 on it; the type's label is in the
   chip as hidden text, so the colour is never its only carrier. */
.task-card-title { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
.task-mark { color: var(--muted); }
.task-card-meta,
.task-card-foot {
    margin: 0; display: flex; align-items: center; gap: 0.4rem;
    flex-wrap: wrap; font-size: 0.85rem; color: var(--muted);
    overflow-wrap: anywhere;
}
.task-card-foot { justify-content: space-between; }
.task-card-people { display: inline-flex; gap: 0.25rem; margin-left: auto; }
.type-chip {
    display: inline-block; max-width: 100%;
    padding: 0.1rem 0.55rem; border-radius: 999px;
    font-size: 0.78rem; font-weight: 600; overflow-wrap: anywhere;
}
.type-fill-accent-tint { background: var(--accent-tint); color: var(--ink); }
.type-fill-accent-tint-border { background: var(--accent-tint-border); color: var(--ink); }
.type-fill-accent { background: var(--accent); color: var(--on-fill); }
.type-fill-brand { background: var(--brand); color: var(--ink); }
.type-fill-accent-hover { background: var(--accent-hover); color: var(--on-fill); }
.assignee-badge {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.75rem; height: 1.75rem; border-radius: 50%;
    background: var(--count-bg); color: var(--ink);
    border: 1px solid var(--count-bg);
    font-size: 0.72rem; font-weight: 700;
}
/* A person's stored colour (issue #669 R-5); after .assignee-badge so it wins. */
.badge-colour-red { background: var(--badge-red); border-color: var(--badge-red); color: var(--on-fill); }
.badge-colour-orange { background: var(--badge-orange); border-color: var(--badge-orange); color: var(--on-fill); }
.badge-colour-olive { background: var(--badge-olive); border-color: var(--badge-olive); color: var(--on-fill); }
.badge-colour-teal { background: var(--badge-teal); border-color: var(--badge-teal); color: var(--on-fill); }
.badge-colour-blue { background: var(--badge-blue); border-color: var(--badge-blue); color: var(--on-fill); }
.badge-colour-indigo { background: var(--badge-indigo); border-color: var(--badge-indigo); color: var(--on-fill); }
.badge-colour-purple { background: var(--badge-purple); border-color: var(--badge-purple); color: var(--on-fill); }
.badge-colour-magenta { background: var(--badge-magenta); border-color: var(--badge-magenta); color: var(--on-fill); }
.assignee-badge-empty { background: transparent; border: 1px dashed var(--control-border); }
.assignee-badge-agent { border-radius: 0; border: 1px solid var(--ink); }

/* A work item card's tasks' progress and its owner (issue #432 R-13, R-14). */
.task-progress {
    margin: 0; display: flex; align-items: center; gap: 0.5rem;
    font-size: 0.85rem; color: var(--muted);
}
.task-progress-bar { display: inline-flex; flex: 1; }
.task-progress-bar progress { width: 100%; height: 0.5rem; accent-color: var(--accent); }
.work-item-card-owner,
.enquiry-card-meta dd:has(.assignee-badge) { display: flex; align-items: center; gap: 0.4rem; }
.enquiry-card-due { margin: 0; font-size: 0.85rem; color: var(--muted); }

/* ============================================================
   Register, library and directory surfaces (#335). Each is
   composed from a component above; nothing here is a new look.
   ============================================================ */
/* A wide table scrolls inside itself on a phone instead of widening the page. */
.table-wrap { overflow-x: auto; }

/* Review-gallery images: figures in the card grid, each a card (above). */
.image-gallery { grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); }
.image-card { margin: 0; display: flex; flex-direction: column; gap: 0.35rem; }
.image-card figcaption { font-size: 0.9rem; }
.image-card .card-sub { margin: 0; }

/* The machine index's filters: fields side by side, wrapping, with the
   submit row on a line of its own. Label and control sizing are the board
   filters', by selector list. */
.filters {
    display: grid; gap: 0 1rem; align-items: end;
    grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
}
.filters .btn-row { grid-column: 1 / -1; }
/* One height for the search box and the dropdowns (#340 scope 6.3). A select
   ignores line-height and a text input takes the body's 1.5, so their natural
   heights differ by about 2.4px; 2rem is the search box's own natural height,
   so only the dropdowns move. Measured: line-height alone leaves 2px. */
.filters select, .filters input[type="search"] { height: 2rem; }

/* Index cards in the card grid; their text joins .card-title and .card-sub. */
.machine-cards { grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); }
/* The card's link is its whole surface inside the border (issue #391 R-4): the
   link takes `.card`'s padding from the card, and `flex: 1` stretches it over
   any spare height the grid gives a card shorter than its row. */
.machine-card { display: flex; flex-direction: column; padding: 0; }
.machine-card-link {
    flex: 1;
    display: flex; flex-direction: column; gap: 0.3rem;
    padding: 1.1rem 1.25rem;
    color: inherit;
}
.machine-card-link,
.machine-card-link:hover { text-decoration: none; }
.machine-card p { margin: 0; }
.machine-card-cues { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-top: 0.3rem; }

/* Lists inside a register card: one row per entry, the name beside it. */
.model-machines, .manufacturer-models, .history { display: flex; flex-direction: column; gap: 0.5rem; }
.model-machine, .manufacturer-model { align-items: baseline; }

/* A machine or model's linked documents (issue #211). A title or synopsis may
   be one unbroken run of 255 or 200 characters, so each row breaks anywhere
   rather than pushing the page sideways at a phone's width (R-10). */
.linked-documents { display: flex; flex-direction: column; gap: 0.75rem; margin-top: 0.75rem; }
.linked-document { overflow-wrap: anywhere; }
.linked-document-title { font-weight: 600; }
.linked-document-meta, .linked-document-synopsis { margin: 0.2rem 0 0; }
.linked-document-meta { color: var(--muted); font-size: 0.9rem; }
.linked-document form { margin-top: 0.4rem; }
.documents-attach-label { margin: 0 0 0.4rem; color: var(--muted); font-size: 0.9rem; }

/* The library's documents section (issue #451 R-7). The file input stays in
   the page and focusable, so the keyboard reaches it and the label opens the
   picker, but only its label shows; the label carries the input's focus
   ring. Both forms wrap rather than widen the page at a phone's width. */
.quick-attach { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.file-picker { position: relative; max-width: 100%; }
.file-picker input[type="file"] {
    position: absolute; width: 1px; height: 1px;
    opacity: 0; overflow: hidden; clip-path: inset(50%);
}
.file-picker:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The library index's page links: small buttons in a row. */
.pagination { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; margin-top: 1rem; }

/* The directory picker: its results are a bordered list under the search
   box, hidden while it holds no choice so an empty box is not drawn. */
.picker { display: flex; flex-direction: column; gap: 0.4rem; }
.picker-results {
    display: flex; flex-direction: column; max-height: 16rem; overflow-y: auto;
    background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
}
.picker-results:not(:has(.picker-result)) { display: none; }
.picker-group { border: 0; margin: 0; padding: 0.25rem 0; }
.picker-group legend { padding: 0.25rem 0.7rem; color: var(--muted); font-size: 0.82rem; font-weight: 600; }
.picker-result { display: flex; align-items: center; gap: 0.5rem; padding: 0.45rem 0.7rem; margin: 0; cursor: pointer; }
.picker-result:hover { background: var(--hover); }
.picker-result input { margin: 0; }

/* The enquiry form offers the prospect name only while no company is chosen
   (#291 R-5.1): a checked company radio, "create a company" included, hides
   it, on first render and after each picker swap alike. "No company" is a
   checked radio too, with an empty value, so it is left out (issue #498 I-5). */
form:has([data-picker="company"] input:checked:not([value=""])) .field[data-field="prospect_name"] { display: none; }

/* Form chrome the model forms render. */
.required { color: var(--error-fg); }
.form-errors ul.errorlist { margin: 0; }

/* ============================================================
   Replacements for inline style attributes (#335). Each carries
   exactly the declaration a template used to write inline.
   `!important` is what makes them faithful, not a shortcut: an
   inline style outranks every rule, so without it `.card h3`
   and `.message` would win back the margins these remove.
   ============================================================ */
.flush       { margin: 0 !important; }
.mt-1        { margin-top: 1rem !important; }
.mb-1        { margin-bottom: 1rem !important; }
.mb-0        { margin-bottom: 0 !important; }
.stack-tight { gap: 0.25rem !important; }
.pre-line    { white-space: pre-line !important; }
