
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    font-family: system-ui, -apple-system, sans-serif;
}

/* ================= BASE ================= */
body {
    margin: 0;
    font-family: Arial, sans-serif;
    font-size: 17px;
    color: #111827;
    background: #f9fafb;

    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.main-content {
    flex: 1;
    overflow-x: clip;
}

.page-shell {
    width: min(100% - 2rem, 1440px);
    margin: 0 auto;
    padding: 3rem 0 4rem;
}

.page-shell--wide {
    width: min(100% - 2rem, 1600px);
}

.page-intro {
    margin: 0 0 0.75rem;
    color: #3c86b4;
    font-size: 0.98rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.s--center {
    text-align: center;
}

.page-intro h1 {
    margin: 0 0 0.5rem;
    font-size: 1.8rem;
}

.page-intro p {
    color: #6b7280;
    font-size: 0.98rem;
    line-height: 1.7;
}

.text-content {
    line-height: 1.8;
    font-size: 1.04rem;
    color: #374151;
}

/* ================= HERO ================= */
.hero {
    position: relative;
    min-height: 34rem;
    height: 72vh;
    max-height: 48rem;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    overflow: hidden;
}

.hero-bg {
    position: absolute;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.hero-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        rgba(17,24,39,0.75),
        rgba(17,24,39,0.65),
        rgba(17,24,39,0.85)
    );
}

.hero-content {
    position: relative;
    z-index: 2;
    width: min(100% - 3rem, 860px);
    padding: 1rem;
}

.hero h1 {
    font-size: clamp(2.7rem, 3.7vw, 4.1rem);
    color: white;
    margin-bottom: 1rem;
    line-height: 1.2;
}

.hero h1 span {
    color: #60a5fa;
}

.hero p {
    font-size: clamp(1.08rem, 1.25vw, 1.3rem);
    color: #e5e7eb;
    margin-bottom: 2rem;
}

.btn-primary {
    background: #3c86b4;
    padding: 1rem 2rem;
    border-radius: 0.7rem;
    color: white;
    text-decoration: none;
    font-size: 1rem;
    font-weight: 600;
    display: inline-block;
    transition: all 0.25s ease;
}

.btn-primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 25px rgba(60,134,180,0.4);
}

/* ================= MAIN GRID ================= */
.main-grid {
    width: min(100% - 2.5rem, 1600px);
    margin: 0 auto;
    padding: clamp(3rem, 5vw, 4.5rem) 0;
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(320px, 0.95fr);
    gap: clamp(2rem, 4vw, 4rem);
}

/* ================= SOLUTIONS ================= */
.solutions-header {
    margin-bottom: 2rem;
}

.solutions-header h2 {
    font-size: 2.2rem;
    margin-bottom: 0.5rem;
}

.solutions-header p {
    color: #6b7280;
    font-size: 1.05rem;
    line-height: 1.7;
}

.solutions-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.5rem;
}

.solutions-grid a {
    display: block;
    color: inherit;
    text-decoration: none;
}

.card {
    background: white;
    border: 1px solid #e5e7eb;
    border-radius: 1rem;
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
    transition: 0.25s;
}

.card:hover {
    transform: translateY(-6px);
    box-shadow: 0 12px 24px rgba(0,0,0,0.12);
}

.card img {
    width: 100%;
    height: 200px;
    object-fit: contain;
    background: #f9fafb;
    padding: 1rem;
}

.card-content {
    padding: 1.2rem;
}

.card-content h3 {
    color: #3c86b4;
    font-size: 0.95rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    margin-bottom: 0.4rem;
}

.card-content p {
    color: #6b7280;
    font-size: 1.02rem;
    line-height: 1.6;
}

/* ================= WHO WE ARE ================= */
.who-page {
    width: min(100% - 2rem, 1520px);
    margin: 0 auto;
    padding: 3.5rem 0 4rem;
}

.who-hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(360px, 1.05fr);
    gap: clamp(1.5rem, 3vw, 3.5rem);
    align-items: center;
}

.who-hero__copy {
    min-width: 0;
}

.who-eyebrow {
    margin: 0 0 0.75rem;
    color: #3c86b4;
    font-size: 1.8rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.who-title {
    margin: 0 0 1.25rem;
    font-size: clamp(2.45rem, 4.1vw, 3.7rem);
    line-height: 1.1;
    color: #111827;
}

.who-lead,
.who-body,
.who-panel__text {
    color: #374151;
    line-height: 1.8;
}

.who-lead {
    margin: 0 0 1rem;
    font-size: 1.12rem;
}

.who-body {
    margin: 0;
    color: #4b5563;
}

.who-hero__media {
    display: flex;
    justify-content: flex-end;
}

.who-hero__image {
    display: block;
    width: min(100%, 600px);
    border-radius: 1rem;
    background: #ffffff;
    box-shadow: 0 18px 45px rgba(17, 24, 39, 0.12);
}

.who-panel {
    margin-top: 3rem;
    padding: 2rem;
    border: 1px solid #e5e7eb;
    border-radius: 1.25rem;
    background: #ffffff;
}

.who-panel__title {
    margin: 0 0 1rem;
    font-size: 1.65rem;
    color: #111827;
}

.who-panel__text {
    margin: 0 0 0.9rem;
}

.who-panel__text--last {
    margin-bottom: 0;
}

/* ================= PROFILE ================= */
.profile-page {
    width: min(100% - 2rem, 1440px);
    margin: 0 auto;
    padding: 3rem 0 4rem;
}

.profile-panel,
.profile-header,
.profile-card {
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 1rem;
    box-shadow: 0 16px 38px rgba(15, 23, 42, 0.05);
}

.profile-header {
    padding: 1.6rem 1.7rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.25rem;
}

.profile-header__copy {
    min-width: 0;
}

.profile-header__title {
    margin: 0 0 0.35rem;
    font-size: clamp(2rem, 3vw, 2.6rem);
    line-height: 1.1;
}

.profile-header__subtitle {
    margin: 0;
    color: #6b7280;
    font-size: 1.04rem;
    line-height: 1.6;
}

.profile-header__brand {
    flex-shrink: 0;
}

.profile-header__logo {
    width: 82px;
    height: 82px;
    object-fit: contain;
}

.profile-card {
    padding: 1.65rem;
}

.profile-card + .profile-card {
    margin-top: 1.25rem;
}

.profile-layout {
    display: grid;
    grid-template-columns: minmax(0, 1.85fr) minmax(340px, 0.85fr);
    gap: 1.4rem;
    align-items: stretch;
}

.profile-main,
.profile-sidebar {
    min-width: 0;
}

.profile-main {
    display: flex;
    flex-direction: column;
}

.profile-panel {
    display: flex;
    flex-direction: column;
    flex: 1;
    overflow: hidden;
}

.profile-panel__header {
    border: 0;
    border-radius: 0;
    border-bottom: 1px solid #e5e7eb;
    box-shadow: none;
}

.profile-panel__body {
    padding: 1.65rem;
    margin-bottom: 2rem;
    display: flex;
    flex-direction: column;
    flex: 1;
}

.profile-sidebar .profile-card {
    position: sticky;
    top: 6.5rem;
    min-height: 100%;
}

.profile-card--sidebar {
    height: 100%;
}

.profile-card__intro {
    margin-bottom: 1.3rem;
}

.profile-card__title {
    margin: 0 0 0.35rem;
    font-size: 1.55rem;
    line-height: 1.2;
}

.profile-card__subtitle {
    margin: 0;
    color: #6b7280;
    line-height: 1.6;
}

.profile-form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem 1.15rem;
}

.profile-field {
    min-width: 0;
}

.profile-label {
    display: block;
    margin-bottom: 0.35rem;
    color: #6b7280;
    font-size: 0.9rem;
    font-weight: 600;
}

.profile-input {
    width: 100%;
    padding: 0.8rem 0.9rem;
    border: 1px solid #d1d5db;
    border-radius: 0.75rem;
    background: #ffffff;
    font: inherit;
}

/* A <select> keeps the browser's own control metrics and quietly ignores most of the
   padding above, so next to a text input of the same class it renders visibly shorter —
   most obviously in Safari. `appearance: none` hands the box back to this stylesheet.
   That also removes the native arrow, so one is drawn as a background image, with room
   reserved on the right so a long option cannot run underneath it. */
select.profile-input {
    -webkit-appearance: none;
    appearance: none;
    padding-right: 2.4rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%236b7280' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.9rem center;
    background-size: 0.7rem;
    cursor: pointer;
}

.profile-input:focus {
    outline: none;
    border-color: #60a5fa;
    box-shadow: 0 0 0 4px rgba(96, 165, 250, 0.18);
}

.profile-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 118px;
    padding: 0.78rem 1.15rem;
    border-radius: 0.7rem;
    border: 1px solid transparent;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}

.profile-button:hover {
    transform: translateY(-1px);
}

/* The margin is spacing on a colour variant, which is why it travels to places that
   do not want it — see the override in .app-modal__actions. Left here because four
   templates rely on it to separate a submit button from the form grid above it. */
.profile-button--primary {
    margin-top: 1.25rem;
    background: #3c86b4;
    color: #ffffff;
}

.profile-button--ghost {
    background: transparent;
    border-color: #d1d5db;
    color: #374151;
}

.profile-button--danger {
    min-width: 148px;
    background: #dc2626;
    color: #ffffff;
}

.profile-actions {
    display: grid;
    gap: 0.9rem;
}

.profile-action-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 1rem 1rem 1rem 1.1rem;
    border: 1px solid #e5e7eb;
    border-radius: 0.9rem;
}

.profile-action-row--danger {
    border-color: #fecaca;
    background: #fef2f2;
}

.profile-action-row__title {
    margin: 0 0 0.2rem;
    font-size: 1.08rem;
}

.profile-action-row__title--danger {
    color: #b91c1c;
}

.profile-action-row__text {
    margin: 0;
    color: #6b7280;
    line-height: 1.5;
}

.profile-action-row__text--danger {
    color: #7f1d1d;
}

.profile-modal {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 100;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.profile-modal__panel {
    background: #ffffff;
    padding: 1.8rem;
    border-radius: 1rem;
    max-width: 440px;
    width: min(100%, 440px);
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}

.profile-modal__title {
    margin: 0 0 1rem;
    text-align: center;
    font-size: 1.4rem;
}

.profile-modal__fields {
    display: grid;
    gap: 0.8rem;
}

/* The irreversible bit, on the delete account modal. */
.profile-modal__warning {
    margin: 0 0 0.75rem;
    padding: 0.7rem 0.9rem;
    background: #fee2e2;
    border: 1px solid #fca5a5;
    border-radius: 0.6rem;
    color: #991b1b;
    font-size: 0.9rem;
    line-height: 1.45;
}

/* What the action does *not* do, which matters more here than what it does. */
.profile-modal__note {
    margin: 0 0 1.1rem;
    color: #4b5563;
    font-size: 0.88rem;
    line-height: 1.5;
}

.profile-modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.8rem;
    margin-top: 1.5rem;
    flex-wrap: wrap;
}

/* ================= SOLUTIONS PAGE ================= */
.solutions-page {
    padding-top: 3rem;
}

.solutions-page__intro {
    margin-bottom: 2.2rem;
}

.solutions-page__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.5rem;
}

.solutions-page__card {
    display: flex;
    align-items: center;
    gap: 1.4rem;
    min-height: 100%;
    text-decoration: none;
    color: inherit;
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 1rem;
    padding: 1.4rem;
    transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease;
}

.solutions-page__card:hover {
    transform: translateY(-4px);
    box-shadow: 0 14px 28px rgba(15, 23, 42, 0.08);
    border-color: #cbd5e1;
}

.solutions-page__card-media {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 180px;
    min-height: 150px;
    padding: 1rem;
    border-radius: 0.9rem;
    background: #f8fafc;
}

.solutions-page__card-media img {
    width: 100%;
    max-width: 150px;
    max-height: 120px;
    object-fit: contain;
}

.solutions-page__card-body {
    flex: 1;
    min-width: 0;
}

.solutions-page__card-title {
    margin: 0 0 0.55rem;
    font-size: 1.32rem;
    line-height: 1.25;
    color: #111827;
}

.solutions-page__card-text {
    margin: 0;
    color: #6b7280;
    line-height: 1.7;
}

/* ================= SEGMENTATION PAGE ================= */

.segmentation-page .directive-panel {
    padding: 0;
    margin-bottom: 2.5rem;
}

.segmentation-page .directive-panel__header {
    padding: 1.6rem 1.8rem;
}

.segmentation-page .directive-panel__colouredHeader {
    padding: 1.6rem 1.8rem;
    background: #eff6ff;
}

.segmentation-page .profile-panel__body {
    padding: 1.8rem;
    gap: 1.2rem;
}

.segmentation-page h3 {
    margin-top: 1rem;
    margin-bottom: 0.4rem;
    font-size: 1.25rem;
    color: #111827;
}

.segmentation-page p {
    margin-bottom: 1rem;
    line-height: 1.75;
}

.segmentation-page h3 + p {
    margin-bottom: 1.3rem;
}

.segmentation-page .profile-form-grid {
    gap: 1.2rem 1.4rem;
    margin-top: 0.5rem;
}

.segmentation-page .profile-button {
    margin-top: 1.5rem;
}

.segmentation-page .directive-bibliography a {
    display: block;
    margin-bottom: 0.7rem;
}

.segmentation-consent {
    margin-top: 1.5rem;
    padding: 1rem 1.1rem;
    border: 1px solid #e5e7eb;
    border-radius: 0.8rem;
    background: #f8fafc;
}

.segmentation-consent__label {
    display: flex;
    align-items: flex-start;
    gap: 0.7rem;
    cursor: pointer;
    line-height: 1.6;
    color: #374151;
}

.segmentation-consent__checkbox {
    margin-top: 0.25rem;
    accent-color: #3c86b4;
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.segmentation-consent__link {
    color: #3c86b4;
    text-decoration: none;
    margin-left: 0.3rem;
}

.segmentation-consent__link:hover {
    text-decoration: underline;
}

/* ================= CONSENT PAGE ================= */

.consent-page__intro {
    display: block;
    width: 100%;
    max-width: none;
    margin: 0;
    padding: 0 1.5rem 2rem;
}

.consent-page__intro h1 {
    color: #3c86b4 !important;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    line-height: 1.1;
    font-size: 2.05rem !important;
    text-align: start;

}

.consent-page__intro p {
    width: 100%;
    max-width: none;
    font-size: 1rem;
    line-height: 1.8;
    color: #4b5563;
    margin-top: 1rem;
}

.consent-info-panel {
    margin: 0;
}

.consent-info-panel .profile-panel__body {
    padding: 1.8rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.consent-info-panel .profile-panel__body p {
    margin: 0;
    font-size: 0.98rem;
    line-height: 1.8;
    color: #374151;
}

.consent-list {
    margin: 0;
    padding-left: 1.35rem;
    display: grid;
    gap: 0.85rem;
}

.consent-list li {
    color: #374151;
    line-height: 1.75;
}

.consent-page .directive-panel {
    padding: 0;
    margin: 0;
}

.consent-page .directive-table-wrap {
    border-radius: 1rem;
}

.consent-page .directive-table td {
    font-size: 0.96rem;
    line-height: 1.7;
}

.consent-page .directive-table__section td {
    font-size: 1rem;
}

@media (max-width: 640px) {
    .consent-page__intro {
        padding: 1.35rem;
    }

    .consent-page__eyebrow {
        font-size: 1.5rem;
    }

    .consent-info-panel .profile-panel__body {
        padding: 1.35rem;
    }

    .consent-list {
        padding-left: 1.1rem;
    }
}

/* ================= DIRECTIVE PAGE ================= */
.directive-page {
    display: grid;
    gap: 1.6rem;
}

.directive-page__intro {
    margin-bottom: 0;
}

.directive-page__intro h1 {
    font-size: clamp(2.4rem, 3.5vw, 3.2rem);
    line-height: 1.1;
    color: #111827;
}

.directive-page__intro h3 {
    margin-top: 0.5rem;
    color: #6b7280;
    font-weight: 500;
    line-height: 1.6;
    font-size: 1.1rem;
}


.directive-resources {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem;
}

.directive-resources__card,
.directive-panel,
.directive-bibliography {
    background: #ffffff;
    border: 1px solid #e5e7eb;
    border-radius: 1.1rem;
    box-shadow: 0 16px 38px rgba(15, 23, 42, 0.05);
    margin-bottom: 2rem;
}

.directive-resources__card {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    padding: 1.35rem;
    color: inherit;
    text-decoration: none;
    transition: transform 0.22s ease, box-shadow 0.22s ease, border-color 0.22s ease;
}

.directive-resources__card:hover {
    transform: translateY(-3px);
    border-color: #cbd5e1;
    box-shadow: 0 18px 34px rgba(15, 23, 42, 0.08);
}

.directive-resources__label {
    color: #3c86b4;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.directive-resources__card strong {
    font-size: 1.08rem;
    line-height: 1.35;
}

.directive-resources__card p,
.directive-panel__header p,
.directive-bibliography p {
    margin: 0;
    color: #6b7280;
    line-height: 1.7;
}

.directive-panel {
    overflow: hidden;
    padding: 0.75rem;
    border-radius: 1.25rem;
}

.directive-panel__header,
.directive-bibliography {
    padding: 1.5rem;
}

.directive-panel__header {
    border-bottom: 1px solid #e5e7eb;
}

.directive-panel__header h2,
.directive-bibliography h2 {
    margin: 0 0 0.45rem;
    font-size: 1.15rem;
    color: #111827;
}

.directive-table-wrap {
    overflow-x: auto;
    border-radius: 1rem;
    background: #ffffff;
    border: 1px solid #e5e7eb;
}

.directive-table {
    border: 1px solid #9ca3af;
    border-collapse: collapse;
    border-radius: 0.8rem;
    overflow: hidden;
}

.directive-table tr {
    border-bottom: 1px solid #cbd5e1;
}

.directive-table tbody tr:hover {
    background: #f8fafc;
}

.directive-table th,
.directive-table td {
    padding: 1rem 1.1rem;
    vertical-align: top;
    text-align: left;
    border: 1px solid #cbd5e1;
    line-height: 1.75;
    color: #374151;
}

.directive-table th {
    position: sticky;
    top: 0;
    background: linear-gradient(180deg, #f1f5f9 0%, #eaf2f8 100%);
    color: #111827;
    font-size: 0.84rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    z-index: 1;
}

.directive-table td:first-child,
.directive-table th:first-child {
    width: 42%;
}

.directive-table a,
.directive-bibliography a {
    color: #3c86b4;
    text-decoration: none;
}

.directive-table a:hover,
.directive-bibliography a:hover {
    text-decoration: underline;
}

.directive-table p {
    margin: 0.45rem 0 0;
}

.directive-table__section td {
    background: linear-gradient(135deg, #e0edff 0%, #f8fafc 100%);
    color: #111827;
    font-weight: 700;
    border: 1px solid #d6e4f0;
}

/* ================= LEARN ================= */
.learn-box {
    background: white;
    border: 1px solid #e5e7eb;
    border-radius: 1rem;
    padding: clamp(1.5rem, 2vw, 2rem);
}

.learn-box h2 {
    margin-bottom: 2rem;
    font-size: 2rem;
}

.learn-box p {
    margin-bottom: 1.2rem;
    color: #374151;
    font-size: 1.02rem;
    overflow-wrap: anywhere;
    word-break: break-word;
}

.learn-box a {
    color: #3c86b4;
    text-decoration: none;
    overflow-wrap: anywhere;
    word-break: break-word;
}

/* ================= CONTACT ================= */
.contact {
    width: min(100% - 3rem, 1100px);
    margin: 0 auto;
    text-align: center;
    padding: 3rem 1.5rem;
    border-bottom: 1px solid #e5e7eb;
    border-top: 1px solid #e5e7eb;
}

.contact p {
    color: #4b5563;
    font-size: 1.02rem;
    margin-top: 1rem;
}

/* ================= LOGOS ================= */
.logos-section {
    padding: 2rem 0;
}

.logos-section__inner {
    width: min(100% - 2.5rem, 1600px);
    margin: 0 auto;
    text-align: center;
}

.logos-section__title {
    margin-top: 1rem;
    font-size: 2rem;
}

.carousel-container {
    overflow: hidden;
    margin-top: 1.5rem;
}

.carousel-track {
    display: flex;
    width: max-content;
    animation: scroll 25s linear infinite;
    align-items: center;
    will-change: transform;
}

.carousel-group {
    display: flex;
    align-items: center;
    gap: 40px;
    flex-shrink: 0;
    padding-right: 40px;
}

.carousel-track img {
    height: 80px;
    opacity: 0.85;
}

@keyframes scroll {
    100% {
        transform: translateX(-50%);
    }
}


/* ================= PRIVATE DASHBOARD ================= */

.private-page {
    display: flex;
    flex-direction: column;
    gap: 2rem;
}

.private-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2rem;
    flex-wrap: wrap;
}

.private-header .page-intro {
    color: #3c86b4;
}

.private-title {
    margin-top: 0.5rem;
    font-size: clamp(2.2rem, 3vw, 3rem);
}

.private-subtitle {
    color: #6b7280;
    margin-top: 0.5rem;
}

/* Header actions: the admin/user view toggle next to the status pill. */
.private-header__actions {
    display: inline-flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.private-view-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.9rem 1.2rem;
    background: white;
    border: 1px solid #3c86b4;
    border-radius: 999px;
    color: #3c86b4;
    font-weight: 700;
    text-decoration: none;
    transition: background 0.15s ease, color 0.15s ease;
}

.private-view-toggle:hover,
.private-view-toggle:focus-visible {
    background: #3c86b4;
    color: white;
}

/* Returning to the admin view is the higher-privilege direction, so it reads as solid
   rather than outlined. */
.private-view-toggle--admin {
    background: #3c86b4;
    color: white;
}

.private-view-toggle--admin:hover,
.private-view-toggle--admin:focus-visible {
    background: #2f6b91;
    color: white;
}

.private-view-toggle__icon {
    font-size: 1rem;
    line-height: 1;
}

.private-status {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.9rem 1.2rem;
    background: #dcfce7;
    border: 1px solid #86efac;
    border-radius: 999px;
    color: #166534;
    font-weight: 700;
}

.private-status__dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #16a34a;
}

/* Two groups side by side on a wide screen -- the job counters and the platform
   counters -- wrapping to one per row when there is no space for both. The user view
   only renders the first group. */
.private-metrics {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
}

.private-metrics__group {
    /* Both the grow factor and the basis scale with the number of cards in the group,
       so four cards and three cards end up about the same width each instead of the
       groups splitting the row in half. --metric-count is set in private.html. */
    flex: var(--metric-count, 1) 1 calc(var(--metric-count, 1) * 8.5rem);
    /* Without this a flex item refuses to shrink below its content, which is what makes
       a long number push the strip wider than the page. */
    min-width: 0;
}

.private-metrics__group-title {
    margin: 0 0 0.7rem;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: #94a3b8;
}

.private-metrics__cards {
    display: grid;
    /* The group's own width already sets how many fit, so auto-fit lands on the right
       count without the stylesheet needing to know it. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 8.5rem), 1fr));
    gap: 1rem;
}

.private-metric-card {
    background: white;
    border: 1px solid #e5e7eb;
    border-radius: 1rem;
    padding: 1.4rem;
    box-shadow: 0 16px 38px rgba(15, 23, 42, 0.05);
    /* The accent is painted by the tone modifiers below; transparent by default so the
       padding does not move when a card lights up. */
    border-left: 3px solid transparent;
}

.private-metric-card__label {
    display: block;
    color: #6b7280;
    margin-bottom: 0.6rem;
    font-size: 0.9rem;
    /* Room for two lines whether the label needs them or not, so the numbers stay on
       one baseline across a row where only "Workers registered" wraps. */
    min-height: 2.5em;
}

.private-metric-card strong {
    font-size: 2rem;
    line-height: 1.1;
    display: block;
}

/* Only applied when the count is non-zero -- the view drops the tone otherwise -- so a
   green or red card means there is something to act on. */
.private-metric-card--live {
    border-left-color: #16a34a;
}

.private-metric-card--live strong {
    color: #15803d;
}

.private-metric-card--alert {
    border-left-color: #dc2626;
}

.private-metric-card--alert strong {
    color: #dc2626;
}

.private-button {
    border: none;
    background: #3c86b4;
    color: white;
    padding: 0.55rem 0.85rem;
    border-radius: 0.55rem;
    cursor: pointer;

    /* Spelled out because this class is worn by both <button> and <a>, and the two
       disagree about everything the browser supplies by default:

       - a <button> uses the browser's own UI face at about 13px, an <a> inherits the
         page's Arial at 17px, so the same class rendered at two sizes in two typefaces
         depending on the element it landed on;
       - an <a> is underlined unless told otherwise, which was being switched off by
         whatever context the button happened to sit in — the job modal, the listing
         tables — so the first one placed somewhere else came out underlined. */
    font-family: inherit;
    font-size: 0.85rem;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
}

/* An icon-only button. Square rather than the text padding, so a row of them lines up,
   and the SVG takes the button's colour through `currentColor` — including the white
   that `.directive-table a.private-button` restores further down, and the grey of
   --muted. The label is still there for assistive technology, in a .visually-hidden
   span, and repeated in `title` for a hovering mouse. */
/* Carries the title for a control that cannot receive mouse events itself. A disabled
   button never fires them, so a tooltip written on it is shown by no browser worth
   naming; the wrapper is not disabled and does. */
.tooltip-host {
    display: inline-flex;
}

.private-button--icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
}

.icon-download,
.icon-unavailable {
    display: block;
}

.private-button--red {
    background: rgba(220, 38, 38, 0.75);
}

.private-button--green {
    background: rgba(22, 163, 74, 0.5);
}

.private-badge {
    display: inline-flex;
    align-items: center;
    padding: 0.45rem 0.75rem;
    border-radius: 999px;
    font-size: 0.84rem;
    font-weight: 700;
}

.private-badge--green {
    background: #dcfce7;
    color: #166534;
}

.private-badge--red {
    background: #fee2e2;
    color: #991b1b;
}

.private-session {
    border: none;
    background: transparent;
    color: #3c86b4;
    cursor: pointer;
    font-weight: 700;
}

.private-page .directive-panel__header {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
        "title search"
        "text search";
    align-items: start;
    column-gap: 1.25rem;
    row-gap: 0.35rem;
}

.private-page .directive-panel__colouredHeader {
    margin: 1rem;
    padding: 1rem;
    background: #eff6ff;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
        "title search"
        "text search";
    align-items: start;
    column-gap: 1.25rem;
    row-gap: 0.35rem;
}

.private-page .directive-panel__header h2 {
    grid-area: title;
    margin-bottom: 0;
}

.private-page .directive-panel__header p {
    grid-area: text;
}

.private-workers-panel .directive-table {
    width: 100%;
    table-layout: auto;
}

.private-workers-panel .directive-table td:first-child,
.private-workers-panel .directive-table th:first-child {
    width: 4.5rem;
}

.private-workers-panel .directive-table td:nth-child(2),
.private-workers-panel .directive-table th:nth-child(2) {
    width: 24%;
}

.private-workers-panel .directive-table td:nth-child(3),
.private-workers-panel .directive-table th:nth-child(3) {
    width: 18%;
}

.private-workers-panel .directive-table td:nth-child(4),
.private-workers-panel .directive-table th:nth-child(4),
.private-workers-panel .directive-table td:nth-child(5),
.private-workers-panel .directive-table th:nth-child(5) {
    width: 96px;
    text-align: center;
}

.private-workers-panel .directive-table td:nth-child(6),
.private-workers-panel .directive-table th:nth-child(6),
.private-workers-panel .directive-table td:nth-child(7),
.private-workers-panel .directive-table th:nth-child(7) {
    width: 156px;
    text-align: center;
}

.private-toolbar {
    grid-area: search;
    display: flex;
    justify-content: flex-end;
    align-self: center;
}




.private-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 0.55rem;
    padding: 1.1rem 1.25rem 0;
}

.private-pagination__control,
.private-pagination__page {
    min-width: 2.4rem;
    height: 2.4rem;
    padding: 0 0.7rem;
    border: 1px solid #d1d5db;
    border-radius: 0.7rem;
    background: #ffffff;
    color: #111827;
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

.private-pagination__control:hover,
.private-pagination__page:hover {
    background: #eff6ff;
    border-color: #93c5fd;
    color: #1d4ed8;
}

.private-pagination__page.is-active {
    background: #3c86b4;
    border-color: #3c86b4;
    color: #ffffff;
}

.private-pagination__control:disabled {
    background: #f8fafc;
    border-color: #e5e7eb;
    color: #9ca3af;
    cursor: not-allowed;
}

.private-consent-cell {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

/* A word, not a symbol. This was a bare ✓ or ✕ beside a blue circle holding a "?" —
   and the "?" was invisible, because `.directive-table a` ([0,1,1]) beat that rule's
   own `color: white` ([0,1,0]) and painted it the same blue as its background. The
   circle linked to the consent document, which the column heading already links to, so
   it is gone rather than fixed. */
.private-consent-state {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    white-space: nowrap;
    font-size: 0.85rem;
    font-weight: 600;
}

.private-consent-state--yes {
    color: #16a34a;
}

.private-consent-state--no {
    color: #dc2626;
}

.private-button--muted {
    background: #cbd5e1;
    color: #475569;
}

/* Only a real disabled control refuses the pointer. The LOG link in My entries also
   carries --muted, to read as secondary, but it is a working download. */
button.private-button--muted[disabled] {
    cursor: not-allowed;
}

/* `.directive-table a` further up sets `color: #3c86b4` and, at [0,1,1] against a bare
   `.private-button` ([0,1,0]), wins however late this rule comes. The DOWNLOAD links in
   My entries therefore rendered #3c86b4 text on the #3b82f6 background of the button —
   a solid blue rectangle with the label invisible inside it.

   The same collision the job modal hit, and it surfaced here once downloads stopped
   being gated on consent: before that most of these cells were LOCKED buttons, which
   are `<button>` elements and never matched the rule. Two classes plus an element
   ([0,2,1]) settle it outright. */
.directive-table a.private-button,
.directive-table a.private-button:hover,
.directive-table a.private-button:focus-visible {
    color: #ffffff;
    text-decoration: none;
}

.directive-table a.private-button--muted,
.directive-table a.private-button--muted:hover,
.directive-table a.private-button--muted:focus-visible {
    color: #475569;
}

/* ================= PRIVATE JOB MODAL ================= */

/* The progress message dialog in My entries. Genuinely modal, unlike the job detail
   below the dashboard's table: it is one short thing to read and then dismiss, and it
   does not need to be compared against the row it came from. */
.job-info-modal[hidden] {
    display: none;
}

.job-info-modal {
    position: fixed;
    inset: 0;
    z-index: 140;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
}

body.job-info-open {
    overflow: hidden;
}

.job-info-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.48);
}

.job-info-modal__panel {
    position: relative;
    width: min(680px, 100%);
    max-height: min(80vh, 640px);
    display: flex;
    flex-direction: column;
    padding: 1.4rem;
    border-radius: 1rem;
    background: #ffffff;
    box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);
}

.job-info-modal__panel:focus {
    outline: none;
}

.job-info-modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 0.9rem;
}

.job-info-modal__title {
    margin: 0;
    font-size: 1.05rem;
    color: #111827;
}

/* The badge is now the control that opens the dialog, so it has to look like one:
   the pointer, a focus ring and a little lift on hover. Everything else about it — the
   colour, the pill shape — stays exactly as the read-only badge. */
.private-badge--button {
    border: 0;
    cursor: pointer;
    font: inherit;
    font-size: inherit;
    font-weight: inherit;
    transition: filter 0.12s ease;
}

.private-badge--button:hover {
    filter: brightness(0.94);
}

.job-info-modal__status {
    margin: 0 0 0.8rem;
    color: #6b7280;
    font-size: 0.88rem;
}

.job-info-modal__footer {
    margin-top: 1rem;
}

.job-info-modal__footer [hidden] {
    display: none;
}

.job-info-modal__hint {
    margin: 0;
    color: #9ca3af;
    font-size: 0.82rem;
}

.job-info-modal__close {
    border: 0;
    background: transparent;
    color: #6b7280;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
}

.job-info-modal__close:hover {
    color: #111827;
}

/* pre-wrap, not pre: a traceback keeps its indentation, but a long single-line message
   still wraps instead of forcing the dialog sideways. */
.job-info-modal__body {
    margin: 0;
    overflow: auto;
    padding: 0.9rem 1rem;
    border-radius: 0.6rem;
    background: #f8fafc;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.82rem;
    line-height: 1.55;
    color: #334155;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Age, TE and TR: short numbers that read better right-aligned and should not claim
   width from the columns beside them. */
.private-cell--number {
    width: 1px;
    white-space: nowrap;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.private-cell--empty {
    color: #9ca3af;
}

.icon-info {
    display: block;
}

body.private-modal-open {
    overflow: hidden;
}

.private-job-modal[hidden] {
    display: none;
}

.private-job-modal {
    position: fixed;
    inset: 0;
    z-index: 120;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
}

.private-job-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.48);
}

.private-job-modal__panel {
    position: relative;
    width: min(1080px, 100%);
    max-height: min(85vh, 900px);
    overflow: auto;
    padding: 1.5rem;
    border-radius: 1rem;
    background: #ffffff;
    box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);
}

.private-job-modal__header {
    display: flex;
    align-items: start;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
}

.private-job-modal__eyebrow {
    margin: 0 0 0.35rem;
    color: #3c86b4;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 0.78rem;
}

.private-job-modal__title {
    margin: 0;
    font-size: 1.45rem;
    color: #111827;
}

.private-job-modal__close {
    border: 0;
    background: transparent;
    color: #6b7280;
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
}

.private-job-modal__status {
    margin: 0 0 1rem;
    color: #374151;
}

.private-job-modal__notice {
    margin: 0 0 1rem;
    padding: 0.9rem 1rem;
    border: 1px solid #fde68a;
    border-radius: 0.8rem;
    background: #fffbeb;
    color: #92400e;
}

.private-job-detail-table {
    width: 100%;
}

.private-job-detail-table td:first-child,
.private-job-detail-table th:first-child {
    width: auto;
}

/* ================= MY ENTRIES ================= */

.my-entries-page {
    display: flex;
    flex-direction: column;
    gap: 2rem;
}

.my-entries-page .directive-panel__header {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
        "title search"
        "text search";
    align-items: start;
    column-gap: 1.25rem;
    row-gap: 0.35rem;
}

.my-entries-page .directive-panel__header h2 {
    grid-area: title;
    margin-bottom: 0;
}

.my-entries-page .directive-panel__header p {
    grid-area: text;
}

.my-entries-page .directive-table {
    width: 100%;
}

.my-entries-page .directive-table td:first-child,
.my-entries-page .directive-table th:first-child {
    width: auto;
}

.my-entries-page .directive-table td:last-child,
.my-entries-page .directive-table th:last-child {
    width: 110px;
}

/* Stacked, not side by side: the word and the REMOVE button in one row made this the
   widest column in the table for no benefit, and horizontal space is what the eight
   columns are short of. */
.my-entries-page .private-consent-cell {
    flex-direction: column;
    align-items: center;
    gap: 0.4rem;
}


@media (max-width: 1100px) {

    /* The panel header stops being two columns well before the phone breakpoint.
       Its toolbar used to be a row count and a search box; it now also carries five
       filter dropdowns, so on the `auto` track it claims most of the width and leaves
       the title column narrow enough to break "Current jobs and execution state." one
       word per line. */
    .private-page .directive-panel__header,
    .my-entries-page .directive-panel__header {
        grid-template-columns: 1fr;
        grid-template-areas:
            "title"
            "text"
            "search";
        row-gap: 0.75rem;
    }

    .private-toolbar {
        justify-content: stretch;
    }

    /* Let the controls wrap across rows instead of holding one line: the filters run
       on as far as they fit, the search box takes what is left and drops to its own
       row when that is not enough. */
    .table-toolbar {
        justify-content: flex-start;
        row-gap: 0.6rem;
    }

    .table-toolbar__search {
        flex: 1 1 16rem;
    }

    .table-search__field {
        width: 100%;
    }
}

@media (max-width: 640px) {

    .private-metrics__cards {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .private-metric-card {
        padding: 1rem;
    }

    .private-metric-card strong {
        font-size: 1.6rem;
    }

    /* The toolbar's two controls stack, and the search field takes the width:
       .private-search was renamed .table-search__input when the search moved
       server-side, so the old rule here had stopped matching anything. */
    .table-toolbar {
        flex-direction: column;
        align-items: stretch;
    }

    .table-search__field,
    .table-search__input {
        width: 100%;
    }

    .table-footer {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.75rem;
    }

    .private-pagination {
        justify-content: flex-start;
        padding-left: 1rem;
        padding-right: 1rem;
    }
}


/* ================= RESPONSIVE ================= */

@media (min-width: 1200px) {
    .solutions-grid {
        grid-template-columns: repeat(2, minmax(280px, 1fr));
    }

    .solutions-page__grid {
        grid-template-columns: repeat(2, minmax(340px, 1fr));
    }

    .who-hero {
        grid-template-columns: minmax(0, 0.92fr) minmax(460px, 1.18fr);
    }

    .who-hero__image {
        width: min(100%, 760px);
    }
}

/* Tablet */
@media (max-width: 1180px) {

    .main-grid {
        grid-template-columns: 1fr;
        width: min(100% - 3rem, 1120px);
        padding: 3rem 0;
        gap: 2rem;
    }

    .hero h1 {
        font-size: 2.4rem;
    }

    .hero p {
        font-size: 1.16rem;
    }

    .logos-section__inner {
        width: min(100% - 3rem, 1120px);
    }

    .who-page {
        width: min(100% - 2.5rem, 1280px);
    }

    .who-hero {
        grid-template-columns: minmax(0, 1fr) minmax(250px, 0.9fr);
        gap: 1.5rem;
    }

    .who-hero__image {
        width: min(100%, 520px);
    }

    .profile-page {
        width: min(100% - 2.5rem, 1240px);
    }

    .page-shell,
    .page-shell--wide {
        width: min(100% - 2.5rem, 1280px);
    }

    .solutions-page__grid {
        grid-template-columns: 1fr;
    }

    .solutions-page__card {
        gap: 1.15rem;
    }

    .directive-resources {
        grid-template-columns: 1fr;
    }

    .solutions-page__card-media {
        flex-basis: 150px;
        min-height: 135px;
    }

    .profile-layout {
        grid-template-columns: 1fr;
    }

    .profile-sidebar .profile-card {
        position: static;
    }

    .profile-form-grid {
        grid-template-columns: 1fr;
    }
}

/* Mobile */
@media (max-width: 640px) {

    .hero {
        min-height: 30rem;
        height: 64vh;
    }

    .hero-content {
        padding: 0 1rem;
    }

    .hero h1 {
        font-size: 1.8rem;
    }

    .hero p {
        font-size: 1.05rem;
    }

    .main-grid {
        width: min(100% - 2rem, 100%);
        padding: 2rem 0;
    }

    .solutions-grid {
        grid-template-columns: 1fr;
    }

    .learn-box {
        padding: 1.2rem;
    }

    .card img {
        height: 160px;
    }

    .carousel-track img {
        height: 50px;
    }

    .logos-section__inner,
    .contact,
    .who-page {
        width: min(100% - 2.5rem, 100%);
    }

    .page-shell,
    .page-shell--wide,
    .profile-page {
        width: min(100% - 2.5rem, 100%);
    }

    .page-shell {
        padding: 2.5rem 0 3rem;
    }

    .solutions-page {
        padding-top: 2.5rem;
    }

    .solutions-page__card {
        flex-direction: column;
        align-items: stretch;
        padding: 1.15rem;
    }

    .solutions-page__card-media {
        flex-basis: auto;
        min-height: 160px;
        padding: 1rem;
    }

    .solutions-page__card-title {
        font-size: 1.18rem;
    }

    .directive-panel__header,
    .directive-bibliography {
        padding: 1.2rem;
    }

    .directive-table th,
    .directive-table td {
        padding: 0.85rem 0.9rem;
    }

    .who-page {
        padding: 2.75rem 0 3rem;
    }

    .who-hero {
        grid-template-columns: minmax(0, 1.05fr) minmax(130px, 0.95fr);
        gap: 1rem;
        align-items: start;
    }

    .who-title {
        font-size: 2.05rem;
    }

    .who-lead {
        font-size: 1.05rem;
    }

    .who-hero__media {
        justify-content: flex-end;
    }

    .who-hero__image {
        width: 100%;
        max-width: 260px;
    }

    .who-panel {
        margin-top: 2rem;
        padding: 1.4rem;
    }

    .profile-page {
        width: min(100% - 2.5rem, 100%);
        padding: 2.4rem 0 3rem;
    }

    .profile-header,
    .profile-card {
        padding: 1.25rem;
    }


    .profile-header {
        align-items: flex-start;
    }

    .profile-header__title {
        font-size: 1.75rem;
    }

    .profile-header__logo {
        width: 64px;
        height: 64px;
    }

    .profile-button,
    .profile-button--danger {
        width: 100%;
        min-width: 0;
    }

    .profile-action-row {
        align-items: stretch;
    }

    .profile-modal__panel {
        padding: 1.35rem;
    }

    .profile-modal__actions {
        flex-direction: column;
    }
}

/* ==========================================================================
   Auth cards (login and register), lifted from inline styles in
   login.html and register.html, which carried the same block twice.
   The two pages had a 0.2rem difference in the label-to-input gap; they are
   unified here on the label's margin.
   ========================================================================== */

.auth-page {
    margin-top: 4vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    background: #f9fafb;
}

.auth-card {
    width: 100%;
    max-width: 420px;
    margin-inline: auto;
    background: #ffffff;
    padding: 2rem;
    border-radius: 1rem;
    border: 1px solid #e5e7eb;
    box-shadow: 0 15px 35px rgba(0, 0, 0, 0.08);
}

.auth-card__title {
    text-align: center;
    margin: 0.5rem 0 1.75rem;
    color: #3c86b4;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.auth-field {
    margin-bottom: 1rem;
}

/* Last field before the submit button, which needs a little more room. */
.auth-field--last {
    margin-bottom: 1.5rem;
}

/* A checkbox and its label on one line. The label is the sibling of the input here,
   not its parent, because the form renders the widget and the caption separately —
   `for` ties them together, so clicking the text still toggles the box. */
.auth-field--check {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* "Keep me signed in" on the left, "Forgot your password?" on the right, which is
   where both live on most sign-in forms. Wraps to two lines on a narrow screen. */
.auth-field--split {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem 1rem;
    flex-wrap: wrap;
}

.auth-field--split .auth-field--check {
    margin: 0;
}

.auth-field--check input[type="checkbox"] {
    /* Checkboxes inherit no sizing and render small next to 17px text. */
    width: 1rem;
    height: 1rem;
    margin: 0;
    flex: none;
}

.auth-field--check .auth-label--muted {
    margin: 0;
    cursor: pointer;
}

/* Explanatory line under a card title, on the password reset pages. */
.auth-intro {
    margin: 0 0 1.25rem;
    color: #4b5563;
    font-size: 0.95rem;
    line-height: 1.5;
}

/* A quieter aside under the intro: the thing a reader needs only if the main path did
   not work for them. Used on the reset pages to point at the confirmation flow. */
.auth-note {
    margin: 0 0 1.25rem;
    padding: 0.7rem 0.9rem;
    background: #f3f4f6;
    border-radius: 0.6rem;
    color: #4b5563;
    font-size: 0.85rem;
    line-height: 1.5;
}

/* The proof-of-work captcha widget. altcha-widget is a custom element with its own
   shadow DOM, so its insides cannot be styled from here — the custom properties below
   are the documented hooks, and they are all that is needed to stop it looking bolted
   on.

   More room above than a normal field gets: it is not a labelled input in the same
   sequence as the others but a control of its own, and at the same 1rem rhythm it read
   as one more empty text box. Adjacent vertical margins collapse, so the gap above is
   this value against the 1.5rem below the field before it — the larger of the two
   wins, it is not their sum. */
.auth-field--altcha {
    margin: 1.9rem 0 1.5rem;
    --altcha-border-width: 1px;
    --altcha-border-radius: 0.5rem;
    --altcha-color-border: #d1d5db;
    --altcha-color-base: #ffffff;
    --altcha-color-text: #374151;
    --altcha-max-width: 100%;
}

/* Django's password validation help text, shown before the password is rejected
   rather than after. It arrives as a <ul>, hence the list reset. */
.auth-help {
    margin: 0.4rem 0 0;
    color: #6b7280;
    font-size: 0.8rem;
    line-height: 1.45;
}

.auth-help ul {
    margin: 0;
    padding-left: 1.1rem;
}

.auth-label {
    display: block;
    margin-bottom: 0.5rem;
}

.auth-label--muted {
    font-size: 0.9rem;
    color: #4b5563;
}

/* The class is applied by this project's own forms, in their __init__. Django's stock
   PasswordResetForm and SetPasswordForm render their widgets without it, so the reset
   pages came out with bare browser-default inputs; matching any input inside an
   .auth-field covers those and anything added later, with no form subclass just to
   attach a class. Checkboxes are excluded — .auth-field--check sizes those. */
.auth-input,
.auth-field input:not([type="checkbox"]) {
    width: 100%;
    padding: 0.7rem;
    border: 1px solid #d1d5db;
    border-radius: 0.5rem;
    /* Form controls do not inherit the page font: without this they fall back to
       the browser default, around 13px in a different family, against the 17px
       body text. The same line appears on .profile-input and .who-contact__input. */
    font: inherit;
}

.auth-submit {
    width: 100%;
    padding: 0.8rem;
    background: #3c86b4;
    color: #ffffff;
    border: none;
    border-radius: 0.6rem;
    font: inherit;
    font-weight: 500;
    cursor: pointer;
}

.auth-footnote {
    margin-top: 1.5rem;
    text-align: center;
    font-size: 0.9rem;
}

.auth-footnote p {
    margin-bottom: 0.5rem;
}

.auth-link {
    color: #3c86b4;
    text-decoration: none;
}

/* The secondary link in an auth card. The size used to come from .auth-footnote, which
   every other use of this class sits inside; the login's "Forgot your password?" moved
   out of the footnote to share a row with "Keep me signed in" and was left inheriting
   the 17px body size, twice the weight of the label beside it. Stated here so the class
   no longer depends on its container — the three links still in a footnote were already
   rendering at this size. */
.auth-link--muted {
    font-size: 0.9rem;
    color: #6b7280;
    text-decoration: none;
}

/* Underline on hover: next to plain label text, a link with no decoration and no hover
   had nothing marking it as clickable. */
.auth-link--muted:hover,
.auth-link--muted:focus-visible {
    color: #3c86b4;
    text-decoration: underline;
}


/* ==========================================================================
   Contact panel on the "who we are" page, lifted from inline styles.
   ========================================================================== */

.who-contact {
    margin-top: 1.5rem;
    padding: 2rem;
    border-radius: 1.25rem;
    background: linear-gradient(135deg, #eff6ff 0%, #f8fafc 100%);
}

.who-contact__title {
    margin: 0 0 0.75rem;
    font-size: 1.5rem;
    color: #111827;
}

.who-contact__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1.5rem;
    align-items: stretch;
}

.who-contact__map {
    min-height: 420px;
    overflow: hidden;
    border: 1px solid #dbeafe;
    border-radius: 1rem;
    background: #ffffff;
}

.who-contact__map iframe {
    width: 100%;
    height: 100%;
    min-height: 420px;
    border: 0;
}

.who-contact__form {
    padding: 1.5rem;
    border: 1px solid #dbeafe;
    border-radius: 1rem;
    background: #ffffff;
}

.who-contact__field {
    margin-bottom: 1rem;
}

/* Last field before the actions row, which needs a little more room. */
.who-contact__field--last {
    margin-bottom: 1.25rem;
}

.who-contact__label {
    display: block;
    margin-bottom: 0.4rem;
    font-weight: 600;
    color: #111827;
}

.who-contact__input {
    width: 100%;
    box-sizing: border-box;
    padding: 0.8rem 0.9rem;
    border: 1px solid #cbd5e1;
    border-radius: 0.75rem;
    font: inherit;
}

.who-contact__input--textarea {
    resize: vertical;
}

.who-contact__actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.who-contact__note {
    margin: 0;
    color: #6b7280;
    font-size: 0.95rem;
}

.who-contact__note a {
    color: #2563eb;
    text-decoration: none;
}

.who-contact__submit {
    padding: 0.85rem 1.2rem;
    border: 0;
    border-radius: 999px;
    background: #111827;
    color: #ffffff;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}


/* ==========================================================================
   Modals. `.is-open` is what the openModal()/closeModal() helpers in
   base.html toggle; they used to assign element.style.display directly,
   which put CSS back into the markup and overrode these rules.
   ========================================================================== */

.app-modal {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 100;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.profile-modal.is-open,
.app-modal.is-open {
    display: flex;
}

.app-modal__panel {
    background: #ffffff;
    padding: 1.5rem;
    border-radius: 1rem;
    max-width: 400px;
    width: 90%;
    text-align: center;
    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}

/* The research consent notice carries much more text than a confirmation. */
.app-modal__panel--wide {
    max-width: 720px;
    padding: 2rem;
    border-radius: 16px;
    text-align: left;
}

.app-modal__title {
    margin-bottom: 1rem;
}

.app-modal__message {
    color: #4b5563;
    margin-bottom: 1.5rem;
}

.app-modal__actions {
    display: flex;
    gap: 1rem;
    justify-content: center;
    /* Without this the forms stretch to the row's height and their buttons stop
       sharing a baseline. */
    align-items: center;
    flex-wrap: wrap;
}

/* Each action is wrapped in its own form, for the CSRF token. `display: contents`
   takes the wrapper out of the layout so the buttons are the flex items and the gap
   applies between them, rather than between two invisible form boxes. */
.app-modal__actions--inline form {
    display: contents;
}

/* .profile-button--primary carries a top margin for the forms it was written for.
   Inside a modal's action row it just pushed the primary button below its neighbour,
   which is what made the consent modal's two buttons sit at different heights. */
.app-modal__actions .profile-button {
    margin-top: 0;
}

/* The global `* { margin: 0 }` reset strips paragraph spacing, so the text in these
   dialogs ran together into one block with the heading. They are the only modals in the
   site carrying several paragraphs, and they are the ones asking for a decision about
   patient data, so they are worth being readable. */
.app-modal__panel--wide h2 {
    margin-bottom: 0.85rem;
}

.app-modal__panel--wide p + p {
    margin-top: 0.85rem;
}

/* The way out of the submit-time consent dialog. Deliberately not a third button in
   the action row: "go back and change my mind" is not a peer of the two answers, and
   three side-by-side buttons made it hard to see which one sends the job. A dialog that
   interrupts a submit needs a visible escape, so this is not left to Escape alone. */
.app-modal__dismiss {
    margin-top: 1.1rem;
    text-align: center;
}

.link-button {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: #4b5563;
    text-decoration: underline;
    cursor: pointer;
}

.link-button:hover {
    color: #111827;
}

.app-modal__button {
    padding: 0.6rem 1.2rem;
    border-radius: 0.5rem;
    font: inherit;
    cursor: pointer;
}

.app-modal__button--cancel {
    border: 1px solid #d1d5db;
    background: #ffffff;
}

/* The accent colour is per-modal data, so it arrives as a custom property on
   the element rather than as a full inline rule. */
.app-modal__button--confirm {
    border: none;
    background: var(--app-modal-accent, #3c86b4);
    color: #ffffff;
}


/* ==========================================================================
   Small utilities that replaced repeated inline styles.
   ========================================================================== */

/* Forms reduced to a single inline control, so they sit next to their siblings
   instead of breaking the line. */
.inline-form {
    display: inline;
}

/* Narrow variant of the header dropdown, for the short language menu. */
.dropdown-menu--narrow {
    min-width: 80px;
}

/* Job status badge. The colour is per-row data and arrives as a custom
   property; the tint is derived here rather than composed in the template.
   color-mix() needs Chrome 111+, Safari 16.2+ or Firefox 113+. */
.private-badge--status {
    color: var(--status-color, #111827);
    background: color-mix(in srgb, var(--status-color, #111827) 12.5%, transparent);
}

/* The dashboard eyebrow used to be an <h1> nested inside a <p>, which is
   invalid and gave the page two first-level headings. It is a paragraph now,
   styled to keep the same look. */
.page-intro--eyebrow {
    color: #3c86b4;
    font-size: 2rem;
    font-weight: 700;
    margin: 0;
}


/* ==========================================================================
   Site header. Moved verbatim from an inline <style> block in
   components/header.html.
   ========================================================================== */
.site-header {
    background: #ffffff;
    border-bottom: 1px solid #e5e7eb;
    position: sticky;
    top: 0;
    z-index: 20;
    overflow: visible;
}

.site-header__inner {
    width: min(100% - 2rem, 1680px);
    margin: 0 auto;
    padding: 1rem 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: nowrap;
    position: relative;
}

.site-header__brand {
    display: inline-flex;
    align-items: center;
    text-decoration: none;
    flex-shrink: 0;
}

.site-header__brand img {
    display: block;
    width: auto;
    height: 42px;
    max-width: min(240px, 60vw);
    object-fit: contain;
}

.site-header__nav {
    min-width: 0;
    margin-left: auto;
}

.site-header__nav-list {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.nav-link {
    color: #111827;
    text-decoration: none;
    font-weight: 500;
    padding: 0.4rem 0.6rem;
    border-radius: 0.5rem;
    transition: all 0.2s ease;
}

.nav-link:hover {
    background-color: #3c86b4;
    color: white;
}

.dropdown {
    position: relative;
}

.dropdown-menu {
    display: none;
    position: absolute;
    right: 0;
    top: 100%;
    min-width: 19rem;
    max-width: min(19rem, calc(100vw - 2rem));
    margin-top: 0.5rem;
    padding: 0.5rem 0;
    background: #ffffff;
    list-style: none;
    border: 1px solid #e5e7eb;
    border-radius: 0.75rem;
    box-shadow: 0 12px 30px rgba(17, 24, 39, 0.12);
}

.dropdown:hover .dropdown-menu {
    display: block;
}

.dropdown-menu a {
    display: block;
    padding: 0.5rem 1rem;
    color: #111827;
    text-decoration: none;
}

.dropdown-menu a:hover {
    background-color: #3c86b4;
    color: white;
}

/* Service links shown to anonymous visitors. They are muted to signal that the
   service is not available yet, but they are real links that lead to the login
   page, so they keep the pointer cursor and stay focusable — the old `.disabled`
   name and `cursor: not-allowed` claimed otherwise. */
.nav-link--needs-login {
    color: #9ca3af;
}

/* HAMBURGER */
.site-header__toggle {
    display: none;
    font-size: 1.5rem;
    line-height: 1;
    width: 44px;
    height: 44px;
    align-items: center;
    justify-content: center;
    color: #111827;
    background: #ffffff;
    border: 1px solid #d1d5db;
    border-radius: 0.8rem;
    box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08);
    cursor: pointer;
}

/* MOBILE */
/* The bar holds a 260px logo plus seven items; it stops fitting at about 1020px,
   so the compact menu has to start before then. main.js uses the same number —
   see NAV_BREAKPOINT there. */
@media (max-width: 1024px) {

    .site-header__inner {
        justify-content: space-between;
    }

    .site-header__toggle {
        display: block;
    }

    .site-header__nav {
        position: absolute;
        top: calc(100% + 0.75rem);
        left: 0;
        right: 0;
        width: auto;
        margin-left: 0;
        display: none;
        background: #ffffff;
        border: 1px solid #e5e7eb;
        border-radius: 1rem;
        box-shadow: 0 18px 40px rgba(15, 23, 42, 0.12);
        padding: 0.65rem;
    }

    .site-header__nav.active {
        display: block;
    }

    .site-header__nav-list {
        flex-direction: column;
        align-items: stretch;
        gap: 0.5rem;
        margin-top: 0;
        padding: 0;
    }

    .nav-link {
        display: block;
        text-align: left;
        padding: 0.6rem 0.8rem;
    }

    /* FIX DROPDOWN MOBILE */
    .dropdown-menu {
        display: none;
        position: static;
        transform: none;
        box-shadow: none;
        border: 1px solid #e5e7eb;
        border-radius: 0.85rem;
        padding: 0.35rem 0;
        margin-top: 0.3rem;
    }

    .dropdown.open .dropdown-menu {
        display: block;
    }
}

/* ==========================================================================
   Site footer. Moved verbatim from components/footer.html, which carried two
   <style> blocks. Both are kept, in their original order, so the cascade is
   exactly what the browser saw before: the second block overrides the first on
   .site-footer__intro, .site-footer__links li and .site-footer__links a:hover.
   Worth de-duplicating one day, but not while moving code.
   ========================================================================== */
.site-footer {
    position: relative;
    background: url("../img/img.f586947a7c2e.png") center/cover no-repeat;
    color: #f9fafb;
    padding: 2.5rem 0 2rem;
    margin-top: 3rem;
    overflow: hidden;
}

.site-footer-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        rgba(17, 24, 39, 0.65),
        rgba(17, 24, 39, 0.8)
    );
    z-index: 0;
}

.site-footer__inner {
    position: relative;
    z-index: 1;
    /* min() like .site-header__inner and .page-shell, not a fixed 15rem
       subtraction: that left a 613px container inside an 853px viewport, narrower
       than the 768px the brand-row grid needs, so the UPV logo overflowed. */
    width: min(100% - 2rem, 1680px);
    margin: 0 auto;
}

.site-footer__top {
    display: grid;
    grid-template-columns: 1.4fr 0.8fr;
    gap: 2rem;
    align-items: start;
}

.site-footer__brand-row {
    display: grid;
    /* auto-fit so the three columns collapse on their own when the container is
       too narrow. The old fixed minima added up to 768px, which a viewport-based
       media query could not rescue: the query sees the window, not the container. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
    gap: 1.5rem;
    align-items: center;
}

.site-footer__logo-link {
    display: flex;
    align-items: center;
    justify-content: center;
}

.site-footer__logo-link img {
    width: 100%;
    max-width: 220px;
    height: auto;
    display: block;
}

.site-footer__intro h2 {
    margin: 0 0 0.75rem;
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: 0.05em;
}

.site-footer__intro {
    width: 100%;
    padding-inline: 1rem;
}

.site-footer__intro p {
    margin: 0;
    max-width: none;
    line-height: 1.7;
    color: #e5e7eb;
    font-size: 0.98rem;
    text-align: justify;
    text-justify: inter-word;
}

.site-footer__heading {
    margin: 0 0 0.6rem;
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #9ca3af;
}

.site-footer__links {
    list-style: none;
    padding: 0;
    margin: 0;
    line-height: 1.9;
}

.site-footer__links li {
    margin-bottom: 0.2rem;
}

.site-footer__links a {
    color: #93c5fd;
    text-decoration: none;
    font-size: 0.95rem;
    transition: opacity 0.2s ease;
}

.site-footer__links a:hover {
    opacity: 0.7;
}

.site-footer__divider {
    border: 0;
    border-top: 1px solid rgba(255,255,255,0.12);
    margin: 1.8rem 0 1.2rem;
}

.site-footer__copyright {
    margin: 0;
    font-size: 0.9rem;
    color: #d1d5db;
}

/* Responsive */
@media (max-width: 768px) {
    .site-footer__top {
        grid-template-columns: 1fr;
        gap: 1.2rem;
    }

    .site-footer__brand-row {
        grid-template-columns: 1fr;
        gap: 1.5rem;
        text-align: center;
    }

    .site-footer__inner {
        width: min(100% - 2rem, 100%);
    }
}
.site-footer {
    position: relative;
    background: url("../img/img.f586947a7c2e.png") center/cover no-repeat;
    color: #f9fafb;
    padding: 2.5rem 0 2rem;
    margin-top: 3rem;
    overflow: hidden;
}

.site-footer-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        rgba(17, 24, 39, 0.65),
        rgba(17, 24, 39, 0.8)
    );
    z-index: 0;
}

.site-footer__inner {
    position: relative;
    z-index: 1;
    /* min() like .site-header__inner and .page-shell, not a fixed 15rem
       subtraction: that left a 613px container inside an 853px viewport, narrower
       than the 768px the brand-row grid needs, so the UPV logo overflowed. */
    width: min(100% - 2rem, 1680px);
    margin: 0 auto;
}

.site-footer__top {
    display: grid;
    grid-template-columns: 1.4fr 0.8fr;
    gap: 2rem;
    align-items: start;
}

.site-footer__brand-row {
    display: grid;
    /* auto-fit so the three columns collapse on their own when the container is
       too narrow. The old fixed minima added up to 768px, which a viewport-based
       media query could not rescue: the query sees the window, not the container. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
    gap: 1.5rem;
    align-items: center;
}

.site-footer__logo-link {
    display: flex;
    align-items: center;
    justify-content: center;
}

.site-footer__logo-link img {
    width: 100%;
    max-width: 220px;
    height: auto;
    display: block;
}

.site-footer__intro h2 {
    margin: 0 0 0.75rem;
    font-size: 1.35rem;
    font-weight: 700;
    letter-spacing: 0.05em;
}

.site-footer__intro {
    width: 100%;
}

.site-footer__intro p {
    margin: 0 0 1rem;
    max-width: none;
    line-height: 1.7;
    color: #e5e7eb;
    font-size: 0.98rem;
    text-align: justify;
    text-justify: inter-word;
}

.site-footer__intro p2 {
    margin: 0;
    max-width: none;
    line-height: 1.7;
    color: rgba(229, 231, 235, 0.75);
    font-size: 0.78rem;
    text-align: justify;
    text-justify: inter-word;
}

.site-footer__col {
    padding-top: 0.2rem;
}

.site-footer__heading {
    margin: 0 0 0.6rem;
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #9ca3af;
}

.site-footer__links {
    list-style: none;
    padding: 0;
    margin: 0;
    line-height: 1.9;
}

.site-footer__links li {
    margin-bottom: 0.35rem;
}

.site-footer__links a {
    color: #93c5fd;
    text-decoration: none;
    font-size: 0.95rem;
    transition: opacity 0.2s ease;
}

.site-footer__links a:hover {
    opacity: 0.75;
}

.site-footer__divider {
    border: 0;
    border-top: 1px solid rgba(255,255,255,0.12);
    margin: 1.8rem 0 1.2rem;
}

.site-footer__copyright {
    margin: 0;
    font-size: 0.9rem;
    color: #d1d5db;
}

/* Responsive */
@media (max-width: 768px) {
    .site-footer__top {
        grid-template-columns: 1fr;
        gap: 1.2rem;
    }

    .site-footer__brand-row {
        grid-template-columns: 1fr;
        gap: 1.5rem;
        text-align: center;
    }

    .site-footer__inner {
        width: min(100% - 2rem, 100%);
    }

    .site-footer__col {
        padding-top: 0;
    }
}


/* ==========================================================================
   Skip link. Hidden until it takes focus, so keyboard users can jump past the
   eight navigation links instead of tabbing through them on every page.
   ========================================================================== */

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
    padding: 0.75rem 1.25rem;
    background: #3c86b4;
    color: #ffffff;
    border-radius: 0 0 0.5rem 0;
    text-decoration: none;
    font-weight: 600;
}

.skip-link:focus {
    left: 0;
}


/* ==========================================================================
   Flash messages, rendered by base.html. The palette follows the status pill
   already used on the dashboard.
   ========================================================================== */

.messages {
    width: min(100% - 2rem, 1680px);
    margin: 1.5rem auto 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: 0.6rem;
}

.messages__item {
    padding: 0.9rem 1.2rem;
    border: 1px solid transparent;
    border-radius: 0.6rem;
    font-weight: 600;
}

.messages__item--success {
    background: #dcfce7;
    border-color: #86efac;
    color: #166534;
}

.messages__item--error {
    background: #fee2e2;
    border-color: #fca5a5;
    color: #991b1b;
}

.messages__item--warning {
    background: #fef3c7;
    border-color: #fcd34d;
    color: #92400e;
}

/* Django also emits `info` and `debug` tags; neither is used by this project
   today, but styling them avoids an unstyled box if one ever appears. */
.messages__item--info,
.messages__item--debug {
    background: #e0f2fe;
    border-color: #7dd3fc;
    color: #075985;
}


/* ==========================================================================
   Log out in the navbar. It has to be a form, because the logout view only
   accepts POST, so the button is stripped back to look like its sibling links.
   ========================================================================== */

/* Flex rather than inline: an inline form around an inline-block button builds a
   line box, whose descender space made this item taller than its sibling links in
   the flex nav. */
.site-header__logout {
    display: flex;
    margin: 0;
}

/* A <button> reset that keeps .nav-link's own box: `padding: 0` here used to
   override the 0.4rem 0.6rem from .nav-link, so the hover background hugged the
   text while every sibling link had room around it. */
.nav-link--button {
    background: none;
    border: 0;
    /* `font: inherit` is what stops a button using the browser's own UI face. It is a
       shorthand, though, so it also resets every sub-property — including the
       `font-weight: 500` this element inherits from .nav-link, which is why Log out
       came out thinner than the links beside it. Restore it after the shorthand. */
    font: inherit;
    font-weight: 500;
    color: inherit;
    cursor: pointer;
    /* A button's default line-height is not `normal`, which made it sit a pixel
       or two off its neighbours. */
    line-height: inherit;
}


/* ==========================================================================
   Validation errors on the auth forms. The templates used to build their
   inputs by hand and never rendered form.errors, so every rejected login or
   signup redrew the page with no explanation.
   ========================================================================== */

.auth-error {
    margin: 0.4rem 0 0;
    color: #991b1b;
    font-size: 0.85rem;
}

/* Errors that belong to the form rather than to one field, such as the login's
   "Invalid username or password". */
.auth-error--summary {
    margin: 0 0 1.25rem;
    padding: 0.8rem 1rem;
    background: #fee2e2;
    border: 1px solid #fca5a5;
    border-radius: 0.6rem;
}

.auth-error--summary p {
    margin: 0;
    color: #991b1b;
    font-size: 0.9rem;
}

/* Field in error: a red border so the eye lands on it. */
.auth-field--invalid .auth-input,
.auth-field--invalid input:not([type="checkbox"]) {
    border-color: #fca5a5;
}


/* ==========================================================================
   Sortable table headers, search form and pager. These replaced the
   client-side versions in private.html and my_entries.html; see base/extra/tables/paginate.py.
   ========================================================================== */

.table-sort {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}

.table-sort:hover,
.table-sort:focus-visible {
    text-decoration: underline;
}

.table-sort--active {
    color: #3c86b4;
}

/* Reserve the arrow's width on every header so the columns do not shift when the
   active one changes. */
.table-sort__arrow {
    display: inline-block;
    min-width: 0.7em;
    font-size: 0.7em;
    line-height: 1;
}

.table-sort__help {
    margin-left: 0.25rem;
    color: #6b7280;
    text-decoration: none;
}





.private-pagination__control.is-disabled,
.private-pagination__page.is-active {
    cursor: default;
}

.private-pagination__control.is-disabled {
    opacity: 0.4;
}

/* The pager is links now rather than buttons, so they need the button look. */
a.private-pagination__control,
a.private-pagination__page {
    text-decoration: none;
}


/* ==========================================================================
   Column layout for the dashboard tables.

   These share `.directive-table` with the two-column table on the directive
   page, which sets `td:first-child { width: 42% }`. That gave the jobs table a
   330px column for a single "+" button and squeezed everything else, so each
   dashboard panel states its own widths. The workers panel already did this;
   jobs, users and entries follow the same pattern.
   ========================================================================== */

.private-workers-panel .directive-table,
.private-jobs-panel .directive-table,
.private-users-panel .directive-table,
.my-entries-panel .directive-table {
    width: 100%;
    table-layout: auto;
}

/* One density for every listing table, so a row is the same height wherever it
   appears. Denser than the prose table these styles came from: twelve columns do
   not fit at 17px with 1.1rem of padding. Applies to the workers panel too,
   which had kept the original padding and looked out of step with the rest. */
.private-workers-panel .directive-table th,
.private-workers-panel .directive-table td,
.private-jobs-panel .directive-table th,
.private-jobs-panel .directive-table td,
.private-users-panel .directive-table th,
.private-users-panel .directive-table td,
.my-entries-panel .directive-table th,
.my-entries-panel .directive-table td {
    padding: 0.55rem 0.65rem;
    font-size: 0.92rem;
    line-height: 1.45;
    height: 3rem;
    vertical-align: middle;
}

/* Timestamps read as one unit; wrapping them over three lines was what made the
   rows so tall. */
.private-cell--date {
    white-space: nowrap;
}

/* --- Jobs columns ---------------------------------------------------------

   By class, not by :nth-child(). The previous rules sized columns 11 and 12 as
   the two action buttons, which was right for the twelve-column table they were
   written for; inserting Info at position 11 shifted every action rule one
   column left, so Info inherited a 6rem centred button width and the Stop
   column lost its own. A class cannot drift like that when a column is added,
   and the same classes now serve both the admin and the user view, which no
   longer render the same number of columns.
   ------------------------------------------------------------------------- */

.private-jobs-panel .directive-table .private-cell--detail {
    width: 3rem;
    text-align: center;
}

.private-jobs-panel .directive-table .private-cell--id {
    width: 4.5rem;
}

.private-jobs-panel .directive-table .private-cell--session {
    width: 7rem;
    text-align: center;
}

.private-jobs-panel .directive-table .private-cell--status {
    width: 7.5rem;
    text-align: center;
}

.private-jobs-panel .directive-table .private-cell--action {
    width: 6rem;
    text-align: center;
}

/* Tighter than the 12rem default for truncated cells: these three were free to
   claim 36rem between them, which is most of what pushed Info past the right
   edge of the panel and out of sight behind the horizontal scroll. */
.private-jobs-panel .directive-table .private-cell--user,
.private-jobs-panel .directive-table .private-cell--worker {
    max-width: 8rem;
}

.private-jobs-panel .directive-table .private-cell--type {
    max-width: 9rem;
}

/* --- Login lockouts: Username | IP | Failures | Last attempt | Unlocks | Unblock --- */

.private-lockouts-panel .directive-table,
.private-lockouts-panel .directive-table th,
.private-lockouts-panel .directive-table td {
    /* Same density as the other listings; the shared rule above only names the four
       panels that existed when it was written. */
    padding: 0.55rem 0.65rem;
    font-size: 0.92rem;
    line-height: 1.45;
    vertical-align: middle;
}

.private-lockouts-panel .directive-table {
    width: 100%;
    table-layout: auto;
}


/* --- Users: ID | Wordpress ID | Username | Email | First | Last | Institution --- */

.private-users-panel .directive-table td:first-child,
.private-users-panel .directive-table th:first-child {
    width: 4.5rem;
}

.private-users-panel .directive-table td:nth-child(2),
.private-users-panel .directive-table th:nth-child(2) {
    width: 7rem;
    text-align: center;
}

/* --- My entries: ID | Type | Status | 3 dates | Consent | 3 downloads --- */

.my-entries-panel .directive-table td:first-child,
.my-entries-panel .directive-table th:first-child {
    width: 4.5rem;
}

.my-entries-panel .directive-table td:nth-child(3),
.my-entries-panel .directive-table th:nth-child(3) {
    width: 7.5rem;
    text-align: center;
}

/* The session id is shortened to "ad19 ... acfd" and copies on click, so it must
   not wrap either. */
.private-session {
    white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Sortable headers inside these tables. `.directive-table a` sets a colour and
   wins on specificity over `.table-sort`, which is why the headings were all
   blue whether sorted or not.
   -------------------------------------------------------------------------- */

.directive-table th .table-sort {
    color: #111827;
}

.directive-table th .table-sort--active {
    color: #3c86b4;
}


/* ==========================================================================
   Table chrome: clickable headers, search, rows-per-page and the pager.
   Replaces the first pass at these styles further up the file.
   ========================================================================== */

/* Standard visually-hidden: reachable by a screen reader, off-screen for
   everyone else, and safe inside a flex container. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* --- Sortable headers: the whole cell is the control ---------------------- */

/* The cell gives up its padding so the link can cover the full area. */
.table-sort-cell {
    padding: 0 !important;
}

.directive-table th .table-sort {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.35rem;
    padding: 0.55rem 0.65rem;
    color: #111827;
    text-decoration: none;
    white-space: nowrap;
}

.directive-table th .table-sort:hover,
.directive-table th .table-sort:focus-visible {
    background: #dbeafe;
    text-decoration: none;
}

.directive-table th .table-sort--active {
    color: #3c86b4;
}

.table-sort__arrow {
    display: inline-block;
    min-width: 0.7em;
    font-size: 0.7em;
    line-height: 1;
}

.table-sort__help {
    position: absolute;
    right: 0.35rem;
    top: 0.35rem;
    color: #6b7280;
    text-decoration: none;
}

/* --- Search --------------------------------------------------------------- */

/* --- Footer: rows-per-page, the count, and the pager --------------------- */

.table-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-top: 1rem;
}






.table-footer__count {
    margin: 0;
    color: #6b7280;
    font-size: 0.85rem;
}

.private-pagination {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin: 0;
}

/* First and last sit slightly apart from the step-by-one controls, so the two
   kinds of jump read as different. */
.private-pagination__control--edge {
    font-size: 0.85rem;
}

.private-pagination__control--first {
    margin-right: 0.2rem;
}

.private-pagination__control--last {
    margin-left: 0.2rem;
}

.private-pagination__control,
.private-pagination__page {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.25rem;
    height: 2.25rem;
    padding: 0 0.5rem;
    border: 1px solid #e5e7eb;
    border-radius: 0.5rem;
    background: #ffffff;
    color: #374151;
    font-size: 0.95rem;
    font-weight: 600;
    /* The old pager was <button>s; these are links, which arrive underlined. */
    text-decoration: none;
    transition: border-color 0.12s ease, color 0.12s ease;
}

a.private-pagination__control:hover,
a.private-pagination__page:hover,
a.private-pagination__control:focus-visible,
a.private-pagination__page:focus-visible {
    border-color: #3c86b4;
    color: #3c86b4;
    text-decoration: none;
}

.private-pagination__page.is-active {
    border-color: #3c86b4;
    background: #3c86b4;
    color: #ffffff;
    cursor: default;
}

.private-pagination__control.is-disabled {
    color: #d1d5db;
    cursor: default;
}

/* Cells whose content can outgrow the column: cut with an ellipsis and let the
   native tooltip from the `title` attribute show the rest on hover. */
.private-cell--ellipsis {
    max-width: 12rem;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}


/* ==========================================================================
   Blocking overlay, raised from JavaScript while a request the user has to wait
   for is in flight — currently the download check, which stats the file on the
   SFTP server and is not instant. Nothing behind it can be clicked, tabbed to
   or scrolled, so the same download cannot be started twice and the modal
   cannot be closed out from under the request.
   ========================================================================== */

.onco-blocker[hidden] {
    display: none;
}

.onco-blocker {
    position: fixed;
    inset: 0;
    /* Above the job modal (120) and below the toasts (200), which report how the
       request ended and have to stay readable while the overlay lifts. */
    z-index: 160;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    /* Tight, because this gap falls between the two text lines as well as under the
       spinner; the message takes its own margin instead. */
    gap: 0.4rem;
    background: rgba(15, 23, 42, 0.55);
    /* Receiving the clicks is the whole point, so say so with the cursor. */
    cursor: progress;
}

/* Focused from script so screen readers land on the message. It is a container,
   not a control, and a focus ring around the whole viewport reads as a glitch. */
.onco-blocker:focus {
    outline: none;
}

body.onco-blocked {
    overflow: hidden;
}

.onco-blocker__spinner {
    width: 2.75rem;
    height: 2.75rem;
    border: 3px solid rgba(255, 255, 255, 0.35);
    border-top-color: #ffffff;
    border-radius: 50%;
    animation: table-spin 0.7s linear infinite;
}

.onco-blocker__message {
    margin: 0.6rem 0 0;
    max-width: min(34rem, 82vw);
    color: #ffffff;
    font-size: 1.05rem;
    font-weight: 600;
    line-height: 1.4;
    text-align: center;
    /* A filename, so it must not be broken across lines at an arbitrary point, but it
       can be long enough to need wrapping somewhere. */
    overflow-wrap: anywhere;
}

/* What happens next, and how long it takes. Dimmed and smaller, so the line above is
   read first — the two used to be one string at one size. */
.onco-blocker__detail[hidden] {
    display: none;
}

.onco-blocker__detail {
    margin: 0;
    max-width: min(30rem, 78vw);
    color: rgba(255, 255, 255, 0.78);
    font-size: 0.85rem;
    line-height: 1.5;
    text-align: center;
}

@media (prefers-reduced-motion: reduce) {
    /* Slowed rather than stopped: it is the only sign the page is still working,
       and a frozen spinner over a frozen page looks like a crash. */
    .onco-blocker__spinner {
        animation-duration: 2.4s;
    }
}


/* ==========================================================================
   Toasts, raised from JavaScript by showToast() in main.js — currently for
   confirming that a session id was copied.
   ========================================================================== */

.toast-container {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 200;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    /* The container spans the corner but must not swallow clicks on the page
       behind it; the toasts themselves are not interactive either. */
    pointer-events: none;
}

.toast {
    padding: 0.75rem 1.1rem;
    border: 1px solid transparent;
    border-radius: 0.6rem;
    font-size: 0.95rem;
    font-weight: 600;
    box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18);
    opacity: 0;
    transform: translateY(0.5rem);
    transition: opacity 0.18s ease, transform 0.18s ease;
}

.toast.is-visible {
    opacity: 1;
    transform: translateY(0);
}

.toast--success {
    background: #dcfce7;
    border-color: #86efac;
    color: #166534;
}

.toast--error {
    background: #fee2e2;
    border-color: #fca5a5;
    color: #991b1b;
}

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


/* ==========================================================================
   Table toolbar: rows-per-page on the left, live search on the right.
   ========================================================================== */

.private-toolbar {
    /* The toolbar spans the header row now, rather than hugging the right. */
    justify-content: space-between;
    width: 100%;
}

.table-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    width: 100%;
}

.table-toolbar__size {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: #6b7280;
    font-size: 0.88rem;
}

.table-toolbar__select {
    padding: 0.4rem 0.5rem;
    border: 1px solid #d1d5db;
    border-radius: 0.5rem;
    background: #ffffff;
    color: #111827;
    font: inherit;
    font-size: 0.9rem;
    cursor: pointer;
}

.table-toolbar__select:focus-visible {
    border-color: #3c86b4;
    outline: 2px solid rgba(60, 134, 180, 0.35);
    outline-offset: 1px;
}

/* --- The search field ----------------------------------------------------- */

.table-search__field {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0 0.7rem;
    border: 1px solid #d1d5db;
    border-radius: 0.7rem;
    background: #ffffff;
    box-shadow: 0 8px 20px rgba(15, 23, 42, 0.05);
}

.table-search__field:focus-within {
    border-color: #3c86b4;
    box-shadow: 0 0 0 3px rgba(60, 134, 180, 0.18);
}

.table-search__icon {
    font-size: 0.9rem;
    line-height: 1;
    opacity: 0.55;
}

.table-search__input {
    width: min(100%, 260px);
    padding: 0.6rem 0;
    border: 0;
    background: transparent;
    color: #111827;
    font: inherit;
    font-size: 0.95rem;
}

.table-search__input:focus {
    outline: none;
}

.table-search__input::placeholder {
    color: #9ca3af;
}

/* ==========================================================================
   Table filters. A <details> per column, inside the toolbar's own form so the
   checkboxes travel with the search term and the page size.
   ========================================================================== */

.table-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.table-filter {
    position: relative;
}

.table-filter__summary {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 0.7rem;
    border: 1px solid #d1d5db;
    border-radius: 0.55rem;
    background: #ffffff;
    font-size: 0.85rem;
    font-weight: 500;
    color: #374151;
    cursor: pointer;
    user-select: none;
    /* The default triangle sits oddly next to a badge, and Safari draws its own
       marker as well unless both are turned off. */
    list-style: none;
}

.table-filter__summary::-webkit-details-marker {
    display: none;
}

.table-filter__summary::after {
    content: "";
    width: 0.4rem;
    height: 0.4rem;
    border-right: 1.5px solid #6b7280;
    border-bottom: 1.5px solid #6b7280;
    transform: translateY(-2px) rotate(45deg);
}

.table-filter[open] > .table-filter__summary {
    border-color: #3c86b4;
    color: #1f2937;
}

.table-filter[open] > .table-filter__summary::after {
    transform: translateY(1px) rotate(-135deg);
}

.table-filter__badge[hidden],
.table-filter__clear[hidden] {
    display: none;
}

.table-filter__badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.15rem;
    height: 1.15rem;
    padding: 0 0.3rem;
    border-radius: 999px;
    background: #3c86b4;
    color: #ffffff;
    font-size: 0.7rem;
    font-weight: 700;
}

/* Fixed, and placed by main.js when it opens.

   Out of the flow so opening one does not push the search box and the table down —
   but absolute was not enough: `.directive-panel` sets `overflow: hidden` to keep the
   table inside its rounded corners, which clipped the dropdown at the panel's edge.
   With few rows, or none, most of the list was cut off. Fixed positioning is measured
   against the viewport and no ancestor can clip it. */
.table-filter__panel {
    position: fixed;
    z-index: 60;
    width: max-content;
    min-width: 12rem;
    max-width: min(20rem, calc(100vw - 2rem));
    padding: 0.6rem;
    border: 1px solid #e5e7eb;
    border-radius: 0.7rem;
    background: #ffffff;
    box-shadow: 0 12px 28px rgba(15, 23, 42, 0.12);
}

.table-filter__search {
    width: 100%;
    margin-bottom: 0.5rem;
    padding: 0.4rem 0.55rem;
    border: 1px solid #d1d5db;
    border-radius: 0.45rem;
    font: inherit;
    font-size: 0.85rem;
}

.table-filter__actions {
    display: flex;
    gap: 0.25rem;
    margin-bottom: 0.4rem;
    padding-bottom: 0.4rem;
    border-bottom: 1px solid #f1f5f9;
}

.table-filter__action {
    padding: 0.2rem 0.45rem;
    border: 0;
    border-radius: 0.35rem;
    background: transparent;
    color: #3c86b4;
    font: inherit;
    font-size: 0.78rem;
    font-weight: 500;
    cursor: pointer;
}

.table-filter__action:hover {
    background: #eff6fb;
}

/* Capped, because the user list runs to hundreds. The ceiling is lowered further by
   main.js when there is not enough room below the button. */
.table-filter__options {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    max-height: 15rem;
    overflow-y: auto;
}

.table-filter__option {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.28rem 0.35rem;
    border-radius: 0.35rem;
    font-size: 0.85rem;
    color: #374151;
    cursor: pointer;
}

.table-filter__option:hover {
    background: #f3f4f6;
}

.table-filter__option[hidden] {
    display: none;
}

.table-filter__empty {
    padding: 0.4rem 0.35rem;
    color: #9ca3af;
    font-size: 0.8rem;
}

.table-filter__clear {
    display: inline-block;
    text-decoration: none;
    padding: 0.45rem 0.7rem;
    border: 1px solid transparent;
    border-radius: 0.55rem;
    background: transparent;
    color: #3c86b4;
    font: inherit;
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
}

.table-filter__clear:hover {
    background: #eff6fb;
}

/* Search results arrive over the network, so the field says when a request is in
   flight. The width is reserved either way to stop the field jumping. */
.table-search__spinner {
    width: 0.9rem;
    height: 0.9rem;
    border: 2px solid #dbeafe;
    border-top-color: #3c86b4;
    border-radius: 50%;
    opacity: 0;
}

.table-toolbar.is-busy .table-search__spinner {
    opacity: 1;
    animation: table-spin 0.7s linear infinite;
}

@keyframes table-spin {
    to { transform: rotate(360deg); }
}

/* While a fetch is in flight the rows are dimmed rather than removed, so the
   table does not collapse and push the page around. */
.directive-table-wrap.is-busy {
    opacity: 0.55;
    transition: opacity 0.12s ease;
}

@media (prefers-reduced-motion: reduce) {
    .table-toolbar.is-busy .table-search__spinner {
        animation: none;
    }

    .directive-table-wrap.is-busy {
        transition: none;
    }
}

/* The progress message can be a whole traceback, so it gets more room than the
   other truncated cells and reads as secondary text.

   min-width is the important half: with `table-layout: auto` a cell that is
   allowed to ellipsise has no minimum of its own, so the browser shrinks it
   first whenever the row is over-constrained. On the admin view, with User and
   Worker also competing, that collapsed Info to nothing and left it readable
   only through the row's "+" detail modal. */
/* Just a button now, in My entries: the width the text column needed is gone with it. */
.private-cell--info-action {
    width: 1px;
    white-space: nowrap;
    text-align: center;
}

.private-cell--info {
    min-width: 11rem;
    max-width: 22rem;
    color: #6b7280;
    font-size: 0.88rem;
    text-align: left;
}

/* The three download cells in the job detail modal, built by main.js. A link when the
   file is offered, plain grey text when it is not — the same shape either way, so the
   row does not jump as different jobs are opened.

   Scoped to the modal, and that is not decoration. The modal's table carries
   `directive-table`, and `.directive-table a` further up this file sets
   `color: #3c86b4` — the very blue used as the background here. On specificity that
   rule ([0,1,1]) beats a bare `.private-download` ([0,1,0]), so the label came out
   blue on blue and the buttons looked like empty boxes. Two classes ([0,2,0]) win the
   class column outright, whatever the source order. */
.private-job-modal .private-download {
    display: inline-block;
    padding: 0.35rem 0.7rem;
    border-radius: 0.5rem;
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
    text-decoration: none;
    background: #3c86b4;
    color: #ffffff;
}

/* The underline also has to be beaten back: `.directive-table a:hover` adds one. */
.private-job-modal .private-download:hover,
.private-job-modal .private-download:focus-visible {
    background: #336f96;
    color: #ffffff;
    text-decoration: none;
}

.private-job-modal .private-download--none {
    background: #e5e7eb;
    color: #6b7280;
    font-weight: 500;
    cursor: default;
}

/* The untruncated progress message in the job detail modal. `pre` because a
   traceback's line breaks and indentation are the whole point. */
.private-job-modal__info {
    margin: 0 0 1rem;
    padding: 0.75rem 0.9rem;
    max-height: 12rem;
    overflow: auto;
    background: #f8fafc;
    border: 1px solid #e5e7eb;
    border-radius: 0.5rem;
    color: #374151;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.82rem;
    white-space: pre-wrap;
    word-break: break-word;
}

/* ==========================================================================
   Refresh controls in a live table's footer.
   ========================================================================== */

.table-refresh {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

.table-refresh__button {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.35rem 0.7rem;
    border: 1px solid #e5e7eb;
    border-radius: 0.5rem;
    background: #ffffff;
    color: #374151;
    font: inherit;
    font-size: 0.85rem;
    cursor: pointer;
}

.table-refresh__button:hover,
.table-refresh__button:focus-visible {
    border-color: #3c86b4;
    color: #3c86b4;
}

.table-refresh__stamp {
    color: #9ca3af;
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
}

/* Badges used by the Ping column: amber for a worker that replies but whose
   clock disagrees with ours, muted while the probe is still in flight. */
.private-badge--amber {
    background: #fef3c7;
    color: #92400e;
}

.private-badge--muted {
    background: #f1f5f9;
    color: #94a3b8;
}

.private-cell--ping {
    white-space: nowrap;
}

.private-cell--ping-idle {
    color: #cbd5e1;
}
