:root {
    --ex-deep: #26404a;
    --ex-accent: #4a7c7e;
    --ex-warm: #f7f4ef;
}

body {
    background-color: var(--ex-warm);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

main { flex: 1 0 auto; }

.bg-primary-deep { background-color: var(--ex-deep) !important; }

.footer {
    background-color: #ffffff;
    border-top: 1px solid #e3ded6;
}

.card { border-color: #e3ded6; }

.card-header {
    background-color: #fbfaf8;
    border-bottom-color: #e3ded6;
}

.btn-accent {
    background-color: var(--ex-accent);
    border-color: var(--ex-accent);
    color: #fff;
}
.btn-accent:hover { background-color: #3d6a6c; border-color: #3d6a6c; color: #fff; }

a { color: var(--ex-accent); }
a:hover { color: #3d6a6c; }

.masked { font-variant-numeric: tabular-nums; letter-spacing: 0.08em; }

.record-row:hover { background-color: #fbfaf8; }

.progress { background-color: #e3ded6; }

.htmx-indicator { opacity: 0; transition: opacity 150ms; }
.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { opacity: 1; }

/* Content-pending panels should read as a gap, not as a blank page. */
.pending-note {
    background-color: #fff8e8;
    border: 1px dashed #dcc9a0;
    border-radius: .5rem;
}

/* Hero panel on the sign-in and registration pages. */
.auth-hero {
    width: 100%;
    height: auto;
}

/* Marketing page imagery. The frames these once carried moved into the
   feather rule below, which clears border, radius and shadow: any of the
   three would trace the hard rectangle the mask removes. */
.marketing-image {
    width: 100%;
    height: auto;
}

/* Recovery codes. Monospace and generously spaced, because these get read
   off a printed sheet under stress, possibly by someone else. */
.recovery-code-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: .5rem;
}
.recovery-code-list li {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 1.05rem;
    letter-spacing: .06em;
    padding: .5rem .75rem;
    background-color: #fbfaf8;
    border: 1px solid #e3ded6;
    border-radius: .375rem;
    text-align: center;
}

/* Country flags on the guidance pages. Sized in em so they track the
   surrounding text, with a hairline border so the white bands stay visible
   against a light card header. */
.country-flag {
    height: 1em;
    width: auto;
    margin-right: .5rem;
    flex: 0 0 auto;
    border: 1px solid rgba(38, 64, 74, .18);
    border-radius: 2px;
    vertical-align: -.1em;
}

/* Illustration on the error pages. */
.error-illustration {
    width: 100%;
    height: auto;
}

/* Markdown tables in guidance prose (rate schedules, commission bands). Bootstrap
   only styles .table, and the markdown renderer emits a bare <table>. */
.topic-body table {
    width: 100%;
    margin: 1rem 0;
    border-collapse: collapse;
    font-size: .95rem;
}
.topic-body th,
.topic-body td {
    padding: .45rem .6rem;
    border-bottom: 1px solid #e3ded6;
    text-align: left;
    vertical-align: top;
}
.topic-body thead th {
    background-color: #fbfaf8;
    border-bottom: 2px solid #dcd5ca;
    font-weight: 600;
}
.topic-body tbody tr:last-child td { border-bottom: none; }

/* Decorative artwork beside a card (see templates/partials/section_art.html).
   Faded while that card still has something outstanding, full strength once it
   does not. The fade is ornament: the progress bar and the fields carry the
   real signal, because opacity reads as "loading" or "disabled" about as often
   as it reads as "not finished yet", and carries nothing at all to a screen
   reader. */
.section-art {
    flex: 0 0 240px;
    width: 240px;
    opacity: .22;
    filter: saturate(.5);
    transition: opacity .45s ease, filter .45s ease;
}
.section-art.is-complete {
    opacity: 1;
    filter: none;
}
@media (prefers-reduced-motion: reduce) {
    .section-art { transition: none; }
}
/* Feathered rather than framed. A hard-edged rectangle with a drop shadow
   reads as a pasted-in photograph — and in a product where a document card can
   hold a real scan of the will, an image that looks attached is an image
   somebody will take for evidence. The mask dissolves the edges into whatever
   is behind them, which works over both the white card and the cream page.
   No border-radius or box-shadow: a shadow is drawn from the element box, so
   it would trace the hard rectangle the mask just removed. */
.section-art img,
.auth-hero,
.marketing-image,
.error-illustration,
.soft-edge {
    /* Two linear masks intersected, rather than one radial. A radial gradient
       carves an oval out of the picture — the corners go first and hardest,
       which is a vignette, not a soft edge. These fade a narrow band at each
       of the four edges and leave the rectangle and the whole subject intact.

       `mask-composite: intersect` is what makes it an edge fade: without it
       the layers are added, and the union of "opaque in the middle
       horizontally" and "opaque in the middle vertically" is a cross. The
       -webkit- keyword is the legacy spelling of the same thing, kept for
       older WebKit. */
    -webkit-mask-image:
        linear-gradient(to right, transparent 0, #000 9%, #000 91%, transparent 100%),
        linear-gradient(to bottom, transparent 0, #000 9%, #000 91%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
        linear-gradient(to right, transparent 0, #000 9%, #000 91%, transparent 100%),
        linear-gradient(to bottom, transparent 0, #000 9%, #000 91%, transparent 100%);
    mask-composite: intersect;
    /* No frame, and this has to win over whatever the image class set. A
       border is painted at the element's edge and a shadow is drawn from its
       box, so either one traces the hard rectangle the mask just removed. */
    border: 0;
    border-radius: 0;
    box-shadow: none;
}

.section-art img {
    width: 100%;
    height: auto;
}

/* Bootstrap has no min-width utility, and a flex child defaults to
   min-width:auto — which stops long text ever wrapping or truncating inside
   one. Needed wherever content sits beside .section-art. */
.min-w-0 { min-width: 0; }

/* The four stages on the onboarding page. Numbered because the order is the
   point — beta testers were arriving with no idea what came after the form in
   front of them. Counter rather than a plain <ol> so the numeral can sit in
   the accent colour and hold its own column when an item runs to several
   lines. */
.ex-journey {
    list-style: none;
    counter-reset: ex-stage;
    padding-left: 0;
}
.ex-journey > li {
    counter-increment: ex-stage;
    position: relative;
    padding-left: 2.25rem;
    margin-bottom: .75rem;
}
.ex-journey > li::before {
    content: counter(ex-stage);
    position: absolute;
    left: 0;
    top: 0;
    width: 1.6rem;
    height: 1.6rem;
    border-radius: 50%;
    background: var(--ex-accent);
    color: #fff;
    font-size: .8rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: center;
}

@media print {
    body { background: #fff; }
    .d-print-none { display: none !important; }
    .card { break-inside: avoid; border: 1px solid #999; }
    .card-header { background: #eee !important; }
    a[href]:after { content: ""; }
    .summary-section { page-break-inside: avoid; }
    h2 { page-break-after: avoid; }
    .topic-body table { break-inside: avoid; }
    .recovery-code-list { break-inside: avoid; }
    .recovery-code-list li { border: 1px solid #999; background: #fff !important; }
}

/* Management panels on the About page.
 *
 * A fixed square that the photograph fills rather than stretches: headshots
 * arrive at whatever size and aspect somebody's phone produced, and letting
 * one set the height makes three panels of three different depths.
 * `object-fit: cover` crops to the middle, which is where a face is.
 *
 * The blank variant is the same circle with an initial in it, so a panel
 * waiting for a photograph is a plain shape rather than a broken image. */
.team-photo {
    width: 7rem;
    height: 7rem;
    border-radius: 50%;
    object-fit: cover;
    display: inline-block;
}

.team-photo-blank {
    background: var(--bs-secondary-bg, #e9ecef);
    color: var(--bs-secondary-color, #6c757d);
    font-size: 2.5rem;
    line-height: 7rem;
    text-align: center;
    text-transform: uppercase;
}

/* The field that stopped a save.
 *
 * **No template change and no JavaScript.** Django writes
 * `aria-invalid="true"` on a widget whose field has an error, so styling that
 * attribute reaches every form in the product at once — including the several
 * that render `{{ field }}` directly rather than going through
 * `_field.html`, which is exactly where a hand-added class would have been
 * forgotten. It is also the attribute a screen reader already announces, so
 * the colour and the spoken version cannot drift apart.
 *
 * **Orange, not red.** Most of these are not mistakes. They are questions not
 * yet answered, on a form somebody is filling in during the worst week of
 * their life, and red is the colour of having done something wrong. The tint
 * carries it on its own for anyone who cannot separate the hues, and the
 * message underneath says what is actually needed.
 */
.form-control[aria-invalid="true"],
.form-select[aria-invalid="true"],
.form-check-input[aria-invalid="true"] {
    border-color: #c2710a;
    background-color: #fffaf2;
}

.form-control[aria-invalid="true"]:focus,
.form-select[aria-invalid="true"]:focus,
.form-check-input[aria-invalid="true"]:focus {
    border-color: #c2710a;
    box-shadow: 0 0 0 0.25rem rgba(194, 113, 10, 0.25);
}

/* The label above it, so the field is findable while scrolling rather than
 * only once it is on screen. */
.mb-3:has([aria-invalid="true"]) > .form-label,
.col-md-6:has([aria-invalid="true"]) > .form-label,
.col-12:has([aria-invalid="true"]) > .form-label {
    color: #8a4f07;
}
