/* ==========================================================================
   Untitled Needleworks — Careers
   Hand-written. No framework, no build step, no third-party CSS, and no
   third-party requests of any kind. The font is served from this origin.
   ========================================================================== */

/* The display face, copied from the main site rather than linked to it: this
   is a separate Pages deployment and a cross-origin font request would be
   exactly the third-party call the cookie policy says the site does not make.

   Two faces under ONE family name, split by DISJOINT unicode-range. The subset
   covers printable ASCII plus the typographic punctuation the font carries; the
   full face covers everything else. A browser only fetches a face some
   character on the page maps to, so ordinary English copy never pulls the
   67 KB full font. The ranges are the main site's, unchanged — they are the
   complement of each other and must stay that way. */
@font-face {
    font-family: 'UN Font';
    src: url('./fonts/UN-Font.woff2') format('woff2'),
         url('./fonts/UN-Font.woff')  format('woff'),
         url('./fonts/UN-Font.ttf')   format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    unicode-range:
        U+0-1F, U+7F-9F, U+A1-AA, U+AC-B6, U+B8-BA, U+BC-2012,
        U+2015-2017, U+201B, U+201F-2021, U+2023-2025, U+2027-2038,
        U+203B-10FFFF;
}
@font-face {
    font-family: 'UN Font';
    src: url('./fonts/UN-Font.subset.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    unicode-range:
        U+20-7E, U+A0, U+AB, U+B7, U+BB, U+2013-2014, U+2018-201A,
        U+201C-201E, U+2022, U+2026, U+2039-203A;
}

/* ============================== TOKENS ==============================
   The main site's palette and scale verbatim. Same names, same values, so a
   rule can move between the two stylesheets without translation.

   No dark theme here. The main site has one and /about/ deliberately does not;
   this is the /about/ case — an applicant fills this in once, in daylight, and
   a second colourway is surface area with no reader asking for it. The tokens
   are the shared ones, so adding it later is a block of overrides, not a
   rewrite. */
:root {
    --cream-00: #F7F4ED;
    --cream-10: #EFE9DC;
    --cream-20: #E2D9C8;

    --brown-90: #241B12;
    --brown-70: #4A3728;
    --brown-50: #6F5945;
    --brown-20: #C4B49E;

    --orange-60: #C2571C;   /* >=24px text, rules, active states */
    --orange-70: #A8460F;   /* small text only — 5.4:1 on cream */

    --font-display: 'UN Font', 'Didot', 'Bodoni MT', Georgia, 'Times New Roman', serif;
    --font-text: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto,
                 Helvetica, Arial, sans-serif;

    --s1: 4px;   --s2: 8px;   --s3: 12px;  --s4: 16px;
    --s5: 24px;  --s6: 32px;  --s7: 48px;  --s8: 64px;
    --s9: 96px;  --s10: 128px; --s11: 160px; --s12: 224px;

    --shell: 1280px;
    --measure: 29.5rem;

    --ease: cubic-bezier(0.22, 1, 0.36, 1);
    --t-micro: 240ms;
    --t-reveal: 720ms;

    --gutter: var(--s5);
    --band-pad: var(--s8);

    /* Careers-only. A form needs a field ground the main site never needed. */
    --field-bg: #FFFDF8;
    --field-line: var(--cream-20);
    --stitch: 1px dashed var(--brown-50);
}

@media (min-width: 760px) {
    :root { --gutter: var(--s7); --band-pad: var(--s9); }
}
@media (min-width: 1100px) {
    :root { --gutter: var(--s8); }
}

/* ============================== RESET ============================== */
*, *::before, *::after { box-sizing: border-box; }
body, h1, h2, h3, h4, p, figure, blockquote, dl, dd, ol, ul, fieldset, legend {
    margin: 0; padding: 0;
}
fieldset { border: 0; min-width: 0; }
ul, ol { list-style: none; }
img { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    background: var(--cream-00);
}

body {
    font-family: var(--font-text);
    font-size: 1.0625rem;
    line-height: 1.75;
    color: var(--brown-70);
    background: var(--cream-00);
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.skip {
    position: absolute; left: -9999px; top: 0;
    background: var(--brown-90); color: var(--cream-00);
    padding: var(--s3) var(--s5); z-index: 100;
}
.skip:focus { left: 0; }

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

:focus-visible {
    outline: 2px solid var(--orange-60);
    outline-offset: 3px;
}

/* ============================== LAYOUT ============================== */
.shell {
    width: 100%;
    max-width: var(--shell);
    margin: 0 auto;
    padding-inline: var(--gutter);
}
.shell--narrow { max-width: 46rem; }

.band { padding-block: var(--band-pad); }
main { flex: 1 0 auto; }

/* ============================== TYPE ============================== */
.title {
    font-family: var(--font-display);
    font-weight: 400;
    font-size: clamp(2rem, 6vw, 3.5rem);
    line-height: 1.05;
    letter-spacing: 0.01em;
    color: var(--brown-90);
}
.sub {
    font-family: var(--font-display);
    font-weight: 400;
    font-size: clamp(1.375rem, 3.2vw, 1.875rem);
    line-height: 1.2;
    color: var(--brown-90);
}
.lead {
    font-size: clamp(1.0625rem, 2.2vw, 1.25rem);
    line-height: 1.6;
    color: var(--brown-90);
    max-width: 34rem;
}
.index {
    font-size: 0.75rem;
    line-height: 1.4;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--brown-50);
}
.index span { color: var(--orange-70); margin-right: var(--s3); }
.prose p + p { margin-top: var(--s4); }
.prose { max-width: var(--measure); }

/* The pull-quote carried over from the main site's /careers/ page.

   A <p>, not a <blockquote>. Nobody said it and there is nobody to cite — it is
   a display line about us, and marking it up as a quotation would tell a screen
   reader it is somebody's words when it is not.

   The rule down its left is the same dashed stitch the rest of the site uses
   for a seam, stood on end. Narrower than .lead's measure on purpose: a display
   face at this size wants fewer words to a line. */
.quote {
    max-width: 32rem;
    padding-left: var(--s5);
    border-left: var(--stitch);
    font-family: var(--font-display);
    font-size: clamp(1.25rem, 3.2vw, 1.75rem);
    line-height: 1.3;
    color: var(--brown-70);
}

/* =============================== HERO ==============================
   The banner carried over from the old careers app — the only asset from it
   worth taking. It appears on the roles list and nowhere else: the role page is
   a form an applicant has already decided to fill in, and the status page is a
   lookup. A decorative band above either would be something to scroll past.

   max-width is the file's own pixel width. The shell runs to 1152px of content
   at the widest gutter, so without this the browser would stretch a 1080px
   image by 7% for no gain. Nothing here upscales it.

   The dark ground is what shows in the frame while the image is still in
   flight, and is close to the banner's own left edge, so the swap is quiet
   rather than a cream flash. */
.hero {
    max-width: 1080px;
    margin-block-end: var(--s7);
    border: 1px solid var(--cream-20);
    background: var(--brown-90);
}
.hero img { width: 100%; }

/* ============================ THE GRAIN LINE ============================
   The challenge that sits between the form and the send button.

   Drawn in the same hand as the 404 needle and the sewing machine on /about/:
   open strokes, one weight, `fill: none` almost everywhere, and the dashed
   stitch the rest of this site already uses. The only filled thing is the
   cloth, and only just — a wash of cream-10 so the pattern piece has something
   to sit on rather than floating in the page ground.

   NOTHING HERE ANIMATES. The piece follows the control directly, with no
   transition and no easing, which is both the better interaction and the
   reason prefers-reduced-motion has almost nothing to do: the one transition
   in the block is the status line's colour, and it is turned off below. A
   rotating diagram that eased into place would be a motion problem for the
   people least able to say so.
   ====================================================================== */
.grain {
    margin-top: var(--s7);
    padding-top: var(--s6);
    border-top: var(--stitch);
    max-width: 38rem;
}
.grain__intro { margin-top: var(--s3); }
/* `display: block` on a class beats the user agent's [hidden] rule, so the
   figure stayed on screen when the text alternative was switched on. Toggling
   .hidden through a class of our own would work too; this keeps the plain
   `hidden` attribute meaningful, which is what assistive technology reads. */
.grain__fig[hidden] { display: none; }
.grain__fig {
    display: block;
    width: 100%;
    max-width: 26rem;
    height: auto;
    margin-top: var(--s5);
    border: 1px solid var(--cream-20);
    background: var(--field-bg);
}
.grain__cloth   { fill: var(--cream-10); stroke: none; }
.grain__weave   { fill: none; stroke: var(--brown-20); stroke-width: 1; }
/* The two long edges. Dashed at the 404's own 9 7, scaled to this viewBox. */
.grain__selv    { fill: none; stroke: var(--brown-50); stroke-width: 1.75;
                  stroke-dasharray: 7 5; }
.grain__piece   { fill: none; stroke: var(--brown-90); stroke-width: 1.75;
                  stroke-linejoin: round; stroke-linecap: round; }
/* Pattern markings that are NOT the grain line: a stitch line following the
   outline, a dart, and the notches. They are on the piece because they are on
   a real pattern piece, and because a solver has to work out which line it is
   being asked about rather than fitting the only line there is. */
.grain__mark    { fill: none; stroke: var(--brown-50); stroke-width: 1;
                  stroke-dasharray: 4 4; }
.grain__notch   { fill: none; stroke: var(--brown-90); stroke-width: 1.5;
                  stroke-linecap: round; }
/* The grain line itself, and the only orange in the drawing: it is the thing
   being turned, and nothing else should compete for that role. */
.grain__line    { fill: none; stroke: var(--orange-60); stroke-width: 2;
                  stroke-linecap: round; stroke-linejoin: round; }

.grain__control { margin-top: var(--s5); }
.grain__control label { display: block; font-weight: 600; color: var(--brown-90); }
.grain__range {
    width: 100%; max-width: 26rem; margin-top: var(--s3);
    accent-color: var(--orange-60);
    /* 44px of thumb target on a phone. A range is a drag on touch, and the
       default 16px thumb is under every tap-target guideline there is. */
    height: 44px;
}
.grain__read {
    margin-top: var(--s2);
    font-variant-numeric: tabular-nums;
    color: var(--brown-50);
    font-size: 0.9375rem;
}

.grain__alt {
    margin-top: var(--s4);
    background: none; border: 0; padding: var(--s2) 0;
    color: var(--orange-70); text-decoration: underline;
    text-underline-offset: 0.18em; cursor: pointer;
    min-height: 44px;
}
.grain__alt:hover { color: var(--brown-90); }

/* The text path. Not a fallback and not hidden behind a detection heuristic —
   it is one button away for anybody, because guessing who needs it is both
   unreliable and rude. It carries the same two numbers the slider announces
   through aria-valuetext, so the task is identical: press an arrow key until
   the numbers agree. */
.grain__text {
    margin-top: var(--s5);
    padding: var(--s5);
    border: var(--stitch);
    background: var(--field-bg);
}
.grain__text p + p { margin-top: var(--s3); }
.grain__text strong { color: var(--brown-90); font-variant-numeric: tabular-nums; }

.grain__status {
    margin-top: var(--s4);
    font-size: 0.9375rem;
    color: var(--brown-70);
    transition: color var(--t-micro) var(--ease);
}
.grain__status--ok  { color: var(--orange-70); font-weight: 600; }
.grain__status--no  { color: var(--brown-90); }

@media (prefers-reduced-motion: reduce) {
    .grain__status { transition: none; }
}

/* ============================== TRUST ==============================
   The anti-phishing panel. It sits at the foot of every page's main content
   rather than in a box near the top: it is a thing to check against when
   something feels wrong, not a thing to read before applying, and putting a
   warning above the roles list would make the real site feel like the risk.

   Wider than --measure, which is set for running prose. This is four short
   paragraphs read one at a time, and at 29.5rem each one runs long enough to
   lose its shape.

   .intro — the "How this works" block above the roles list — is here too. It
   takes the seam, the dashed rule with air above it, from this rule rather than
   repeating the three declarations, so the page's two breaks cannot drift.
   Everything below this rule stays .trust's alone: its type is a size down
   because it is fine print an applicant checks us against, where .intro is the
   thing they came to read. */
.trust,
.intro {
    max-width: 38rem;
    margin-top: var(--s9);
    padding-top: var(--s6);
    border-top: var(--stitch);
}
.intro .sub { margin-top: var(--s3); }
.intro p { margin-top: var(--s4); }

.trust p {
    font-size: 0.9375rem;
    line-height: 1.65;
    color: var(--brown-70);
}
.trust p:first-of-type { margin-top: var(--s4); }
.trust p + p { margin-top: var(--s3); }
.trust strong { color: var(--brown-90); }

/* =============================== NAV =============================== */
/* STATIC below 760, sticky above it.
 *
 * The header wraps to three rows at 320 — mark, then the three links over two
 * lines — because three labels totalling 279px do not fit 257px of content
 * width. That is the right answer to the overlap, and it is 168px tall.
 *
 * Pinned, 168px is about a third of a 320x568 viewport, permanently, on pages
 * whose main content is a long form. A header that large stops being navigation
 * and starts being a wall the applicant scrolls the form past.
 *
 * So below the breakpoint it scrolls away with the page and comes back at the
 * top, where a header belongs on a phone. Nothing is hidden and nothing is
 * shrunk: the wordmark is still 198px, all three links are still present, and
 * the wrapping is unchanged. Only the pinning goes.
 *
 * Above 760 the header is one 68px row and pinning costs almost nothing, so it
 * stays as designed.
 *
 * `top` and `z-index` live in the media query rather than the base rule
 * because both are inert on a static element; leaving them outside would be two
 * declarations that read as if they do something. */
.nav {
    border-bottom: 1px solid var(--cream-20);
    background: rgba(247, 244, 237, 0.88);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    position: static;
}
@media (min-width: 760px) {
    .nav {
        position: sticky;
        top: 0;
        z-index: 50;
    }
    /* Anchor jumps must clear the bar the header became. Without this the skip
       link lands #main underneath 69px of sticky header — the first thing a
       keyboard user does on the page, and the content they asked for is behind
       the chrome. Derived rather than typed: s3 padding top and bottom, the
       44px tap-target row, and the 1px border. Only inside this query, because
       below 760 the header is static and there is nothing to clear. */
    html { scroll-padding-top: calc(var(--s3) * 2 + 44px + 1px); }
}
.nav__inner {
    max-width: var(--shell);
    margin-inline: auto;
    padding: var(--s3) var(--gutter);
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* WRAP, and this is half of the overlap fix.
       Without it the two children have to share one line however little room
       there is, and the only way flexbox can do that is by squeezing them. With
       it, the links drop to a second row when the mark and the links cannot
       both fit — which is the honest answer at 320px, and costs a row of height
       rather than costing legibility. */
    flex-wrap: wrap;
    gap: var(--s2) var(--s5);
}

/* 44x44 from one rule at every width, the main site's convention. The floor is
   on the box so the type is untouched.

   `flex: 0 0 auto` is the other half of the overlap fix, and the important
   half. `min-width: 44px` is what caused the bug: setting ANY min-width on a
   flex item replaces the automatic minimum size (`min-width: auto`) that would
   otherwise stop it shrinking below its own content. So the floor that was
   added for the tap target quietly told flexbox it could squeeze this box to
   44px — and it did, to 54px at 320. `white-space: nowrap` meanwhile kept the
   TEXT at its full 198px, so the ink rendered outside its own box and painted
   straight across the nav links. The box never overlapped anything; the
   lettering did, which is why a rectangle-intersection test reported "none"
   while the screenshot plainly showed the collision.

   flex-shrink: 0 puts the floor back. min-width stays as what it was meant to
   be all along — a floor for content smaller than 44px, never a licence to
   shrink to it. */
.nav__mark {
    font-family: var(--font-display);
    font-size: 0.9375rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--brown-90);
    text-decoration: none;
    white-space: nowrap;
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    min-height: 44px;
    min-width: 44px;
}
/* The links wrap among themselves too, so three of them are not forced onto one
   272px line at 320. The row gap keeps two rows of 44px targets from butting
   against each other. */
.nav__links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1) var(--s4);
    align-items: center;
}
.nav__links a {
    font-size: 0.6875rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--brown-50);
    text-decoration: none;
    position: relative;
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding-bottom: 3px;
}
/* Hit area and underline are separate pseudo-elements: the box stays at text
   width so no label shifts and no underline is wider than its word. */
.nav__links a::before {
    content: '';
    position: absolute;
    left: 50%; top: 50%;
    transform: translate(-50%, -50%);
    width: 44px; min-width: 100%;
    height: 44px;
}
.nav__links a::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 1px;
    background: var(--orange-60);
    opacity: 0;
    transition: opacity var(--t-micro) var(--ease);
}
.nav__links a:hover::after,
.nav__links a[aria-current="page"]::after { opacity: 1; }

/* ============================== FOOTER ============================== */
.foot {
    background: var(--brown-90);
    color: var(--brown-20);
    padding-block: var(--s7);
    margin-top: var(--s9);
    flex-shrink: 0;
}
.foot a { color: var(--cream-20); text-decoration: none; }
.foot a:hover { color: var(--orange-60); }
.foot__mark {
    font-family: var(--font-display);
    font-size: 1rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--cream-00);
}
.foot__line { margin-top: var(--s2); font-size: 0.9375rem; max-width: 34ch; }

/* Brand, hiring channels, policies — three columns across the width.

   It was `minmax(0, 1fr) auto`, which is what put a lake in the middle: the
   brand column absorbed every spare pixel and the three social labels hugged
   the right edge, so at 1024 and wider several hundred pixels of the footer
   were doing nothing. Two blocks cannot fill a 1280px row; three can.

   The proportions are the main site's own footer language — it runs
   `1.35fr 0.85fr 0.85fr 1.25fr` at its desktop breakpoint, brand widest and the
   link lists narrower. Dropping its fourth column (the address block, which
   this footer does not carry) leaves 1.35 / 0.85 / 0.85, and the column gap
   escalates s7 -> s8 the way the main site's does. Same rhythm, one column
   fewer.

   Below 760 it is a single column and everything stacks, unchanged. */
.foot__grid { display: grid; gap: var(--s7); }
@media (min-width: 760px) {
    .foot__grid {
        grid-template-columns: 1.35fr 0.85fr 0.85fr;
        column-gap: var(--s7);
        row-gap: var(--s5);
        align-items: start;
    }
}
/* The brand column is narrowed at the wide breakpoint. At 1.35fr it was 453px
   at 1440 while its ink stops around 315 — the subtitle is capped at 34ch for
   readability, so the column carried ~138px of internal slack and the gap
   before the next column read as twice the one after it. 1.1fr sizes the
   column closer to what it actually contains. It is still the widest of the
   three, which is the main site's proportion; it is just no longer wider than
   its own content. */
@media (min-width: 1000px) {
    .foot__grid {
        grid-template-columns: 1.1fr 0.95fr 0.95fr;
        column-gap: var(--s8);
    }
}

/* Mark on the left, name and line to the right of it, on one row — the two
   are one identity and reading them as a stack made the mark look like a
   separate ornament sitting above some text. */
.foot__brand {
    display: flex;
    align-items: center;
    gap: var(--s4);
}
/* 88px for a 264px file — 3x, which is the ceiling any current display asks
   for. The height is pinned as well as the width so the reserved box is square
   before the image arrives and nothing below it moves, and flex-shrink is off
   for the same reason the nav mark's is: a squeezed box with a fixed-size
   image inside it is how the header bug happened. */
.foot__logo {
    width: 88px;
    height: 88px;
    flex: 0 0 auto;
}
.foot__id { min-width: 0; }

.foot__h {
    font-size: 0.6875rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--brown-20);
    margin-bottom: var(--s2);
}
/* The main site's footer lists are block links with generous vertical padding
   rather than text runs, because in its layout the footer IS the navigation
   below 640. The same treatment here for the same reason it works there: a
   column of four policy links wants to be four targets, not a paragraph. */
.foot__list { font-size: 0.9375rem; }
.foot__list a {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding-right: var(--s2);
}
.foot__list li + li { margin-top: 2px; }

.foot__social { display: flex; flex-direction: column; }
.foot__social a {
    display: flex;
    align-items: center;
    gap: var(--s3);
    /* The tap target. 44 tall from the row and well past 44 wide from the icon
       plus the label, so it clears 2.5.5 on both axes without a fixed width. */
    min-height: 44px;
    padding-right: var(--s2);
    font-size: 0.9375rem;
}

/* Line art, monochrome, inheriting the link colour — so the icon moves to the
   accent on hover and focus with the word, rather than staying behind. */
.ico {
    width: 24px;
    height: 24px;
    flex: none;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}
/* The one filled part in the set: the tittle of the "i", which at 24px is
   below the size a stroked ring reads at. */
.ico__dot { fill: currentColor; stroke: none; }

.foot__bottom {
    margin-top: var(--s7);
    padding-top: var(--s4);
    border-top: 1px solid rgba(196, 180, 158, 0.25);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s2) var(--s5);
    align-items: center;
    justify-content: space-between;
    font-size: 0.8125rem;
}

/* ============================== BUTTONS ============================== */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    min-height: 44px;
    padding: var(--s3) var(--s5);
    border: 1px solid var(--brown-50);
    border-radius: 2px;
    background: transparent;
    color: var(--brown-90);
    font-size: 0.8125rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--t-micro) var(--ease),
                border-color var(--t-micro) var(--ease),
                color var(--t-micro) var(--ease);
}
.btn:hover { border-color: var(--brown-90); background: var(--cream-10); }
/* The single accent per viewport. */
.btn--primary {
    background: var(--brown-90);
    border-color: var(--brown-90);
    color: var(--cream-00);
}
.btn--primary:hover { background: var(--orange-70); border-color: var(--orange-70); }
.btn--small { min-height: 44px; padding: var(--s2) var(--s4); font-size: 0.75rem; }
.btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.btn[disabled]:hover { background: transparent; border-color: var(--brown-50); }
.btn--primary[disabled]:hover { background: var(--brown-90); border-color: var(--brown-90); }

.btn-row { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center; }

/* ============================== ROLE CARDS ============================== */
.roles { display: grid; gap: var(--s5); margin-top: var(--s7); }
@media (min-width: 760px) { .roles { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .roles { grid-template-columns: repeat(3, 1fr); } }

.role-card {
    border: 1px solid var(--cream-20);
    background: rgba(255, 253, 248, 0.7);
    padding: var(--s5);
    display: flex;
    flex-direction: column;
    gap: var(--s3);
}
.role-card__title {
    font-family: var(--font-display);
    font-size: 1.375rem;
    line-height: 1.2;
    color: var(--brown-90);
}
.role-card__meta {
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--brown-50);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s1) var(--s3);
}
.role-card__desc {
    font-size: 0.9375rem;
    /* Three lines, then it stops. The full text is one click away and a card
       grid with one very tall card reads as broken. */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.role-card .btn { margin-top: auto; align-self: flex-start; }

/* ============================== STATES ============================== */
.state {
    border: var(--stitch);
    padding: var(--s7) var(--s5);
    text-align: center;
    margin-top: var(--s7);
}
.state__title {
    font-family: var(--font-display);
    font-size: 1.5rem;
    color: var(--brown-90);
    margin-bottom: var(--s3);
}
.state p { max-width: 40ch; margin-inline: auto; }
.state .btn { margin-top: var(--s5); }

.err-box {
    border: 1px solid var(--orange-70);
    border-left-width: 3px;
    background: rgba(168, 70, 15, 0.05);
    padding: var(--s4);
    margin-top: var(--s5);
    color: var(--brown-90);
    font-size: 0.9375rem;
}
.err-box strong { color: var(--orange-70); }

/* Busy indicator. Reduced motion gets a static word, never a frozen spinner. */
.busy { color: var(--brown-50); font-size: 0.9375rem; }
.busy::after {
    content: '';
    display: inline-block;
    width: 0.6em; height: 0.6em;
    margin-left: var(--s2);
    border: 1px solid var(--brown-50);
    border-top-color: transparent;
    border-radius: 50%;
    animation: spin 720ms linear infinite;
    vertical-align: baseline;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .busy::after { display: none; }
}

/* ============================== FORM ============================== */
.form { margin-top: var(--s7); }

.block {
    border-top: 1px solid var(--cream-20);
    padding-block: var(--s6);
}
.block:first-of-type { border-top: 0; padding-top: 0; }
.block__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s2) var(--s3);
    margin-bottom: var(--s4);
}
.block__title {
    font-family: var(--font-display);
    font-size: 1.25rem;
    color: var(--brown-90);
}
.block__flag {
    font-size: 0.6875rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--brown-50);
}
.block__flag--req { color: var(--orange-70); }
.block__hint { font-size: 0.9375rem; color: var(--brown-50); margin-bottom: var(--s4); }

/* The consent block's plain-English summary, and any other block prose that
   carries a link out. Held to --measure for the same reason the rest of the
   prose is: this is the paragraph on the page most likely to be read rather
   than skimmed, and a 1000px line is not read. */
.block__intro {
    font-size: 0.9375rem;
    line-height: 1.65;
    color: var(--brown-70);
    max-width: var(--measure);
    margin-bottom: var(--s3);
}
.block__intro + .fields { margin-top: var(--s4); }

/* Links inside body copy get an underline that is visibly a link at 15px, and
   the small-text orange, which is the only one of the two that clears 4.5:1 on
   cream. */
.block__intro a, .prose a, .trust a, .intro a {
    color: var(--orange-70);
    text-underline-offset: 0.18em;
    text-decoration-thickness: 1px;
}
.block__intro a:hover, .prose a:hover, .trust a:hover, .intro a:hover { color: var(--brown-90); }

/* Fields sit on a base-4 grid. One column on a phone, two where there is room
   — driven by the field's own span, not by a per-breakpoint rewrite. */
.fields { display: grid; gap: var(--s4); }
@media (min-width: 620px) {
    .fields { grid-template-columns: repeat(2, 1fr); }
    .field--wide { grid-column: 1 / -1; }
}

.field { display: flex; flex-direction: column; gap: var(--s2); }
.field > label, .field__legend {
    font-size: 0.8125rem;
    letter-spacing: 0.06em;
    color: var(--brown-70);
}
.field__req { color: var(--orange-70); margin-left: 2px; }
.field__note { font-size: 0.8125rem; color: var(--brown-50); }

.field input[type="text"],
.field input[type="email"],
.field input[type="tel"],
.field input[type="url"],
.field input[type="date"],
.field input[type="month"],
.field input[type="number"],
.field select,
.field textarea {
    width: 100%;
    min-height: 44px;
    padding: var(--s2) var(--s3);
    border: 1px solid var(--field-line);
    border-radius: 2px;
    background: var(--field-bg);
    color: var(--brown-90);
    font-size: 1rem;
    transition: border-color var(--t-micro) var(--ease);
}
.field textarea { min-height: 7rem; resize: vertical; line-height: 1.6; }
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--brown-20); }
.field input:focus, .field select:focus, .field textarea:focus {
    border-color: var(--orange-60);
    outline: none;
}
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible {
    outline: 2px solid var(--orange-60);
    outline-offset: 2px;
}
.field input:user-invalid, .field textarea:user-invalid, .field select:user-invalid {
    border-color: var(--orange-70);
}
.field ::placeholder { color: var(--brown-20); opacity: 1; }

/* Radios and checkboxes keep the real control — it is already accessible and
   already 44px once the label wraps it. */
.choices { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); }
.choice {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    min-height: 44px;
    font-size: 1rem;
    cursor: pointer;
}
.choice input { width: 18px; height: 18px; accent-color: var(--orange-60); cursor: pointer; }
.check {
    display: flex;
    align-items: flex-start;
    gap: var(--s3);
    min-height: 44px;
    font-size: 0.9375rem;
    cursor: pointer;
}
.check input { width: 18px; height: 18px; margin-top: 5px; accent-color: var(--orange-60); flex-shrink: 0; }

/* File input. The native control is kept and styled around rather than
   replaced: a custom one has to reimplement keyboard access and the filename
   readout, and gains nothing here. */
.field input[type="file"] {
    width: 100%;
    padding: var(--s3);
    border: var(--stitch);
    border-radius: 2px;
    background: var(--field-bg);
    font-size: 0.9375rem;
    cursor: pointer;
}
.field input[type="file"]::file-selector-button {
    font: inherit;
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin-right: var(--s3);
    padding: var(--s2) var(--s4);
    min-height: 36px;
    border: 1px solid var(--brown-50);
    border-radius: 2px;
    background: transparent;
    color: var(--brown-90);
    cursor: pointer;
}
.field input[type="file"]::file-selector-button:hover { background: var(--cream-10); }

.file-msg { font-size: 0.875rem; margin-top: var(--s1); }
.file-msg--ok { color: var(--brown-50); }
.file-msg--bad { color: var(--orange-70); }

/* ---- repeatable entries ---- */
.repeat { display: grid; gap: var(--s5); }
.entry {
    border-left: 2px solid var(--cream-20);
    padding-left: var(--s4);
    display: grid;
    gap: var(--s4);
}
.entry__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s3);
}
.entry__n {
    font-size: 0.6875rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--brown-50);
}
.repeat__foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s3);
    margin-top: var(--s1);
}
.repeat__count { font-size: 0.8125rem; color: var(--brown-50); }

/* ---- conditional reveal ---- */
/* [hidden] alone loses to display:grid on .field, so it is stated again. */
[hidden] { display: none !important; }

/* ============================== CONFIRMATION ============================== */
.conf { display: grid; gap: var(--s7); margin-top: var(--s6); }
@media (min-width: 900px) {
    .conf { grid-template-columns: 1fr 1fr; align-items: start; }
}

.ref {
    border: var(--stitch);
    padding: var(--s5);
    text-align: center;
}
.ref__label {
    font-size: 0.6875rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--brown-50);
}
.ref__value {
    font-family: var(--font-display);
    /* Sized to fit UN-XXXX-XX on one line inside half of the narrow shell. The
       display face is wide and the tracking adds to it, so the ceiling is lower
       than it looks: at 2.5rem the reference broke across two lines, which is
       the one string on this page that must never wrap. */
    font-size: clamp(1.5rem, 5vw, 2rem);
    letter-spacing: 0.08em;
    white-space: nowrap;
    color: var(--brown-90);
    margin-top: var(--s2);
    /* A reference is read aloud and typed back in. Selecting it should take
       the whole thing. */
    user-select: all;
}

.summary { margin-top: var(--s5); }
.summary__block { margin-top: var(--s5); }
.summary__block h3 {
    font-family: var(--font-display);
    font-size: 1.0625rem;
    color: var(--brown-90);
    margin-bottom: var(--s2);
    padding-bottom: var(--s1);
    border-bottom: 1px solid var(--cream-20);
}
.summary dl { display: grid; gap: var(--s2); }
@media (min-width: 620px) {
    .summary dl { grid-template-columns: 12rem 1fr; gap: var(--s2) var(--s4); }
}
.summary dt { font-size: 0.8125rem; color: var(--brown-50); }
.summary dd { color: var(--brown-90); overflow-wrap: anywhere; }
.summary dd:empty::after { content: '—'; color: var(--brown-20); }

/* ============================== TASK / COUNTDOWN ============================== */
.task { border: 1px solid var(--cream-20); background: rgba(255, 253, 248, 0.7); padding: var(--s5); }
.task__text { white-space: pre-wrap; margin-top: var(--s4); }

.clock {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--s2) var(--s4);
    margin-top: var(--s4);
    padding-top: var(--s4);
    border-top: 1px solid var(--cream-20);
}
.clock__label {
    font-size: 0.6875rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--brown-50);
}
.clock__value {
    font-family: var(--font-display);
    font-size: 1.5rem;
    color: var(--brown-90);
    font-variant-numeric: tabular-nums;
}
.clock--soon .clock__value { color: var(--orange-70); }
.clock--over .clock__value { color: var(--brown-50); }

/* ============================== UTILITIES ==============================
   A short spacing ladder, so no element needs a style attribute. That is not
   tidiness: it lets the Content-Security-Policy forbid inline styles outright
   rather than carrying 'unsafe-inline' for the sake of a few margins. */
.mt-3 { margin-top: var(--s3); }
.mt-4 { margin-top: var(--s4); }
.mt-5 { margin-top: var(--s5); }
.mt-6 { margin-top: var(--s6); }
.mt-7 { margin-top: var(--s7); }
.mt-9 { margin-top: var(--s9); }
.nowrap { white-space: nowrap; }
.upper { text-transform: uppercase; }

/* ============================== PRINT ============================== */
@media print {
    .nav, .foot, .no-print, .btn { display: none !important; }
    html, body { background: #fff; color: #000; }
    body { font-size: 11pt; line-height: 1.4; }
    .band { padding-block: 0; }
    .conf { display: block; }
    .ref { border: 1px solid #000; margin-bottom: 12pt; }
    .summary__block { break-inside: avoid; }
    .summary dl { grid-template-columns: 12rem 1fr; }
    a[href]::after { content: ''; }
}

/* ============================== MOTION ============================== */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}
