/*
 * QC Workspace — design tokens and base styles.
 *
 * Section 16: calm Quiet Collab gradients, light and readable, professional,
 * human-centered, privacy-first, modern, high contrast. Avoid the dark green
 * direction, neon greens, fintech styling and low contrast.
 *
 * Palette note — this matters for the "high contrast" requirement:
 *   #00FCFC on white is roughly 1.3:1. It cannot carry text and cannot sit
 *   behind white text. It is used ONLY for gradients, accent rules and focus
 *   states. #0E7E72 (about 4.9:1 on white) is the primary action colour and
 *   #366050 carries headings.
 */

:root {
    /* Brand */
    --qc-teal:        #00FCFC;
    --qc-green-1:     #0E7E72;
    --qc-green-2:     #366050;
    --qc-black:       #000000;
    --qc-white:       #FFFFFF;

    /* Derived surfaces — light neutrals, never dark backgrounds */
    --surface:        #FFFFFF;
    --surface-sunken: #F4F8F8;
    --surface-raised: #FFFFFF;
    --border:         #DCECEB;
    --border-strong:  #B9D6D3;

    /* Text */
    --text:           #1D2B28;
    --text-muted:     #5D6B68;
    --text-inverse:   #FFFFFF;

    /* Actions */
    --action:         var(--qc-green-1);
    --action-hover:   #0A6259;
    --action-quiet:   #E7F3F1;

    /* Status */
    --ok:             #0E7E72;
    --warn:           #8A6100;
    --warn-bg:        #FFF6E0;
    --danger:         #A32A22;
    --danger-bg:      #FDEDEC;

    /* The teal earns its place here — decorative, never behind text */
    --gradient-brand: linear-gradient(120deg, var(--qc-green-1) 0%, var(--qc-teal) 100%);
    --gradient-calm:  linear-gradient(160deg, #F4F8F8 0%, #E9F5F4 100%);

    --radius:         10px;
    --radius-lg:      14px;
    --shadow:         0 1px 2px rgba(29, 43, 40, .06), 0 4px 16px rgba(29, 43, 40, .05);
    --focus:          0 0 0 3px rgba(0, 252, 252, .45);

    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    font-family: var(--font);
    font-size: 15px;
    line-height: 1.6;
    color: var(--text);
    background: var(--gradient-calm);
    background-attachment: fixed;
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
    color: var(--qc-green-2);
    line-height: 1.25;
    margin: 0 0 .5rem;
    font-weight: 650;
}

h1 { font-size: 1.7rem; }
h2 { font-size: 1.25rem; }
h3 { font-size: 1.05rem; }

p { margin: 0 0 1rem; }
a { color: var(--action); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
    outline: none;
    box-shadow: var(--focus);
    border-radius: 6px;
}

/* ---------------------------------------------------------------- layout */

.masthead {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: relative;
}

/* The brand gradient as a hairline — decorative use of the teal */
.masthead::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: -1px;
    height: 3px;
    background: var(--gradient-brand);
}

.masthead-inner {
    max-width: 1100px;
    margin: 0 auto;
    padding: .85rem 1.5rem;
    display: flex;
    align-items: center;
    gap: 1.25rem;
}

.brand {
    display: flex;
    align-items: center;
    gap: .6rem;
    font-weight: 700;
    color: var(--qc-green-2);
    letter-spacing: -.01em;
}
.brand:hover { text-decoration: none; }

/* Placeholder logo — section 16 says use a replaceable placeholder */
.brand-mark {
    width: 30px; height: 30px;
    border-radius: 8px;
    background: var(--gradient-brand);
    display: grid;
    place-items: center;
    color: var(--qc-white);
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .02em;
    flex: none;
}

.masthead nav { display: flex; gap: .35rem; margin-left: auto; align-items: center; }

.navlink {
    padding: .4rem .7rem;
    border-radius: var(--radius);
    color: var(--text-muted);
    font-weight: 500;
}
.navlink:hover { background: var(--surface-sunken); color: var(--text); text-decoration: none; }
.navlink.is-active { background: var(--action-quiet); color: var(--action); }

.container {
    max-width: 1100px;
    margin: 0 auto;
    padding: 2rem 1.5rem 4rem;
}
.container.narrow { max-width: 460px; }
.container.medium { max-width: 760px; }

/*
 * A working surface, not a reading one.
 *
 * Wide enough that a section list and the work beside it both have room, and
 * capped so running text never stretches to an unreadable line — the cap
 * applies to the CONTAINER, and the prose inside it is bounded again by
 * `.card` and by the form controls' own widths.
 *
 * Deliberately not edge-to-edge. A form field spanning a 34-inch monitor is a
 * different failure from a form field squeezed into 500px, not a fix for it.
 */
.container.workspace { max-width: 1400px; }

/* At this width the section list can afford to be readable rather than terse. */
/*
 * Only above the breakpoint. This used to be unconditional, and being
 * three classes deep it beat the narrow-screen override further down the
 * file however the media query was written — so every workspace kept a
 * 250px sidebar on a phone and gave the content what was left.
 */
@media (min-width: 901px) {
    .container.workspace .config-layout { grid-template-columns: minmax(210px, 250px) 1fr; gap: 2rem; }
}

/*
 * Inside a wide working surface, a two-column field grid stops being cramped
 * and starts being the obvious way to read a form. Below the breakpoint it
 * collapses, because two columns of 300px is worse than one of 600px.
 */
@media (min-width: 1100px) {
    .container.workspace .grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .container.workspace .card { padding-left: 1.6rem; padding-right: 1.6rem; }
}

/* ----------------------------------------------------------------- cards */

.card {
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    padding: 1.5rem;
    margin-bottom: 1.25rem;
}
.card-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
}
.card-header h2, .card-header h3 { margin: 0; }

.muted { color: var(--text-muted); }
.small { font-size: .875rem; }
.mono  { font-family: var(--mono); font-size: .85rem; }

/* ----------------------------------------------------------------- forms */

.field { margin-bottom: 1rem; }

label {
    display: block;
    font-weight: 600;
    font-size: .875rem;
    margin-bottom: .3rem;
    color: var(--qc-green-2);
}

input[type=text], input[type=email], input[type=password],
input[type=search], select, textarea {
    width: 100%;
    padding: .6rem .75rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    font: inherit;
    color: var(--text);
    background: var(--surface);
}

input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--action);
    box-shadow: var(--focus);
}

.hint { font-size: .8rem; color: var(--text-muted); margin-top: .3rem; }
.field-error { font-size: .8rem; color: var(--danger); margin-top: .3rem; }
input.has-error, select.has-error { border-color: var(--danger); }

/* --------------------------------------------------------------- buttons */

.btn {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .6rem 1.05rem;
    border-radius: var(--radius);
    border: 1px solid transparent;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    background: var(--action);
    color: var(--text-inverse);
}
.btn:hover { background: var(--action-hover); text-decoration: none; }

.btn-secondary {
    background: var(--surface);
    color: var(--action);
    border-color: var(--border-strong);
}
.btn-secondary:hover { background: var(--action-quiet); }

/*
 * A card for the one action on a page that cannot be undone.
 *
 * Marked by a rule down its edge rather than a red panel: the whole card
 * shouting makes the text inside it harder to read, and the text inside it is
 * the part that says exactly what will be cleared and what will not.
 */
.card-danger {
    border-color: var(--danger);
    border-left-width: 4px;
}

.card-danger .card-header h2 { color: var(--danger); }

.btn-danger { background: var(--danger); }
.btn-danger:hover { background: #8B231C; }

.btn-small { padding: .35rem .7rem; font-size: .85rem; }
.btn-block { width: 100%; justify-content: center; }

/* --------------------------------------------------------------- notices */

.notice {
    padding: .8rem 1rem;
    border-radius: var(--radius);
    margin-bottom: 1.25rem;
    border: 1px solid;
    font-size: .9rem;
}
.notice-ok     { background: var(--action-quiet); border-color: #BFE3DE; color: #0A5C53; }
.notice-warn   { background: var(--warn-bg);      border-color: #F0DDAE; color: var(--warn); }
.notice-danger { background: var(--danger-bg);    border-color: #F3C9C5; color: var(--danger); }
.notice-info   { background: var(--surface-sunken); border-color: var(--border); color: var(--text-muted); }

/*
 * ── AN OUTCOME IS NOT AN ASIDE ──────────────────────────────────────────────
 *
 * .notice-info is built to recede: a sunken background that is nearly the page
 * colour, a hairline border, muted grey text at .9rem. That is right for
 * standing information a screen carries all the time.
 *
 * It was also what a page used to report that something had just HAPPENED. A
 * Collaborator who had accepted a position landed on a page where the loudest
 * element was a filter card, scrolled DOWN past the one sentence that mattered,
 * and described the moment as "what is this". The confirmation was not missed
 * because it was absent. It was missed because it was dressed as furniture.
 *
 * So an outcome gets its own tone: full-strength text at body size, the brand
 * green carrying it, and a solid edge on the leading side so it still reads as
 * a banner rather than as a new section of the page.
 */
.notice-outcome {
    background: var(--action-quiet);
    border-color: #BFE3DE;
    border-left: 4px solid var(--ok);
    color: #0A5C53;
    font-size: 1rem;
    line-height: 1.55;
    padding: 1rem 1.1rem;
}

/*
 * Where the sentence sends somebody, reachable FROM the sentence.
 *
 * A notice that says "it is in your assignments now" and then leaves the
 * reader to go and find their assignments has given an instruction instead of
 * a way through. The destination is chosen in code — never taken from the
 * text, which arrives in a query string.
 */
.notice-action {
    display: inline-block;
    margin-top: .55rem;
    font-weight: 600;
    color: #0A5C53;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.notice-action:hover,
.notice-action:focus { color: var(--ok); }
.notice-action::after { content: " \2192"; text-decoration: none; }

/* ---------------------------------------------------------------- tables */

.table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.table th {
    text-align: left;
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-muted);
    padding: .5rem .6rem;
    border-bottom: 1px solid var(--border);
    font-weight: 700;
}
.table td { padding: .6rem; border-bottom: 1px solid var(--border); vertical-align: top; }
.table tr:last-child td { border-bottom: none; }
.table-wrap { overflow-x: auto; }

/* ------------------------------------------------------------------ tags */

.tag {
    display: inline-block;
    padding: .15rem .5rem;
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 600;
    background: var(--surface-sunken);
    color: var(--text-muted);
    border: 1px solid var(--border);
}
.tag-ok     { background: var(--action-quiet); color: #0A5C53; border-color: #BFE3DE; }
.tag-warn   { background: var(--warn-bg);      color: var(--warn); border-color: #F0DDAE; }
.tag-danger { background: var(--danger-bg);    color: var(--danger); border-color: #F3C9C5; }
.tag-quiet  { background: transparent; }

/* Private data marker — makes section 11 visible in the interface */
.tag-private {
    background: #F3EEFB;
    color: #5B3B8C;
    border-color: #DFD2F2;
}

/* --------------------------------------------------------------- context */

.context-switch {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .3rem .35rem .3rem .7rem;
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: .85rem;
}
.context-switch form { display: inline; }
.context-switch select {
    width: auto;
    padding: .25rem 1.6rem .25rem .5rem;
    font-size: .85rem;
    border-radius: 999px;
    border-color: transparent;
    background-color: var(--surface);
    font-weight: 600;
    color: var(--action);
}

/* ------------------------------------------------------------------ misc */

.grid { display: grid; gap: 1.25rem; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

.stack > * + * { margin-top: .75rem; }
.row { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
.spread { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }

.divider { height: 1px; background: var(--border); border: 0; margin: 1.25rem 0; }

.capability-list { columns: 2; column-gap: 1.5rem; }
.capability-list label {
    display: flex;
    gap: .5rem;
    align-items: flex-start;
    font-weight: 400;
    color: var(--text);
    break-inside: avoid;
    margin-bottom: .45rem;
    text-transform: none;
    font-size: .875rem;
}
.capability-list input { width: auto; margin-top: .25rem; }

/*
 * Administration grouping in the masthead.
 *
 * Users, permissions, settings, integrations and audit are account and access
 * management, not operational directories, so they collapse behind one entry
 * rather than sitting beside Contacts and Projects.
 */
.nav-group { position: relative; display: inline-block; }
.nav-group > summary {
    list-style: none;
    cursor: pointer;
    user-select: none;
}
.nav-group > summary::-webkit-details-marker { display: none; }
.nav-group > summary::after { content: " ▾"; opacity: .55; }
.nav-group-menu {
    position: absolute;
    right: 0;
    top: calc(100% + .4rem);
    z-index: 40;
    min-width: 12rem;
    display: flex;
    flex-direction: column;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 8px 24px -10px rgba(29, 43, 40, .28);
    padding: .35rem;
}
.nav-group-menu a {
    padding: .45rem .6rem;
    border-radius: calc(var(--radius) - 2px);
    font-size: .9rem;
    text-decoration: none;
    color: var(--text);
}
.nav-group-menu a:hover { background: var(--action-quiet); }
.nav-group-menu a.is-active { background: var(--action-quiet); font-weight: 600; }

.lang-switch { display: inline-flex; align-items: center; }
.lang-switch select { padding: .25rem .4rem; font-size: .8rem; }

/*
 * The QR code keeps its own white ground whatever the surrounding surface is.
 * Scanners need dark-on-light in that polarity, so this is one place the theme
 * does not get a say.
 */
.qr-enrolment {
    display: flex;
    justify-content: center;
    padding: 1rem;
}
.qr-enrolment svg {
    display: block;
    width: 100%;
    max-width: 260px;
    height: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.qr-hint { text-align: center; }

.manual-key summary {
    cursor: pointer;
    color: var(--action);
    font-size: .9rem;
    margin-bottom: .75rem;
}
.manual-key summary:focus-visible { outline: 2px solid var(--qc-teal); outline-offset: 2px; }

.secret-code {
    font-family: var(--mono);
    font-size: 1.05rem;
    letter-spacing: .08em;
    background: var(--surface-sunken);
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius);
    padding: .75rem;
    text-align: center;
    word-break: break-all;
}

.recovery-codes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
    gap: .5rem;
    font-family: var(--mono);
    font-size: .9rem;
}
.recovery-codes span {
    background: var(--surface-sunken);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: .4rem;
    text-align: center;
}

.empty {
    text-align: center;
    padding: 2.5rem 1rem;
    color: var(--text-muted);
}

.auth-head { text-align: center; margin-bottom: 1.5rem; }
.auth-head .brand-mark { width: 44px; height: 44px; margin: 0 auto .75rem; font-size: .9rem; }

.footnote {
    text-align: center;
    font-size: .85rem;
    color: var(--text-muted);
    margin-top: 1.25rem;
}

@media (max-width: 640px) {
    .masthead-inner { flex-wrap: wrap; gap: .6rem; }
    .capability-list { columns: 1; }
    .container { padding: 1.25rem 1rem 3rem; }
}

/* ===========================================================================
 * APPLICATION SHELL
 *
 * The old masthead listed every module on every page. It wrapped on a laptop
 * and would have been unusable on a phone. What remains is only what must be
 * reachable from anywhere; navigation lives in the dashboard launcher.
 *
 * Written mobile-first: the base rules are the narrow layout, and wider
 * screens add to them. That ordering is deliberate — a desktop-first shell
 * that later needs a mobile pass is exactly what this is avoiding.
 * ======================================================================== */

.shell {
    position: sticky;
    top: 0;
    z-index: 50;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}
.shell-inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: .55rem .9rem;
    display: flex;
    align-items: center;
    gap: .6rem;
    min-height: 52px;
}
.shell-spacer { flex: 1 1 auto; }

.shell .brand { display: inline-flex; align-items: center; gap: .5rem; text-decoration: none; color: var(--text); }
.shell .brand-name { font-weight: 700; font-size: .95rem; }

/* The product name is the first thing to go when width is scarce; the mark
   still identifies the workspace. */
@media (max-width: 560px) {
    .shell .brand-name { display: none; }
}

.shell-home {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .4rem .65rem;
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--text-muted);
    font-size: .9rem;
    min-height: 40px;
}
.shell-home:hover { background: var(--action-quiet); color: var(--text); }
.shell-home.is-active { background: var(--action-quiet); color: var(--qc-green-1); font-weight: 600; }
@media (max-width: 700px) {
    .shell-home-label { display: none; }
}

.shell-context {
    display: inline-flex;
    flex-direction: column;
    line-height: 1.15;
    padding: .2rem .6rem;
    border-left: 2px solid var(--border);
    font-size: .82rem;
}
.shell-context-label { color: var(--text-muted); font-size: .68rem; text-transform: uppercase; letter-spacing: .06em; }
@media (max-width: 820px) {
    .shell-context { display: none; }
}

/* --- account and secondary menu ------------------------------------------ */

.shell-menu { position: relative; }
.shell-menu > summary {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .3rem .5rem;
    border-radius: var(--radius);
    min-height: 40px;
}
.shell-menu > summary::-webkit-details-marker { display: none; }
.shell-menu > summary:hover { background: var(--action-quiet); }
.shell-menu > summary:focus-visible { outline: 2px solid var(--qc-teal); outline-offset: 2px; }

.shell-avatar {
    width: 28px; height: 28px;
    border-radius: 50%;
    background: var(--qc-green-1);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .8rem;
    font-weight: 700;
    flex: none;
}
.shell-menu-name { font-size: .88rem; }
@media (max-width: 560px) { .shell-menu-name { display: none; } }

.shell-menu-panel {
    position: absolute;
    right: 0;
    top: calc(100% + .45rem);
    width: min(19rem, calc(100vw - 1.5rem));
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 10px 30px -12px rgba(29, 43, 40, .35);
    padding: .75rem;
    display: flex;
    flex-direction: column;
    gap: .75rem;
}
.shell-menu-heading {
    display: block;
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--text-muted);
    margin-bottom: .3rem;
}
.shell-menu-panel select { width: 100%; }
.shell-menu-links { display: flex; flex-direction: column; border-top: 1px solid var(--border); padding-top: .6rem; }
.shell-menu-links a {
    padding: .5rem .4rem;
    border-radius: calc(var(--radius) - 2px);
    text-decoration: none;
    color: var(--text);
    font-size: .9rem;
    min-height: 40px;
    display: flex;
    align-items: center;
}
.shell-menu-links a:hover { background: var(--action-quiet); }
.shell-menu-signout { border-top: 1px solid var(--border); padding-top: .6rem; }

/* ===========================================================================
 * DASHBOARD LAUNCHER
 *
 * auto-fit rather than fixed breakpoint columns: the grid finds its own column
 * count, so it works at widths nobody thought to test.
 * ======================================================================== */

.page-head { margin: 1.5rem 0 1.25rem; }
.page-head h1 { margin: 0 0 .3rem; }
.page-head .crumb { margin: 0 0 .4rem; font-size: .85rem; }

.launcher {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    gap: .9rem;
    margin-bottom: 2.5rem;
}

.launch-card {
    display: flex;
    flex-direction: column;
    gap: .3rem;
    padding: 1.1rem 1.15rem;
    min-height: 118px;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    text-decoration: none;
    color: var(--text);
    transition: border-color .12s ease, transform .12s ease, box-shadow .12s ease;
}
.launch-card:hover {
    border-color: var(--qc-green-1);
    transform: translateY(-1px);
    box-shadow: 0 8px 20px -14px rgba(29, 43, 40, .5);
}
.launch-card:focus-visible { outline: 2px solid var(--qc-teal); outline-offset: 2px; }
.launch-card.needs-attention { border-left: 3px solid var(--warn); }

.launch-title { font-weight: 700; font-size: 1.02rem; color: var(--qc-green-2); }
.launch-hint { font-size: .85rem; color: var(--text-muted); flex: 1 1 auto; }
.launch-metrics { display: flex; gap: 1.1rem; margin-top: .4rem; }
.launch-metric { display: flex; flex-direction: column; line-height: 1.1; }
.launch-count { font-size: 1.35rem; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--qc-green-1); }
.launch-metric.is-attention .launch-count { color: var(--warn); }
.launch-metric-label { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); }

@media (prefers-reduced-motion: reduce) {
    .launch-card { transition: none; }
    .launch-card:hover { transform: none; }
}

/* --- context chooser ----------------------------------------------------- */

.chooser { padding-top: 2.5rem; }
.chooser-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
    gap: .85rem;
    margin: 1.5rem 0;
}
.chooser-card {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    text-align: left;
    padding: 1.25rem;
    min-height: 116px;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    cursor: pointer;
    font: inherit;
    color: var(--text);
}
.chooser-card:hover { border-color: var(--qc-green-1); background: var(--action-quiet); }
.chooser-card:focus-visible { outline: 2px solid var(--qc-teal); outline-offset: 2px; }
.chooser-card.is-current { border-color: var(--qc-green-1); }
.chooser-name { font-weight: 700; font-size: 1.05rem; color: var(--qc-green-2); }
.chooser-hint { font-size: .85rem; color: var(--text-muted); }
.chooser-badge {
    align-self: flex-start;
    margin-top: .3rem;
    font-size: .65rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    background: var(--action-quiet);
    color: var(--qc-green-1);
    padding: .12rem .4rem;
    border-radius: 4px;
}

/* ===========================================================================
 * WORKSPACE ASSISTANT
 *
 * Bottom-right, and deliberately clear of the corner itself so it does not
 * collide with mobile browser chrome. env(safe-area-inset-*) keeps it above
 * the home indicator on phones that have one.
 * ======================================================================== */

.assistant-launcher {
    position: fixed;
    right: max(1rem, env(safe-area-inset-right));
    bottom: calc(1rem + env(safe-area-inset-bottom));
    z-index: 60;
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: .7rem 1rem;
    min-height: 48px;
    border: none;
    border-radius: 999px;
    background: var(--qc-green-1);
    color: #fff;
    font: inherit;
    font-weight: 600;
    font-size: .9rem;
    cursor: pointer;
    box-shadow: 0 6px 20px -6px rgba(14, 126, 114, .55);
}
.assistant-launcher:hover { background: var(--action-hover); }
.assistant-launcher:focus-visible { outline: 3px solid var(--qc-teal); outline-offset: 2px; }
.assistant-launcher-mark {
    width: 22px; height: 22px;
    border-radius: 50%;
    background: rgba(255, 255, 255, .22);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: .78rem; font-weight: 700;
}

/* On a phone the label goes and the control becomes a circle, so it covers as
   little of the page as possible while staying easy to hit. */
@media (max-width: 560px) {
    .assistant-launcher-label { display: none; }
    .assistant-launcher { padding: 0; width: 52px; height: 52px; justify-content: center; }
}

.assistant-panel {
    position: fixed;
    right: max(1rem, env(safe-area-inset-right));
    bottom: calc(4.75rem + env(safe-area-inset-bottom));
    z-index: 61;
    width: min(22rem, calc(100vw - 2rem));
    max-height: min(32rem, calc(100vh - 7rem));
    display: flex;
    flex-direction: column;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 16px 44px -16px rgba(29, 43, 40, .45);
    overflow: hidden;
}
.assistant-panel[hidden] { display: none; }

/* On a narrow screen it becomes a sheet across the bottom rather than a
   floating card that would leave almost no usable width. */
@media (max-width: 560px) {
    .assistant-panel {
        left: 0; right: 0; bottom: 0;
        width: 100%;
        max-height: 82vh;
        border-radius: var(--radius) var(--radius) 0 0;
        border-left: none; border-right: none; border-bottom: none;
        padding-bottom: env(safe-area-inset-bottom);
    }
}

.assistant-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .5rem;
    padding: .85rem 1rem;
    border-bottom: 1px solid var(--border);
    background: var(--surface-sunken);
}
.assistant-sub { font-size: .75rem; color: var(--text-muted); }
.assistant-close {
    border: none; background: none; cursor: pointer;
    font-size: 1.4rem; line-height: 1; color: var(--text-muted);
    min-width: 40px; min-height: 40px;
}
.assistant-close:focus-visible { outline: 2px solid var(--qc-teal); outline-offset: 2px; }

.assistant-body { padding: .9rem 1rem 1.1rem; overflow-y: auto; display: flex; flex-direction: column; gap: 1rem; }
.assistant-disclosure {
    margin: 0;
    font-size: .78rem;
    color: var(--text-muted);
    background: var(--surface-sunken);
    border-radius: calc(var(--radius) - 2px);
    padding: .5rem .6rem;
}
.assistant-section { display: flex; flex-direction: column; gap: .4rem; }
.assistant-label { font-size: .68rem; text-transform: uppercase; letter-spacing: .07em; color: var(--text-muted); }
.assistant-list { display: flex; flex-direction: column; gap: .35rem; }
.assistant-item {
    display: flex; flex-direction: column; gap: .12rem;
    padding: .55rem .6rem;
    border: 1px solid var(--border);
    border-radius: calc(var(--radius) - 2px);
    text-decoration: none; color: var(--text);
    font-size: .85rem;
    min-height: 44px;
}
.assistant-item:hover { background: var(--action-quiet); }
.assistant-item span { color: var(--text-muted); font-size: .78rem; }
.assistant-empty { margin: 0; font-size: .82rem; color: var(--text-muted); }
.assistant-result { margin: .5rem 0 0; font-size: .82rem; }
.assistant-panel textarea { width: 100%; font: inherit; font-size: .88rem; }

/* ===========================================================================
 * IDENTITY AND WORKING CONTEXT
 *
 * The person leads; the context is stated separately as something they are
 * doing. A circle holding one letter of a username is not an identity.
 * ======================================================================== */

.shell-identity {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding-bottom: .7rem;
    border-bottom: 1px solid var(--border);
}
.shell-avatar-lg { width: 38px; height: 38px; font-size: 1rem; }

/*
 * Contexts as a list rather than a dropdown. With three or more, a <select>
 * hides most of them behind an interaction and gives no sense of what is
 * available.
 */
.context-list { display: flex; flex-direction: column; gap: .15rem; }
.context-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    width: 100%;
    padding: .55rem .6rem;
    min-height: 44px;
    border: 1px solid transparent;
    border-radius: calc(var(--radius) - 2px);
    background: none;
    font: inherit;
    font-size: .9rem;
    color: var(--text);
    cursor: pointer;
    text-align: left;
}
.context-option:hover { background: var(--action-quiet); }
.context-option:focus-visible { outline: 2px solid var(--qc-teal); outline-offset: 2px; }
.context-option.is-current {
    background: var(--action-quiet);
    border-color: var(--border-strong);
    font-weight: 600;
    color: var(--qc-green-1);
}
.context-check { color: var(--qc-green-1); font-weight: 700; }

/* Who is responsible for a work item. */
.resp-line { margin-bottom: .4rem; }
.resp-line:last-child { margin-bottom: 0; }

/* ===========================================================================
 * CONFIGURATION: a side menu with the editor beside it, rather than one long
 * page of stacked tables the owner had to scroll through and re-find.
 * ======================================================================== */

.config-layout {
    display: grid;
    grid-template-columns: minmax(190px, 220px) 1fr;
    gap: 1.5rem;
    align-items: start;
}
@media (max-width: 900px) {
    .config-layout { grid-template-columns: 1fr; gap: 1rem; }
}

.config-menu {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .4rem;
    position: sticky;
    top: 64px;
}
@media (max-width: 900px) {
    /*
     * It used to become a row of chips scrolling sideways. That is a menu you
     * cannot see the end of, on the device with the least room to spare, and
     * it put a horizontal scroll inside a page that should have none.
     *
     * Below the breakpoint the whole thing is a closed disclosure instead: the
     * summary says which section you are in, opening it lists the rest, and
     * the content keeps the full width either way.
     */
    .config-menu {
        position: static;
        flex-direction: column;
        overflow: visible;
        gap: .15rem;
        margin-top: .4rem;
    }
}
.config-menu a {
    padding: .55rem .7rem;
    border-radius: calc(var(--radius) - 2px);
    text-decoration: none;
    color: var(--text);
    font-size: .9rem;
    min-height: 40px;
    display: flex;
    align-items: center;
}
.config-menu a:hover { background: var(--action-quiet); }
.config-menu a.is-active { background: var(--action-quiet); color: var(--qc-green-1); font-weight: 600; }

/* Persisted-state feedback. The owner should never wonder whether a save
   landed, or which of two edited values it covered. */
.save-state {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: .8rem;
    color: var(--ok);
}
.row-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}
.row-form .grow { flex: 1 1 12rem; min-width: 0; }

.checkbox-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: .3rem .9rem;
}
.checkbox-grid label {
    display: flex;
    align-items: center;
    gap: .45rem;
    font-weight: 400;
    text-transform: none;
    font-size: .88rem;
    color: var(--text);
    margin: 0;
    min-height: 32px;
}

/* ===========================================================================
 * OPPORTUNITY CARDS
 * ======================================================================== */

.opp-tabs {
    display: flex;
    gap: .35rem;
    flex-wrap: wrap;
    margin-bottom: 1.1rem;
}
.opp-tab {
    padding: .45rem .85rem;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-muted);
    text-decoration: none;
    font-size: .88rem;
    min-height: 40px;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
}
.opp-tab:hover { background: var(--action-quiet); text-decoration: none; }
.opp-tab.is-active { background: var(--qc-green-1); border-color: var(--qc-green-1); color: #fff; font-weight: 600; }
.opp-tab .count { font-variant-numeric: tabular-nums; opacity: .8; }

/*
 * The filter bar. Quieter than the tabs above it on purpose: the tabs are
 * where you are, the filters are how you narrowed it, and a control that
 * only ever removes rows should not compete with one that changes shelf.
 */
/*
 * The context, under the name, inside the control that changes it.
 * Quiet: it is a statement of where you are, not a call to act.
 */
/*
 * ACTING AS, and the language picker, as first-class header controls.
 *
 * The eyebrow is small and quiet; the value carries the weight, because
 * the value is the fact somebody is checking. Both hide their label text
 * on narrow screens rather than wrapping the header onto three lines.
 */
.shell-util-context { display: inline-flex; flex-direction: column; line-height: 1.15; }
.shell-util-context summary { display: inline-flex; flex-direction: column; line-height: 1.15; cursor: pointer; }
.shell-util-context.is-static { cursor: default; }
.shell-util-eyebrow { font-size: .62rem; text-transform: uppercase; letter-spacing: .07em; color: var(--text-muted); }
.shell-util-value { font-size: .84rem; font-weight: 600; }
.shell-util-lang { display: inline-flex; align-items: center; margin: 0; }
.shell-util-lang select { min-height: 34px; padding: .2rem .45rem; font-size: .82rem; max-width: 8.5rem; }
@media (max-width: 900px) {
    /*
     * The language SELECT leaves the bar and reappears inside the account
     * menu — see .shell-menu-lang below. It used to be hidden with nowhere to
     * go, which left a reader who cannot read the page with no way to change
     * it: the one control that had to survive was the one that disappeared.
     *
     * The ACTING AS eyebrow stays. Without it the header shows a bare role
     * name beside a bell and nothing says what it is. There is room for it
     * now that the person's name no longer repeats the same word underneath.
     */
    .shell-util-lang { display: none; }
    .shell-util-eyebrow { font-size: .58rem; }
}

.shell-menu-name { display: inline-flex; flex-direction: column; line-height: 1.15; text-align: left; }
.shell-menu-context { font-size: .72rem; font-weight: 400; color: var(--text-muted); letter-spacing: .02em; }

/* ===========================================================================
 * A COLLABORATOR'S HOME
 *
 * The work panel gets the width; everything else is a sidebar of small,
 * quiet facts. Single column below the breakpoint, because on a phone the
 * question is still "am I working today" and that answer must come first.
 * ======================================================================== */

/* ===========================================================================
 * SETTLING IN
 *
 * A real proportion of real steps. The number is always stated in words
 * beside the bar, because a bar cannot be read by everybody and cannot be
 * read precisely by anybody.
 * ======================================================================== */

.progress { height: 8px; border-radius: 999px; background: var(--surface-sunken); overflow: hidden; margin: .5rem 0 .9rem; }
.progress-fill { display: block; height: 100%; background: var(--qc-green-1); border-radius: inherit; }

.onboarding-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; }
.onboarding-steps li { display: flex; align-items: baseline; gap: .55rem; }
.onboarding-steps li.is-done a { color: var(--text-muted); text-decoration: line-through; }
.onboarding-tick { color: var(--qc-green-1); font-weight: 700; }
.onboarding-steps li.is-done .onboarding-tick { opacity: .7; }

.onboarding-video { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-top: .8rem; }
.home-grid { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 1rem; align-items: start; }
@media (max-width: 860px) { /* ===========================================================================
 * SETTLING IN
 *
 * A real proportion of real steps. The number is always stated in words
 * beside the bar, because a bar cannot be read by everybody and cannot be
 * read precisely by anybody.
 * ======================================================================== */

.progress { height: 8px; border-radius: 999px; background: var(--surface-sunken); overflow: hidden; margin: .5rem 0 .9rem; }
.progress-fill { display: block; height: 100%; background: var(--qc-green-1); border-radius: inherit; }

.onboarding-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: .45rem; }
.onboarding-steps li { display: flex; align-items: baseline; gap: .55rem; }
.onboarding-steps li.is-done a { color: var(--text-muted); text-decoration: line-through; }
.onboarding-tick { color: var(--qc-green-1); font-weight: 700; }
.onboarding-steps li.is-done .onboarding-tick { opacity: .7; }

.onboarding-video { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-top: .8rem; }
.home-grid { grid-template-columns: 1fr; } }

.home-side { display: flex; flex-direction: column; gap: 1rem; }

.card-header.spread, .notice.spread { display: flex; align-items: center; justify-content: space-between; gap: .75rem; flex-wrap: wrap; }

.home-row { display: flex; justify-content: space-between; gap: 1rem; padding: .8rem 0; border-top: 1px solid var(--border); flex-wrap: wrap; }
.home-row:first-of-type { border-top: 0; }
.home-row-main { min-width: 0; flex: 1 1 18rem; }
.home-row-side { display: flex; flex-direction: column; align-items: flex-end; gap: .35rem; }

/* The time and its zone are one fact, so they sit on one line and the zone
   never wraps away from the number it qualifies. */
.home-when { margin-top: .35rem; display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; }
.home-when-range { font-weight: 600; font-variant-numeric: tabular-nums; }
.home-when-zone { font-size: .8rem; color: var(--text-muted); }

.home-amount { font-weight: 600; font-variant-numeric: tabular-nums; }
.home-amount-lg { font-size: 1.35rem; font-weight: 600; font-variant-numeric: tabular-nums; margin: .2rem 0; }

/* A number you can press, not a card with a word on it. */
.home-stat { display: flex; align-items: baseline; gap: .55rem; padding: .5rem 0; text-decoration: none; color: inherit; border-top: 1px solid var(--border); }
.home-stat:first-of-type { border-top: 0; }
.home-stat:hover { text-decoration: none; }
.home-stat:hover .home-stat-label { text-decoration: underline; }
.home-stat-figure { font-size: 1.5rem; font-weight: 600; font-variant-numeric: tabular-nums; min-width: 2ch; }
.home-stat-label { color: var(--text-muted); }
.home-stat-quiet .home-stat-figure { font-size: 1.1rem; font-weight: 500; }

.home-quiet-links { font-size: .85rem; color: var(--text-muted); display: flex; gap: .4rem; flex-wrap: wrap; }
.opp-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem .75rem;
    margin: -.4rem 0 1rem;
    padding: .55rem .7rem;
    border: 1px solid var(--border);
    border-radius: var(--radius, 10px);
    background: var(--surface-muted, var(--surface));
}
.opp-filters select { min-height: 40px; max-width: 15rem; }
.opp-filters .check { display: inline-flex; align-items: center; gap: .4rem; font-size: .88rem; }

.opp-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 21rem), 1fr)); gap: .9rem; }

.opp-card {
    display: flex;
    flex-direction: column;
    gap: .55rem;
    padding: 1.1rem 1.2rem;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
}
.opp-card h3 { margin: 0; font-size: 1.05rem; }
.opp-meta { display: flex; flex-wrap: wrap; gap: .35rem; }
/* ===========================================================================
 * AVAILABILITY — a day is a list of windows
 *
 * Blocks rather than table rows, so a day with two windows looks like a day
 * with two windows instead of a table that has quietly grown a row.
 * ======================================================================== */
/* A Role row you can act on: rate, basis, main, save — then a quiet way out. */
.lang-edit { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.lang-edit select { max-width: 11rem; }
.lang-edit input[type="text"] { max-width: 9rem; }
.role-edit { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.role-edit input[type="text"] { max-width: 6.5rem; }
.role-edit select { max-width: 9rem; }
.role-withdraw { margin-top: .35rem; }

/*
 * The shared time-of-day control: hour : minute AM/PM.
 *
 * Sized so the three read as one reading rather than three questions, and
 * kept on one line — a meridiem that wraps away from its hour is worse than
 * no meridiem at all.
 */
.time-select { display: inline-flex; align-items: center; gap: .2rem; white-space: nowrap; }
.time-select select { min-height: 40px; padding: .3rem .35rem; }
.time-select-hour, .time-select-minute { width: 4.2rem; text-align: center; font-variant-numeric: tabular-nums; }
.time-select-meridiem { width: 4.6rem; margin-left: .25rem; }
.time-select-colon { color: var(--text-muted); font-weight: 600; }

.avail-week { display: grid; gap: .5rem; }
.avail-day { border: 1px solid var(--border); border-radius: var(--radius); padding: .7rem .85rem; background: var(--surface-raised); }
.avail-day-head { display: flex; align-items: center; justify-content: space-between; gap: .75rem; }
.avail-day-name { font-weight: 600; }
.avail-windows { display: grid; gap: .4rem; margin-top: .5rem; }
.avail-window { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
/* Wide enough for "12:30 PM" without the text scrolling inside the box. */
.avail-window input[type="text"] { max-width: 8.5rem; min-height: 40px; }
.avail-to { color: var(--text-muted); font-size: .88rem; }
.avail-remove { line-height: 1; padding: .25rem .5rem; }
/* A suggestion reads as a suggestion, not as a fact about this person. */
.avail-proposed { margin: .45rem 0 0; font-size: .8rem; color: var(--text-muted); font-style: italic; }
.avail-none { margin: .45rem 0 0; font-size: .85rem; color: var(--text-muted); }

/*
 * Two lines of the seat's own summary, clamped rather than truncated: the
 * whole sentence stays in the document for a screen reader, and no card is
 * ever three paragraphs taller than the one beside it.
 */
.opp-summary {
    font-size: .9rem;
    color: var(--text-muted);
    margin: .35rem 0 0;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.opp-terms {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    padding: .55rem .7rem;
    background: var(--surface-sunken);
    border-radius: calc(var(--radius) - 2px);
}
.opp-amount { font-size: 1.1rem; font-weight: 700; color: var(--qc-green-1); }
.opp-model { font-size: .78rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); }
.opp-tbd { font-size: .9rem; color: var(--text-muted); font-style: italic; }

/*
 * Personal filing on its own row, the commitment action beneath it, so
 * "keep track of this" never reads as "take this on".
 */
.opp-actions { display: flex; flex-direction: column; align-items: flex-start; gap: .5rem; margin-top: auto; padding-top: .5rem; }
.opp-actions-personal { display: flex; flex-wrap: wrap; gap: .4rem; }
/* The zone, inline beside the time it qualifies. Quiet, and it never wraps
   away from the number — a stray zone on its own line is worse than none. */
.tz-echo { color: var(--text-muted); white-space: nowrap; }

.opp-blocked-row { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.opp-actions form { display: inline; }
.opp-blocked { font-size: .82rem; color: var(--warn); }

/* ------------------------------------------------------- the intake console

   Two columns because the job has two halves: what the representative is
   ASKING, and what they might need to GLANCE at while the client answers. On a
   narrow screen the aside falls below the questions rather than beside them —
   somebody running a session from a phone still needs the questions first.     */

.split {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
}

@media (max-width: 900px) {
    .split { grid-template-columns: 1fr; }
}

.split-main > * + *,
.split-aside > * + * { margin-top: 1.25rem; }

/* A quieter button for the secondary action beside a primary one. */
.btn-quiet {
    background: transparent;
    color: var(--text);
    border: 1px solid var(--border);
}

.btn-quiet:hover { background: var(--surface-raised); }

/* ------------------------------------------------- the six-objective front door

   Image-led cards. The grid is responsive by construction rather than by
   breakpoint arithmetic: auto-fit with a sensible minimum gives three across on
   a desktop, two on a tablet and one on a phone without naming any of them.

   Explanations open on TAP, not hover — hover does not exist on a phone, and
   this is the first screen a prospect meets.                                  */

.objective-head { max-width: 46rem; margin-bottom: 1.75rem; }

.objective-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 1.25rem;
    list-style: none;
    margin: 0 0 1.5rem;
    padding: 0;
}

.objective-card {
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: 14px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    transition: border-color .15s ease, transform .15s ease;
}

.objective-card:hover,
.objective-card:focus-within { border-color: var(--qc-green-2); transform: translateY(-2px); }

/* A path that is described but not built reads quieter than one that works. */
.objective-card.is-soon { opacity: .82; }

.objective-link {
    display: flex;
    flex-direction: column;
    gap: .75rem;
    text-decoration: none;
    color: inherit;
    height: 100%;
}

.objective-image {
    display: block;
    aspect-ratio: 16 / 9;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    overflow: hidden;
}

.objective-image img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The replaceable slot, when no artwork has been supplied yet. */
.objective-image-slot {
    display: block;
    width: 100%;
    height: 100%;
    background:
        linear-gradient(135deg, color-mix(in srgb, var(--qc-green-2) 14%, transparent), transparent 60%),
        repeating-linear-gradient(45deg, var(--surface) 0 10px, var(--surface-raised) 10px 20px);
}

.objective-body {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    padding: 0 1rem 1rem;
}

.objective-title { font-weight: 650; font-size: 1.02rem; line-height: 1.3; }

.objective-more { padding: 0 1rem 1rem; }
.objective-more summary { cursor: pointer; font-size: .86rem; color: var(--qc-green-2); }
.objective-more p { font-size: .88rem; margin: .5rem 0 0; color: var(--muted); }

@media (prefers-reduced-motion: reduce) {
    .objective-card { transition: none; }
    .objective-card:hover, .objective-card:focus-within { transform: none; }
}

/* Facilitator guidance reads differently from client help, so somebody
   conducting a call can tell at a glance which they are looking at. */
.hint-facilitator {
    border-left: 3px solid var(--qc-green-2);
    padding-left: .7rem;
    background: color-mix(in srgb, var(--qc-green-2) 6%, transparent);
}

/* ---------------------------------------------------------------------------
   The public front door: three audiences.

   Larger and calmer than the objective grid below it, because this is the
   first screen anybody sees and there are only three choices. Three columns on
   a desktop, one on a phone, and the middle step handled by auto-fit rather
   than a breakpoint guess.
   --------------------------------------------------------------------------- */

.entry-head { max-width: 44rem; margin: .5rem auto 2rem; text-align: center; }
.entry-head h1 { margin-bottom: .6rem; }
.entry-lede { color: var(--muted); font-size: 1.05rem; margin: 0; }

.audience-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    gap: 1.25rem;
    list-style: none;
    margin: 0 0 1.75rem;
    padding: 0;
}

.audience-card {
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: 16px;
    overflow: hidden;
    display: flex;
    transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}

.audience-card:hover,
.audience-card:focus-within {
    border-color: var(--qc-green-2);
    transform: translateY(-3px);
    box-shadow: 0 8px 24px color-mix(in srgb, var(--qc-green-1) 12%, transparent);
}

.audience-link {
    display: flex;
    flex-direction: column;
    text-decoration: none;
    color: inherit;
    width: 100%;
}

.audience-image {
    display: block;
    aspect-ratio: 3 / 2;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    overflow: hidden;
}

.audience-image img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Replaceable slot. No brand artwork is invented in code. */
.audience-image-slot {
    display: block;
    width: 100%;
    height: 100%;
    background:
        linear-gradient(135deg, color-mix(in srgb, var(--qc-green-2) 18%, transparent), transparent 62%),
        repeating-linear-gradient(45deg, var(--surface) 0 12px, var(--surface-raised) 12px 24px);
}

.audience-body {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding: 1.1rem 1.15rem 1.25rem;
    flex: 1;
}

.audience-title { font-weight: 650; font-size: 1.15rem; line-height: 1.25; }
.audience-blurb { color: var(--muted); font-size: .92rem; line-height: 1.5; flex: 1; }
.audience-go { color: var(--qc-green-2); font-size: .88rem; font-weight: 600; }

.entry-footnote { color: var(--muted); font-size: .92rem; text-align: center; max-width: 42rem; margin: 0 auto .75rem; }
.entry-signin { text-align: center; font-size: .92rem; color: var(--muted); margin: 0; }

@media (prefers-reduced-motion: reduce) {
    .audience-card { transition: none; }
    .audience-card:hover, .audience-card:focus-within { transform: none; }
}

/* The front door on a small screen.

   Two things go wrong at 375px and both are about a card being mostly empty
   space: a 3:2 image slot on a full-width card pushes the words below the
   fold, and the shell's guest links wrap into a three-line masthead. Cap the
   image, and drop the "Plan an event" shortcut — on this page it duplicates
   the first card, and sign in, create account and language must not wrap
   around it. */
@media (max-width: 560px) {
    .audience-image { aspect-ratio: auto; height: 7rem; }
    .audience-body { padding: .95rem 1rem 1.1rem; }
    .entry-head { margin-bottom: 1.4rem; }
    .entry-head h1 { font-size: 1.55rem; }
    .entry-lede { font-size: .98rem; }
    .shell-inner > .navlink[href="/plan"] { display: none; }
}

/* ---------------------------------------------------------------------------
   Fast answers for a count.

   A row of chips above the field they fill. The field stays visible because it
   is what gets saved — a control that hides the number it will submit is how a
   band quietly becomes a price.
   --------------------------------------------------------------------------- */

.quick-picks {
    display: flex;
    flex-wrap: wrap;
    gap: .45rem;
    margin: .45rem 0 .6rem;
}

.quick-pick {
    font: inherit;
    font-size: .9rem;
    line-height: 1.2;
    padding: .5rem .85rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
    /* Comfortably tappable on a phone, and on an Admin's laptop mid-call. */
    min-height: 2.4rem;
    transition: border-color .12s ease, background-color .12s ease;
}

.quick-pick:hover { border-color: var(--qc-green-2); }

.quick-pick.is-chosen {
    border-color: var(--qc-green-1);
    background: color-mix(in srgb, var(--qc-green-2) 14%, transparent);
    font-weight: 600;
}

.quick-exact { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.quick-exact input[type="number"] { max-width: 11rem; }
.quick-exact-label {
    font-size: .82rem;
    color: var(--muted);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 400;
    margin: 0;
    flex-basis: 100%;
}
.quick-unit { font-size: .88rem; color: var(--muted); }

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

/* ---------------------------------------------------------------------------
   The content editor.

   One field per block, each locale on its own row with its own save. A field
   the editor may not change is shown greyed with the reason rather than hidden
   — a missing field reads as a bug, and being told why teaches the rule.
   --------------------------------------------------------------------------- */

.content-field {
    border-top: 1px solid var(--border);
    padding: 1rem 0;
}
.content-field:first-of-type { border-top: none; }
.content-field.is-locked { opacity: .7; }

.content-field-head {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: baseline;
}
.content-field-head code { font-size: .78rem; }

.content-locale { margin: .5rem 0 0; }
.content-locale-label {
    display: flex;
    gap: .5rem;
    align-items: baseline;
    font-size: .8rem;
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    margin: 0 0 .25rem;
}
.content-locale textarea { min-height: 3.2rem; }
.content-locale-actions {
    display: flex;
    gap: .75rem;
    align-items: center;
    flex-wrap: wrap;
    margin-top: .35rem;
}

/* ---------------------------------------------------------------------------
   The assessment progress rail.

   Section names rather than a question count. "Your event → Audience →
   Technology → Support → Results" says both where you are and that there is an
   end to this; "3 of 35" says only the second, discouragingly.

   On a phone it becomes a compact row of dots with the current section named
   underneath, because five labels will not fit and a wrapped rail reads as
   clutter rather than as progress.
   --------------------------------------------------------------------------- */

.steps {
    display: flex;
    align-items: flex-start;
    gap: .25rem;
    list-style: none;
    margin: 0 0 1.25rem;
    padding: 0;
    counter-reset: step;
}

.step {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .35rem;
    flex: 1;
    position: relative;
    text-align: center;
    min-width: 0;
}

/* The connector, drawn behind the dots rather than as separate elements. */
.step::before {
    content: "";
    position: absolute;
    top: .85rem;
    left: -50%;
    width: 100%;
    height: 2px;
    background: var(--border);
    z-index: 0;
}
.step:first-child::before { display: none; }
.step.is-now::before,
.step.is-done::before { background: var(--qc-green-2); }

.step-dot {
    position: relative;
    z-index: 1;
    display: grid;
    place-items: center;
    width: 1.7rem;
    height: 1.7rem;
    border-radius: 999px;
    border: 2px solid var(--border);
    background: var(--surface-raised);
    font-size: .8rem;
    font-weight: 600;
    color: var(--muted);
}

.step.is-now .step-dot {
    border-color: var(--qc-green-1);
    background: var(--qc-green-1);
    color: #fff;
}
.step.is-done .step-dot { border-color: var(--qc-green-2); color: var(--qc-green-2); }

.step-name { font-size: .78rem; line-height: 1.25; color: var(--muted); }
.step.is-now .step-name { color: var(--text); font-weight: 600; }

@media (max-width: 640px) {
    /* Only the current section is named. The rest stay as dots, so the rail is
       one short row instead of five wrapped labels. */
    .step-name { display: none; }
    .step.is-now .step-name {
        display: block;
        position: absolute;
        top: 2.2rem;
        left: 50%;
        transform: translateX(-50%);
        white-space: nowrap;
        font-size: .82rem;
    }
    .steps { margin-bottom: 2.6rem; }
}

.choice-row {
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    display: flex;
    gap: .55rem;
    align-items: center;
    margin: 0;
}

/* The three things a visitor can do after the free result. Each says what it
   is not before its button, because a reassurance that arrives after the click
   is an apology. */
.next-steps .next-step {
    display: flex;
    gap: 1rem;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    border-top: 1px solid var(--border);
    padding: 1rem 0;
}
.next-steps .next-step:first-of-type { border-top: none; padding-top: .25rem; }
.next-step-body { flex: 1 1 18rem; min-width: 0; }
.next-step-body p { margin: .25rem 0 0; }

/* Custom duration: hours and minutes, revealed only when the presets do not
   cover it. Nobody should have to know that five and a half hours is 330. */
.duration-custom { margin: .5rem 0 0; }
.duration-parts { display: flex; gap: 1rem; flex-wrap: wrap; align-items: flex-end; }
.duration-part { display: flex; flex-direction: column; gap: .2rem; }
.duration-part label {
    font-size: .78rem; color: var(--muted); text-transform: none;
    letter-spacing: 0; font-weight: 600; margin: 0;
}
.duration-part input, .duration-part select { max-width: 7rem; }

/* A picture you can see before you keep it. */
.content-image-preview {
    display: flex;
    gap: .75rem;
    align-items: center;
    margin: 0 0 .5rem;
    flex-wrap: wrap;
}
.content-image-preview img {
    width: 8rem;
    height: 5.4rem;
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--surface);
}
.content-upload { margin: .5rem 0 0; padding-top: .5rem; border-top: 1px dashed var(--border); }
.content-upload input[type="file"] { font-size: .88rem; }

/* An asking amount: a number, a currency and a basis, on one line. */
.ask-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }

/* ---------------------------------------------------------------------------
   Image slot specification
   ---------------------------------------------------------------------------
   Answers "what should I prepare?" above the file picker rather than in a
   refusal afterwards. Required terms are marked so advice cannot be mistaken
   for a rule. */
.image-spec {
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    padding: .75rem .9rem;
    margin: 0 0 .75rem;
}

.image-spec-row {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem .75rem;
    padding: .2rem 0;
    font-size: .875rem;
}

.image-spec-term {
    flex: 0 0 12rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    font-size: .7rem;
    font-weight: 600;
    color: var(--muted);
    align-self: center;
}

.image-spec-required::after {
    content: " *";
    color: var(--warn);
}

.image-spec-value { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------------------
   What "I'm available" means
   ---------------------------------------------------------------------------
   A details element rather than a tooltip: this is advance consent, and a
   tooltip cannot be read on a phone. */
.consent-note {
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    padding: .6rem .85rem;
    margin: 0 0 .75rem;
    font-size: .875rem;
}

.consent-note > summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--heading);
}

.consent-note p  { margin: .6rem 0 0; }
.consent-note ul { margin: .5rem 0 0; padding-left: 1.15rem; }
.consent-note li { margin: .25rem 0; }

/* ---------------------------------------------------------------------------
   Direct project intake
   ---------------------------------------------------------------------------
   One screen for a phone call. Roles are visually separated so four of them
   do not read as one long form. */
.intake-role {
    border: 1px solid var(--border);
    border-left: 3px solid var(--qc-green-2);
    border-radius: 10px;
    padding: .9rem 1rem .4rem;
    margin: 0 0 .9rem;
    background: var(--surface);
}

.grid-4 { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

.duration-choice {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
}

.duration-choice input { width: auto; }

/* ---------------------------------------------------------------------------
   Plain-language help for figures that are easy to confuse
   ---------------------------------------------------------------------------
   Collapsed by default so it does not add height to a form somebody already
   knows, and open in one click when they do not. */
.commercial-help,
.charge-explain {
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    padding: .7rem .9rem;
    margin: 0 0 .9rem;
    font-size: .875rem;
}

.commercial-help > summary { cursor: pointer; font-weight: 600; color: var(--heading); }
.commercial-help dl { margin: .7rem 0 0; }

.commercial-help dt,
.charge-explain dt,
.credential-summary dt {
    font-weight: 600;
    color: var(--heading);
    margin-top: .6rem;
}

.commercial-help dt:first-child,
.charge-explain dt:first-child,
.credential-summary dt:first-child { margin-top: 0; }

.commercial-help dd,
.charge-explain dd,
.credential-summary dd { margin: .2rem 0 0; color: var(--muted); }

/* What is recorded for a provider, minus the one field that is never readable. */
.credential-summary { margin: .5rem 0 0; }
.credential-summary code { word-break: break-all; }

/* ---------------------------------------------------------------------------
   What QC checked
   ---------------------------------------------------------------------------
   Three states, told apart by shape and word as well as colour: a tick, a
   cross, and a dash for "QC was not told, so this was not checked". The dash
   matters most — it is the state somebody can fix, and rendering it as a
   failure would tell them they do not qualify when they have simply not
   answered a question yet. */
.qualification-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .4rem;
}

.qualification-list li {
    display: flex;
    align-items: baseline;
    gap: .55rem;
    font-size: .9375rem;
}

.qualification-mark {
    flex: 0 0 auto;
    width: 1.1rem;
    text-align: center;
    font-weight: 700;
}

.qualification-met   .qualification-mark { color: var(--ok); }
.qualification-unmet .qualification-mark { color: var(--warn); }

.qualification-not_stated .qualification-mark,
.qualification-not_stated { color: var(--muted); }
/* ---------------------------------------------------------------------------
   "Your local time"
   ---------------------------------------------------------------------------
   Deliberately quieter than the event's own time, and consistently so across
   every Collaborator screen. The event zone is the fact somebody was booked
   in; this is a convenience, and it should look like one. Uses the existing
   surface and muted tokens rather than a new colour. */
.local-time {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .35rem .5rem;
    margin: .45rem 0 0;
    padding: .35rem .55rem;
    border-left: 2px solid var(--border);
    background: var(--surface);
    border-radius: 0 6px 6px 0;
    font-size: .8125rem;
}

.local-time-label {
    text-transform: uppercase;
    letter-spacing: .04em;
    font-size: .6875rem;
    font-weight: 600;
    color: var(--muted);
}

.local-time-value { color: var(--text); font-variant-numeric: tabular-nums; }
.local-time-zone  { color: var(--muted); }

.local-time-inline { margin-top: .3rem; padding: .2rem .45rem; }

/* ---------------------------------------------------------------------------
   A connected account
   ---------------------------------------------------------------------------
   Name, state and the one action, on a line. Reusable for whatever else gets
   connected later; nothing here is Google-specific. */
.connected-account {
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    padding: .85rem 1rem;
    margin: 0 0 1rem;
}

.connected-account-head {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: center;
    justify-content: space-between;
}

/* A URI somebody has to copy exactly. Selectable, and it wraps rather than
   overflowing, because half a redirect URI is worse than none. */
.callback-uri {
    user-select: all;
    word-break: break-all;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: .3rem .5rem;
    font-size: .8125rem;
}

/* A role's duties: the duty on the left, the way to remove it on the right. */
.responsibility-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .35rem; }
.responsibility-list li { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; }

/* A role's duties on one project: tick what applies, and re-word the odd one. */
.role-duties { border: 1px solid var(--border); border-radius: 8px; padding: .7rem .9rem; margin: .75rem 0; }
.role-duties legend { font-weight: 600; padding: 0 .35rem; }
.role-duties .duty { display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem; margin: .35rem 0; }
.role-duties .duty label { flex: 1 1 22rem; }
.role-duties .duty-wording { flex: 1 1 14rem; font-size: .8125rem; }
.intake-actions { gap: .6rem; }

/* Who is on a staffing seat: name, kind, state. QC-only surface. */
.staffed-by { list-style: none; margin: .4rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .2rem; }
.staffed-by li { display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; }

/* A figure somebody opened the page to read. */
.figure-large { font-size: 1.75rem; font-weight: 700; color: var(--heading); margin: .2rem 0; font-variant-numeric: tabular-nums; }

/* The forecast tile on the launcher: a card that is also a link. */

/* ── PERSONAL UTILITIES IN THE HEADER ───────────────────────────────────────
 *
 * A forecast and an unread count, each a few characters wide, sitting where
 * the eye already goes for personal state. Both were pages of their own
 * before: the forecast filled the middle of the dashboard and the unread
 * count hid inside the account menu.
 *
 * Sized in ch and rem so a four-figure amount widens the control instead of
 * clipping, and so nothing here depends on the figure being short.
 */
.shell-util {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .3rem .5rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    color: var(--text);
    text-decoration: none;
    font-size: .82rem;
    line-height: 1;
    white-space: nowrap;
}
.shell-util:hover { border-color: var(--border); background: var(--surface-raised); }
.shell-util:focus-visible { outline: 2px solid var(--focus, var(--heading)); outline-offset: 2px; }

details.shell-util > summary {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    cursor: pointer;
    list-style: none;
}
details.shell-util > summary::-webkit-details-marker { display: none; }
details.shell-util[open] { border-color: var(--border); background: var(--surface-raised); }

.shell-util-icon   { font-size: .95rem; }
.shell-util-figure { font-weight: 600; font-variant-numeric: tabular-nums; }
.shell-util-more   { color: var(--text-muted); font-size: .7rem; }

/* The badge is a convenience, never the only carrier: the accessible label on
   the link says how many are unread, so this does not depend on seeing it. */
.shell-util-badge {
    min-width: 1.15rem;
    padding: 0 .25rem;
    border-radius: 999px;
    background: var(--warn, #B4531B);
    color: #fff;
    font-size: .66rem;
    font-weight: 700;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.shell-util-panel { gap: .4rem; }
.shell-util-amount {
    margin: 0;
    font-size: 1.25rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* Narrow screens keep the controls and drop the words. The bell and the
   amount are the point; the "+2" and the panel still work by tap. */
@media (max-width: 640px) {
    .shell-util { padding: .3rem .35rem; }
    .shell-util-more { display: none; }
}

/* ── REARRANGING THE DASHBOARD ──────────────────────────────────────────────
 *
 * The editing state has to LOOK different from the locked one, or somebody
 * cannot tell whether their next click opens a tile or moves it.
 */
.layout-entry { margin-top: 1.25rem; }

.launcher-editing { list-style: none; margin: 0; padding: 0; }
.launch-card-editing {
    position: relative;
    cursor: grab;
    border-style: dashed;
    padding-left: 2.6rem;
}
.launch-card-editing:active { cursor: grabbing; }
.launch-card-editing.is-dragging { opacity: .45; }

/* The position is stated, not implied by where the tile happens to sit —
   useful when the arrows are the only way somebody is moving things. */
.launch-position {
    position: absolute;
    top: .75rem;
    left: .85rem;
    min-width: 1.4rem;
    height: 1.4rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    font-size: .7rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}

.launch-move { display: flex; gap: .3rem; margin-top: .6rem; }
.launch-move button[disabled] { opacity: .35; cursor: default; }

.layout-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: 1rem;
}

/* Records inside the open section. Indented and quieter than the sections
   themselves, so the nav still reads as a list of areas with one of them
   opened rather than as a flat list of everything QC has. */
.config-menu-items {
    list-style: none;
    margin: .1rem 0 .35rem;
    padding: 0 0 0 .55rem;
    border-left: 2px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: .05rem;
}
.config-menu-items a {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .4rem;
    font-size: .8rem;
    padding: .25rem .4rem;
}
.config-menu-item-label { overflow-wrap: anywhere; }
.config-menu-item-meta  { color: var(--text-muted); font-size: .68rem; white-space: nowrap; }
.config-menu-count {
    color: var(--text-muted);
    font-size: .7rem;
    font-variant-numeric: tabular-nums;
    margin-left: .35rem;
}
@media (max-width: 760px) {
    /* The nav is a chip row here; a nested list inside it would not fit, so
       records stack under it instead of beside it. */
    .config-menu-items {
        flex-direction: row;
        overflow-x: auto;
        border-left: 0;
        padding-left: 0;
    }
    .config-menu-items a { white-space: nowrap; }
}

/* ── CHOOSING AN IMAGE BY LOOKING AT IT ─────────────────────────────────────
 *
 * This was a dropdown of generated filenames. The thumbnail is the label now;
 * the words underneath are for confirming, not for identifying.
 */
.media-picker {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: .6rem;
    margin: .4rem 0;
    max-height: 26rem;
    overflow-y: auto;
    padding: .2rem;
}
.media-option {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    padding: .4rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-raised);
    cursor: pointer;
}
.media-option:hover { border-color: var(--heading); }

/* The radio stays in the accessible tree and keyboard order — it is simply
   small, because the picture is doing the work. */
.media-option input[type="radio"] { margin: 0; }
.media-option:focus-within { outline: 2px solid var(--focus, var(--heading)); outline-offset: 2px; }
.media-option:has(input:checked) { border-color: var(--heading); border-width: 2px; padding: calc(.4rem - 1px); }

.media-thumb {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: calc(var(--radius) / 1.5);
    background: var(--surface);
}
.media-name {
    font-size: .74rem;
    line-height: 1.25;
    overflow-wrap: anywhere;
}
.media-meta { font-size: .66rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }

.media-option-none { justify-content: center; align-items: center; text-align: center; }
.media-none-mark { font-size: 1.6rem; color: var(--text-muted); }

/* The library as a place, where a picture can be given a name. */
.media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: .9rem;
    margin-top: .6rem;
}
.media-card {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: .5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-raised);
}
.media-rename { display: flex; gap: .3rem; }
.media-rename input[type="text"] { flex: 1 1 auto; min-width: 0; font-size: .78rem; }
.media-card .media-meta { margin: 0; line-height: 1.3; overflow-wrap: anywhere; }
.media-original { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

/* Present to a screen reader, absent to the eye. */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ── OPPORTUNITIES: A LIST WITH THE CHOSEN ONE BESIDE IT ────────────────────
 *
 * Unlike the configuration screens, the LIST is the primary content here —
 * these are operational records somebody scans. So it keeps the whole width
 * until something is chosen, and a substantial share afterwards rather than
 * shrinking to a sidebar of names.
 */
.opp-workspace { display: block; }

@media (min-width: 900px) {
    .opp-workspace.has-selection {
        display: grid;
        grid-template-columns: minmax(19rem, 2fr) 3fr;
        gap: 1.25rem;
        align-items: start;
    }
    .opp-workspace.has-selection .opp-list { max-height: 78vh; overflow-y: auto; }
    .opp-detail { position: sticky; top: 64px; max-height: 82vh; overflow-y: auto; }
    /* The back link is only the way out on a stacked screen. */
    .opp-detail-back { display: none; }
}

/* Rows, not tiles. The grid that made every opportunity an equal card is what
   turned twelve of them into a wall. */
.opp-list {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    grid-template-columns: none;
}
.opp-card { padding: .75rem .9rem; gap: .35rem; }
.opp-card h3 { font-size: .98rem; }
.opp-card.is-chosen {
    border-color: var(--heading);
    border-left-width: 3px;
    background: var(--surface);
}

/* The panel renders the full opportunity view, which brings its own page
   container. Neutralised here so it fills the panel instead of centring a
   narrow column inside it. */
.opp-detail > .container,
.opp-detail > .container.medium {
    max-width: none;
    margin: 0;
    padding: 0;
}
.opp-detail {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-raised);
    padding: 1rem 1.1rem;
}

/* The unsaved-changes choice. Native <dialog>: focus-trapped and escapable
   without a line of extra code. */
.unsaved-dialog {
    max-width: min(30rem, calc(100vw - 2rem));
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-raised);
    color: var(--text);
    padding: 1.1rem 1.25rem;
}
.unsaved-dialog::backdrop { background: rgba(29, 43, 40, .45); }
.unsaved-dialog h2 { margin: 0 0 .4rem; }

/* Everything a Collaborator still has to answer, gathered. Set apart from the
   requirement list above it: that one states how things stand, this one is a
   list of things to do. */
.needed {
    margin-top: .9rem;
    padding-top: .8rem;
    border-top: 1px solid var(--border);
}
.needed-list { list-style: none; margin: .5rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.needed-list li {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    align-items: center;
    justify-content: space-between;
}

/* ---------------------------------------------------------------------------
   EARNINGS — THREE STATES THAT MUST NOT LOOK ALIKE
   ---------------------------------------------------------------------------

   An amount, "nothing recorded yet", and "being developed" are three different
   claims about what QC knows. They are given three different treatments so that
   somebody scanning the page cannot mistake one for another — in particular so
   that a card QC cannot calculate never reads like a card that came out at zero.

   The developing state is deliberately the quietest thing on the screen: a
   dashed border, muted text, no figure typeface anywhere near it.
   --------------------------------------------------------------------------- */

.figure-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
    gap: 1rem;
}

.figure-card {
    padding: 1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-sunken);
}

/*
 * A card QC cannot fill yet.
 *
 * The first version made this transparent, and on a dashboard of filled tiles
 * it read as broken or unstyled rather than as deliberate — which is the wrong
 * kind of unmistakable. It keeps the same ground and the same geometry as its
 * neighbours now; a dashed border and a small badge carry the state, so it is
 * still impossible to mistake for a card holding a real figure.
 */
.figure-card-developing {
    border-style: dashed;
    border-color: var(--border-strong);
}

.figure-badge {
    display: inline-block;
    margin: 0 0 .4rem;
    padding: .12rem .5rem;
    border-radius: 999px;
    background: var(--action-quiet);
    color: var(--qc-green-2);
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

/* The button row under the statement period, aligned with the inputs beside
   it rather than floating up against their labels. (.field-error already
   exists further up; a second copy here would be two rules fighting over the
   same selector for no reason.) */
.field-actions { display: flex; align-items: flex-end; }

.figure-label {
    margin: 0 0 .35rem;
    font-size: .8rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.figure-amount {
    margin: 0 0 .15rem;
    font-size: 1.45rem;
    font-weight: 700;
    color: var(--text);
    /* Digits line up between cards rather than shuffling by a pixel. */
    font-variant-numeric: tabular-nums;
}

.figure-empty,
.figure-developing {
    margin: 0 0 .15rem;
    font-size: .95rem;
    font-style: italic;
    color: var(--text-muted);
}

.figure-hint {
    margin: .35rem 0 0;
    font-size: .8rem;
    line-height: 1.45;
    color: var(--text-muted);
}

/* ---------------------------------------------------------------------------
   PAYOUT PREFERENCES — ORDER WITHOUT DRAGGING
   --------------------------------------------------------------------------- */

.preference-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .5rem;
}

.preference-row {
    display: flex;
    align-items: center;
    gap: .6rem;
}

.preference-rank {
    min-width: 1.5rem;
    font-weight: 700;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.preference-select { flex: 1 1 auto; min-width: 0; max-width: 26rem; }

/* A number field wide enough for three digits and no wider. */
.input-tiny { width: 4.5rem; }

/* A wide table scrolls inside its own box; the page never scrolls sideways. */
.table-scroll { overflow-x: auto; }

.stack-list {
    margin: 0;
    padding-left: 1.1rem;
    display: flex;
    flex-direction: column;
    gap: .6rem;
}

/* ---------------------------------------------------------------------------
   BETA FEEDBACK — ALWAYS THERE, NEVER IN THE WAY
   ---------------------------------------------------------------------------

   Sits beside the assistant and is deliberately quieter than it: this is a door
   somebody should be able to find when they need it, not a thing competing for
   attention on every screen.
   --------------------------------------------------------------------------- */

.beta-launcher {
    position: fixed;
    right: max(1rem, env(safe-area-inset-right));
    bottom: calc(4.75rem + env(safe-area-inset-bottom));
    z-index: 60;
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .5rem .85rem;
    min-height: 40px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text);
    text-decoration: none;
    font-size: .82rem;
    font-weight: 600;
    box-shadow: var(--shadow);
}

.beta-launcher:hover { background: var(--action-quiet); }
.beta-launcher:focus-visible { outline: 3px solid var(--qc-teal); outline-offset: 2px; }

.beta-launcher-mark {
    font-weight: 700;
    color: var(--qc-green-1);
}

@media (max-width: 640px) {
    /* On a small screen the assistant keeps its label and this becomes a mark,
       so the two never stack into a wall of buttons over the content. */
    .beta-launcher { padding: 0; width: 40px; height: 40px; justify-content: center; }
    .beta-launcher-label { display: none; }
}

/* ===========================================================================
 * THE SHARED WORKSPACE SHELL, ON A PHONE
 *
 * Profile, Settings and Earnings & Payouts are all a section list beside a
 * working surface. On a narrow screen that shape has to change rather than
 * shrink — a 250px menu next to a 120px column is two things neither of which
 * can be used.
 *
 * One markup, two shapes, and no page-specific media queries anywhere: fixing
 * the primitive fixes every workspace that uses it, including ones not built
 * yet.
 * ======================================================================== */

.config-nav { display: block; }

/*
 * Above the breakpoint the disclosure is not a disclosure. It is forced open,
 * its summary is hidden, and what is left is the sidebar that has always been
 * there — sticky, so it stays put while a long form scrolls.
 */
@media (min-width: 901px) {
    /*
     * The open/closed state is IGNORED above the breakpoint, deliberately and
     * in writing. A user agent hides a closed disclosure's children, so a
     * sidebar that only appeared because another rule happened to set display
     * would vanish the day that rule moved. Here it is said outright: on a wide
     * screen this is a sidebar, whatever the element thinks it is.
     */
    .config-nav[open] > .config-menu,
    .config-nav > .config-menu {
        display: flex;
        position: sticky;
        top: 64px;
    }

    .config-nav-summary { display: none; }
    .config-menu { margin-top: 0; }
}

/*
 * Below it, a real control: a full-width button showing where you are, which
 * opens into the list. Sized for a thumb rather than a cursor.
 */
@media (max-width: 900px) {
    .config-nav-summary {
        display: flex;
        flex-direction: column;
        gap: .1rem;
        list-style: none;
        cursor: pointer;
        padding: .6rem .8rem;
        min-height: 48px;
        justify-content: center;
        background: var(--surface-raised);
        border: 1px solid var(--border);
        border-radius: var(--radius);
    }

    .config-nav-summary::-webkit-details-marker { display: none; }
    .config-nav-summary:focus-visible { outline: 2px solid var(--qc-teal); outline-offset: 2px; }

    .config-nav-eyebrow {
        font-size: .62rem;
        text-transform: uppercase;
        letter-spacing: .07em;
        color: var(--text-muted);
    }

    .config-nav-current { font-weight: 600; font-size: .95rem; }

    /* The open state gets a marker so it does not read as a floating list. */
    .config-nav[open] > .config-menu {
        border: 1px solid var(--border);
        border-radius: var(--radius);
        padding: .35rem;
    }

    .config-menu a { min-height: 44px; }
}

/*
 * ── THE CHEVRON ─────────────────────────────────────────────────────────────
 *
 * The section card on a phone is a real control and did not look like one: an
 * eyebrow, a name, and nothing to say it opens. People read it as a heading and
 * never tapped it, which left every section but the first one unreachable to
 * anybody who did not already know.
 *
 * This is an AFFORDANCE and nothing else. The card keeps its size, its type and
 * its spacing; a chevron appears at the right and turns to point up when the
 * list is open.
 *
 * Drawn on the summary in CSS rather than added as an element, deliberately:
 * there is no arrow to aim at, so the whole card stays the tap target. The only
 * spacing that changes is room at the right for the mark to sit in without the
 * longest section name running underneath it.
 *
 * Nothing here appears above the breakpoint — the summary is not shown there,
 * because on a wide screen this is a sidebar and not a disclosure at all.
 */
@media (max-width: 900px) {
    .config-nav-summary {
        position: relative;
        padding-right: 2.2rem;
    }

    .config-nav-summary::after {
        content: '';
        position: absolute;
        right: .95rem;
        top: 50%;
        width: .48rem;
        height: .48rem;
        margin-top: -.34rem;
        border-right: 2px solid var(--text-muted);
        border-bottom: 2px solid var(--text-muted);
        transform: rotate(45deg);
        transition: transform .15s ease, margin-top .15s ease;
    }

    /* Open: the same mark, pointing back at the card it came from. */
    .config-nav[open] > .config-nav-summary::after {
        margin-top: -.12rem;
        transform: rotate(-135deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .config-nav-summary::after { transition: none; }
}

/*
 * Language, once it has moved off the bar.
 *
 * Shown only at the width where the header control is hidden, so the same
 * choice is never offered in two places at once.
 */
.shell-menu-lang { display: none; }

@media (max-width: 900px) {
    .shell-menu-lang {
        display: block;
        padding: .35rem .25rem .6rem;
        border-bottom: 1px solid var(--border);
        margin-bottom: .5rem;
    }

    .shell-menu-lang label {
        display: block;
        font-size: .72rem;
        text-transform: uppercase;
        letter-spacing: .06em;
        color: var(--text-muted);
        margin-bottom: .25rem;
    }

    .shell-menu-lang select { width: 100%; min-height: 40px; }
}

/* ===========================================================================
 * THE TOP BAR, ON A PHONE
 *
 * It was one flex row that never wrapped, so at narrow widths the controls
 * squeezed each other until labels clipped. It wraps now, and the utilities
 * keep their own row rather than fighting the brand for the first one.
 * ======================================================================== */

@media (max-width: 700px) {
    /*
     * ── ONE ROW, DELIBERATELY, WITH THE WORDS REMOVED ───────────────────────
     *
     * Wrapping the utilities onto a second row was tried and abandoned: the
     * panels below hang off whichever control opened them, and a control that
     * has moved to a second row near the left edge opens a 320px panel three
     * hundred pixels off the side of the screen. Measured at 360px, the
     * account menu sat at left: -259.
     *
     * So the bar stays one row and the LABELS go instead — brand mark, home
     * icon, the context, the bell, the avatar. That is an intentional icon
     * hierarchy rather than desktop controls squeezed until they clip, and
     * every one of them keeps its accessible name.
     */
    .shell-inner {
        flex-wrap: nowrap;
        padding: .5rem .6rem;
        gap: .35rem;
    }

    /* A tapped control, not a clicked one. */
    .shell-util, .shell-menu > summary, .shell-home { min-height: 44px; }

    /* Nothing in the bar may force it wider than the screen. */
    .shell-util, .shell-menu, .shell-home, .shell .brand { min-width: 0; }
    .shell-util-value { overflow: hidden; text-overflow: ellipsis; }
}

/*
 * Language leaves the bar and reappears inside the account menu.
 *
 * Declared here, beside the control that replaces it, so the pair cannot drift
 * apart — and last in the file, so the intent wins the cascade rather than
 * depending on which earlier rule happens to come second.
 */
@media (max-width: 900px) {
    .shell-util-lang { display: none !important; }
}

/*
 * A panel that opens inside the screen it is opened on.
 *
 * It hangs off its own button, which on a phone is a few pixels from the right
 * edge. Clamped to the viewport rather than allowed to run past it — at 360px
 * the unclamped version put a quarter of the panel outside the window.
 */
@media (max-width: 640px) {
    /*
     * Both panels are pinned to the VIEWPORT rather than to the button that
     * opened them. Anchored to the button, the context panel opened at x=82 on
     * a 360px screen and ran to 402 — a third of it off the side. Which control
     * you press should not decide whether you can read what it opens.
     */
    .shell-menu-panel, .shell-util-panel {
        position: fixed;
        left: .5rem;
        right: .5rem;
        top: auto;
        width: auto;
        max-width: none;
        margin-top: .35rem;
    }
}

/*
 * The person's name, on a screen with no room for it.
 *
 * There has been a rule hiding this below 560px since the shell was written,
 * and it has never once applied: it sits earlier in this file than the base
 * rule that sets display, and at equal specificity the later one wins. The
 * avatar carried the identity and the name pushed the bar 90px past the edge
 * of a 360px screen.
 *
 * Restated here, last, where it takes effect. The accessible name on the
 * summary is unchanged, so nothing is lost to a screen reader.
 */
@media (max-width: 560px) {
    .shell-menu-name { display: none; }
}

/* ===========================================================================
 * NOTHING SPILLS SIDEWAYS
 *
 * Not overflow-x: hidden anywhere — that conceals a broken layout rather than
 * fixing one, and hides real content with it. These are the two things that
 * actually cause a workspace to overflow: a grid child refusing to shrink
 * below its content, and a control wider than its column.
 * ======================================================================== */

.config-layout > * { min-width: 0; }
.card { min-width: 0; }

input, select, textarea { max-width: 100%; }

/* Long unbroken things — a URL in a hint, a reference — wrap instead of
   pushing the page wider. */
.hint, .field-error, .muted, .empty { overflow-wrap: anywhere; }

@media (max-width: 640px) {
    .container.workspace { padding-left: .85rem; padding-right: .85rem; }

    /* Two columns of 140px is worse than one of 300px. */
    .grid-2, .grid-3 { grid-template-columns: 1fr; }

    /* A row of buttons wraps rather than running off the edge. */
    .row { flex-wrap: wrap; }
}

/* ===========================================================================
 * BUSINESS (IF APPLICABLE)
 * ======================================================================== */

/* A revealed step is not a new card; it is the next question in the same one. */
.business-step { display: block; }
.business-step[hidden] { display: none; }

.business-other { margin-top: .5rem; }

.checkbox-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
    gap: .35rem .9rem;
}

.radio-row { display: flex; flex-wrap: wrap; gap: .5rem 1.25rem; }

/* Touch targets, and a label that wraps with its control rather than under it. */
.choice {
    display: inline-flex;
    align-items: flex-start;
    gap: .45rem;
    min-height: 40px;
    padding: .35rem 0;
    line-height: 1.35;
}

.choice input { margin-top: .2rem; flex: none; }

fieldset.field { border: 0; padding: 0; margin: 0 0 1rem; }
fieldset.field > legend { padding: 0; font-weight: 600; font-size: .92rem; }

/*
 * ── DEVICES ON A WIDE SCREEN ────────────────────────────────────────────────
 *
 * Fourteen questions, each one word long, each answered Yes / No / Not said.
 * Stacked, that is a page somebody scrolls for half a minute to answer a list
 * they can read in five seconds — the name on one line, the three buttons on
 * the next, fourteen times over.
 *
 * So above the breakpoint the answer sits at the RIGHT of the item it answers,
 * on the same line as its name, and the list reads like a list: what it is on
 * the left, what you said on the right, one row per device.
 *
 * BELOW the breakpoint nothing here applies and the stacking stays exactly as
 * it is. On a phone the name and its three buttons need the full width, and
 * that layout is accepted as it stands.
 *
 * The answers are positioned in a column the item reserves with padding, not
 * dropped on top of it. A long description — the broadcast-software one runs to
 * three lines — flows down the left and the buttons stay where they are, and
 * the reserved minimum height keeps a device with no description from ending
 * above its own controls.
 */
@media (min-width: 901px) {
    .device-item {
        position: relative;
        padding-right: 15rem;
        min-height: 2.75rem;
    }

    .device-item > .radio-row {
        position: absolute;
        top: 0;
        right: 0;
        width: 14rem;
        justify-content: flex-end;
        gap: .5rem .9rem;
        margin: 0;
    }

    /* The follow-up answers belong to the item, so they stay under its name
       rather than being dragged out to the answer column. */
    .device-item > .device-count,
    .device-item > .device-children { margin-top: .35rem; }
}


/* ============================================================ PRINTED BRIEF */

/*
 * The brief as a document, at /collaborator/…/print.
 *
 * Two jobs. On screen it strips the ACTIONS out of the brief — a printed Accept
 * button is furniture, and a form on a page whose purpose is to be carried into
 * a hall is a control nobody can press. On paper it strips everything that is
 * about being on a screen at all.
 *
 * Nothing here removes information. What a Collaborator may read is decided by
 * the services the print route calls, and a stylesheet is the wrong place for a
 * rule about that — so this hides controls and chrome, and not one field.
 */
.print-body {
    margin: 0;
    background: var(--surface);
    color: var(--text);
}

.print-bar {
    display: flex;
    gap: .5rem;
    align-items: center;
    justify-content: flex-end;
    padding: .75rem 1rem;
    border-bottom: 1px solid var(--border);
    background: var(--surface-raised);
    position: sticky;
    top: 0;
    z-index: 5;
}

.print-doc { padding: 1rem 0 3rem; }

/* Actions, gone. The brief is the facts. */
.print-doc form,
.print-doc .btn,
.print-doc .layout-actions,
.print-doc .print-offer { display: none; }

@media print {
    /* Everything that exists because this is a browser. */
    .print-bar,
    .app-shell,
    .shell-menu,
    .site-footer,
    .notice { display: none !important; }

    .print-body { background: #fff; color: #000; }

    .print-doc { padding: 0; }

    .container,
    .container.medium { max-width: none; padding: 0; }

    /*
     * Cards are boxes on a screen and lines on paper. Keeping the border but
     * dropping the fill stops a printer laying down a page of grey.
     */
    .card {
        break-inside: avoid;
        box-shadow: none;
        background: transparent;
    }

    /* A heading at the foot of a page with its content overleaf is a brief
       somebody misreads on site. */
    h1, h2, h3 { break-after: avoid; }

    a[href] { text-decoration: none; color: inherit; }

    /* The offer to print, on the printed copy, would be a joke at the reader's
       expense. */
    .print-offer { display: none !important; }

    /*
     * ON PAPER THE NOTE IS OPEN, NOT A CONTROL.
     *
     * The estimate carries a disclosure explaining that it is an estimate
     * and naming the authoritative amount. Printed as a collapsed triangle
     * that is a caveat nobody can read, beside a second currency figure —
     * which is the one arrangement of this feature that could mislead.
     *
     * So the summary marker goes and the body is shown inline.
     */
    .money-note > summary { display: none !important; }
    .money-note[open] > *, .money-note > * { display: inline !important; }
    .money-note-body { font-size: .85em; }

    /* Nor is the jump navigation or the picker that produced this document. */
    .ws-nav,
    .ws-print { display: none !important; }

    /*
     * A LINK HAS TO BE READABLE ON PAPER.
     *
     * The audit found the joining link printed as the words "Join the event",
     * which is a button somebody is holding in their hand. The address is
     * rendered as text beside every link for exactly this: on screen it is the
     * quiet line under the button, and here it is the only way in.
     *
     * Broken anywhere rather than running off the edge of the page — a URL is
     * one unbreakable word to a text engine and will happily overflow.
     */
    .printable-link {
        display: block;
        word-break: break-all;
        color: #000;
    }

    /* Everything the reader chose is open. A fold on paper is a fold nobody
       can undo. */
    .ws-page-print details > * { display: block !important; }
}

/* ========================================================== ROLES AND RATES */

/*
 * The add rows at the top of Roles & Rates.
 *
 * ON A WIDE SCREEN one line per Role: what you do, the least you want for it,
 * and what that amount is per. Three fields on one line is the shape of the
 * sentence somebody is writing, and stacking them would make a five-Role batch
 * fifteen rows long.
 *
 * ON A PHONE they stack, because 360px cannot hold three labelled fields and
 * the accepted mobile design stacks everything else. Nothing here changes card
 * styling, type, spacing or the section chrome — a fieldset with a grid on it,
 * and that is all.
 */
.role-row {
    display: grid;
    gap: .5rem .75rem;
    grid-template-columns: 1fr;
    border: 0;
    padding: 0 0 .75rem;
    margin: 0 0 .75rem;
    border-bottom: 1px solid var(--border);
}

/* The last row is the blank one waiting to be used, so it needs no divider
   under it — the Save all button is the end of the list. */
.role-row:last-of-type { border-bottom: 0; margin-bottom: .25rem; }

.role-row-field { min-width: 0; }
.role-row-field label { margin-bottom: .25rem; }

.role-row-field select,
.role-row-field input { width: 100%; }

/* The currency sits with the box rather than in it: a number typed into a
   field labelled USD cannot be mistaken for a number in somebody's own money,
   and it is not part of the value being submitted. */
.role-row-money {
    display: flex;
    align-items: stretch;
    gap: .35rem;
}

.role-row-currency {
    display: inline-flex;
    align-items: center;
    padding: 0 .55rem;
    font-size: .82rem;
    font-weight: 600;
    letter-spacing: .03em;
    color: var(--text-muted);
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    white-space: nowrap;
}

.role-row-money input { flex: 1; min-width: 0; }

/* The QC-confirmation note belongs to the whole row, under all three fields. */
.role-row-note { margin: 0; }

@media (min-width: 720px) {
    .role-row { grid-template-columns: 2fr 1.4fr 1.1fr; align-items: end; }
    .role-row-note { grid-column: 1 / -1; }
}

/*
 * A saved Role's editable cell. It was already a row of controls; this only
 * stops the basis select and the rate box from colliding on a narrow screen.
 */
.role-edit {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    align-items: center;
    margin: 0 0 .35rem;
}

.role-edit input[type="text"] { width: 6.5rem; }
.role-edit select { width: auto; }
.role-withdraw { margin: 0; }

/*
 * ── SERVICE LINES ───────────────────────────────────────────────────────────
 *
 * Name, then three or four words under it saying what the line covers. The
 * descriptor is quieter and smaller than the name because the name is the
 * choice and the descriptor is the explanation of it.
 */
.service-line-choice { align-items: flex-start; }

.service-line-note {
    display: block;
    font-size: .8rem;
    color: var(--text-muted);
    line-height: 1.3;
}

/* ================================================ ADMIN MANAGEMENT WORKSPACE */

/*
 * Group headings inside a section menu.
 *
 * Eighteen Admin destinations in one list is a list nobody reads. Five headings
 * turn it into something somebody scans — and a heading is a heading, not a
 * link, because there is no page called "Waiting on QC".
 */
.config-menu-group {
    margin: .9rem 0 .2rem;
    padding: 0 .35rem;
    font-size: .66rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.config-menu-group:first-child { margin-top: 0; }

/* ============================================================= ROW ACTIONS */

/*
 * The three-dot menu on a row.
 *
 * ── WHY IT IS A <details> ────────────────────────────────────────────────────
 *
 * Keyboard operation, focus, Escape-to-close and the tab order all come from
 * the browser. Every hand-rolled menu this could have been is a menu somebody
 * cannot open without a mouse.
 *
 * ── WHY IT OPENS ON CLICK AND NEVER ON HOVER ─────────────────────────────────
 *
 * A menu that opens on hover opens by accident on a touch screen, cannot be
 * opened at all from a keyboard, and closes when a shaky hand drifts off it.
 *
 * ── 44px ─────────────────────────────────────────────────────────────────────
 *
 * The smallest control a thumb hits reliably. The dots are 12px; the target
 * around them is not.
 */
.row-actions { position: relative; display: inline-block; }

.row-actions-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--radius);
    cursor: pointer;
    list-style: none;
    font-size: 1.15rem;
    line-height: 1;
    color: var(--text-muted);
    user-select: none;
}

.row-actions-trigger::-webkit-details-marker { display: none; }
.row-actions-trigger:hover { background: var(--surface-raised); color: var(--text); }

/* Visible focus, always. A control somebody can reach with Tab and cannot see
   is a control they cannot use. */
.row-actions-trigger:focus-visible {
    outline: 3px solid var(--qc-teal);
    outline-offset: -2px;
    color: var(--text);
}

.row-actions[open] > .row-actions-trigger { background: var(--surface-raised); color: var(--text); }

.row-actions-menu {
    position: absolute;
    right: 0;
    top: calc(100% + .2rem);
    z-index: 20;
    min-width: 12rem;
    padding: .25rem;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 6px 18px rgba(0, 0, 0, .12);
}

/* Near the right edge of a phone the menu would otherwise hang off the screen. */
@media (max-width: 480px) {
    .row-actions-menu { right: 0; left: auto; min-width: 11rem; max-width: 78vw; }
}

.row-actions-menu form { margin: 0; }

.row-actions-item {
    display: block;
    width: 100%;
    padding: .6rem .65rem;
    min-height: 44px;
    border: 0;
    border-radius: calc(var(--radius) - 2px);
    background: none;
    text-align: left;
    font: inherit;
    font-size: .875rem;
    color: var(--text);
    cursor: pointer;
    text-decoration: none;
}

.row-actions-item:hover { background: var(--surface); }
.row-actions-item:focus-visible { outline: 3px solid var(--qc-teal); outline-offset: -3px; }

/*
 * A table cell holding one of these should not stretch to the menu's height.
 * The menu is positioned, so it takes no space until it is open.
 */
.table td .row-actions { vertical-align: middle; }

/* ─────────────────────────────────────────── SAVED LANGUAGES, ON A PHONE ── */

/*
 * The row is the language. Two persistent action labels beside it —
 * "Make this my main language" and "Remove" — took more width than the thing
 * they act on, and on 360px pushed it onto a second line.
 *
 * What stays visible is what somebody needs to READ: the language, and whether
 * it is their main one. What moves into the menu is what they might DO.
 */
.language-row {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .35rem 0;
    border-bottom: 1px solid var(--border);
}

.language-row:last-child { border-bottom: 0; }
.language-row-name { flex: 1; min-width: 0; }
.language-row-name strong { font-weight: 600; }
.language-row-level { display: block; font-size: .8rem; color: var(--text-muted); }

/* ───────────────────────────────── FLOATING CONTROLS AND THE SAVE BUTTON ── */

/*
 * ── THE COLLISION ───────────────────────────────────────────────────────────
 *
 * Two controls float bottom-right on every screen: beta feedback and the
 * assistant. On a phone they sit exactly where the last thing on a form sits,
 * which is the Save button — so the primary action of the page is underneath a
 * secondary one, and somebody tapping Save opens the assistant instead.
 *
 * ── THE FIX, AND WHY IT IS THIS ONE ─────────────────────────────────────────
 *
 * Not moving the floating controls, which are where people expect them, and not
 * a drag-to-reposition system, which is a preference to store and restore and
 * get wrong on a second device. The page simply ends further up: enough room
 * below the last element that nothing important can be underneath them.
 *
 * Two floating controls of 52px stacked with their gaps need about 8rem of
 * clearance on a phone. On a wide screen they are nowhere near the content, so
 * the rule does not apply.
 */
@media (max-width: 700px) {
    body { padding-bottom: 8rem; }
}

/*
 * A collapsed group of settings inside a form.
 *
 * Five payment methods with nine fields each, or fifteen platforms with fifteen
 * checkboxes each, is a page nobody can find anything on. Most visits are to
 * change one of them, so they open one at a time — and <details> means opening
 * works from a keyboard and without JavaScript.
 */
.platcap-platform {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .5rem .7rem;
    margin-bottom: .5rem;
}

.platcap-platform > summary {
    cursor: pointer;
    min-height: 44px;
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
}

.platcap-platform > summary:focus-visible { outline: 3px solid var(--qc-teal); outline-offset: 2px; }
.platcap-platform[open] > summary { margin-bottom: .5rem; }

/* ============================================== THE ADMIN WORKSPACE, IN GREEN */

/*
 * ── WHY THE NAVIGATION IS COLOURED AT ALL ───────────────────────────────────
 *
 * White links beside a white form are not navigation, they are a column of text
 * somebody has to work out the status of. The header already carries QC's
 * green; this extends it to the one other region that is the same kind of thing
 * — somewhere you move FROM, not somewhere you work IN.
 *
 * Restrained on purpose. A saturated block down the side of every admin screen
 * would compete with the work; this is enough to read as a different surface
 * and no more.
 *
 * Contrast is checked at both ends: the ink is near-white on QC's darker green,
 * and the active state is a lighter panel rather than a colour shift, so it
 * survives being looked at by somebody who cannot rely on hue.
 */
.admin-nav > .config-menu {
    background: linear-gradient(180deg, var(--qc-green-2) 0%, var(--qc-green-1) 100%);
    border-radius: var(--radius);
    padding: .6rem .45rem;
}

.admin-nav > .config-menu a {
    color: rgba(255, 255, 255, .88);
    border-radius: calc(var(--radius) - 2px);
}

.admin-nav > .config-menu a:hover { background: rgba(255, 255, 255, .12); color: #fff; }

/* Focus is a ring the green cannot swallow. */
.admin-nav > .config-menu a:focus-visible {
    outline: 3px solid var(--qc-teal);
    outline-offset: -2px;
    color: #fff;
}

/* Active is a panel, not a hue — legible without relying on colour. */
.admin-nav > .config-menu a.is-active {
    background: rgba(255, 255, 255, .18);
    color: #fff;
    font-weight: 600;
}

.admin-nav > .config-menu .config-menu-group {
    color: rgba(255, 255, 255, .62);
    margin-top: .8rem;
}

.admin-nav > .config-menu .config-menu-group:first-child { margin-top: .1rem; }

.admin-nav > .config-menu .config-menu-count {
    background: rgba(255, 255, 255, .2);
    color: #fff;
}

/* The way out sits apart from the list of where you are. */
.admin-nav-back {
    display: flex;
    gap: .4rem;
    align-items: center;
    font-size: .82rem;
    opacity: .85;
    margin-bottom: .35rem;
    border-bottom: 1px solid rgba(255, 255, 255, .18);
    padding-bottom: .5rem;
}

/* On a phone the summary is the control, so it carries the colour instead. */
@media (max-width: 900px) {
    .admin-nav > .config-nav-summary {
        background: linear-gradient(180deg, var(--qc-green-2) 0%, var(--qc-green-1) 100%);
        border-color: transparent;
        color: #fff;
    }

    .admin-nav > .config-nav-summary .config-nav-eyebrow { color: rgba(255, 255, 255, .7); }
    .admin-nav > .config-nav-summary::after { border-color: rgba(255, 255, 255, .8); }
    .admin-nav[open] > .config-menu { margin-top: .4rem; }
}

/* ─────────────────────────────────────────────────────── OVERVIEW FIGURES ── */

/*
 * Counts, as the first thing administration says rather than a menu of doors.
 * The number is the largest thing on the card because the number is the point;
 * the label and the definition sit under it so nobody has to guess what was
 * counted.
 */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: .75rem;
    margin-bottom: 1rem;
}

.stat-card {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding: .9rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-raised);
    text-decoration: none;
    color: inherit;
}

.stat-card:hover { border-color: var(--qc-green-1); }
.stat-card:focus-visible { outline: 3px solid var(--qc-teal); outline-offset: 2px; }

.stat-card-figure {
    font-size: 1.9rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--qc-green-2);
    font-variant-numeric: tabular-nums;
}

.stat-card-label { font-weight: 600; font-size: .9rem; }
.stat-card-hint { font-size: .75rem; color: var(--text-muted); line-height: 1.3; }

/* ────────────────────────────────────────────────── COMPACT APPROVAL ROWS ── */

/*
 * A queue is a list. Each proposal used to be a card several inches tall with
 * three buttons on it, so six of them filled a screen and "how much is waiting
 * on me" needed scrolling to answer.
 *
 * One line: who, what for, how much, where it stands, and one control for what
 * you can do about it.
 */
.approval-list { list-style: none; margin: 0; padding: 0; }

.approval-row {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .45rem 0;
    border-bottom: 1px solid var(--border);
}

.approval-row:last-child { border-bottom: 0; }
.approval-select { margin: 0; display: flex; align-items: center; }
.approval-main { flex: 1; min-width: 0; }
.approval-main strong { font-weight: 600; }

.approval-meta {
    display: block;
    font-size: .8rem;
    color: var(--text-muted);
    line-height: 1.3;
}

.approval-amount { font-weight: 600; white-space: nowrap; font-variant-numeric: tabular-nums; }
.approval-state { white-space: nowrap; }

/* Naming a different amount needs a field, and a field cannot live in a menu —
   so it opens under the row rather than widening every row that does not need
   it. */
.approval-counter { list-style: none; padding: 0 0 .5rem 0; border-bottom: 1px solid var(--border); }
.approval-counter summary { cursor: pointer; font-size: .82rem; color: var(--text-muted); min-height: 32px; }
.approval-counter summary:focus-visible { outline: 3px solid var(--qc-teal); outline-offset: 2px; }

@media (max-width: 640px) {
    .approval-row { flex-wrap: wrap; }
    .approval-main { flex-basis: 100%; order: 1; }
    .approval-select { order: 0; }
    .approval-amount, .approval-state { order: 2; }
    .approval-row .row-actions { order: 3; margin-left: auto; }
}

/* ──────────────────────────────────────────────────── COUNTRIES, BY NAME ── */

/*
 * The chips are what somebody reads; the select is how they change it. Nobody
 * types a country code anywhere — a value is one of the canonical options or it
 * is nothing.
 */
.country-chips { display: flex; flex-wrap: wrap; gap: .35rem; margin-bottom: .4rem; }

.country-chip {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .2rem .3rem .2rem .6rem;
    border-radius: 999px;
    background: var(--surface-raised);
    border: 1px solid var(--border);
    font-size: .82rem;
}

.country-chip-remove {
    border: 0;
    background: none;
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 0 .25rem;
    min-width: 28px;
    min-height: 28px;
    color: var(--text-muted);
    border-radius: 999px;
}

.country-chip-remove:hover { background: var(--surface); color: var(--text); }
.country-chip-remove:focus-visible { outline: 2px solid var(--qc-teal); outline-offset: 1px; }

[data-country-select] { width: 100%; min-height: 12rem; }

/* ─────────────────────────────────────────────────── SAVED ROLES, COMPACT ── */

/*
 * A saved Role was a table row carrying a rate box, a select, a checkbox and
 * two buttons — so four of them filled a screen and the thing somebody came to
 * read was the smallest part of it.
 *
 * The row states the facts. Editing opens under it, because a rate needs a
 * field and a field cannot live in a menu, and only one row is ever edited.
 */
.role-list { list-style: none; margin: 0; padding: 0; }

.role-item {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .45rem 0;
}

.role-item-main { flex: 1; min-width: 0; }
.role-item-main strong { font-weight: 600; }

.role-item-meta {
    display: block;
    font-size: .82rem;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.role-item-state { white-space: nowrap; }
.role-item-locked { max-width: 12rem; line-height: 1.25; }

.role-item-edit { list-style: none; padding: 0 0 .5rem; border-bottom: 1px solid var(--border); }
.role-item-edit:last-child { border-bottom: 0; }

.role-item-edit summary {
    cursor: pointer;
    font-size: .8rem;
    color: var(--text-muted);
    min-height: 32px;
    display: flex;
    align-items: center;
}

.role-item-edit summary:focus-visible { outline: 3px solid var(--qc-teal); outline-offset: 2px; }

@media (max-width: 640px) {
    .role-item { flex-wrap: wrap; }
    .role-item-main { flex-basis: 100%; }
    .role-item .row-actions { margin-left: auto; }
    .role-item-locked { flex-basis: 100%; max-width: none; }
}

/*
 * A chip's remove control is 28px, which is fine beside a mouse and too small
 * for a thumb. On a touch-sized screen it grows to the 44px everything else
 * uses — the chip grows with it rather than the button overflowing it.
 */
@media (max-width: 700px) {
    .country-chip { padding: .3rem .3rem .3rem .7rem; }
    .country-chip-remove { min-width: 44px; min-height: 44px; }
}

/* ═══════════════════════════════════════════ THE PERSISTENT ADMIN SHELL ══
 *
 * ── WHAT CHANGED, AND WHY IT IS IN THE LAYOUT ────────────────────────────
 *
 * Admin used to be a page that sometimes had navigation beside it. It is now
 * a shell: navigation on the left that does not move, and a workspace on the
 * right that does. The columns are declared once, here, because the layout
 * renders them once — no screen can arrange them differently and no screen
 * can forget.
 *
 * Below the breakpoint the columns become rows and the tree becomes the
 * disclosure card it already was, so a 375px screen never carries a permanent
 * sidebar.
 * ======================================================================== */

.admin-shell {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    align-items: start;
}

.admin-workspace { min-width: 0; }

@media (min-width: 901px) {
    .admin-shell {
        grid-template-columns: minmax(220px, 260px) 1fr;
        gap: 1.5rem;
        max-width: 1180px;
        margin: 0 auto;
        padding: 1.25rem 1rem 2.5rem;
    }

    /*
     * The screens inside still open with their own .container, which used to
     * supply the page's own gutter. Inside the shell the shell supplies it,
     * so the inner container stops adding a second one.
     */
    .admin-workspace > .container { padding-left: 0; padding-right: 0; max-width: none; }
}

@media (max-width: 900px) {
    .admin-shell { padding: .75rem .9rem 2rem; }
    .admin-workspace > .container { padding-left: 0; padding-right: 0; }
}

/* ── THE TREE ─────────────────────────────────────────────────────────────
 *
 * Real nested lists, so a screen reader is told this is a hierarchy instead
 * of being asked to infer it from indentation. The list markers are removed;
 * the structure is not.
 */
.admin-tree-list,
.admin-tree-children {
    list-style: none;
    margin: 0;
    padding: 0;
}

.admin-tree-menu { display: flex; flex-direction: column; }

.admin-tree-link,
.admin-tree-parent,
.admin-tree-child {
    display: flex;
    align-items: center;
    gap: .45rem;
    padding: .42rem .55rem;
    border-radius: calc(var(--radius) - 2px);
    color: rgba(255, 255, 255, .88);
    text-decoration: none;
    font-size: .86rem;
    line-height: 1.3;
}

.admin-tree-link:hover,
.admin-tree-parent:hover,
.admin-tree-child:hover { background: rgba(255, 255, 255, .12); color: #fff; }

.admin-tree-link:focus-visible,
.admin-tree-parent:focus-visible,
.admin-tree-child:focus-visible {
    outline: 3px solid var(--qc-teal);
    outline-offset: -2px;
    color: #fff;
}

.admin-tree-link.is-active,
.admin-tree-parent.is-active {
    background: rgba(255, 255, 255, .18);
    color: #fff;
    font-weight: 600;
}

/*
 * A branch's summary is a control, so it is sized like one and its marker is
 * ours rather than the browser's triangle — which differs per platform and
 * cannot be coloured.
 */
.admin-tree-parent {
    cursor: pointer;
    list-style: none;
    min-height: 34px;
}

.admin-tree-parent::-webkit-details-marker { display: none; }

.admin-tree-twisty {
    flex: none;
    width: .55rem;
    height: .55rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
    transition: transform .12s ease;

    /*
     * ── THE ACCENT BELONGS TO THE HIERARCHY, NOT TO ONE BRANCH ──────────
     *
     * The teal used to appear in exactly one place: whichever row you were
     * standing on, plus the bar on its parent. So the tree looked as though
     * Site content had been given a special treatment nobody else had —
     * when in fact nothing was special-cased, and every other expandable
     * topic simply had no hierarchy marking at all.
     *
     * The chevron is what tells somebody a topic CONTAINS things. It now
     * carries the accent wherever it appears, so Projects, Approvals,
     * Payment methods and the rest read as the same kind of thing as
     * Configuration — open or closed, current or not.
     *
     * It is deliberately quiet until the branch is opened. A row of nine
     * equally bright chevrons would be its own kind of uniform.
     */
    color: var(--qc-teal);
    opacity: .6;
}

.admin-tree-parent:hover .admin-tree-twisty,
.admin-tree-parent:focus-visible .admin-tree-twisty,
.admin-tree-branch[open] > .admin-tree-parent .admin-tree-twisty {
    opacity: 1;
}

.admin-tree-branch[open] > .admin-tree-parent .admin-tree-twisty {
    transform: rotate(45deg);
}

.admin-tree-label { flex: 1 1 auto; min-width: 0; }

/* The name of a branch is now a link to that branch's own page, so it takes
   the summary's colour rather than a browser blue, and keeps the whole
   remaining width of the row as its target — the twisty keeps the toggle.
   Padding lifts the hit area to a comfortable height on a phone without
   moving the text, which still lines up with every other row. */
a.admin-tree-label {
    color: inherit;
    text-decoration: none;
    padding: .18rem 0;
}
a.admin-tree-label:hover,
a.admin-tree-label:focus-visible { color: #fff; }

/*
 * Children are subordinate and must read as such without becoming unreadable.
 * A lighter panel, an indent, and a rule down the left that shows how far the
 * branch runs — contrast stays well above the floor because the ink only
 * drops to .78 and the ground is unchanged.
 */
.admin-tree-children {
    margin: .1rem 0 .35rem 1.05rem;
    padding-left: .5rem;
    /*
     * And the rule under an open topic is the same accent, so the topic and
     * the things inside it read as one shape rather than as a heading that
     * happens to be followed by some links.
     */
    border-left: 2px solid rgba(0, 252, 252, .38);
    display: flex;
    flex-direction: column;
    gap: .05rem;
}

.admin-tree-child {
    color: rgba(255, 255, 255, .78);
    font-size: .82rem;
    padding: .34rem .5rem;
}

.admin-tree-child.is-active {
    background: var(--qc-teal);
    color: #06302b;
    font-weight: 600;
}

/* Add is an action among destinations, so it is marked as one and put first. */
.admin-tree-add { font-weight: 600; color: #fff; }

.admin-tree-heading { margin: 0; }

/*
 * Touch targets. A tree is denser than a list of five sections, so below the
 * breakpoint every row grows to something a thumb can hit.
 */
@media (max-width: 700px) {
    .admin-tree-link,
    .admin-tree-parent,
    .admin-tree-child { min-height: 44px; }
}

/* ── CHOOSING WHAT YOU ARE OPEN TO ────────────────────────────────────────
 *
 * A checklist, because the question is "which of these apply to you" and the
 * honest control for that question is a list of boxes. It replaced a dropdown
 * that added one Role at a time — on a phone, four Roles meant four native
 * select sheets and four taps on a small "+".
 *
 * The row is the target, not the box. A checkbox is about 14px on a phone and
 * nobody aims at it; the name and the description are what somebody is looking
 * at when they decide, so the whole cell is what they tap.
 */
.role-pick-set {
    border: 0;
    margin: 0;
    padding: 0;
    min-width: 0;
}

.role-pick-legend {
    padding: 0;
    font-weight: 600;
    font-size: .95rem;
}

.role-pick-hint { margin: .25rem 0 .7rem; }

.role-pick-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .4rem;
}

.role-pick {
    display: flex;
    align-items: flex-start;
    gap: .7rem;
    padding: .7rem .8rem;
    min-height: 44px;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    cursor: pointer;
}

.role-pick:hover { border-color: var(--border-strong); background: var(--surface-sunken); }

/*
 * The box is sized to be seen rather than to be hit — hitting it is the row's
 * job. It sits on the first line of the name rather than centred against a
 * three-line description.
 */
.role-pick-box {
    flex: none;
    width: 1.15rem;
    height: 1.15rem;
    margin: .12rem 0 0;
    accent-color: var(--qc-green-1);
}

.role-pick-box:focus-visible { outline: 3px solid var(--qc-teal); outline-offset: 2px; }

.role-pick-body {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    min-width: 0;
}

.role-pick-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .4rem;
}

.role-pick-name { font-weight: 600; line-height: 1.3; }

.role-pick-desc {
    font-size: .84rem;
    line-height: 1.4;
    color: var(--text-muted);
}

/* A fact about the Role, stated before somebody chooses it rather than after. */
.role-pick-flag {
    flex: none;
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    padding: .1rem .4rem;
    border-radius: 999px;
    background: var(--action-quiet);
    color: var(--qc-green-1);
}

/*
 * CHOSEN. The name carries the state on its own, so a browser without :has()
 * still shows which rows are ticked; :has() then lifts the whole cell, which
 * is what makes four ticks countable at a glance on a small screen.
 */
.role-pick-box:checked ~ .role-pick-body .role-pick-name { color: var(--qc-green-1); }

.role-pick:has(.role-pick-box:checked) {
    border-color: var(--qc-green-1);
    background: var(--action-quiet);
}

/*
 * On a phone this is the whole interaction, so the rows grow, the description
 * stays readable rather than being truncated, and the box grows with them.
 */
@media (max-width: 700px) {
    .role-pick { padding: .85rem .85rem; gap: .8rem; min-height: 56px; }
    .role-pick-box { width: 1.35rem; height: 1.35rem; margin-top: .1rem; }
    .role-pick-name { font-size: 1rem; }
    .role-pick-desc { font-size: .88rem; }
}
/* ── SELECTING SAVED ROLES ────────────────────────────────────────────────
 *
 * The box is at the left of the row because that is where a list of things
 * you can act on is read from. A confirmed Role has no box — there is nothing
 * to select it for — but keeps the space, so the Role names stay in one
 * column rather than shifting left and right down the list.
 */
.role-item-pick {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    min-height: 1.6rem;
    margin: 0;
}

.role-item-pick input { margin: 0; }

.role-item-pick-empty { display: inline-block; }

.role-bulk {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem .8rem;
    margin-top: .6rem;
    padding-top: .6rem;
    border-top: 1px solid var(--border);
}

.role-bulk .hint { margin: 0; flex: 1 1 16rem; }

/* A checkbox is a small target; on a phone the whole cell is the target. */
@media (max-width: 700px) {
    .role-item-pick { width: 2.75rem; min-height: 2.75rem; }
}

/* ══════════════════════════════════ HIERARCHY IN THE ADMIN TREE ══════════
 *
 * The owner's words: it "appears visually too uniform". She was right — every
 * row was the same size, the same weight and nearly the same colour, so the
 * tree read as one long list rather than as a shape with a place in it.
 *
 * Four signals now, and they stack rather than compete:
 *
 *   DEPTH    each level steps in, and a rule down the left shows how far the
 *            branch runs, so an item's parent is visible without scrolling up
 *   WEIGHT   a parent is heavier than its children
 *   SIZE     a child is smaller than a parent, a grandchild smaller again
 *   STATE    where you ARE is QC teal; the branch you are IN is a lit panel
 *
 * The teal is the accent this application already uses for focus and for the
 * chosen row. Using it for "you are here" is the same idea, not a new one.
 * ======================================================================== */

/* An open branch is lit, so the region you are working in is one shape. */
.admin-tree-branch[open] {
    background: rgba(255, 255, 255, .07);
    border-radius: calc(var(--radius) - 2px);
    padding-bottom: .15rem;
}

.admin-tree-branch[open] > .admin-tree-parent {
    font-weight: 600;
    color: #fff;
}

/* And the parent of the branch you are actually inside is marked as such. */
.admin-tree-parent.is-active {
    background: rgba(255, 255, 255, .16);
    box-shadow: inset 3px 0 0 var(--qc-teal);
}

/*
 * WHERE YOU ARE. One row in the whole tree carries this, and it is the
 * strongest thing on the panel — teal ground, dark ink, so it reads at a
 * glance and does not depend on hue alone: it is also the only row with a
 * light background.
 */
.admin-tree-link.is-active,
.admin-tree-child.is-active {
    background: var(--qc-teal);
    color: #06302b;
    font-weight: 600;
}

/*
 * The row that opens the parent's own screen. Quieter than its siblings,
 * because "Open Site content" is a way back to the top of a branch rather
 * than one of the things inside it.
 */
.admin-tree-self {
    font-style: italic;
    opacity: .82;
}

/*
 * ── WHERE YOU ARE, WHICH HAD STOPPED BEING TEAL AT ALL ──────────────────
 *
 * The rule above says the row you are on is a teal ground with dark ink. It
 * never rendered. The flat Admin menu this tree replaced still carries
 *
 *     .admin-nav > .config-menu a.is-active { background: rgba(255,255,255,.18) }
 *
 * and that selector is more specific than .admin-tree-child.is-active, so the
 * current row was painted the old white panel — while a LATER, still more
 * specific rule set the dark ink meant for teal. The result on the deepest
 * level was #06302b on a translucent white over a dark green gradient: the
 * strongest signal in the whole design, and the least readable thing on it.
 *
 * Matched depth for depth here rather than reached for !important, so the
 * flat menu keeps its own treatment wherever it is still used.
 */
.admin-nav > .admin-tree-menu .admin-tree-link.is-active,
.admin-nav > .admin-tree-menu .admin-tree-child.is-active,
.admin-nav > .admin-tree-menu .admin-tree-children .admin-tree-children .admin-tree-child.is-active {
    background: var(--qc-teal);
    color: #06302b;
    font-weight: 600;
}

/*
 * ── AND ONLY ONE ROW MAY SAY IT ─────────────────────────────────────────
 *
 * "Open Site content" and the page you are actually reading are both active
 * on /admin/content, because the bare address renders the branch's default
 * surface. Both being a solid teal ground would be two answers to "where am
 * I", which is the competing indicator this whole treatment exists to avoid.
 *
 * So the parent's own row never takes the fill. It keeps the accent as a
 * STROKE, which is what it already is: a way back to the top of the branch,
 * not one of the things inside it. Exactly one row in the tree is filled.
 *
 * This changes nothing about which rows are marked active — that is the
 * navigation's decision and it is left alone.
 */
.admin-tree-self.is-active {
    font-style: normal;
    opacity: 1;
}

.admin-nav > .admin-tree-menu .admin-tree-self.is-active,
.admin-nav > .admin-tree-menu .admin-tree-children .admin-tree-children .admin-tree-self.is-active {
    background: rgba(255, 255, 255, .16);
    color: #fff;
    box-shadow: inset 3px 0 0 var(--qc-teal);
}

/* Depth. Each level steps in and gets a little quieter and a little smaller. */
.admin-tree-children .admin-tree-children {
    margin-left: .75rem;
    border-left-color: rgba(0, 252, 252, .22);
}

.admin-tree-children .admin-tree-children .admin-tree-child {
    font-size: .78rem;
    color: rgba(255, 255, 255, .74);
}

.admin-tree-children .admin-tree-children .admin-tree-child.is-active {
    color: #06302b;
}

/* A branch inside a branch keeps its parent's weight but not its size. */
.admin-tree-children .admin-tree-parent {
    font-size: .82rem;
    font-weight: 500;
}

/* Group headings sit above the rows they name rather than floating between. */
.admin-tree-menu .config-menu-group {
    margin: .9rem 0 .15rem;
    letter-spacing: .06em;
}

.admin-tree-menu .admin-tree-heading:first-child .config-menu-group { margin-top: .2rem; }

/* ── THE THREE PUBLISHED PAGES ────────────────────────────────────────────
 *
 * A document, not an application screen. One column, generous measure, and
 * nothing that needs a script — these are read by people who have no account
 * and, in at least one case, by an automated reviewer.
 */
.public-doc {
    background: var(--surface);
    min-height: 60vh;
}

.public-doc-inner {
    max-width: 44rem;
    margin: 0 auto;
    padding: 1.5rem 1.15rem 3rem;
}

.public-doc-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    padding-bottom: .8rem;
    margin-bottom: 1.4rem;
    border-bottom: 1px solid var(--border);
    font-size: .9rem;
}

.public-doc-nav a { color: var(--qc-green-1); text-decoration: none; }
.public-doc-nav a:hover { text-decoration: underline; }

/* Where you are, said once and without colour doing the work alone. */
.public-doc-nav a[aria-current="page"] { font-weight: 600; text-decoration: underline; }

.public-doc-body h1 {
    margin: 0 0 .3rem;
    font-size: 1.7rem;
    line-height: 1.2;
    text-wrap: balance;
}

.public-doc-updated {
    margin: 0 0 1.4rem;
    font-size: .82rem;
    color: var(--text-muted);
}

.public-doc-lede {
    font-size: 1.05rem;
    line-height: 1.6;
    margin: 0 0 1.6rem;
}

.public-doc-body h2 {
    margin: 2rem 0 .5rem;
    font-size: 1.12rem;
    line-height: 1.3;
}

.public-doc-body h3 {
    margin: 1.4rem 0 .4rem;
    font-size: .98rem;
}

/* A policy is read end to end, not scanned like a screen, so it gets a
   larger size and a longer leading than the rest of the application. The
   surrounding page sets .82rem, which is right for a dense workspace view and
   too small for two thousand words of terms. */
.public-doc-body,
.public-doc-body p,
.public-doc-body li {
    font-size: .95rem;
    line-height: 1.65;
}

.public-doc-body p { margin: 0 0 .9rem; }

.public-doc-body ul {
    margin: 0 0 1rem;
    padding-left: 1.15rem;
    display: flex;
    flex-direction: column;
    gap: .5rem;
}

.public-doc-body a { color: var(--qc-green-1); }

.public-doc-links { list-style: none; padding-left: 0; }

.public-doc-beta {
    margin-top: 2rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
}

@media (max-width: 700px) {
    .public-doc-inner { padding: 1.1rem .95rem 2.5rem; }
    .public-doc-body h1 { font-size: 1.45rem; }
    .public-doc-nav { gap: .9rem; }
    /* A link in a row of three needs a target, not just a word. */
    .public-doc-nav a { padding: .7rem 0; }
    .public-doc-body,
    .public-doc-body p,
    .public-doc-body li { font-size: 1rem; }
}
/* ── ARRANGING A MENU ─────────────────────────────────────────────────── */

.menu-table td { vertical-align: middle; }

.menu-position {
    width: 4.5rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* ── WHICH PAGE AM I EDITING ──────────────────────────────────────────── */

.page-identity .card-header h2 { margin: 0; }

.page-identity-edit summary {
    cursor: pointer;
    font-size: .84rem;
    color: var(--qc-green-1);
}

.page-identity-edit[open] { margin-top: .5rem; }

/* ── WHAT A ROLE INVOLVES, AND THE TWO ANSWERS TO IT ──────────────────────
 *
 * The duties read as a list because they are one — QC's expectations, in QC's
 * order — and the two answers sit below them on their own line rather than
 * wrapping around a checkbox they have nothing to do with.
 */
.responsibility-list {
    list-style: disc;
    margin: .5rem 0 .75rem 1.1rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .3rem;
}

.responsibility-list li { line-height: 1.4; }

.responsibility-list strong { font-weight: 600; }

.responsibility-list .small { display: block; margin-top: .1rem; }

/* Two separate forms, side by side, each with its own submit. */
.role-item-edit .role-edit {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem 1rem;
    margin-top: .5rem;
    padding-top: .5rem;
    border-top: 1px solid var(--border);
}

.role-item-edit .role-edit form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin: 0;
}

/* ── THE SIX TILES, ALL SIX AT ONCE ───────────────────────────────────────
 *
 * The owner replaced one picture, lost sight of the other five, and had to
 * navigate back and rediscover them. So they are a grid: every tile is on the
 * screen while any one of them is being worked on, and each card carries its
 * own preview so the comparison is possible without opening the public page
 * in another window.
 */
.tile-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    margin-top: 1rem;
}

@media (min-width: 780px) {
    .tile-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 1240px) {
    .tile-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.tile-card { display: flex; flex-direction: column; gap: .55rem; }

.tile-card h3 { display: flex; align-items: center; gap: .5rem; margin: 0; font-size: 1rem; }

/* The public order, said out loud, because "third tile" is how somebody
   describes it on the phone to whoever is looking at the live page. */
.tile-position {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    background: var(--qc-green-1);
    color: #fff;
    font-size: .75rem;
    font-variant-numeric: tabular-nums;
}

.tile-blurb { margin: 0; }

/* 16:9, because that is what the public card crops to. Showing it any other
   shape here would teach the owner to prepare the wrong picture. */
.tile-preview {
    aspect-ratio: 16 / 9;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}

.tile-preview img { width: 100%; height: 100%; object-fit: cover; display: block; }

.tile-preview-empty { color: var(--text-muted); font-size: .82rem; }

/* The picker scrolls inside the card rather than making the card tall enough
   to push the other five tiles off the screen — which is the whole problem. */
.media-picker-compact {
    max-height: 13rem;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: .4rem;
}

.tile-choose, .tile-upload { display: flex; flex-direction: column; gap: .45rem; margin: 0; }

.tile-upload { border-top: 1px solid var(--border); padding-top: .55rem; }

.tile-overrides summary { cursor: pointer; font-size: .82rem; color: var(--qc-green-1); }

.tile-override-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
    margin: .5rem 0 0;
}

.tile-override-row select { flex: 1 1 8rem; min-width: 0; }

/* ── PRE-QUALIFYING A ROLE, IN BUSINESS LANGUAGE ──────────────────────────
 *
 * The rule QC is writing has to be readable before any control is touched, so
 * the sentence that governs the whole screen sits above it and looks like a
 * statement rather than a hint somebody scrolls past.
 */
.prequalify-law {
    margin: .6rem 0 .2rem;
    padding: .55rem .7rem;
    border-left: 3px solid var(--qc-teal);
    background: var(--action-quiet);
    font-size: .88rem;
}

/* And each requirement says what it means, so nobody has to read a checkbox
   grid and translate it in their head. */
.prequalify-plain {
    margin: .3rem 0 .5rem;
    font-size: .88rem;
    font-weight: 600;
}

.prequalify-advanced summary {
    cursor: pointer;
    font-size: .82rem;
    color: var(--qc-green-1);
}

.prequalify-advanced[open] { margin-top: .5rem; }

/* ── The controlled-beta notice ──────────────────────────────────────────────
 *
 * It covers the page because it is asking for an acknowledgement, and an
 * acknowledgement somebody can scroll past is not one. Everything else about
 * it is deliberately quiet: this is a statement about the product, not an
 * alarm about the person's work.
 */
.beta-notice-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.25rem;
    background: rgba(29, 43, 40, .55);
    overflow-y: auto;
}

.beta-notice {
    max-width: 32rem;
    width: 100%;
    margin: auto;
}

.beta-notice h2 { margin-top: 0; }

/* A phone should show the whole of it, and scroll if the wording is long. */
@media (max-width: 30rem) {
    .beta-notice-backdrop { padding: .75rem; align-items: flex-start; }
}

/* The password field and its reveal sit on one line without the button
   stretching to match the input. */
.row-tight { display: flex; gap: .4rem; align-items: stretch; }
.row-tight input { flex: 1 1 auto; min-width: 0; }
.row-tight .btn { flex: 0 0 auto; }


/* ── CAPABILITIES & FAMILIARITY: ONE SECTION PER PLATFORM ──────────────────
 *
 * The capabilities used to run together as one grid with a small legend every
 * so often. On a phone the Zoom Meetings questions and the OBS questions were a
 * single unbroken column, and somebody scrolling could not tell which platform
 * they were answering for.
 *
 * The treatment is deliberately quiet: a sunken header strip, a hairline
 * border, and space between sections. It has to read as structure rather than
 * decoration, because there may be six of these on one screen.
 */
.cap-group {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin-bottom: .75rem;
    background: var(--surface);
}

.cap-group-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .75rem;
    padding: .6rem .8rem;
    background: var(--surface-sunken);
    border-radius: var(--radius);
    cursor: pointer;
    /* The whole strip is the target, not the little triangle: a 12px marker is
       not something a thumb aims at. */
    list-style: none;
}

.cap-group[open] > .cap-group-head {
    border-bottom: 1px solid var(--border);
    border-radius: var(--radius) var(--radius) 0 0;
}

.cap-group-head::-webkit-details-marker { display: none; }

.cap-group-head::after {
    content: "▾";
    opacity: .55;
    margin-left: auto;
    flex: 0 0 auto;
}

.cap-group[open] > .cap-group-head::after { content: "▴"; }

.cap-group-head:focus-visible {
    outline: 2px solid var(--qc-teal);
    outline-offset: 2px;
}

.cap-group-name { font-weight: 600; }

/* Pushed away from the arrow rather than floated, so a long platform name and
   a two-digit count never collide. */
.cap-group-count { flex: 0 0 auto; margin-left: auto; }

.cap-group-body { padding: .7rem .8rem .8rem; }

/* The two section controls sit above the grid, on one line where there is
   room and wrapping on a phone rather than shrinking. They are visibly not
   two of the answers. */
.cap-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem .9rem;
    margin-bottom: .55rem;
    padding-bottom: .55rem;
    border-bottom: 1px dashed var(--border);
}

.cap-select-all { display: inline-flex; margin: 0; }

/* Small, because it sits beside a checkbox and not at the end of a form. */
.cap-actions .btn-small { margin-left: auto; }


/* ============================================ THE COLLABORATOR PROJECT WORKSPACE */

/*
 * ONE NAVIGATION, TWO SHAPES.
 *
 * The markup is a single <details> holding anchors. On a phone that is exactly
 * what it looks like: a closed menu on one line, opened by a tap, closed again
 * once somebody has jumped.
 *
 * On a wide screen the same element is forced open and pinned beside the page,
 * because there is room for it and a rail you can see is faster than a menu you
 * have to remember to open. Nothing about the markup changes — a second mobile
 * navigation would be a second list to keep in step with this one.
 */

.ws-nav { margin: 0 0 1rem; }

.ws-nav-menu {
    border: 1px solid var(--border);
    border-radius: var(--radius, 6px);
    background: var(--surface, #fff);
}

.ws-nav-menu > summary {
    cursor: pointer;
    padding: .6rem .8rem;
    font-weight: 600;
}

.ws-nav-menu ul {
    list-style: none;
    margin: 0;
    padding: .2rem .8rem .7rem;
    display: flex;
    flex-wrap: wrap;
    gap: .35rem .9rem;
}

.ws-nav-menu a {
    display: inline-block;
    padding: .15rem 0;
    font-size: .92em;
}

@media (min-width: 1240px) {
    /*
     * The rail. Pinned to the viewport rather than to the page, so it is still
     * there after somebody has scrolled past six sections — which is the whole
     * point of it.
     *
     * Placed in the gutter the centred container already leaves, so no width is
     * taken from the content and nothing below has to move.
     *
     * THE BREAKPOINT IS ARITHMETIC, NOT TASTE. The page is .container.medium —
     * 760px, centred — so the gutter either side is (100vw - 760px) / 2. A rail
     * of 11rem with a 1rem gap needs 12rem of that, and 760 + 2 x 12rem + a
     * margin is 1240. Set a round 1100 instead and the rail lands ON the first
     * card between 1100 and 1176, which is exactly what it did until this was
     * worked out rather than guessed.
     */
    .ws-nav {
        position: fixed;
        top: 6rem;
        left: max(1rem, calc(50vw - 35.75rem));
        width: 11rem;
        margin: 0;
        z-index: 5;
    }

    .ws-nav-menu { border: 0; background: transparent; }

    /* Open, and with no control to close it: on this screen it is a rail. */
    .ws-nav-menu > summary { display: none; }

    .ws-nav-menu ul {
        display: block;
        padding: 0;
        border-left: 2px solid var(--border);
    }

    .ws-nav-menu li { margin: 0; }

    .ws-nav-menu a {
        padding: .28rem .7rem;
        border-left: 2px solid transparent;
        margin-left: -2px;
        text-decoration: none;
    }

    .ws-nav-menu a:hover,
    .ws-nav-menu a:focus {
        border-left-color: var(--accent, #2f6f4f);
        text-decoration: underline;
    }
}

/*
 * The section somebody has just jumped to, marked briefly.
 *
 * :target rather than script, so it works from a pasted link and from the back
 * button. Quiet on purpose — it says "here", not "look at this".
 */
.ws-page section[id]:target {
    box-shadow: 0 0 0 2px var(--accent, #2f6f4f);
}

@media (prefers-reduced-motion: no-preference) {
    .ws-page section[id] { transition: box-shadow .4s ease; }
}

/* Anchored sections must not arrive under a sticky header. */
.ws-page section[id] { scroll-margin-top: 5rem; }

/* ------------------------------------------------------- choosing what to print */

.ws-print > summary {
    cursor: pointer;
    font-weight: 600;
    padding: .2rem 0;
}

.ws-print-list {
    display: grid;
    gap: .35rem;
    grid-template-columns: 1fr;
}

@media (min-width: 720px) {
    .ws-print-list { grid-template-columns: 1fr 1fr; }
}

.ws-print-list .choice {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    flex-wrap: wrap;
}

/* ------------------------------------------------------------- the run of show */

.ws-flow-when { white-space: nowrap; vertical-align: top; width: 1%; }

.ws-flow-who { vertical-align: top; text-align: right; white-space: nowrap; }

/*
 * The reader's own steps, marked by a rule down the left rather than by colour
 * alone — the distinction has to survive a greyscale print and a reader who
 * cannot see the difference between two greens.
 */
.ws-flow-mine > td:first-child { border-left: 3px solid var(--accent, #2f6f4f); }

/* ------------------------------------------------------------------ readiness */

.ws-check {
    padding: .7rem 0;
    border-top: 1px dashed var(--border);
}

.ws-check:first-of-type { border-top: 0; }

.ws-check-form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .5rem .7rem;
    margin-top: .4rem;
}

.ws-check-form input[type="text"] { flex: 1 1 16rem; min-width: 0; }

.ws-check-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
}

/*
 * NOTHING HERE MAKES "ISSUE" LOOK LIKE A FAILURE.
 *
 * No red, no warning icon, no weight the other two do not have. Recording that
 * the presenter's microphone is dead is the check working, and a control styled
 * as an alarm is a control people quietly stop using.
 */

/* ----------------------------------------------------------------- the crew */

.ws-team-member {
    padding: .55rem 0;
    border-top: 1px dashed var(--border);
}

.ws-team-member:first-of-type { border-top: 0; }

.ws-team-member > details > summary { cursor: pointer; }

/* ------------------------------------------------------ the advertised wording */

/*
 * Subordinate by construction. The structured snapshot above is the current
 * list; this is what the position said before anybody accepted, and it is
 * folded, smaller and set back so the two can never read as two equal answers
 * to the same question.
 */
.ws-legacy {
    margin-top: .8rem;
    padding-left: .8rem;
    border-left: 2px solid var(--border);
}

.ws-legacy > summary { cursor: pointer; }

/* What the attendees were told, set apart from what the crew was told. */
.ws-attendee-access {
    margin-top: .7rem;
    padding-top: .6rem;
    border-top: 1px dashed var(--border);
}

/* On screen the address under a button is a whisper; it still has to wrap. */
.printable-link { word-break: break-all; }

/* ===================================== PASSING ON A PIECE OF WORK */

/*
 * The reason, behind a disclosure beside the button.
 *
 * The button on its own still declines in one click — that was the design and
 * it has not changed. This only gives the reason somewhere to live for the
 * people willing to offer one, without putting a form in front of everybody who
 * is not.
 */
.opp-decline-why {
    margin-top: .35rem;
}

.opp-decline-why > summary {
    cursor: pointer;
    font-size: .85em;
    color: var(--muted, #667);
}

.opp-decline-why select,
.opp-decline-why input[type="text"] {
    width: 100%;
    margin-top: .35rem;
}

/* A capability section nobody has answered yet — see migration 136. Marked
   by a rule down the side as well as by the badge, because the badge alone is
   colour and a word, and this has to survive a greyscale print and a reader who
   is scanning. */
.cap-group-unreviewed { border-left: 3px solid var(--accent, #2f6f4f); }

.cap-group-flag { margin-left: .4rem; }


/* ============================================ AUTHORITATIVE MONEY, AND AN ESTIMATE */

/*
 * The two are never equals. The agreed amount is the money; the equivalent is
 * a courtesy, and it is smaller, quieter and on its own line so the hierarchy
 * survives a glance.
 */
.money-authoritative { font-weight: 600; }

.money-equivalent {
    display: block;
    font-size: .85em;
    font-weight: 400;
    color: var(--muted, #667);
}

/* Nothing in the estimate line is a large or confident number. */
.figure-large .money-equivalent,
.home-amount-lg .money-equivalent { font-size: .45em; }

/*
 * The note. A <details> rather than a tooltip, so it opens by keyboard, by
 * tap and to a screen reader — a title attribute does none of the three, and
 * most of the people this is for are reading on a phone.
 */
.money-note { display: inline; }

.money-note > summary {
    display: inline;
    cursor: pointer;
    list-style: none;
    margin-left: .2rem;
}

.money-note > summary::-webkit-details-marker { display: none; }

/* A real focus ring: this is a control, and it is small. */
.money-note > summary:focus-visible { outline: 2px solid var(--accent, #2f6f4f); outline-offset: 2px; }

.money-note-body {
    display: block;
    margin-top: .3rem;
    max-width: 34rem;
    font-size: .95em;
}

.money-note-rate { display: block; margin-top: .15rem; }

/* Said once, quietly, and never where a number would have been. */
.money-unavailable { font-style: italic; }

/* ─────────────────────────────────────────────── the Session scheduling area
 *
 * A real three-Session Project was entered and the Owner could not tell whether
 * Sessions 2 and 3 had been entered at all. The fields were there; nothing said
 * where one Session ended and the next began.
 *
 * So each Session is a card with its own name in a tinted bar. The requirement
 * is that somebody can glance at this section and count the Sessions without
 * reading a word of it. */
.session-block {
    border: 1px solid var(--line, #dceceb);
    padding: 0 1rem 1rem;
}

.session-legend {
    padding: .35rem .7rem;
    margin-left: -.25rem;
    background: var(--tint, #eef6f5);
    border-radius: 6px;
    font-size: 1.02rem;
}

/* Derived, never entered. It reads as a statement rather than a field, because
 * an editable-looking Belize time is an invitation to type the wrong hour into
 * it. */
.session-derived {
    margin-top: .8rem;
    padding: .6rem .8rem;
    border-left: 3px solid var(--accent, #0E7E72);
    background: var(--tint, #f4f8f8);
    border-radius: 0 6px 6px 0;
}

.session-derived p { margin: .15rem 0; }
.session-derived strong { display: inline-block; min-width: 8.5rem; }

/* ───────────────────────────────────────────── the deliverable measurement row
 *
 * Four controls that store four different things, and they used to read
 * "4 hours Up to Session" — two unlabelled text boxes side by side looking like
 * the same question asked twice, with the word that distinguishes them nowhere
 * on the screen.
 *
 * Now it reads as the sentence it is: Up to | 4 | hours | per | Session. Narrow
 * boxes for short values, and "per" printed between the two units so the last
 * one can never again be mistaken for a duplicate. */
.measure {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    flex-wrap: wrap;
}

.measure-kind { width: auto; min-width: 5.5rem; }
.measure-qty  { max-width: 4.2rem; text-align: center; }
.measure-unit { max-width: 7rem; }

/* The word, not a field. It has to look like prose or it reads as a label for
 * something the reader is supposed to fill in. */
.measure-per {
    padding: 0 .1rem;
    color: var(--muted, #5d6b68);
    font-style: italic;
}

/* The Session a seat belongs to, beside the Project it belongs to. Quiet, but
 * present — three cards that differ only by date are three cards nobody can
 * tell apart at a glance. */
.session-tag {
    display: inline-block;
    padding: .05rem .45rem;
    margin-right: .25rem;
    border-radius: 4px;
    background: var(--tint, #eef6f5);
    color: var(--ink, #1d2b28);
    font-weight: 600;
    font-size: .85em;
}

/* ── DICTATION, WHILE IT IS LISTENING ────────────────────────────────────────
 *
 * The behaviour set this class from the day it was written and nothing ever
 * drew it, so a microphone that was live looked exactly like one that was not.
 * Somebody talking to a form deserves to see that it is hearing them. */
/* ── A MICROPHONE INSIDE THE FIELD ───────────────────────────────────────────
 *
 * The wrapper is the positioning context. It reports itself as block so a
 * field that filled its column still fills it, and the icon sits in the
 * padding the field gives up on its right. */
.has-mic {
    position: relative;
    display: block;
}

.has-mic-field { padding-right: 2.3rem !important; }

.mic-button {
    position: absolute;
    top: 50%;
    right: .45rem;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--muted, #6b7a77);
    cursor: pointer;
    line-height: 0;
}

/* On a textarea it belongs at the top, clear of the text growing beneath it. */
.has-mic-block .mic-button {
    top: .45rem;
    transform: none;
}

/* Provisional words, while she is still speaking them. Positioned out of the
   flow so appearing and disappearing never nudges the form, and above what
   follows so it is readable wherever a field sits. */
.mic-interim {
    position: absolute;
    left: .25rem;
    right: 2.3rem;
    top: 100%;
    z-index: 3;
    margin-top: .15rem;
    padding: .1rem .35rem;
    border-radius: 4px;
    background: var(--tint, #eef6f5);
    color: var(--muted, #6b7a77);
    font-size: .85em;
    font-style: italic;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    pointer-events: none;
}

.mic-button:hover   { color: var(--ink, #1d2b28); background: var(--tint, #eef6f5); }
.mic-button:focus-visible {
    outline: 2px solid var(--accent, #0f766e);
    outline-offset: 1px;
}

/* Add image keeps a word, because unlike the microphone it is not a way of
   typing — it hands QC a document to read, and that needs saying. */
.btn-image {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
}

/* Stroked, so they inherit the button's colour in both states and in either
   theme rather than carrying a fill of their own. */
.mic-icon,
.image-icon {
    width: 1.05em;
    height: 1.05em;
    flex: none;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.mic-icon path:first-child { fill: currentColor; stroke: none; }

/* Listening. The control keeps its size and place; only the colour changes,
   so nothing shifts under the cursor mid-sentence. */
.mic-button.is-listening {
    background: var(--danger, #b3261e);
    color: #fff;
}

.mic-button.is-listening .mic-icon {
    animation: qc-pulse 1.2s ease-in-out infinite;
}

@keyframes qc-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: .3; }
}

/* Somebody who has asked for less movement still gets the colour and the
   changed label. Only the movement stops. */
@media (prefers-reduced-motion: reduce) {
    .mic-button.is-listening .mic-icon { animation: none; }
}

/* ── READING NOTES INTO THE FORM ─────────────────────────────────────────────
 *
 * Set apart from the form it feeds, because it is not part of it: what it
 * shows is a proposal, and the boxes below are still where the Project is. */
/* Speak and Add image sit together as ways of filling the box; Read this is
   pushed to the end because it is the only one that does something. */
.capture-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin-top: .5rem;
}

.capture-actions [data-capture-read] { margin-left: auto; }

/* A label styled as a button, because the real file input is screen-reader
   only — clicking the label is what opens the picker. */
.capture-actions label.btn { cursor: pointer; margin: 0; }

.capture.is-dropping {
    outline: 2px dashed var(--accent, #0f766e);
    outline-offset: 4px;
}

/* What is attached. A list rather than a gallery: a PDF and a screenshot are
   the same kind of thing here — something QC has been given to read. */
.capture-sources { margin-top: .7rem; }

.capture-sources h3 {
    margin: 0 0 .35rem;
    font-size: .9rem;
    font-weight: 600;
}

.capture-source {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .25rem 0;
    border-top: 1px solid var(--line, #dfe5e4);
}

.capture-source-icon { font-size: 1.1em; }

.capture-source-name {
    font-weight: 600;
    font-size: .9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The remove control sits at the end of its row, whatever the name's length. */
.capture-source .btn { margin-left: auto; flex: none; }

/* Two sources saying the same thing is a stronger proposal than one. */
.capture-line.is-corroborated { font-weight: 600; }

/* A disagreement is not a gap and must not read like one — it is the one
   thing on this panel that needs a decision rather than a correction. */
.capture-conflicts .capture-line { color: var(--danger, #b3261e); }

/* ---------------------------------------------------------------------------
   A DISAGREEMENT YOU CAN FINISH WITH

   The conflict panel listed both answers in red and offered no way to accept
   either, so a conflict the Admin had already judged stayed on screen in the
   colour reserved for things that are wrong. An alert that cannot be
   discharged becomes furniture, and then so does the next real one.

   Each disagreement is now one block: its answers, a button that chooses one,
   and a button that declines both. Once decided the block drops out of the
   danger colour entirely — that change of colour is the acknowledgement.
   --------------------------------------------------------------------------- */
.capture-conflict { padding: .45rem 0; }
.capture-conflict + .capture-conflict { border-top: 1px solid var(--line, #dfe5e4); }

.capture-conflict .capture-line {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
}

/* Settled, so it is no longer a warning. The override has to name the panel
   too, or the rule above it wins on specificity and the line stays red. */
.capture-conflicts .capture-conflict.is-settled .capture-line { color: var(--text); }

.capture-dismiss { margin-top: .35rem; }

.capture-pending .capture-line { color: var(--muted, #6b7a77); }

.capture .capture-review {
    margin-top: .75rem;
    padding-top: .75rem;
    border-top: 1px solid var(--line, #dfe5e4);
}

.capture-group { margin-bottom: .75rem; }

.capture-group h3 {
    margin: 0 0 .35rem;
    font-size: .9rem;
    font-weight: 600;
}

.capture-line {
    padding: .1rem 0;
    font-size: .9rem;
}

/* What the text did not say. Deliberately as prominent as what it did — a
   reading that hides its gaps reads as more complete than it is. */
.capture-gaps .capture-line { color: var(--danger, #b3261e); }

/* ---------------------------------------------------------------- intake steps

   Starting a Project, one section at a time. The panels are built by
   wireIntakeSteps(); nothing here hides anything until that has run and added
   .has-intake-steps to the body, so a page without script is the long form it
   always was rather than a blank one.
*/
.has-intake-steps .is-step-hidden { display: none; }

.intake-steps {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin: 0 0 1rem;
}

.intake-step-tab {
    appearance: none;
    border: 1px solid var(--line, #dceceb);
    background: #fff;
    color: var(--ink, #1d2b28);
    border-radius: 999px;
    padding: .45rem .9rem;
    font: inherit;
    font-size: .92rem;
    line-height: 1.2;
    cursor: pointer;
}

.intake-step-tab:hover { border-color: var(--brand, #0E7E72); }

.intake-step-tab.is-current {
    background: var(--brand, #0E7E72);
    border-color: var(--brand, #0E7E72);
    color: #fff;
    font-weight: 600;
}

/* Back and Next sit at the end of each step, where the eye already is when a
   section is finished — not at the top, where it would mean scrolling up to
   move on. */
.intake-step-move {
    gap: .6rem;
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line, #dceceb);
}

.intake-preview-block { margin-bottom: 1.25rem; }

.intake-preview-block h3 {
    font-size: 1rem;
    margin: 0 0 .4rem;
    color: var(--brand, #0E7E72);
}

.intake-preview-block ul {
    list-style: none;
    padding: 0;
    margin: 0;
}

.intake-preview-block li {
    padding: .22rem 0;
    border-bottom: 1px solid var(--line-faint, #eef5f4);
}

.intake-preview-block li:last-child { border-bottom: 0; }

/* On a phone the tabs wrap to several rows and the gap matters more than the
   pill shape; keep them tappable rather than pretty. */
@media (max-width: 640px) {
    .intake-step-tab { padding: .5rem .8rem; font-size: .88rem; }
}

/* ------------------------------------------------------- delivery measures

   The four controls on a deliverable row appear when the row is ticked.
   Guarded on .has-delivery-rows so that a page without script shows every
   control, exactly as before — a row whose inputs were hidden with no way to
   reveal them would be worse than a long list.
*/
.has-delivery-rows .measure.is-measure-hidden { display: none; }

/*
 * .measure sets display:inline-flex, and a class selector outranks the
 * browser's own [hidden] { display: none }. So marking the element hidden
 * would have left it on screen — the attribute set, the controls visible,
 * and the two halves of the page disagreeing about whether a deliverable was
 * chosen. This makes the attribute mean what it says.
 */
.measure[hidden] { display: none; }

/* Proportions. The number is the thing being read, so it is no longer the
   smallest box on the row; the two word boxes no longer dwarf it. */
.measure-qty  { max-width: 5.5rem; }
.measure-unit { max-width: 9rem; }

/* ---------------------------------------------------- preferred collaborator

   Three controls that now carry visible labels rather than only a placeholder
   the box was too narrow to finish.

   align-items moves to flex-start because the row's items are no longer bare
   controls of equal height — a label sits above each one, and centring them
   would stagger the inputs. The 12rem basis is the same figure .row-form .grow
   uses, so the three columns collapse to one on a phone at the same width the
   rest of the form already does.
*/
.preferred-row { align-items: flex-start; }
.preferred-row .field { flex: 1 1 12rem; min-width: 0; margin-bottom: 0; }

/* ---------------------------------------------------------------------------
   THE ORDER, MADE VISIBLE

   Three people for one seat is a sequence, and it was rendering as three
   identical strips of controls with the word "First refusal" buried in a
   narrow column at the same weight as every label beside it. Nothing on the
   card said the blocks were ordered, so the card read as one undivided mass.

   The heading and its rule are the whole fix: a reader's eye lands on which
   turn this is before it lands on anything inside the turn.
   --------------------------------------------------------------------------- */
.preferred-slot { padding-top: .9rem; }
.preferred-slot + .preferred-slot { border-top: 1px solid var(--line, #dfe5e4); margin-top: .4rem; }

.preferred-slot-head {
    font-size: .78rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin: 0 0 .6rem;
}

/* ---------------------------------------------------------------------------
   FIELD HELP

   A small round (i) beside a label. Deliberately quiet: it is a way back to
   an explanation somebody has already read, not a thing competing with the
   field for attention.

   Sized against the text rather than in pixels, so it keeps its proportion
   when the reader has made the page larger.
   --------------------------------------------------------------------------- */
.field-help-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.15em;
    height: 1.15em;
    padding: 0;
    border: 1px solid var(--line, #dfe5e4);
    border-radius: 50%;
    background: transparent;
    color: var(--text-muted);
    font-size: .72rem;
    font-style: italic;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    vertical-align: middle;
}

.field-help-toggle:hover,
.field-help-toggle:focus-visible {
    color: var(--text);
    border-color: var(--text-muted);
}

.field-help-toggle[aria-expanded="true"] {
    background: var(--line, #dfe5e4);
    color: var(--text);
}

.field-help { margin-top: .35rem; }

/* -------------------------------------------------------- session schedule

   The date picker rendered at the browser's own minimum, which on this form
   sat beside a full-width name field and a three-part time control and read
   as an afterthought — small enough that the owner reported it as hard to
   use rather than merely ugly. The native control still does the work; it is
   simply given room to be aimed at, on a phone as much as a desktop.
*/
input[type="date"][data-session-date] {
    min-width: 11rem;
    min-height: 2.5rem;
    padding: .35rem .5rem;
}

/* Hours and minutes became pick-lists rather than steppers. Sized to their
   contents so two short numbers do not sprawl across the row. */
.duration-part { max-width: 5.5rem; }

/* Copy Session 1's details down to the others.

   It was btn-quiet — transparent, hairline border — and sat between two
   Sessions where the eye is already moving. The owner reported it as missing
   and found it only after going to look for it. Tinted with the action colour
   rather than filled with it: loud enough to be seen, quiet enough not to
   compete with Next, which is the actual way forward. */
.btn-copy {
    background: var(--action-quiet);
    color: var(--action);
    border: 1px solid var(--action);
    font-weight: 500;
}

/* An inset ring on hover rather than a colour swap — no layout shift, and it
   reads the same whichever theme the viewer is in. */
.btn-copy:hover { box-shadow: inset 0 0 0 1px var(--action); }

/* ------------------------------------------------- intake sub-section titles

   "How people get into the event" is a subject of its own inside a long card,
   and it was marked up as <legend class="small"> — set at .875rem, which is
   SMALLER than the body text beneath it. The owner reported having to squint
   at it, and a Collaborator raised the same thing from her side of the
   application: a heading quieter than the text it introduces does not read as
   a heading at all, so a card with four subjects in it looks like one
   undifferentiated scroll.

   Sized above the body text so the eye can find the boundaries. The weight and
   padding already come from .role-duties legend. */
.intake-subhead {
    font-size: 1.05rem;
    line-height: 1.3;
}

/* ===========================================================================
   THE DECISION BAR ON AN OPPORTUNITY
   ===========================================================================

   The Accept button sits at the foot of a page that takes a dozen swipes to
   reach the end of on a phone. It sits there on purpose — pressing it is
   consent to the dates and the fee above it — so this does not move it. It
   pins a second press to the bottom of the screen once the reader has gone
   past the figure, and submits the very same form.

   hidden until the script says otherwise, so a browser with no JavaScript
   never sees a bar it cannot dismiss. [hidden] is set explicitly because
   display:flex below would otherwise beat the user-agent rule for it. */
.opp-sticky {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .7rem 1rem;
    /* Clear of the home indicator on a phone, and of nothing on a desktop. */
    padding-bottom: calc(.7rem + env(safe-area-inset-bottom, 0px));
    background: var(--surface, #fff);
    border-top: 1px solid var(--border, #d9ddd9);
    box-shadow: 0 -2px 14px rgba(0, 0, 0, .10);
}

.opp-sticky[hidden] {
    display: none;
}

/* The title, so a bar pinned over the page still says what it is about.
   It yields all its space to the button before the button shrinks. */
.opp-sticky-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: .9rem;
    color: var(--muted, #5b635b);
}

.opp-sticky .btn {
    flex: 0 0 auto;
}

/* On a narrow screen the title is the first thing to go: a truncated
   sentence helps nobody, and the button is the reason the bar exists. */
@media (max-width: 30rem) {
    .opp-sticky-label {
        display: none;
    }

    .opp-sticky .btn {
        width: 100%;
    }
}

/* Nothing should end up underneath the bar. The page gets room for it at the
   bottom whenever one is present. */
body:has(.opp-sticky:not([hidden])) {
    padding-bottom: 5rem;
}

/* A bar that slid into place is easier to notice than one that blinks on,
   but not for somebody who has asked for less movement. */
@media (prefers-reduced-motion: no-preference) {
    .opp-sticky {
        transition: transform .18s ease-out;
    }

    .opp-sticky[data-entering] {
        transform: translateY(100%);
    }
}

/* It is pinned to the screen, so it has no place on paper. */
@media print {
    .opp-sticky {
        display: none !important;
    }
}

/* ===========================================================================
   A MENU ON A TABLE ROW
   ===========================================================================

   Duplicating an engagement used to mean opening the Project and hunting for
   the control on it. This puts it on the row, where the thing it acts on
   already is.

   Native <details>, so it opens, closes and takes keyboard focus with no
   JavaScript — the same bargain the rest of this site makes. */
.row-actions {
    width: 1%;
    white-space: nowrap;
    text-align: right;
}

.row-menu {
    position: relative;
    display: inline-block;
}

/* The ⋮ itself. The marker is removed because the glyph IS the control, and
   a disclosure triangle beside it reads as two things. */
.row-menu > summary {
    list-style: none;
    cursor: pointer;
    padding: .2rem .5rem;
    border-radius: .35rem;
    color: var(--muted, #5b635b);
    line-height: 1;
    font-size: 1.15rem;
    user-select: none;
}

.row-menu > summary::-webkit-details-marker,
.row-menu > summary::marker {
    display: none;
    content: '';
}

.row-menu > summary:hover,
.row-menu[open] > summary {
    background: var(--surface-2, #eef1ee);
    color: var(--text, #1d241d);
}

.row-menu > summary:focus-visible {
    outline: 2px solid var(--accent, #2f6f4f);
    outline-offset: 2px;
}

/* ABSOLUTE, AND RIGHT-ALIGNED TO THE CELL. A menu that pushed the row open
   would move every row below it the moment somebody looked at one.

   The last rows of a table are the ones most likely to open downward into
   nothing, so the panel is allowed to sit over whatever follows: z-index
   above the table, and the table itself must not clip it — see .table-wrap
   below. */
.row-menu-items {
    position: absolute;
    right: 0;
    top: calc(100% + .25rem);
    z-index: 30;
    min-width: 12rem;
    padding: .3rem;
    text-align: left;
    background: var(--surface, #fff);
    border: 1px solid var(--border, #d9ddd9);
    border-radius: .5rem;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .12);
}

.row-menu-items form {
    margin: 0;
}

.row-menu-items button,
.row-menu-items a {
    display: block;
    width: 100%;
    padding: .45rem .6rem;
    border: 0;
    border-radius: .35rem;
    background: none;
    text-align: left;
    font: inherit;
    color: var(--text, #1d241d);
    text-decoration: none;
    cursor: pointer;
}

.row-menu-items button:hover,
.row-menu-items a:hover,
.row-menu-items button:focus-visible,
.row-menu-items a:focus-visible {
    background: var(--surface-2, #eef1ee);
}

/* A table that scrolls sideways clips anything absolutely positioned inside
   it, which would cut the menu off at the edge of the cell. Overflow is
   restored while a menu on that table is open, and only then. */
.table-wrap:has(.row-menu[open]) {
    overflow: visible;
}

/* ===========================================================================
   RELEASING AN EARNING EARLY, FROM THE QUEUE
   ===========================================================================

   The first version of this put a one-line text input and a microphone into
   a narrow dropdown. Three things went wrong at once, and the Owner hit all
   three on his first real use:

     the reason is REQUIRED and nothing said so, so the first press failed
     with no explanation on the form;

     dictated into one line, a sentence scrolled out of sight the moment it
     grew — which reads, to the person speaking, as the words vanishing;

     and the live-transcription caption sits absolutely below its field, so
     in a cramped panel it landed over the very box it was describing.

   So the panel is wide enough to write a sentence in, the field is a
   textarea that wraps, and the caption is given somewhere of its own to
   sit. */
.release-panel {
    min-width: 26rem;
    max-width: min(30rem, calc(100vw - 2rem));
    padding: .5rem .75rem .75rem;
}

.release-form {
    gap: .4rem;
}

.release-form label {
    display: block;
    font-size: .85rem;
    font-weight: 600;
}

/* The asterisk earns its place only next to a control that is genuinely
   refused when empty. */
.release-form .req {
    color: var(--danger, #a4302b);
    margin-left: .15rem;
}

.release-form textarea {
    width: 100%;
    resize: vertical;
    min-height: 3.2rem;
}

/* ROOM BENEATH THE FIELD FOR THE PROVISIONAL WORDS.
 *
 * .mic-interim is absolutely positioned at top:100% of its field. Inside a
 * dropdown that ends at the field, it had nowhere to go and overlapped the
 * button below it. The padding here is that somewhere. */
.release-form .has-mic {
    margin-bottom: 1.35rem;
}

/* While it is listening, the caption is the thing being read — so it gets
   the page's own surface rather than a tint that reads as disabled, and a
   border so its edge is obvious against the panel behind it. */
.release-panel .mic-interim {
    background: var(--surface, #fff);
    border: 1px solid var(--border, #d9ddd9);
    color: var(--ink, #1d2b28);
    white-space: normal;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .08);
}

@media (max-width: 34rem) {
    .release-panel {
        position: fixed;
        left: 1rem;
        right: 1rem;
        min-width: 0;
        max-width: none;
    }
}
